# circuit_noise.py
#
# This file is part of scqubits: a Python package for superconducting qubits,
# Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/
#
# Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
############################################################################
from abc import ABC
import operator as builtin_op
import functools
from numpy import ndarray
import numpy as np
import qutip as qt
import scipy as sp
import re
import copy
from scqubits.core.noise import NOISE_PARAMS, NoisySystem, calc_therm_ratio
from scqubits.core.circuit_utils import get_trailing_number, keep_terms_for_subsystem
from scqubits.utils.misc import is_string_float, Qobj_to_scipy_csc_matrix
import scqubits.core.units as units
from types import MethodType
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
import sympy as sm
from scqubits.core.symbolic_circuit import Branch
[docs]
class NoisyCircuit(NoisySystem, ABC):
"""
Noisy Quantum Circuit Class
---------------------------
This class represents a noisy quantum circuit, extending the `NoisySystem` class. It provides a suite of methods for generating and evaluating expressions related to the Hamiltonian and its derivatives, as well as calculating the 1/f dephasing time or rate due to various noise sources.
Attributes:
-----------
- noise_helper_methods (dict): A dictionary that stores the noise helper methods generated by `generate_methods_d_hamiltonian_d()`.
Methods:
--------
- Q_from_branch(branch): A static method that retrieves the Q factor function or value associated with a given branch.
- generate_methods_d_hamiltonian_d(): Generates methods for calculating the derivative of the Hamiltonian with respect to offset charges, external fluxes, and junction energies.
- _transform_expr_to_new_variables(expr_node_vars, substitute_symbol=None): Transforms the given expression node variables to new variables using the transformation matrix.
- junction_related_evaluation(branch_junction, calc="dhdEJ"): Evaluates the expression related to a junction branch and returns the result.
- generate_tphi_1_over_f_methods(): Generates methods for calculating the 1/f dephasing time or rate due to different noise sources.
Note:
-----
This class is designed to be subclassed and extended with specific noise models for different types of quantum circuits.
"""
[docs]
@staticmethod
def Q_from_branch(branch):
"""
Extracts the Quality Factor 'Q' from a Given Branch
---------------------------------------------------
This method performs the following steps:
1. Determines the type of the branch ('L' for inductor or 'C' for capacitor) and constructs a key for the quality factor ('Q_ind' for inductor or 'Q_cap' for capacitor).
2. Checks if the key is in the auxiliary parameters of the branch. If not, the method returns None.
3. If the key is in the auxiliary parameters, it retrieves the value of the quality factor, which is a string.
4. Checks if the string can be evaluated as a float using the 'is_string_float' helper function. If it can, it converts the string to a float and returns it.
5. If the string cannot be evaluated as a float, it assumes that the string represents a function of frequency 'omega' and temperature 'T'. It defines a new function 'Q_func' that takes 'omega' and 'T' as parameters and evaluates the string using Python's 'eval' function. It returns 'Q_func'.
Parameters:
----------
branch (Branch): The branch from which to extract the quality factor. The branch should be an instance of the 'Branch' class. It should have a 'type' attribute that is either 'L' or 'C', and an 'aux_params' dictionary that contains a key 'Q_ind' or 'Q_cap' with the quality factor as the value.
Returns:
--------
float or function: The quality factor as a float, or a function that calculates the quality factor given frequency and temperature. If the branch does not have a quality factor, None is returned.
Raises:
-------
AttributeError: If the branch does not have a 'type' attribute or an 'aux_params' dictionary.
ValueError: If the branch's type is not 'L' or 'C', or if the quality factor string cannot be evaluated as a float or a function of 'omega' and 'T'.
"""
key = "Q_" + ("ind" if branch.type == "L" else "cap")
if key in branch.aux_params.keys():
Q_str = branch.aux_params[key]
if not is_string_float(Q_str):
def Q_func(omega, T):
return eval(Q_str)
return Q_func
else:
return float(Q_str)
return None
[docs]
def generate_methods_d_hamiltonian_d(self):
"""
Derivative of the Hamiltonian Generator
---------------------------------------
This method generates methods that return the derivative of the Hamiltonian with respect to offset charges, external fluxes, and junction energies.
Steps:
------
1. Initializes three dictionaries to store the generated methods: `ext_flux_1_over_f_methods` for methods related to external fluxes, `ng_1_over_f_methods` for methods related to offset charges, and `cc_1_over_f_methods` for methods related to junction energies.
2. Iterates over the external fluxes (`self.external_fluxes`) and offset charges (`self.offset_charges`) of the current instance. For each parameter, it defines a new method `param_derivative` that calculates the derivative of the Hamiltonian with respect to that parameter. This is done by first fetching the symbolic Hamiltonian of the parent circuit, differentiating it with respect to the parameter using sympy's `diff` function, substituting all symbolic parameters with their actual values, and finally evaluating the expression. The new method is then stored in the corresponding dictionary with a key that is based on the parameter's name.
3. Iterates over the branches (`self.branches`) of the current instance. For each branch that is a junction (i.e., its type contains 'JJ'), it defines a new method `param_derivative` that calculates the derivative of the Hamiltonian with respect to the junction energy. This is done by calling the `junction_related_evaluation` method with the branch and the calculation type 'dhdEJ'. The new method is then stored in the corresponding dictionary with a key that is based on the branch's ID.
4. Combines the dictionaries of methods into a single dictionary (`noise_helper_methods`) and stores this dictionary in the 'noise_helper_methods' attribute of the current instance.
5. Adds each method to the current instance as an attribute with the same name as the key in the dictionary using `setattr`.
The generated methods use the 'return_parent_circuit' method to get a parent circuit with a symbolic Hamiltonian and symbolic parameters, the 'fetch_symbolic_hamiltonian' method to get the symbolic Hamiltonian, the '_evaluate_symbolic_expr' method to evaluate a symbolic expression, and the 'junction_related_evaluation' method to calculate a quantity related to a junction.
Raises:
-------
AttributeError: If the current instance does not have the required methods or attributes.
Note:
-----
This method modifies the current instance by adding new attributes and updating the 'noise_helper_methods' attribute.
"""
ext_flux_1_over_f_methods = {}
ng_1_over_f_methods = {}
cc_1_over_f_methods = {}
for param_sym in self.external_fluxes + self.offset_charges:
def param_derivative(self, param_sym=param_sym):
parent_instance = self.return_parent_circuit()
# hamiltonian = parent_instance.fetch_symbolic_hamiltonian()
hamiltonian = parent_instance._hamiltonian_sym_for_numerics
hamiltonian = hamiltonian.subs("I", 1)
all_sym_parameters = (
list(parent_instance.symbolic_params.keys())
+ parent_instance.external_fluxes
+ parent_instance.offset_charges
)
diff_sym_expr = hamiltonian.diff(param_sym)
diff_sym_expr = diff_sym_expr.expand()
# substitute all symbolic params
for param in all_sym_parameters:
diff_sym_expr = diff_sym_expr.subs(
param, getattr(parent_instance, param.name)
)
# evaluate the expression
return parent_instance._evaluate_symbolic_expr(diff_sym_expr)
if param_sym in self.external_fluxes:
ext_flux_1_over_f_methods[
f"d_hamiltonian_d_flux{get_trailing_number(param_sym.name)}"
] = param_derivative
elif param_sym in self.offset_charges:
ng_1_over_f_methods[
f"d_hamiltonian_d_ng{get_trailing_number(param_sym.name)}"
] = param_derivative
## cc noise methods
junction_branches = [branch for branch in self.branches if "JJ" in branch.type]
for idx, branch in enumerate(junction_branches):
def param_derivative(self, branch=branch):
return -self.junction_related_evaluation(branch, calc="dhdEJ")
cc_1_over_f_methods[f"d_hamiltonian_d_EJ{branch.index}"] = param_derivative
noise_helper_methods = {
**ext_flux_1_over_f_methods,
**ng_1_over_f_methods,
**cc_1_over_f_methods,
}
self.noise_helper_methods = noise_helper_methods
for method_name in noise_helper_methods:
setattr(
self, method_name, MethodType(noise_helper_methods[method_name], self)
)
def _transform_expr_to_new_variables(
self, expr_node_vars: sm.Expr, substitute_symbol: Optional[str] = None
):
"""
Transformation of Symbolic Expressions
--------------------------------------
This method transforms a symbolic expression that represents a physical quantity in the circuit from the old variables to the new variables.
Steps:
------
1. Retrieves the transformation matrix from the current instance.
2. Expands the input expression, which is a symbolic expression that represents a physical quantity in the circuit (e.g., the Hamiltonian, a current, a voltage, etc.) in terms of the old variables.
3. Determines the number of variables based on the number of nodes in the symbolic circuit of the current instance. If the circuit is grounded, one is subtracted from the number of nodes.
4. Generates a list of new variables and a list of old variables. The new variables are named 'θ' followed by an index, and the old variables are named 'φ' followed by an index.
5. Calculates the transformed expression by multiplying the transformation matrix with the new variables.
6. Substitutes each old variable in the input expression with the corresponding term from the transformed expression.
7. If a substitute symbol is provided, substitutes each free symbol in the input expression with a new symbol that has the substitute symbol followed by the trailing number of the old symbol.
Parameters:
----------
expr_node_vars (sm.Expr): The symbolic expression to transform. This should be a sympy expression that represents a physical quantity in the circuit (e.g., the Hamiltonian, a current, a voltage, etc.) in terms of the old variables.
substitute_symbol (str, optional): The symbol to use for the new variables. If this is provided, each free symbol in the input expression is replaced with a new symbol that has this substitute symbol followed by the trailing number of the old symbol. Defaults to None.
Returns:
-------
sm.Expr: The transformed expression. This is a sympy expression that represents the same physical quantity as the input expression, but in terms of the new variables.
Raises:
------
AttributeError: If the current instance does not have the required methods or attributes.
"""
transformation_mat = self.transformation_matrix
expr_node_vars = expr_node_vars.expand()
num_vars = len(self.symbolic_circuit.nodes) - (1 if self.is_grounded else 0)
new_vars = [sm.symbols(f"θ{index}") for index in range(1, 1 + num_vars)]
old_vars = [sm.symbols(f"φ{index}") for index in range(1, 1 + num_vars)]
transformed_expr = transformation_mat.dot(new_vars)
for idx, var in enumerate(old_vars):
expr_node_vars = expr_node_vars.subs(var, transformed_expr[idx])
if substitute_symbol:
for var in expr_node_vars.free_symbols:
expr_node_vars = expr_node_vars.subs(
var,
sm.symbols(f"{substitute_symbol}{get_trailing_number(var.name)}"),
)
return expr_node_vars
[docs]
def generate_tphi_1_over_f_methods(self):
"""
Generate Methods for 1/f Dephasing Time (or Rate) Calculations
--------------------------------------------------------------
This function is a dynamic method generator, crafting methods for calculating the 1/f dephasing time (or rate) due to different types of noise in the quantum circuit. The generated methods are named `tphi_1_over_f_{noise_type}{index}`, where `noise_type` can be 'cc', 'ng', or 'flux', and `index` differentiates individual noise sources.
Parameters
----------
None
Attributes
----------
external_fluxes : list
A collection of symbols representing external fluxes in the circuit.
offset_charges : list
A collection of symbols representing offset charges in the circuit.
branches : list
A collection of branches in the circuit.
Returns
-------
None
Notes
-----
1. Identifies the branches in the circuit that are junctions (indicated by "JJ" in the branch type).
2. Initializes dictionaries to store the generated methods for each type of noise.
3. Iterates over the external fluxes, offset charges, and junction branches in the circuit.
4. Depending on the type of parameter (external flux, offset charge, or junction branch), it determines the type of noise and the function to differentiate the Hamiltonian with respect to the parameter.
5. If the parameter is an expression, it extracts the trailing number from the parameter name and uses it to get the appropriate differentiation function from the current instance.
6. If the parameter is a junction branch, it uses the branch's ID string as the trailing number and gets the appropriate differentiation function from the current instance.
7. Defines a function `tphi_1_over_f_func` that calculates the 1/f dephasing time (or rate) for the current parameter. This function invokes the differentiation function to generate the noise operator, converts the noise operator to a sparse matrix if necessary, and then calls the `tphi_1_over_f` method of the current instance to calculate the 1/f dephasing time (or rate).
8. Adds the `tphi_1_over_f_func` function to the appropriate dictionary, depending on the type of parameter.
9. Merges the dictionaries into a single dictionary `noise_methods`.
10. Iterates over the `noise_methods` dictionary and adds each method as an attribute of the current instance.
This function does not return anything; it modifies the current instance by adding the 1/f dephasing time (or rate) calculation methods as attributes.
"""
# calculating the rates from each of the flux sources
junction_branches = [branch for branch in self.branches if "JJ" in branch.type]
methods_noise_rates_from_flux = {}
methods_noise_rates_from_ng = {}
methods_noise_rates_from_cc = {}
for param_sym in self.external_fluxes + self.offset_charges + junction_branches:
if param_sym in self.external_fluxes:
diff_func_name = "d_hamiltonian_d_flux"
noise_type = "flux"
elif param_sym in self.offset_charges:
diff_func_name = "d_hamiltonian_d_ng"
noise_type = "ng"
if param_sym in junction_branches:
diff_func_name = "d_hamiltonian_d_EJ"
noise_type = "cc"
if isinstance(param_sym, sm.Expr):
trailing_number = get_trailing_number(param_sym.name)
noise_op_func = getattr(self, f"{diff_func_name}{trailing_number}")
elif param_sym in junction_branches:
trailing_number = param_sym.index
noise_op_func = getattr(self, f"{diff_func_name}{trailing_number}")
def tphi_1_over_f_func(
self=self,
A_noise: float = NOISE_PARAMS[f"A_{noise_type}"],
i: int = 0,
j: int = 1,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
noise_op_func=noise_op_func,
noise_type=noise_type,
**kwargs,
) -> float:
r"""
1/f Dephasing Time (or Rate) Calculator
---------------------------------------
This function calculates the 1/f dephasing time (or rate) due to a specific type of noise in the quantum circuit.
Steps:
------
1. Invokes the `noise_op_func` function to generate the noise operator. The noise operator is a quantum object that represents the effect of the noise on the quantum states of the circuit.
2. If the noise operator is a quantum object, it is converted to a sparse matrix. This is done to facilitate the subsequent calculations.
3. Calls the `tphi_1_over_f` method of the current instance to calculate the 1/f dephasing time (or rate). The `tphi_1_over_f` method calculates the dephasing time (or rate) by integrating the noise power spectral density over frequency.
Parameters:
----------
A_noise (float): The strength of the noise. This is a scaling factor that determines the magnitude of the noise.
i (int): The state index that, along with `j`, defines a qubit. This is an integer that identifies one of the states of the qubit.
j (int): The state index that, along with `i`, defines a qubit. This is an integer that identifies one of the states of the qubit.
esys (Tuple[ndarray, ndarray]): A tuple containing the eigenvalues and eigenvectors of the system Hamiltonian. This is used to calculate the transition frequencies between the states of the qubit.
get_rate (bool): A flag that determines whether to calculate the dephasing time or the dephasing rate. If this is True, the function calculates the dephasing rate; otherwise, it calculates the dephasing time.
noise_op_func (function): A function that generates the noise operator. The noise operator is a quantum object that represents the effect of the noise on the quantum states of the circuit.
Returns:
-------
float: The calculated 1/f dephasing time (or rate). This is a numerical value that represents the time it takes for the qubit to lose phase coherence due to the noise, or the rate at which the qubit loses phase coherence due to the noise.
"""
if noise_type != "cc":
noise_op = noise_op_func()
else:
noise_op = noise_op_func()
if isinstance(noise_op, qt.Qobj):
noise_op = Qobj_to_scipy_csc_matrix(noise_op)
if isinstance(noise_op, list):
noise_op_new = []
for op in noise_op:
if isinstance(op, qt.Qobj):
op = Qobj_to_scipy_csc_matrix(op)
noise_op_new.append(op)
noise_op = noise_op_new
return self.tphi_1_over_f(
A_noise=A_noise,
i=i,
j=j,
noise_op=noise_op,
esys=esys,
get_rate=get_rate,
**kwargs,
)
if param_sym in self.external_fluxes:
methods_noise_rates_from_flux[
f"tphi_1_over_f_flux{trailing_number}"
] = tphi_1_over_f_func
elif param_sym in self.offset_charges:
methods_noise_rates_from_ng[f"tphi_1_over_f_ng{trailing_number}"] = (
tphi_1_over_f_func
)
elif param_sym in junction_branches:
methods_noise_rates_from_cc[f"tphi_1_over_f_cc{trailing_number}"] = (
tphi_1_over_f_func
)
noise_methods = {
**methods_noise_rates_from_flux,
**methods_noise_rates_from_ng,
**methods_noise_rates_from_cc,
}
for method_name in noise_methods:
setattr(self, method_name, MethodType(noise_methods[method_name], self))
[docs]
def generate_overall_tphi_cc(self):
"""
Overall Dephasing Time (Tphi) Calculator due to 1/f Critical Current (cc) Noise
-------------------------------------------------------------------------------
This function calculates the overall dephasing time (Tphi) due to 1/f critical current (cc) noise in the quantum circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
Steps:
------
1. Checks if there are any existing methods for calculating Tphi due to 1/f cc noise. This is done by searching the methods of the current instance for method names that match the pattern "tphi_1_over_f_cc\\d+$". If such methods exist, the function returns None and does not generate a new method.
2. Defines a new method `tphi_1_over_f_cc` for calculating the overall Tphi due to 1/f cc noise. This method performs the following steps:
a. Initializes an empty list `tphi_times` to store the Tphi times for each junction branch in the circuit.
b. Iterates over the junction branches in the circuit. A junction branch is a branch that represents a Josephson junction (JJ).
c. Calls the method for calculating Tphi due to 1/f cc noise for the current junction branch. The method is an attribute of the current instance that is named "tphi_1_over_f_cc" followed by the ID string of the branch.
d. Appends the calculated Tphi time to the `tphi_times` list.
e. Calculates the total rate of dephasing by summing the reciprocals of the Tphi times.
f. If the `get_rate` parameter is True, returns the total rate of dephasing. Otherwise, returns the reciprocal of the total rate (the overall Tphi time), or infinity if the total rate is zero.
3. Adds the `tphi_1_over_f_cc` method as an attribute of the current instance. This is done using the `setattr` function and the `MethodType` class to bind the method to the current instance.
Notes:
------
This function does not return anything; it modifies the current instance by adding the `tphi_1_over_f_cc` method as an attribute.
"""
if not any([re.match(r"tphi_1_over_f_cc\d+$", method) for method in dir(self)]):
return None
def tphi_1_over_f_cc(
self=self,
A_noise: float = NOISE_PARAMS["A_cc"],
i: int = 0,
j: int = 1,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
**kwargs,
) -> float:
tphi_times = []
for branch in [brnch for brnch in self.branches if "JJ" in brnch.type]:
tphi_times.append(
getattr(self, f"tphi_1_over_f_cc{branch.index}")(
A_noise=A_noise,
i=i,
j=j,
esys=esys,
**kwargs,
)
)
total_rate = sum([1 / tphi for tphi in tphi_times])
if get_rate:
return total_rate
return 1 / total_rate if total_rate != 0 else np.inf
setattr(self, "tphi_1_over_f_cc", MethodType(tphi_1_over_f_cc, self))
[docs]
def generate_overall_tphi_flux(self):
"""
Overall Dephasing Time (Tphi) Calculator due to 1/f Flux Noise
--------------------------------------------------------------
This function calculates the overall dephasing time (Tphi) due to 1/f flux noise in the quantum circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
Steps:
------
1. Checks if there are any existing methods for calculating Tphi due to 1/f flux noise. This is done by searching the methods of the current instance for method names that match the pattern "tphi_1_over_f_flux\\d+$". If such methods exist, the function returns None and does not generate a new method.
2. Defines a new method `tphi_1_over_f_flux` for calculating the overall Tphi due to 1/f flux noise. This method performs the following steps:
a. Initializes an empty list `tphi_times` to store the Tphi times for each external flux in the circuit.
b. Iterates over the external fluxes in the circuit.
c. Calls the method for calculating Tphi due to 1/f flux noise for the current external flux. The method is an attribute of the current instance that is named "tphi_1_over_f_flux" followed by the trailing number in the name of the flux.
d. Appends the calculated Tphi time to the `tphi_times` list.
e. Calculates the total rate of dephasing by summing the reciprocals of the Tphi times.
f. If the `get_rate` parameter is True, returns the total rate of dephasing. Otherwise, returns the reciprocal of the total rate (the overall Tphi time), or infinity if the total rate is zero.
3. Adds the `tphi_1_over_f_flux` method as an attribute of the current instance. This is done using the `setattr` function and the `MethodType` class to bind the method to the current instance.
Notes:
------
This function does not return anything; it modifies the current instance by adding the `tphi_1_over_f_flux` method as an attribute.
"""
if not any(
[re.match(r"tphi_1_over_f_flux\d+$", method) for method in dir(self)]
):
return None
def tphi_1_over_f_flux(
self=self,
A_noise: float = NOISE_PARAMS["A_flux"],
i: int = 0,
j: int = 1,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
**kwargs,
) -> float:
tphi_times = []
for flux_sym in self.external_fluxes:
tphi_times.append(
getattr(
self, f"tphi_1_over_f_flux{get_trailing_number(flux_sym.name)}"
)(
A_noise=A_noise,
i=i,
j=j,
esys=esys,
)
)
total_rate = sum([1 / tphi for tphi in tphi_times])
if get_rate:
return total_rate
return 1 / total_rate if total_rate != 0 else np.inf
setattr(self, "tphi_1_over_f_flux", MethodType(tphi_1_over_f_flux, self))
[docs]
def generate_overall_tphi_ng(self):
"""
Overall Dephasing Time (Tphi) Calculator due to 1/f Flux Noise
--------------------------------------------------------------
This function calculates the overall dephasing time (Tphi) due to 1/f flux noise in the quantum circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
Steps:
------
1. Checks if there are any existing methods for calculating Tphi due to 1/f flux noise. This is done by searching the methods of the current instance for method names that match the pattern "tphi_1_over_f_flux\\d+$". If such methods exist, the function returns None and does not generate a new method.
2. Defines a new method `tphi_1_over_f_flux` for calculating the overall Tphi due to 1/f flux noise. This method performs the following steps:
a. Initializes an empty list `tphi_times` to store the Tphi times for each external flux in the circuit.
b. Iterates over the external fluxes in the circuit.
c. Calls the method for calculating Tphi due to 1/f flux noise for the current external flux. The method is an attribute of the current instance that is named "tphi_1_over_f_flux" followed by the trailing number in the name of the flux.
d. Appends the calculated Tphi time to the `tphi_times` list.
e. Calculates the total rate of dephasing by summing the reciprocals of the Tphi times.
f. If the `get_rate` parameter is True, returns the total rate of dephasing. Otherwise, returns the reciprocal of the total rate (the overall Tphi time), or infinity if the total rate is zero.
3. Adds the `tphi_1_over_f_flux` method as an attribute of the current instance. This is done using the `setattr` function and the `MethodType` class to bind the method to the current instance.
Notes:
------
This function does not return anything; it modifies the current instance by adding the `tphi_1_over_f_flux` method as an attribute.
"""
if not any([re.match(r"tphi_1_over_f_ng\d+$", method) for method in dir(self)]):
return None
def tphi_1_over_f_ng(
self=self,
A_noise: float = NOISE_PARAMS["A_ng"],
i: int = 0,
j: int = 1,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
**kwargs,
) -> float:
tphi_times = []
for flux_sym in self.offset_charges:
tphi_times.append(
getattr(
self, f"tphi_1_over_f_ng{get_trailing_number(flux_sym.name)}"
)(
A_noise=A_noise,
i=i,
j=j,
esys=esys,
)
)
total_rate = sum([1 / tphi for tphi in tphi_times])
if get_rate:
return total_rate
return 1 / total_rate if total_rate != 0 else np.inf
setattr(self, "tphi_1_over_f_ng", MethodType(tphi_1_over_f_ng, self))
[docs]
def generate_t1_flux_bias_line_methods(self):
"""
T1 Coherence Time Calculator due to Flux Bias Line Noise
--------------------------------------------------------
This function calculates the T1 coherence times due to flux bias line noise for each external flux in the quantum circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
Returns:
-------
None
Steps:
------
1. Initializes an empty dictionary `flux_bias_line_methods` to store the T1 calculation methods for each external flux.
2. Iterates over the external fluxes in the circuit.
3. Extracts the trailing number from the name of the current flux using the `get_trailing_number` function.
4. Retrieves the method for calculating the derivative of the Hamiltonian with respect to the current flux. This is done using the `getattr` function and the name of the method, which is "d_hamiltonian_d_flux" followed by the trailing number.
5. Defines a new method `flux_bias_noise` for calculating the T1 time due to flux bias line noise for the current flux. This method performs the following steps:
a. Calls the `t1_flux_bias_line` method of the `NoisySystem` class with the current instance, the state indices `i` and `j`, the noise parameters `M`, `Z`, and `T`, the `total` flag, the system eigenvalues and eigenvectors `esys`, the `get_rate` flag, and the noise operator method as arguments.
b. Returns the calculated T1 time.
6. Adds the `flux_bias_noise` method to the `flux_bias_line_methods` dictionary with a key that is "t1_flux_bias_line" followed by the trailing number.
7. Iterates over the keys in the `flux_bias_line_methods` dictionary.
8. Adds each method in the `flux_bias_line_methods` dictionary as an attribute of the current instance. This is done using the `setattr` function and the `MethodType` class to bind the method to the current instance.
Notes:
------
This function does not return anything; it modifies the current instance by adding the T1 calculation methods as attributes.
"""
flux_bias_line_methods = {}
for flux_sym in self.external_fluxes:
trailing_number = get_trailing_number(flux_sym.name)
noise_op_method = getattr(self, f"d_hamiltonian_d_flux{trailing_number}")
def flux_bias_noise(
self=self,
i: int = 1,
j: int = 0,
M: float = NOISE_PARAMS["M"],
Z: Union[complex, float, Callable] = NOISE_PARAMS["R_0"],
T: float = NOISE_PARAMS["T"],
total: bool = True,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
noise_op_method=noise_op_method,
):
return NoisySystem.t1_flux_bias_line(
self=self,
i=i,
j=j,
M=M,
Z=Z,
T=T,
total=total,
esys=esys,
get_rate=get_rate,
noise_op_method=noise_op_method,
)
flux_bias_line_methods[f"t1_flux_bias_line{trailing_number}"] = (
flux_bias_noise
)
for method_name in flux_bias_line_methods:
setattr(
self, method_name, MethodType(flux_bias_line_methods[method_name], self)
)
[docs]
def generate_t1_methods(self):
"""
Generates methods for calculating the T1 coherence times due to capacitive, inductive, and charge impedance noise for each branch in the circuit.
Parameters
----------
self : object
The instance of the class where this method is being added.
Returns
-------
None
Notes
-----
This function performs the following steps:
1. Initializes empty dictionaries `t1_capacitive_methods`, `t1_inductive_methods`, and `t1_charge_impedance_methods` to store the T1 calculation methods for each type of noise.
2. Iterates over the branches in the circuit.
3. Checks the type of the current branch.
4. If the branch type is "L", adds a method for calculating the T1 time due to inductive noise to the `t1_inductive_methods` dictionary. The method is generated by calling the `wrapper_t1_inductive_capacitive` function with the current branch as an argument. The key for the method in the dictionary is "t1_inductive" followed by the ID string of the branch.
5. If the branch type is not "L", adds a method for calculating the T1 time due to capacitive noise to the `t1_capacitive_methods` dictionary. The method is generated in the same way as for inductive noise.
6. Merges the `t1_capacitive_methods`, `t1_inductive_methods`, and `t1_charge_impedance_methods` dictionaries into a single `noise_methods` dictionary.
7. Iterates over the keys in the `noise_methods` dictionary.
8. Adds each method in the `noise_methods` dictionary as an attribute of the current instance. This is done using the `setattr` function and the `MethodType` class to bind the method to the current instance.
This function does not return anything; it modifies the current instance by adding the T1 calculation methods as attributes.
"""
t1_capacitive_methods = {}
t1_inductive_methods = {}
t1_charge_impedance_methods = {}
t1_quasiparticle_tunneling_methods = {}
for branch in self.branches:
if branch.type == "L":
t1_inductive_methods[f"t1_inductive{branch.index}"] = (
self.wrapper_t1_inductive_capacitive(branch)
)
else:
t1_capacitive_methods[f"t1_capacitive{branch.index}"] = (
self.wrapper_t1_inductive_capacitive(branch)
)
# # quasiparticle noise
# if "JJ" in branch.type:
# t1_quasiparticle_tunneling_methods[
# f"t1_quasiparticle_tunneling{branch.index}"
# ] = self.wrapper_t1_quasiparticle_tunneling(branch)
# quasiparticle noise methods are not included yet
noise_methods = {
**t1_capacitive_methods,
**t1_inductive_methods,
**t1_charge_impedance_methods,
}
for method_name in noise_methods:
setattr(self, method_name, MethodType(noise_methods[method_name], self))
# self._data.update(t1_quasiparticle_tunneling_methods)
def wrapper_t1_quasiparticle_tunneling(self, branch: Branch):
def t1_quasiparticle_tunneling(
self=self,
i: int = 1,
j: int = 0,
Y_qp: Union[float, Callable] = None,
x_qp: float = NOISE_PARAMS["x_qp"],
T: float = NOISE_PARAMS["T"],
Delta: float = NOISE_PARAMS["Delta"],
total: bool = True,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
) -> float:
"""
T1 Coherence Time Calculator due to Quasiparticle Tunneling Noise
-----------------------------------------------------------------
This function calculates the T1 coherence time due to quasiparticle tunneling noise for a given branch in the quantum circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
i : int, optional
The index of the initial state in the transition. The default is 1.
j : int, optional
The index of the final state in the transition. The default is 0.
Y_qp : float or Callable, optional
The quasiparticle tunneling admittance. If a callable, it should take a frequency as input and return an admittance. The default is None.
x_qp : float, optional
The quasiparticle tunneling noise power spectral density. The default is the value of "x_qp" in the NOISE_PARAMS dictionary.
T : float, optional
The temperature of the system. The default is the value of "T" in the NOISE_PARAMS dictionary.
Delta : float, optional
The superconducting gap energy. The default is the value of "Delta" in the NOISE_PARAMS dictionary.
total : bool, optional
Whether to calculate the total T1 time (True) or the T1 time for a specific transition (False). The default is True.
esys : Tuple[ndarray, ndarray], optional
The system eigenvalues and eigenvectors. If None, they are calculated within the function. The default is None.
get_rate : bool, optional
Whether to return the rate of decoherence (True) or the T1 time (False). The default is False.
Returns:
-------
float
The calculated T1 time (or rate of decoherence if get_rate is True).
Notes:
------
This function calls the `t1_quasiparticle_tunneling` method of the `NoisySystem` class with the current instance and the provided parameters as arguments. The noise operator is calculated by calling the `junction_related_evaluation` method of the current instance with the branch and "sin_phi_qp" as arguments.
"""
return NoisySystem.t1_quasiparticle_tunneling(
self=self,
i=i,
j=j,
Y_qp=Y_qp,
x_qp=x_qp,
T=T,
Delta=Delta,
total=total,
esys=esys,
get_rate=get_rate,
noise_op=self.junction_related_evaluation(branch, calc="sin_phi_qp"),
)
return t1_quasiparticle_tunneling
def wrapper_t1_charge_impedance(self, branch: Branch):
def t1_charge_impedance(
self,
i: int = 1,
j: int = 0,
Z: Union[float, Callable] = NOISE_PARAMS["R_0"],
T: float = NOISE_PARAMS["T"],
total: bool = True,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
branch=branch,
) -> float:
"""
T1 Coherence Time Calculator due to Charge Impedance Noise
----------------------------------------------------------
This function calculates the T1 coherence time due to charge impedance noise for a given branch in the quantum circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
i : int, optional
The index of the initial state in the transition. The default is 1.
j : int, optional
The index of the final state in the transition. The default is 0.
Z : float or Callable, optional
The charge impedance. If a callable, it should take a frequency as input and return an impedance. The default is the value of "R_0" in the NOISE_PARAMS dictionary.
T : float, optional
The temperature of the system. The default is the value of "T" in the NOISE_PARAMS dictionary.
total : bool, optional
Whether to calculate the total T1 time (True) or the T1 time for a specific transition (False). The default is True.
esys : Tuple[ndarray, ndarray], optional
The system eigenvalues and eigenvectors. If None, they are calculated within the function. The default is None.
get_rate : bool, optional
Whether to return the rate of decoherence (True) or the T1 time (False). The default is False.
branch : object
The branch of the circuit for which to calculate the T1 time.
Returns:
-------
float
The calculated T1 time (or rate of decoherence if get_rate is True).
Steps:
------
1. Retrieves the parent circuit of the current instance.
2. Retrieves the symbolic expression for the branch variable. If the branch type is "L", the charge is not returned.
3. Checks the type of the branch. If the branch type is not "L", retrieves the "EC" parameter if the branch type is "C" or the "ECJ" parameter otherwise. If the branch type is "L", retrieves the "EL" parameter.
4. If the branch parameter is a symbolic expression, retrieves the value of the parameter from the parent circuit.
5. Calls the `t1_charge_impedance` method of the `NoisySystem` class with the current instance, the provided parameters, and the evaluated symbolic expression for the branch variable as arguments.
Notes:
------
This function does not return anything; it modifies the current instance by adding the T1 calculation methods as attributes.
"""
parent_circuit = self.return_parent_circuit()
branch_var_expr = parent_circuit.symbolic_circuit._branch_sym_expr(
branch, return_charge=False if branch.type == "L" else True
)
if branch.type != "L":
branch_param = (
branch.parameters["EC"]
if branch.type == "C"
else branch.parameters["ECJ"]
)
else:
branch_param = branch.parameters["EL"]
if isinstance(branch_param, sm.Expr):
branch_param = getattr(parent_circuit, branch_param.name)
return NoisySystem.t1_charge_impedance(
self=self,
i=i,
j=j,
Z=Z,
T=T,
total=total,
esys=esys,
get_rate=get_rate,
noise_op=parent_circuit._evaluate_symbolic_expr(branch_var_expr),
)
return t1_charge_impedance
[docs]
def wrapper_t1_inductive_capacitive(
self,
branch: Branch,
):
"""
T1 Coherence Time Calculator due to Inductive or Capacitive Noise
-----------------------------------------------------------------
This function generates a method for calculating the T1 coherence time due to inductive or capacitive noise for a given branch in the quantum circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
branch : Branch
The branch of the circuit for which to calculate the T1 time.
Returns:
-------
function
The generated method for calculating the T1 time.
Steps:
------
1. Checks the type of the branch.
2. If the branch type is not "L", generates a method for calculating the T1 time due to capacitive noise.
3. If the branch type is "L", generates a method for calculating the T1 time due to inductive noise by calling the `t1_inductive` method.
4. Returns the generated method.
Notes:
------
This function does not modify the current instance; it returns a new method for calculating the T1 time.
"""
if branch.type != "L":
def t1_method(
self,
i: int = 1,
j: int = 0,
Q_cap: Union[float, Callable] = None,
T: float = NOISE_PARAMS["T"],
total: bool = True,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
branch: Branch = branch,
) -> float:
"""
T1 Coherence Time Calculator due to Capacitive Noise
----------------------------------------------------
This function calculates the T1 coherence time due to capacitive noise for a given branch in the quantum circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
i : int, optional
The index of the initial state in the transition. The default is 1.
j : int, optional
The index of the final state in the transition. The default is 0.
Q_cap : float or Callable, optional
The capacitive noise power spectral density. If a callable, it should take a frequency as input and return a power spectral density. The default is None.
T : float, optional
The temperature of the system. The default is the value of "T" in the NOISE_PARAMS dictionary.
total : bool, optional
Whether to calculate the total T1 time (True) or the T1 time for a specific transition (False). The default is True.
esys : Tuple[ndarray, ndarray], optional
The system eigenvalues and eigenvectors. If None, they are calculated within the function. The default is None.
get_rate : bool, optional
Whether to return the rate of decoherence (True) or the T1 time (False). The default is False.
branch : Branch
The branch of the circuit for which to calculate the T1 time.
Returns:
-------
float
The calculated T1 time (or rate of decoherence if get_rate is True).
Steps:
------
1. Retrieves the parent circuit of the current instance.
2. Retrieves the symbolic expression for the branch charge.
3. Checks the type of the branch. If the branch type is "C", retrieves the "EC" parameter. If the branch type is not "C", retrieves the "ECJ" parameter.
4. If the branch parameter is a symbolic expression, retrieves the value of the parameter from the parent circuit.
5. Calls the `t1_capacitive` method of the `NoisySystem` class with the current instance, the provided parameters, the evaluated symbolic expression for the branch charge, and the branch parameter as arguments.
Notes:
------
This function does not return anything; it modifies the current instance by adding the T1 calculation methods as attributes.
"""
parent_circuit = self.return_parent_circuit()
branch_charge_expr = parent_circuit.symbolic_circuit._branch_sym_expr(
branch, return_charge=True
)
branch_param = (
branch.parameters["EC"]
if branch.type == "C"
else branch.parameters["ECJ"]
)
if isinstance(branch_param, sm.Expr):
branch_param = getattr(parent_circuit, branch_param.name)
return NoisySystem.t1_capacitive(
self=self,
i=i,
j=j,
Q_cap=Q_cap or self.Q_from_branch(branch),
T=T,
total=total,
esys=esys,
get_rate=get_rate,
noise_op=parent_circuit._evaluate_symbolic_expr(branch_charge_expr),
branch_params=branch_param,
)
else:
def t1_method(
self,
i: int = 1,
j: int = 0,
Q_ind: Union[float, Callable] = None,
T: float = NOISE_PARAMS["T"],
total: bool = True,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
branch: Branch = branch,
) -> float:
"""
T1 Coherence Time Calculator due to Inductive Noise
---------------------------------------------------
This function calculates the T1 coherence time due to inductive noise for a given branch in the quantum circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
i : int, optional
The index of the initial state in the transition. The default is 1.
j : int, optional
The index of the final state in the transition. The default is 0.
Q_ind : float or Callable, optional
The inductive noise power spectral density. If a callable, it should take a frequency as input and return a power spectral density. The default is None.
T : float, optional
The temperature of the system. The default is the value of "T" in the NOISE_PARAMS dictionary.
total : bool, optional
Whether to calculate the total T1 time (True) or the T1 time for a specific transition (False). The default is True.
esys : Tuple[ndarray, ndarray], optional
The system eigenvalues and eigenvectors. If None, they are calculated within the function. The default is None.
get_rate : bool, optional
Whether to return the rate of decoherence (True) or the T1 time (False). The default is False.
branch : Branch
The branch of the circuit for which to calculate the T1 time.
Returns:
-------
float
The calculated T1 time (or rate of decoherence if get_rate is True).
Steps:
------
1. Retrieves the parent circuit of the current instance.
2. Retrieves the symbolic expression for the branch variable.
3. Retrieves the "EL" parameter of the branch.
4. If the branch parameter is a symbolic expression, retrieves the value of the parameter from the parent circuit.
5. Calls the `t1_inductive` method of the `NoisySystem` class with the current instance, the provided parameters, the evaluated symbolic expression for the branch variable, and the branch parameter as arguments.
Notes:
------
This function does not return anything; it modifies the current instance by adding the T1 calculation methods as attributes.
"""
parent_circuit = self.return_parent_circuit()
branch_var_expr = parent_circuit.symbolic_circuit._branch_sym_expr(
branch
)
branch_param = branch.parameters["EL"]
if isinstance(branch_param, sm.Expr):
branch_param = getattr(parent_circuit, branch_param.name)
return NoisySystem.t1_inductive(
self=self,
i=i,
j=j,
Q_ind=Q_ind or self.Q_from_branch(branch),
T=T,
total=total,
esys=esys,
get_rate=get_rate,
noise_op=parent_circuit._evaluate_symbolic_expr(branch_var_expr),
branch_params=branch_param,
)
return t1_method
[docs]
def generate_overall_t1_quasiparticle_tunneling(self):
"""
T1 Coherence Time Calculator due to Quasiparticle Tunneling
------------------------------------------------------------
This function generates an overall method for calculating the T1 coherence time due to quasiparticle tunneling for the entire circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
Returns:
-------
None
Description:
------------
1. Checks if there are any existing methods for calculating T1 due to quasiparticle tunneling for any of the branches in the circuit. If not, it returns None.
2. Checks if the circuit is purely harmonic, in which case it also returns None.
3. If the checks pass, it defines a new method `t1_quasiparticle_tunneling` that calculates the overall T1 time due to quasiparticle tunneling.
4. This method iterates over all the branches in the circuit that are Josephson junctions (indicated by "JJ" in the branch type), and for each branch, it calls the corresponding branch-specific T1 calculation method.
5. It then calculates the total rate of decoherence as the sum of the reciprocals of the T1 times for all the branches, and returns the reciprocal of the total rate (or the total rate itself if `get_rate` is True).
6. Finally, the function adds the `t1_quasiparticle_tunneling` method as an attribute of the current instance.
Notes:
------
This function does not return anything; it modifies the current instance by adding the T1 calculation methods as attributes.
"""
if not any(
[
re.match(r"t1_quasiparticle_tunneling\d+$", method)
for method in dir(self)
]
):
return None
if self.is_purely_harmonic:
return None
def t1_quasiparticle_tunneling(
self=self,
i: int = 1,
j: int = 0,
Y_qp: Union[float, Callable] = None,
x_qp: float = NOISE_PARAMS["x_qp"],
T: float = NOISE_PARAMS["T"],
Delta: float = NOISE_PARAMS["Delta"],
total: bool = True,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
) -> float:
"""
T1 Coherence Time Calculator due to Quasiparticle Tunneling
------------------------------------------------------------
This function calculates the T1 coherence time due to quasiparticle tunneling for the entire circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
i : int, optional
The index of the initial state in the transition. The default is 1.
j : int, optional
The index of the final state in the transition. The default is 0.
Y_qp : float or Callable, optional
The quasiparticle tunneling admittance. If a callable, it should take a frequency as input and return an admittance. The default is None.
x_qp : float, optional
The quasiparticle imbalance. The default is the value of "x_qp" in the NOISE_PARAMS dictionary.
T : float, optional
The temperature of the system. The default is the value of "T" in the NOISE_PARAMS dictionary.
Delta : float, optional
The superconducting gap energy. The default is the value of "Delta" in the NOISE_PARAMS dictionary.
total : bool, optional
Whether to calculate the total T1 time (True) or the T1 time for a specific transition (False). The default is True.
esys : Tuple[ndarray, ndarray], optional
The system eigenvalues and eigenvectors. If None, they are calculated within the function. The default is None.
get_rate : bool, optional
Whether to return the rate of decoherence (True) or the T1 time (False). The default is False.
Returns:
-------
float
The calculated T1 time (or rate of decoherence if get_rate is True).
Steps:
------
1. Initializes an empty list for storing the T1 times for each branch.
2. Iterates over all the branches in the circuit that are Josephson junctions (indicated by "JJ" in the branch type).
3. For each branch, it calls the corresponding branch-specific T1 calculation method and appends the result to the list of T1 times.
4. Calculates the total rate of decoherence as the sum of the reciprocals of the T1 times for all the branches.
5. If get_rate is True, it returns the total rate of decoherence. Otherwise, it returns the reciprocal of the total rate (or infinity if the total rate is zero).
Notes:
------
This function does not return anything; it modifies the current instance by adding the T1 calculation methods as attributes.
"""
t1_times = []
for branch in [b for b in self.branches if "JJ" in b.type]:
t1_times.append(
getattr(self, f"t1_quasiparticle_tunneling{branch.index}")(
i=i,
j=j,
Y_qp=Y_qp,
x_qp=x_qp,
T=T,
Delta=Delta,
total=total,
esys=esys,
)
)
total_rate = sum([1 / t1 for t1 in t1_times])
if get_rate:
return total_rate
return 1 / total_rate if total_rate != 0 else np.inf
setattr(
self,
"t1_quasiparticle_tunneling",
MethodType(t1_quasiparticle_tunneling, self),
)
[docs]
def generate_overall_t1_inductive(self):
"""
T1 Coherence Time Calculator due to Inductive Noise
---------------------------------------------------
This function generates an overall method for calculating the T1 coherence time due to inductive noise for the entire circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
Returns:
-------
None
Description:
------------
1. Checks if there are any existing methods for calculating T1 due to inductive noise for any of the branches in the circuit. If not, it returns None.
2. If the checks pass, it defines a new method `t1_method` that calculates the overall T1 time due to inductive noise.
3. This method iterates over all the branches in the circuit that are inductors (indicated by "L" in the branch type), and for each branch, it calls the corresponding branch-specific T1 calculation method.
4. It then calculates the total rate of decoherence as the sum of the reciprocals of the T1 times for all the branches, and returns the reciprocal of the total rate (or the total rate itself if `get_rate` is True).
5. Finally, the function adds the `t1_method` method as an attribute of the current instance.
Notes:
------
This function does not return anything; it modifies the current instance by adding the T1 calculation methods as attributes.
"""
if not any([re.match(r"t1_inductive\d+$", method) for method in dir(self)]):
return None
def t1_method(
self,
i: int = 1,
j: int = 0,
Q_ind: Union[float, Callable] = None,
T: float = NOISE_PARAMS["T"],
total: bool = True,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
) -> float:
"""
T1 Coherence Time Calculator due to Inductive Noise
---------------------------------------------------
This function calculates the T1 coherence time due to inductive noise for the entire circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
i : int, optional
The index of the initial state in the transition. The default is 1.
j : int, optional
The index of the final state in the transition. The default is 0.
Q_ind : float or Callable, optional
The inductive noise power spectral density. If a callable, it should take a frequency as input and return a power spectral density. The default is None.
T : float, optional
The temperature of the system. The default is the value of "T" in the NOISE_PARAMS dictionary.
total : bool, optional
Whether to calculate the total T1 time (True) or the T1 time for a specific transition (False). The default is True.
esys : Tuple[ndarray, ndarray], optional
The system eigenvalues and eigenvectors. If None, they are calculated within the function. The default is None.
get_rate : bool, optional
Whether to return the rate of decoherence (True) or the T1 time (False). The default is False.
Returns:
-------
float
The calculated T1 time (or rate of decoherence if get_rate is True).
Steps:
------
1. Initializes an empty list for storing the T1 times for each branch.
2. Iterates over all the branches in the circuit that are inductors (indicated by "L" in the branch type).
3. For each branch, it calls the corresponding branch-specific T1 calculation method and appends the result to the list of T1 times.
4. Calculates the total rate of decoherence as the sum of the reciprocals of the T1 times for all the branches.
5. If get_rate is True, it returns the total rate of decoherence. Otherwise, it returns the reciprocal of the total rate (or infinity if the total rate is zero).
"""
t1_times = []
parent_circuit = self.return_parent_circuit()
for branch in [b for b in parent_circuit.branches if b.type == "L"]:
t1_times.append(
getattr(parent_circuit, f"t1_inductive{branch.index}")(
i=i,
j=j,
Q_ind=Q_ind or self.Q_from_branch(branch),
T=T,
total=total,
esys=esys,
)
)
total_rate = sum([1 / t1 for t1 in t1_times])
if get_rate:
return total_rate
return 1 / total_rate if total_rate != 0 else np.inf
setattr(self, "t1_inductive", MethodType(t1_method, self))
[docs]
def generate_overall_t1_capacitive(self):
"""
T1 Coherence Time Calculator due to Capacitive Noise
----------------------------------------------------
This function generates an overall method for calculating the T1 coherence time due to capacitive noise for the entire circuit.
Parameters:
----------
self : object
The instance of the class where this method is being added.
Returns:
-------
None
Description:
------------
1. Checks if there are any existing methods for calculating T1 due to capacitive noise for any of the branches in the circuit. If not, it returns None.
2. If the checks pass, it defines a new method `t1_method` that calculates the overall T1 time due to capacitive noise.
3. This method iterates over all the branches in the circuit that are not inductors (indicated by "L" in the branch type), and for each branch, it calls the corresponding branch-specific T1 calculation method.
4. It then calculates the total rate of decoherence as the sum of the reciprocals of the T1 times for all the branches, and returns the reciprocal of the total rate (or the total rate itself if `get_rate` is True).
5. Finally, the function adds the `t1_method` method as an attribute of the current instance.
Notes:
------
This function does not return anything; it modifies the current instance by adding the T1 calculation methods as attributes.
"""
if not any([re.match(r"t1_capacitive\d+$", method) for method in dir(self)]):
return None
def t1_method(
self,
i: int = 1,
j: int = 0,
Q_cap: Union[float, Callable] = None,
T: float = NOISE_PARAMS["T"],
total: bool = True,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
) -> float:
"""
T1 Coherence Time Calculation Due to Capacitive Noise
----------------------------------------------------
This function calculates the T1 coherence time due to capacitive noise for the entire quantum circuit. It can also return the rate of decoherence if specified.
Parameters
----------
self : object
The instance of the class where this method is being added.
i : int, optional
The index of the initial state in the transition. Default is 1.
j : int, optional
The index of the final state in the transition. Default is 0.
Q_cap : float or Callable, optional
The capacitive noise power spectral density. If a callable, it should take a frequency as input and return a power spectral density. Default is None.
T : float, optional
The temperature of the system. Default is the value of "T" in the NOISE_PARAMS dictionary.
total : bool, optional
Whether to calculate the total T1 time (True) or the T1 time for a specific transition (False). Default is True.
esys : Tuple[ndarray, ndarray], optional
The system eigenvalues and eigenvectors. If None, they are calculated within the function. Default is None.
get_rate : bool, optional
Whether to return the rate of decoherence (True) or the T1 time (False). Default is False.
Returns
-------
float
The calculated T1 time (or rate of decoherence if get_rate is True).
Notes
-----
This function performs the following steps:
1. Initializes an empty list for storing the T1 times for each branch.
2. Retrieves the parent circuit of the current branch.
3. Iterates over all the branches in the parent circuit that are not inductors (indicated by "L" in the branch type).
4. For each branch, it calls the corresponding branch-specific T1 calculation method and appends the result to the list of T1 times.
5. Calculates the total rate of decoherence as the sum of the reciprocals of the T1 times for all the branches.
6. If get_rate is True, it returns the total rate of decoherence. Otherwise, it returns the reciprocal of the total rate (or infinity if the total rate is zero).
7. The method is then set as an attribute of the current instance with the name "t1_capacitive".
"""
t1_times = []
parent_circuit = self.return_parent_circuit()
for branch in [b for b in parent_circuit.branches if b.type != "L"]:
t1_times.append(
getattr(parent_circuit, f"t1_capacitive{branch.index}")(
i=i,
j=j,
Q_cap=Q_cap or self.Q_from_branch(branch),
T=T,
total=total,
esys=esys,
)
)
total_rate = sum([1 / t1 for t1 in t1_times])
if get_rate:
return total_rate
return 1 / total_rate if total_rate != 0 else np.inf
setattr(self, "t1_capacitive", MethodType(t1_method, self))
[docs]
def generate_overall_t1_charge_impedance(self):
"""
T1 Coherence Time Calculation Due to Charge Impedance Noise
----------------------------------------------------------
This function dynamically generates a method for calculating the T1 coherence time due to charge impedance noise for the entire quantum circuit.
Parameters
----------
self : object
The instance of the class where this method is being added.
Returns
-------
None
Notes
-----
This function performs the following steps:
1. Checks if there are any existing methods for calculating T1 due to charge impedance noise for any of the branches in the circuit. If not, it returns None.
2. Defines a new method `t1_method` that calculates the overall T1 time due to charge impedance noise. This method:
- Iterates over all the branches in the circuit that are not inductors (indicated by "L" in the branch type).
- For each branch, it calls the corresponding branch-specific T1 calculation method.
- Calculates the total rate of decoherence as the sum of the reciprocals of the T1 times for all the branches.
- Returns the reciprocal of the total rate (or the total rate itself if `get_rate` is True).
3. Adds the `t1_method` method as an attribute of the current instance.
This function does not return anything; it modifies the current instance by adding the T1 calculation method as an attribute.
"""
if not any(
[re.match(r"t1_charge_impedance\d+$", method) for method in dir(self)]
):
return None
def t1_method(
self=self,
i: int = 1,
j: int = 0,
Z: Union[float, Callable] = NOISE_PARAMS["R_0"],
T: float = NOISE_PARAMS["T"],
total: bool = True,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
) -> float:
"""
T1 Coherence Time Calculation Due to Charge Impedance Noise
-----------------------------------------------------------
This function calculates the T1 coherence time due to charge impedance noise for the entire quantum circuit. It can also return the rate of decoherence if specified.
Parameters
----------
self : object
The instance of the class where this method is being added.
i : int, optional
The index of the initial state in the transition. Default is 1.
j : int, optional
The index of the final state in the transition. Default is 0.
Z : float or Callable, optional
The charge impedance noise power spectral density. If a callable, it should take a frequency as input and return a power spectral density. Default is the value of "R_0" in the NOISE_PARAMS dictionary.
T : float, optional
The temperature of the system. Default is the value of "T" in the NOISE_PARAMS dictionary.
total : bool, optional
Whether to calculate the total T1 time (True) or the T1 time for a specific transition (False). Default is True.
esys : Tuple[ndarray, ndarray], optional
The system eigenvalues and eigenvectors. If None, they are calculated within the function. Default is None.
get_rate : bool, optional
Whether to return the rate of decoherence (True) or the T1 time (False). Default is False.
Returns
-------
float
The calculated T1 time (or rate of decoherence if get_rate is True).
Notes
-----
This function performs the following steps:
1. Initializes an empty list for storing the T1 times for each branch.
2. Retrieves the parent circuit of the current branch.
3. Iterates over all the branches in the parent circuit that are not inductors (indicated by "L" in the branch type).
4. For each branch, it calls the corresponding branch-specific T1 calculation method and appends the result to the list of T1 times.
5. Calculates the total rate of decoherence as the sum of the reciprocals of the T1 times for all the branches.
6. If get_rate is True, it returns the total rate of decoherence. Otherwise, it returns the reciprocal of the total rate (or infinity if the total rate is zero).
7. The method is then set as an attribute of the current instance with the name "t1_charge_impedance".
"""
t1_times = []
parent_circuit = self.return_parent_circuit()
for branch in [b for b in parent_circuit.branches if b.type != "L"]:
t1_times.append(
getattr(parent_circuit, f"t1_charge_impedance{branch.index}")(
i=i,
j=j,
Z=Z,
T=T,
total=total,
esys=esys,
)
)
total_rate = sum([1 / t1 for t1 in t1_times])
if get_rate:
return total_rate
return 1 / total_rate if total_rate != 0 else np.inf
setattr(self, "t1_charge_impedance", MethodType(t1_method, self))
[docs]
def generate_overall_t1_flux_bias_line(self):
"""
T1 Coherence Time Calculation Due to Flux Bias Line Noise
--------------------------------------------------------
This function dynamically generates a method for calculating the T1 coherence time due to flux bias line noise for the entire quantum circuit.
Parameters
----------
self : object
The instance of the class where this method is being added.
Returns
-------
None
Notes
-----
This function performs the following steps:
1. Checks if there are any existing methods for calculating T1 due to flux bias line noise for any of the branches in the circuit. If not, it returns None.
2. Defines a new method `t1_flux_bias_line` that calculates the overall T1 time due to flux bias line noise. This method:
- Iterates over all the external fluxes in the circuit.
- For each flux, it calls the corresponding branch-specific T1 calculation method.
- Calculates the total rate of decoherence as the sum of the reciprocals of the T1 times for all the fluxes.
- Returns the reciprocal of the total rate (or the total rate itself if `get_rate` is True).
3. Adds the `t1_flux_bias_line` method as an attribute of the current instance.
This function does not return anything; it modifies the current instance by adding the T1 calculation method as an attribute.
"""
if not any(
[re.match(r"t1_flux_bias_line\d+$", method) for method in dir(self)]
):
return None
def t1_flux_bias_line(
self=self,
i: int = 1,
j: int = 0,
M: float = NOISE_PARAMS["M"],
Z: Union[complex, float, Callable] = NOISE_PARAMS["R_0"],
T: float = NOISE_PARAMS["T"],
total: bool = True,
esys: Tuple[ndarray, ndarray] = None,
get_rate: bool = False,
) -> float:
"""
T1 Coherence Time Calculation Due to Flux Bias Line Noise
--------------------------------------------------------
This function calculates the T1 coherence time due to flux bias line noise for the entire quantum circuit. It can also return the rate of decoherence if specified.
Parameters
----------
self : object
The instance of the class where this method is being added.
i : int, optional
The index of the initial state in the transition. Default is 1.
j : int, optional
The index of the final state in the transition. Default is 0.
M : float, optional
The mutual inductance between the qubit and the flux bias line. Default is the value of "M" in the NOISE_PARAMS dictionary.
Z : complex, float, or Callable, optional
The impedance of the flux bias line. If a callable, it should take a frequency as input and return an impedance. Default is the value of "R_0" in the NOISE_PARAMS dictionary.
T : float, optional
The temperature of the system. Default is the value of "T" in the NOISE_PARAMS dictionary.
total : bool, optional
Whether to calculate the total T1 time (True) or the T1 time for a specific transition (False). Default is True.
esys : Tuple[ndarray, ndarray], optional
The system eigenvalues and eigenvectors. If None, they are calculated within the function. Default is None.
get_rate : bool, optional
Whether to return the rate of decoherence (True) or the T1 time (False). Default is False.
Returns
-------
float
The calculated T1 time (or rate of decoherence if get_rate is True).
Notes
-----
This function performs the following steps:
1. Initializes an empty list for storing the T1 times for each external flux.
2. Iterates over all the external fluxes in the circuit.
3. For each flux, it retrieves the corresponding branch-specific T1 calculation method (indicated by the trailing number in the flux name) and calls it with the provided parameters.
4. Appends the result to the list of T1 times.
5. Calculates the total rate of decoherence as the sum of the reciprocals of the T1 times for all the fluxes.
6. If get_rate is True, it returns the total rate of decoherence. Otherwise, it returns the reciprocal of the total rate (or infinity if the total rate is zero).
7. The method is then set as an attribute of the current instance with the name "t1_flux_bias_line".
"""
t1_times = []
for external_flux_sym in self.external_fluxes:
t1_times.append(
getattr(
self,
f"t1_flux_bias_line{get_trailing_number(external_flux_sym.name)}",
)(
i=i,
j=j,
M=M,
Z=Z,
T=T,
total=total,
esys=esys,
)
)
total_rate = sum([1 / t1 for t1 in t1_times])
if get_rate:
return total_rate
return 1 / total_rate if total_rate != 0 else np.inf
setattr(self, "t1_flux_bias_line", MethodType(t1_flux_bias_line, self))
[docs]
def generate_noise_methods(self):
"""
Noise Method Generation for Quantum Circuit
-------------------------------------------
This function is responsible for dynamically generating all the methods that calculate the different types of noise in the quantum circuit.
Parameters
----------
self : object
The instance of the class where this method is being added.
Returns
-------
None
Notes
-----
This function performs the following steps:
1. Unfreezes the circuit to allow changes.
2. Generates methods for calculating the derivatives of the Hamiltonian with respect to the different circuit parameters.
3. Generates methods for calculating the Tphi times due to 1/f noise for each branch in the circuit.
4. Generates methods for calculating the T1 times due to flux bias line noise for each branch in the circuit.
5. Generates methods for calculating the T1 times due to various noise sources for each branch in the circuit.
6. Generates methods for calculating the overall Tphi times due to charge coupling, flux, and ng noise for the entire circuit.
7. Generates methods for calculating the overall T1 times due to capacitive, charge impedance, inductive, flux bias line, and quasiparticle tunneling noise for the entire circuit.
8. Sets the `_noise_methods_generated` attribute to True.
9. Freezes the circuit again to prevent further changes.
The methods generated by this function are:
- `generate_methods_d_hamiltonian_d`
- `generate_tphi_1_over_f_methods`
- `generate_t1_flux_bias_line_methods`
- `generate_t1_methods`
- `generate_overall_tphi_cc`
- `generate_overall_tphi_flux`
- `generate_overall_tphi_ng`
- `generate_overall_t1_capacitive`
- `generate_overall_t1_charge_impedance`
- `generate_overall_t1_inductive`
- `generate_overall_t1_flux_bias_line`
- `generate_overall_t1_quasiparticle_tunneling`
This function does not return anything; it modifies the current instance by adding the noise calculation methods as attributes.
"""
self._frozen = False
self.generate_methods_d_hamiltonian_d()
self.generate_tphi_1_over_f_methods()
self.generate_t1_flux_bias_line_methods()
self.generate_t1_methods()
self.generate_overall_tphi_cc()
self.generate_overall_tphi_flux()
self.generate_overall_tphi_ng()
self.generate_overall_t1_capacitive()
self.generate_overall_t1_charge_impedance()
self.generate_overall_t1_inductive()
self.generate_overall_t1_flux_bias_line()
self.generate_overall_t1_quasiparticle_tunneling()
self._noise_methods_generated = True
self._frozen = True