You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

scqubits

Package Overview
Dependencies
Maintainers
2
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

scqubits - pypi Package Compare versions

Comparing version
3.1.0
to
3.1.1
+1
-1
optional-requirements.txt
h5py>=2.10
pytest
ipywidgets
pathos
pathos>=0.3.0
typing_extensions
Metadata-Version: 2.1
Name: scqubits
Version: 3.1.0
Version: 3.1.1
Summary: scqubits: superconducting qubits in Python

@@ -5,0 +5,0 @@ Home-page: https://scqubits.readthedocs.io

Metadata-Version: 2.1
Name: scqubits
Version: 3.1.0
Version: 3.1.1
Summary: scqubits: superconducting qubits in Python

@@ -5,0 +5,0 @@ Home-page: https://scqubits.readthedocs.io

@@ -79,3 +79,2 @@ LICENSE

scqubits/tests/test_zeropi.py
scqubits/tests/data/.fullzeropi_1.hdf5
scqubits/tests/data/circuit_DFC.yaml

@@ -82,0 +81,0 @@ scqubits/tests/data/circuit_fluxonium.yaml

@@ -17,3 +17,2 @@ # central_dispatch.py

import weakref
from types import MethodType

@@ -20,0 +19,0 @@ from weakref import WeakKeyDictionary

@@ -15,3 +15,3 @@ # circuit-utils.py

from typing import TYPE_CHECKING, Any, Callable, Dict, List, Union
from typing import TYPE_CHECKING, Any, Callable, List, Union

@@ -245,3 +245,3 @@ import numpy as np

matrix = sparse.dia_matrix(
(np.ones(dim_theta), [1]),
(np.ones(dim_theta), [-1]),
shape=(dim_theta, dim_theta),

@@ -258,3 +258,3 @@ ).tocsc()

matrix = sparse.dia_matrix(
(np.ones(dim_theta), [-1]),
(np.ones(dim_theta), [1]),
shape=(dim_theta, dim_theta),

@@ -261,0 +261,0 @@ ).tocsc()

@@ -26,3 +26,3 @@ # cos2phi_qubit.py

from scipy import sparse
from scipy.sparse import csc_matrix, dia_matrix
from scipy.sparse import coo_matrix, csc_matrix, dia_matrix

@@ -38,3 +38,2 @@ import scqubits.core.constants as constants

import scqubits.io_utils.fileio_serializers as serializers
import scqubits.settings as settings
import scqubits.utils.plotting as plot

@@ -47,22 +46,32 @@ import scqubits.utils.spectrum_utils as utils

# -Cosine two phi qubit noise class
# - Cosine-2-phi qubit noise class ------------------------------------------------------------------------------------
class NoisyCos2PhiQubit(NoisySystem, ABC):
@abstractmethod
def phi_1_operator(self) -> csc_matrix:
def phi_1_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
pass
@abstractmethod
def phi_2_operator(self) -> csc_matrix:
def phi_2_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
pass
@abstractmethod
def n_1_operator(self) -> csc_matrix:
def n_1_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
pass
@abstractmethod
def n_2_operator(self) -> csc_matrix:
def n_2_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
pass
@abstractmethod
def n_zeta_operator(self) -> csc_matrix:
def n_zeta_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
pass

@@ -607,7 +616,28 @@

def phi_operator(self) -> csc_matrix:
r"""Returns :math:`\phi` operator"""
return self._kron3(
def phi_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Returns the :math:`\phi` operator in the native or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns :math:`\phi` operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns :math:`\phi` operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns :math:`\phi` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
:math:`\phi` operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`\phi` operator has dimensions of truncated_dim
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, :math:`\phi`
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
native = self._kron3(
self._phi_operator(), self._identity_zeta(), self._identity_theta()
)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -626,7 +656,29 @@ def _n_phi_operator(self) -> csc_matrix:

def n_phi_operator(self) -> csc_matrix:
r"""Returns :math:`n_\phi` operator"""
return self._kron3(
# changed expected from csc_matrix to Union[ndarray, coo_matrix]
def n_phi_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, coo_matrix]:
r"""
Returns the :math:`n_\phi` operator in the harmonic oscillator or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns :math:`n_\phi` operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns :math:`n_\phi` operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns :math:`n_\phi` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
:math:`n_\phi` operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`n_\phi` operator has dimensions of truncated_dim
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, :math:`n_\phi`
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray :math:`\zeta`.
"""
native = self._kron3(
self._n_phi_operator(), self._identity_zeta(), self._identity_theta()
)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -645,7 +697,28 @@ def _zeta_operator(self) -> csc_matrix:

def zeta_operator(self) -> csc_matrix:
r"""Returns :math:`\zeta` operator"""
return self._kron3(
def zeta_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Returns the :math:`\zeta` operator in the harmonic oscillator or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns :math:`\zeta` operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns :math:`\zeta` operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns :math:`\zeta` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
:math:`\zeta` operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`\zeta` operator has dimensions of truncated_dim
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, :math:`\zeta`
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
native = self._kron3(
self._identity_phi(), self._zeta_operator(), self._identity_theta()
)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -664,7 +737,28 @@ def _n_zeta_operator(self) -> csc_matrix:

def n_zeta_operator(self) -> csc_matrix:
r"""Returns :math:`n_\zeta` operator"""
return self._kron3(
def n_zeta_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Returns the :math:`n_\zeta` operator in the harmonic oscillator or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns :math:`n_\zeta` operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns :math:`n_\zeta` operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns :math:`n_\zeta` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
:math:`n_\zeta` operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`n_\zeta` operator has dimensions of truncated_dim
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, :math:`n_\zeta`
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
native = self._kron3(
self._identity_phi(), self._n_zeta_operator(), self._identity_theta()
)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -707,7 +801,28 @@ def _exp_i_phi_operator(self) -> csc_matrix:

def n_theta_operator(self) -> csc_matrix:
r"""Returns :math:`n_\theta` operator"""
return self._kron3(
def n_theta_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Returns the :math:`n_\theta` operator in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns :math:`n_\theta` operator in the charge basis.
If `True`, the energy eigenspectrum is computed, returns :math:`n_\theta` operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns :math:`n_\theta` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
:math:`n_\theta` operator in chosen basis. If charge basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`n_\theta` operator has dimensions of truncated_dim
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, :math:`n_\theta`
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
native = self._kron3(
self._identity_phi(), self._identity_zeta(), self._n_theta_operator()
)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -737,3 +852,3 @@ def _cos_theta_operator(self) -> csc_matrix:

* sparse.dia_matrix(
(np.ones(self._dim_theta()), [1]),
(np.ones(self._dim_theta()), [-1]),
shape=(self._dim_theta(), self._dim_theta()),

@@ -745,3 +860,3 @@ ).tocsc()

* sparse.dia_matrix(
(np.ones(self._dim_theta()), [-1]),
(np.ones(self._dim_theta()), [1]),
shape=(self._dim_theta(), self._dim_theta()),

@@ -785,7 +900,24 @@ ).tocsc()

def hamiltonian(self) -> csc_matrix:
def hamiltonian(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> csc_matrix:
"""Returns Hamiltonian in basis obtained by employing harmonic basis for
:math:`\\phi, \\zeta` and charge basis for :math:`\\theta` or in the eigenenerg basis.
Parameters
----------
energy_esys:
If `False` (default), returns Hamiltonian in basis obtained by employing harmonic basis for
:math:`\\phi, \\zeta` and charge basis for :math:`\\theta`.
If `True`, the energy eigenspectrum is computed, returns Hamiltonian in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns Hamiltonian in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Hamiltonian in chosen basis as csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, the Hamiltonian has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m,
for m given eigenvectors.
"""
Returns Hamiltonian in basis obtained by employing harmonic basis for
:math:`\\phi, \\zeta` and charge basis for :math:`\\theta`.
"""
phi_osc_mat = self._kron3(

@@ -858,3 +990,3 @@ op.number_sparse(self._dim_phi(), self.phi_plasma()),

return (
hamiltonian_mat = (
phi_osc_mat

@@ -868,2 +1000,6 @@ + zeta_osc_mat

)
native = hamiltonian_mat.tocsc()
return self.process_hamiltonian(
native_hamiltonian=native, energy_esys=energy_esys
)

@@ -1093,23 +1229,129 @@ def _evals_calc(self, evals_count) -> ndarray:

def phi_1_operator(self) -> csc_matrix:
"""Returns operator representing the phase across inductor 1"""
return self.zeta_operator() - self.phi_operator()
def phi_1_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
"""
Returns operator representing the phase across inductor 1 in harmonic oscillator or eigenenergy basis.
def phi_2_operator(self) -> csc_matrix:
"""Returns operator representing the phase across inductor 2"""
return -self.zeta_operator() - self.phi_operator()
Parameters
----------
energy_esys:
If `False` (default), returns operator in the harmonic oscillator basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
def n_1_operator(self) -> csc_matrix:
"""Returns operator representing the charge difference across junction 1"""
return 0.5 * self.n_phi_operator() + 0.5 * (
Returns
-------
Operator in chosen basis. If harmonic oscillator basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
native = self.zeta_operator() - self.phi_operator()
return self.process_op(native_op=native, energy_esys=energy_esys)
def phi_2_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
"""
Returns operator representing the phase across inductor 2 in harmonic oscillator or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the harmonic oscillator basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If harmonic oscillator basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
native = -self.zeta_operator() - self.phi_operator()
return self.process_op(native_op=native, energy_esys=energy_esys)
def n_1_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
"""
Returns operator representing the charge difference across junction 1 in native or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the harmonic oscillator basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
native = 0.5 * self.n_phi_operator() + 0.5 * (
self.n_theta_operator() - self.n_zeta_operator()
)
return self.process_op(native_op=native, energy_esys=energy_esys)
def n_2_operator(self) -> csc_matrix:
"""Returns operator representing the charge difference across junction 2"""
return 0.5 * self.n_phi_operator() - 0.5 * (
def n_2_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
"""
Returns operator representing the charge difference across junction 2 in native or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the harmonic oscillator basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
native = 0.5 * self.n_phi_operator() - 0.5 * (
self.n_theta_operator() - self.n_zeta_operator()
)
return self.process_op(native_op=native, energy_esys=energy_esys)
def d_hamiltonian_d_flux(self) -> csc_matrix:
def d_hamiltonian_d_flux(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
flux in the native or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
phi_flux_term = self._sin_phi_operator() * np.cos(

@@ -1139,5 +1381,28 @@ self.flux * np.pi

)
return junction_mat + dis_junction_mat
native = junction_mat + dis_junction_mat
return self.process_op(native_op=native, energy_esys=energy_esys)
def d_hamiltonian_d_EJ(self) -> csc_matrix:
def d_hamiltonian_d_EJ(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
EJ in the native or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
phi_flux_term = self._cos_phi_operator() * np.cos(

@@ -1160,6 +1425,29 @@ self.flux * np.pi

)
return junction_mat + dis_junction_mat
native = junction_mat + dis_junction_mat
return self.process_op(native_op=native, energy_esys=energy_esys)
def d_hamiltonian_d_ng(self) -> csc_matrix:
return (
def d_hamiltonian_d_ng(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
ng in the native or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
native = (
4 * self.dCJ * self._disordered_ecj() * self.n_phi_operator()

@@ -1170,1 +1458,2 @@ - 4

)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -27,2 +27,3 @@ # discretization.py

FIRST_STENCIL_COEFFS: Dict[int, List[float]] = {

@@ -29,0 +30,0 @@ 3: [-1 / 2, 0.0, 1 / 2],

@@ -16,3 +16,3 @@ # flux_qubit.py

from abc import ABC, abstractmethod
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional, Tuple, Union

@@ -41,11 +41,17 @@ import numpy as np

@abstractmethod
def d_hamiltonian_d_EJ1(self) -> ndarray:
def d_hamiltonian_d_EJ1(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
pass
@abstractmethod
def d_hamiltonian_d_EJ2(self) -> ndarray:
def d_hamiltonian_d_EJ2(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
pass
@abstractmethod
def d_hamiltonian_d_EJ3(self) -> ndarray:
def d_hamiltonian_d_EJ3(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
pass

@@ -408,3 +414,3 @@

evals = sp.linalg.eigh(
hamiltonian_mat, eigvals=(0, evals_count - 1), eigvals_only=True
hamiltonian_mat, subset_by_index=(0, evals_count - 1), eigvals_only=True
)

@@ -416,3 +422,3 @@ return np.sort(evals)

evals, evecs = sp.linalg.eigh(
hamiltonian_mat, eigvals=(0, evals_count - 1), eigvals_only=False
hamiltonian_mat, subset_by_index=(0, evals_count - 1), eigvals_only=False
)

@@ -507,25 +513,106 @@ evals, evecs = spec_utils.order_eigensystem(evals, evecs)

def hamiltonian(self) -> ndarray:
"""Return Hamiltonian in basis obtained by employing charge basis for both
degrees of freedom"""
return self.kineticmat() + self.potentialmat()
def hamiltonian(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Return Hamiltonian in the basis obtained by employing charge basis for both
degrees of freedom or in the eigenenergy basis.
def d_hamiltonian_d_EJ1(self) -> ndarray:
"""Returns operator representing a derivative of the Hamiltonian with
respect to EJ1."""
return -0.5 * np.kron(
Parameters
----------
energy_esys:
If `False` (default), returns Hamiltonian in the basis obtained by employing charge basis for both degrees of freedom.
If `True`, the energy eigenspectrum is computed, returns Hamiltonian in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns Hamiltonian in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Hamiltonian in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, the Hamiltonian has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m,
for m given eigenvectors.
"""
hamiltonian_mat = self.kineticmat() + self.potentialmat()
return self.process_hamiltonian(
native_hamiltonian=hamiltonian_mat, energy_esys=energy_esys
)
def d_hamiltonian_d_EJ1(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
EJ1 in the native Hamiltonian basis or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native Hamiltonian basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m,
for m given eigenvectors.
"""
native = -0.5 * np.kron(
self._exp_i_phi_operator() + self._exp_i_phi_operator().T, self._identity()
)
return self.process_op(native_op=native, energy_esys=energy_esys)
def d_hamiltonian_d_EJ2(self) -> ndarray:
"""Returns operator representing a derivative of the Hamiltonian with
respect to EJ2."""
return -0.5 * np.kron(
def d_hamiltonian_d_EJ2(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
EJ2 in the native Hamiltonian basis or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native Hamiltonian basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m,
for m given eigenvectors.
"""
native = -0.5 * np.kron(
self._identity(), self._exp_i_phi_operator() + self._exp_i_phi_operator().T
)
return self.process_op(native_op=native, energy_esys=energy_esys)
def d_hamiltonian_d_EJ3(self) -> ndarray:
"""Returns operator representing a derivative of the Hamiltonian with
respect to EJ3."""
return (
def d_hamiltonian_d_EJ3(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
EJ3 in the native Hamiltonian basis or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native Hamiltonian basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m,
for m given eigenvectors.
"""
native = (
-0.5

@@ -543,2 +630,3 @@ * (

)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -552,3 +640,3 @@ def _n_operator(self) -> ndarray:

off_diag_elements = np.ones(dim - 1, dtype=np.complex_)
e_iphi_matrix = np.diag(off_diag_elements, k=1)
e_iphi_matrix = np.diag(off_diag_elements, k=-1)
return e_iphi_matrix

@@ -560,41 +648,199 @@

def n_1_operator(self) -> ndarray:
r"""Return charge number operator conjugate to :math:`\phi_1`"""
return np.kron(self._n_operator(), self._identity())
def n_1_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
r"""
Returns the charge number operator conjugate to :math:`\phi_1` in the charge? or eigenenergy basis.
def n_2_operator(self) -> ndarray:
r"""Return charge number operator conjugate to :math:`\phi_2`"""
return np.kron(self._identity(), self._n_operator())
Parameters
----------
energy_esys:
If `False` (default), returns the charge number operator conjugate to :math:`\phi_1` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns the charge number operator conjugate to :math:`\phi_1` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns the charge number operator conjugate to :math:`\phi_1` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
def exp_i_phi_1_operator(self) -> ndarray:
r"""Return operator :math:`e^{i\phi_1}` in the charge basis."""
return np.kron(self._exp_i_phi_operator(), self._identity())
Returns
-------
Charge number operator conjugate to :math:`\phi_1` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, for m given eigenvectors.
"""
native = np.kron(self._n_operator(), self._identity())
return self.process_op(native_op=native, energy_esys=energy_esys)
def exp_i_phi_2_operator(self) -> ndarray:
r"""Return operator :math:`e^{i\phi_2}` in the charge basis."""
return np.kron(self._identity(), self._exp_i_phi_operator())
def n_2_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
r"""
Returns the charge number operator conjugate to :math:`\phi_2` in the charge? or eigenenergy basis.
def cos_phi_1_operator(self) -> ndarray:
"""Return operator :math:`\\cos \\phi_1` in the charge basis"""
Parameters
----------
energy_esys:
If `False` (default), returns the charge number operator conjugate to :math:`\phi_2` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns the charge number operator conjugate to :math:`\phi_2` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns the charge number operator conjugate to :math:`\phi_2` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Charge number operator conjugate to :math:`\phi_2` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, for m given eigenvectors.
"""
native = np.kron(self._identity(), self._n_operator())
return self.process_op(native_op=native, energy_esys=energy_esys)
def exp_i_phi_1_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
r"""
Returns operator :math:`e^{i\phi_1}` in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator :math:`e^{i\phi_1}` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator :math:`e^{i\phi_1}` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator :math:`e^{i\phi_1}` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator :math:`e^{i\phi_1}` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`e^{i\phi_1}` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`e^{i\phi_1}` has dimensions of m x m,
for m given eigenvectors.
"""
native = np.kron(self._exp_i_phi_operator(), self._identity())
return self.process_op(native_op=native, energy_esys=energy_esys)
def exp_i_phi_2_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
r"""
Returns operator :math:`e^{i\phi_2}` in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator :math:`e^{i\phi_2}` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator :math:`e^{i\phi_2}` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator :math:`e^{i\phi_2}` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator :math:`e^{i\phi_2}` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`e^{i\phi_2}` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`e^{i\phi_2}` has dimensions of m x m,
for m given eigenvectors.
"""
native = np.kron(self._identity(), self._exp_i_phi_operator())
return self.process_op(native_op=native, energy_esys=energy_esys)
def cos_phi_1_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator :math:`\\cos \\phi_1` in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator :math:`\\cos \\phi_1` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator :math:`\\cos \\phi_1` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator :math:`\\cos \\phi_1` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator :math:`\\cos \\phi_1` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`\\cos \\phi_1` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\cos \\phi_1` has dimensions of m x m,
for m given eigenvectors.
"""
cos_op = 0.5 * self.exp_i_phi_1_operator()
cos_op += cos_op.T
return cos_op
native = cos_op
return self.process_op(native_op=native, energy_esys=energy_esys)
def cos_phi_2_operator(self) -> ndarray:
"""Return operator :math:`\\cos \\phi_2` in the charge basis"""
def cos_phi_2_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator :math:`\\cos \\phi_2` in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator :math:`\\cos \\phi_2` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator :math:`\\cos \\phi_2` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator :math:`\\cos \\phi_2` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator :math:`\\cos \\phi_2` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`\\cos \\phi_2` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\cos \\phi_2` has dimensions of m x m,
for m given eigenvectors.
"""
cos_op = 0.5 * self.exp_i_phi_2_operator()
cos_op += cos_op.T
return cos_op
native = cos_op
return self.process_op(native_op=native, energy_esys=energy_esys)
def sin_phi_1_operator(self) -> ndarray:
"""Return operator :math:`\\sin \\phi_1` in the charge basis"""
def sin_phi_1_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator :math:`\\sin \\phi_1` in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator :math:`\\sin \\phi_1` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator :math:`\\sin \\phi_1` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator :math:`\\sin \\phi_1` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator :math:`\\sin \\phi_1` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`\\sin \\phi_1` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\sin \\phi_1` has dimensions of m x m,
for m given eigenvectors.
"""
sin_op = -1j * 0.5 * self.exp_i_phi_1_operator()
sin_op += sin_op.conj().T
return sin_op
native = sin_op
return self.process_op(native_op=native, energy_esys=energy_esys)
def sin_phi_2_operator(self) -> ndarray:
"""Return operator :math:`\\sin \\phi_2` in the charge basis"""
def sin_phi_2_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator :math:`\\sin \\phi_2` in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator :math:`\\sin \\phi_2` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator :math:`\\sin \\phi_1` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator :math:`\\sin \\phi_1` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator :math:`\\sin \\phi_2` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`\\sin \\phi_2` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\sin \\phi_2` has dimensions of m x m,
for m given eigenvectors.
"""
sin_op = -1j * 0.5 * self.exp_i_phi_2_operator()
sin_op += sin_op.conj().T
return sin_op
native = sin_op
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -601,0 +847,0 @@ def plot_potential(

@@ -142,10 +142,25 @@ # fluxonium.py

def phi_operator(self) -> ndarray:
def phi_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns the phi operator in the LC harmonic oscillator or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns phi operator in the LC harmonic oscillator basis.
If `True`, the energy eigenspectrum is computed, returns phi operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns phi operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Returns the phi operator in the LC harmonic oscillator basis
Phi operator in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, phi operator has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, phi operator has dimensions of m x m,
for m given eigenvectors.
"""
dimension = self.hilbertdim()
return (
native = (
(op.creation(dimension) + op.annihilation(dimension))

@@ -156,11 +171,27 @@ * self.phi_osc()

def n_operator(self) -> ndarray:
return self.process_op(native_op=native, energy_esys=energy_esys)
def n_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns the :math:`n = - i d/d\\phi` operator in the LC harmonic oscillator or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns the :math:`n = - i d/d\\phi` operator in the LC harmonic oscillator basis.
If `True`, the energy eigenspectrum is computed, returns the :math:`n = - i d/d\\phi` operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns the :math:`n = - i d/d\\phi` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Returns the :math:`n = - i d/d\\phi` operator in the LC harmonic
oscillator basis
Operator :math:`n = - i d/d\\phi` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`n = - i d/d\\phi` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`n = - i d/d\\phi` has dimensions of
m x m, for m given eigenvectors.
"""
dimension = self.hilbertdim()
return (
native = (
1j

@@ -170,39 +201,113 @@ * (op.creation(dimension) - op.annihilation(dimension))

)
return self.process_op(native_op=native, energy_esys=energy_esys)
def exp_i_phi_operator(self, alpha: float = 1.0, beta: float = 0.0) -> ndarray:
def exp_i_phi_operator(
self,
alpha: float = 1.0,
beta: float = 0.0,
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> ndarray:
"""
Returns the :math:`e^{i (\\alpha \\phi + \\beta) }` operator, with :math:`\\alpha` and :math:`\\beta` being
numbers, in the LC harmonic oscillator or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns the :math:`e^{i (\\alpha \\phi + \\beta) }` operator in the LC harmonic
oscillator basis. If `True`, the energy eigenspectrum is computed, returns the
:math:`e^{i (\\alpha \\phi + \\beta) }` operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns the :math:`e^{i (\\alpha \\phi + \\beta) }` operator in the energy eigenbasis, and does not have to
recalculate eigenspectrum.
Returns
-------
Returns the :math:`e^{i (\\alpha \\phi + \\beta) }` operator in the
LC harmonic oscillator basis,
with :math:`\\alpha` and :math:`\\beta` being numbers
Operator :math:`e^{i (\\alpha \\phi + \\beta) }` in chosen basis as ndarray. If the eigenenergy basis is
chosen, unless energy_esys is specified, :math:`e^{i (\\alpha \\phi + \\beta) }` has dimensions of
`truncated_dim`x `truncated_dim`. Otherwise, if eigenenergy basis is chosen,
:math:`e^{i (\\alpha \\phi + \\beta) }` has dimensions of m x m, for m given eigenvectors.
"""
exponent = 1j * (alpha * self.phi_operator())
return sp.linalg.expm(exponent) * cmath.exp(1j * beta)
native = sp.linalg.expm(exponent) * cmath.exp(1j * beta)
return self.process_op(native_op=native, energy_esys=energy_esys)
def cos_phi_operator(self, alpha: float = 1.0, beta: float = 0.0) -> ndarray:
def cos_phi_operator(
self,
alpha: float = 1.0,
beta: float = 0.0,
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> ndarray:
"""
Returns the :math:`\\cos (\\alpha \\phi + \\beta)` operator with :math:`\\alpha` and :math:`\\beta` being
numbers, in the LC harmonic oscillator or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns the :math:`\\cos (\\alpha \\phi + \\beta)` operator in the LC harmonic oscillator basis.
If `True`, the energy eigenspectrum is computed, returns the :math:`\\cos (\\alpha \\phi + \\beta)` operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns the :math:`\\cos (\\alpha \\phi + \\beta)` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Returns the :math:`\\cos (\\alpha \\phi + \\beta)` operator in the LC
harmonic oscillator basis,
with :math:`\\alpha` and :math:`\\beta` being numbers
Operator :math:`\\cos (\\alpha \\phi + \\beta)` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`\\cos (\\alpha \\phi + \\beta)` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\cos (\\alpha \\phi + \\beta)` has dimensions of m x m, for m given eigenvectors.
"""
argument = alpha * self.phi_operator() + beta * np.eye(self.hilbertdim())
return sp.linalg.cosm(argument)
native = sp.linalg.cosm(argument)
return self.process_op(native_op=native, energy_esys=energy_esys)
def sin_phi_operator(self, alpha: float = 1.0, beta: float = 0.0) -> ndarray:
def sin_phi_operator(
self,
alpha: float = 1.0,
beta: float = 0.0,
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> ndarray:
"""
Returns the :math:`\\sin (\\alpha \\phi + \\beta)` operator with :math:`\\alpha` and :math:`\\beta` being
numbers, in the LC harmonic oscillator or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns the :math:`\\sin (\\alpha \\phi + \\beta)` operator in the LC harmonic oscillator basis.
If `True`, the energy eigenspectrum is computed, returns the :math:`\\sin (\\alpha \\phi + \\beta)` operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns the :math:`\\sin (\\alpha \\phi + \\beta)` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Returns the :math:`\\sin (\\alpha \\phi + \\beta)` operator in the
LC harmonic oscillator basis
with :math:`\\alpha` and :math:`\\beta` being numbers
Operator :math:`\\sin (\\alpha \\phi + \\beta)` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`\\sin (\\alpha \\phi + \\beta)` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\sin (\\alpha \\phi + \\beta)` has dimensions of m x m, for m given eigenvectors.
"""
argument = alpha * self.phi_operator() + beta * np.eye(self.hilbertdim())
return sp.linalg.sinm(argument)
native = sp.linalg.sinm(argument)
return self.process_op(native_op=native, energy_esys=energy_esys)
def hamiltonian(self) -> ndarray: # follow Zhu et al., PRB 87, 024510 (2013)
"""Construct Hamiltonian matrix in harmonic-oscillator basis, following Zhu
et al., PRB 87, 024510 (2013)"""
def hamiltonian(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray: # follow Zhu et al., PRB 87, 024510 (2013)
"""
Constructs Hamiltonian matrix in harmonic-oscillator, following Zhu
et al., PRB 87, 024510 (2013), or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns Hamiltonian in the harmonic-oscillator basis.
If `True`, the energy eigenspectrum is computed, returns Hamiltonian in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns Hamiltonian in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Hamiltonian in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, the Hamiltonian has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m,
for m given eigenvectors.
"""
dimension = self.hilbertdim()

@@ -215,20 +320,64 @@ diag_elements = [(i + 0.5) * self.plasma_energy() for i in range(dimension)]

hamiltonian_mat = lc_osc_matrix - self.EJ * cos_matrix
return hamiltonian_mat
return self.process_hamiltonian(
native_hamiltonian=hamiltonian_mat, energy_esys=energy_esys
)
def d_hamiltonian_d_EJ(self) -> ndarray:
"""Returns operator representing a derivative of the Hamiltonian with respect
to `EJ`.
def d_hamiltonian_d_EJ(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
EJ in the harmonic-oscillator or eigenenergy basis. The flux is grouped as in the Hamiltonian.
The flux is grouped as in the Hamiltonian.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m,
for m given eigenvectors.
"""
return -self.cos_phi_operator(1, 2 * np.pi * self.flux)
native = -self.cos_phi_operator(1, 2 * np.pi * self.flux)
def d_hamiltonian_d_flux(self) -> ndarray:
"""Returns operator representing a derivative of the Hamiltonian with respect
to `flux`.
return self.process_op(native_op=native, energy_esys=energy_esys)
Flux is grouped as in the Hamiltonian.
# def d_hamiltonian_d_flux(self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False) -> ndarray:
# """Returns operator representing a derivative of the Hamiltonian with respect
# to flux.
#
# The flux is grouped as in the Hamiltonian."""
# return -2 * np.pi * self.EJ * self.sin_phi_operator(1, 2 * np.pi * self.flux, energy_esys=energy_esys)
def d_hamiltonian_d_flux(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
return -2 * np.pi * self.EJ * self.sin_phi_operator(1, 2 * np.pi * self.flux)
Returns operator representing a derivative of the Hamiltonian with respect to
flux in the harmonic-oscillator or eigenenergy basis. The flux is grouped as in the Hamiltonian.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m,
for m given eigenvectors.
"""
native = -2 * np.pi * self.EJ * self.sin_phi_operator(1, 2 * np.pi * self.flux)
return self.process_op(native_op=native, energy_esys=energy_esys)
def hilbertdim(self) -> int:

@@ -235,0 +384,0 @@ """

@@ -27,3 +27,3 @@ # generic_qubit.py

# —generic qubit (two-level system)——————————————————————————————————————————————
# -generic qubit (two-level system)----------------------------------------------

@@ -30,0 +30,0 @@

@@ -318,3 +318,3 @@ # hilbert_space.py

"""Class holding information about the full Hilbert space, usually composed of
multiple subsys_list. The class provides methods to turn subsystem operators into
multiple subsystems. The class provides methods to turn subsystem operators into
operators acting on the full Hilbert space, and establishes the interface to

@@ -335,2 +335,3 @@ qutip. Returned operators are of the `qutip.Qobj` type. The class also provides

_lookup_exists = False
osc_subsys_list = descriptors.ReadOnlyProperty(OscillatorList)

@@ -353,3 +354,3 @@ qbt_subsys_list = descriptors.ReadOnlyProperty(QubitList)

)
self._subsystems: Tuple[QuantumSys, ...] = tuple(subsystem_list)
self._subsystems: List[QuantumSys] = subsystem_list
self._subsys_by_id_str = {

@@ -367,3 +368,2 @@ obj._id_str: self[index] for index, obj in enumerate(self)

self._lookup: Optional[spec_lookup.SpectrumLookupAdapter] = None
self._osc_subsys_list = [

@@ -447,8 +447,3 @@ subsys for subsys in self if isinstance(subsys, osc.Oscillator)

@property
def lookup(self):
"""[Legacy] supporting old lookup interface."""
return self._lookup
@property
def hilbertspace(self) -> "HilbertSpace":
def hilbertspace(self) -> HilbertSpace:
"""[Legacy] Auxiliary reference to self for compatibility with

@@ -460,2 +455,5 @@ SpectrumLookupMixin

@property
@utils.DeprecationMessage(
"`subsys_list` is deprecated. Use `subsystem_list` instead."
)
def subsys_list(self) -> List[QuantumSys]:

@@ -471,3 +469,3 @@ return list(self._subsystems)

@classmethod
def deserialize(cls, io_data: "IOData") -> "HilbertSpace":
def deserialize(cls, io_data: "IOData") -> HilbertSpace:
"""

@@ -480,5 +478,4 @@ Take the given IOData and return an instance of the described class,

data = alldata_dict.pop("_data", {})
new_hilbertspace: "HilbertSpace" = cls(**alldata_dict)
new_hilbertspace: HilbertSpace = cls(**alldata_dict)
new_hilbertspace._data = data
new_hilbertspace._lookup = spec_lookup.SpectrumLookupAdapter(new_hilbertspace)
return new_hilbertspace

@@ -512,3 +509,3 @@

@classmethod
def create(cls) -> "HilbertSpace":
def create(cls) -> HilbertSpace:
hilbertspace = cls([])

@@ -524,12 +521,12 @@ scqubits.ui.hspace_widget.create_hilbertspace_widget(hilbertspace.__init__)

self.broadcast("HILBERTSPACE_UPDATE")
if self._lookup:
self._lookup._out_of_sync = True
if self.lookup_exists():
self._out_of_sync = True
elif event == "INTERACTIONTERM_UPDATE" and sender in self.interaction_list:
self.broadcast("HILBERTSPACE_UPDATE")
if self._lookup:
self._lookup._out_of_sync = True
if self.lookup_exists():
self._out_of_sync = True
elif event == "INTERACTIONLIST_UPDATE" and sender is self:
self.broadcast("HILBERTSPACE_UPDATE")
if self._lookup:
self._lookup._out_of_sync = True
if self.lookup_exists():
self._out_of_sync = True

@@ -546,3 +543,3 @@ ###################################################################################

@property
def subsystem_list(self) -> Tuple[QuantumSys, ...]:
def subsystem_list(self) -> List[QuantumSys]:
return self._subsystems

@@ -562,3 +559,3 @@

def subsystem_count(self) -> int:
"""Returns number of subsys_list composing the joint Hilbert space"""
"""Returns number of subsystems composing the joint Hilbert space"""
return len(self._subsystems)

@@ -570,3 +567,3 @@

def generate_lookup(self, update_subsystem_indices: List[int] = None) -> None:
self._lookup_exists = True
bare_esys_dict = self.generate_bare_esys(

@@ -590,5 +587,7 @@ update_subsystem_indices=update_subsystem_indices

)
self._lookup = spec_lookup.SpectrumLookupAdapter(self)
def generate_bare_esys(self, update_subsystem_indices: List[int] = None) -> None:
def lookup_exists(self) -> bool:
return self._lookup_exists
def generate_bare_esys(self, update_subsystem_indices: List[int] = None) -> dict:
# update all the subsystems when update_subsystem_indices is set to None

@@ -638,3 +637,3 @@ if update_subsystem_indices is None:

"""Calculates eigenvalues of the full Hamiltonian using
`qutip.Qob.eigenenergies()`.
`qutip.Qobj.eigenenergies()`.

@@ -658,3 +657,3 @@ Parameters

"""Calculates eigenvalues and eigenvectors of the full Hamiltonian using
`qutip.Qob.eigenstates()`.
`qutip.Qobj.eigenstates()`.

@@ -732,3 +731,3 @@ Parameters

-------
composite Hamiltonian composed of bare Hamiltonians of subsys_list
composite Hamiltonian composed of bare Hamiltonians of subsystems
independent of the external parameter

@@ -771,3 +770,3 @@ """

operator_list.append(
term.hamiltonian(self.subsys_list, bare_esys=bare_esys)
term.hamiltonian(self.subsystem_list, bare_esys=bare_esys)
)

@@ -798,3 +797,3 @@ else:

diag_qt_op = qt.Qobj(inpt=np.diagflat(evals[0:evals_count])) # type:ignore
return spec_utils.identity_wrap(diag_qt_op, subsystem, self.subsys_list)
return spec_utils.identity_wrap(diag_qt_op, subsystem, self.subsystem_list)

@@ -808,3 +807,3 @@ ###################################################################################

the `Qobj` operator for the full Hilbert space (perform wrapping in
identities for other subsys_list).
identities for other subsystems).

@@ -822,3 +821,3 @@ Parameters

diag_matrix[index, index] = diag_elements
return spec_utils.identity_wrap(diag_matrix, subsystem, self.subsys_list)
return spec_utils.identity_wrap(diag_matrix, subsystem, self.subsystem_list)

@@ -837,3 +836,3 @@ def hubbard_operator(self, j: int, k: int, subsystem: QuantumSys) -> Qobj:

operator = qt.states.basis(dim, j) * qt.states.basis(dim, k).dag()
return spec_utils.identity_wrap(operator, subsystem, self.subsys_list)
return spec_utils.identity_wrap(operator, subsystem, self.subsystem_list)

@@ -850,3 +849,3 @@ def annihilate(self, subsystem: QuantumSys) -> Qobj:

operator = qt.destroy(dim)
return spec_utils.identity_wrap(operator, subsystem, self.subsys_list)
return spec_utils.identity_wrap(operator, subsystem, self.subsystem_list)

@@ -950,2 +949,59 @@ ###################################################################################

def standardize_eigenvector_phases(self) -> None:
"""
Standardize the phases of the (dressed) eigenvectors.
"""
for idx, evec in enumerate(self._data["evecs"][0]):
phase = spec_utils.extract_phase(evec.data.toarray())
self._data["evecs"][0][idx] = evec * np.exp(-1j * phase)
def op_in_dressed_eigenbasis(self, **kwargs) -> Qobj:
"""
Express a subsystem operator in the dressed eigenbasis of the full system
(as opposed to both the "native basis" or "bare eigenbasis" of the subsystem).
`op_in_dressed_eigenbasis(...)` offers two different interfaces:
1. subsystem operators may be expressed as Callables
signature::
.op_in_dressed_eigenbasis(op=<Callable>)
2. subsystem operators may be passed as arrays, along with the
corresponding subsystem. In this case the user must additionally
specify if the operator is in the native, subsystem-internal
basis or the subsystem bare eigenbasis::
.op_in_dressed_eigenbasis(op=(<ndarray>, <subsys>),
op_in_bare_eigenbasis=<Bool>)
"""
op_callable_or_tuple = kwargs.pop("op")
if isinstance(op_callable_or_tuple, Callable):
subsys_index, op = self._parse_op(op_callable_or_tuple)
return self._op_in_dressed_eigenbasis(
op, subsys_index, op_in_bare_eigenbasis=False
)
else:
op, subsys = op_callable_or_tuple
op_in_bare_eigenbasis = kwargs.pop("op_in_bare_eigenbasis", False)
subsys_index = self.get_subsys_index(subsys)
return self._op_in_dressed_eigenbasis(
op, subsys_index, op_in_bare_eigenbasis
)
def _op_in_dressed_eigenbasis(
self, op: ndarray, subsys_index: int, op_in_bare_eigenbasis: bool = False
) -> Qobj:
bare_evecs = self._data["bare_evecs"][subsys_index][0]
id_wrapped_op = spec_utils.identity_wrap(
op,
self.subsystem_list[subsys_index],
self.subsystem_list,
op_in_eigenbasis=op_in_bare_eigenbasis,
evecs=bare_evecs,
)
dressed_evecs = self._data["evecs"][0]
dressed_op = id_wrapped_op.transform(dressed_evecs)
return dressed_op
###################################################################################

@@ -1015,4 +1071,4 @@ # HilbertSpace: add interaction and parsing arguments to .add_interaction

)
if self._lookup is not None:
self._lookup._out_of_sync = True
if self.lookup_exists():
self._out_of_sync = True

@@ -1019,0 +1075,0 @@ self.interaction_list.append(interaction)

@@ -22,2 +22,3 @@ # namedslots_array.py

from matplotlib import rc_context
from matplotlib.axes import Axes

@@ -610,2 +611,3 @@ from matplotlib.figure import Figure

@rc_context(settings.matplotlib_settings)
def plot(self, **kwargs) -> Tuple[Figure, Axes]:

@@ -612,0 +614,0 @@ if len(self._parameters) != 1:

@@ -20,5 +20,5 @@ # noise.py

import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
import scipy as sp
import scipy.constants

@@ -36,3 +36,5 @@ from matplotlib.axes import Axes

from scqubits.core.storage import SpectrumData
from scqubits.settings import matplotlib_settings
# flag that lets us show a warning about the default t1 behavior

@@ -123,2 +125,3 @@ # (i.e., total=True setting) only once. Using the standard warnings

@mpl.rc_context(matplotlib_settings)
def plot_coherence_vs_paramvals(

@@ -253,3 +256,2 @@ self,

for channel_idx, noise_channel in enumerate(noise_channels): # type:ignore
# case 1: noise_channel is a string representing the noise method

@@ -338,2 +340,3 @@ if isinstance(noise_channel, str):

@mpl.rc_context(matplotlib_settings)
def plot_t1_effective_vs_paramvals(

@@ -493,2 +496,3 @@ self,

@mpl.rc_context(matplotlib_settings)
def plot_t2_effective_vs_paramvals(

@@ -575,3 +579,2 @@ self,

if spectrum_data is None:
# We have to figure out the largest energy level involved in the

@@ -674,6 +677,4 @@ # calculations, to know how many levels we need from the diagonalization.

for n, noise_channel in enumerate(noise_channels):
# noise_channel is a string representing the noise method
if isinstance(noise_channel, str):
noise_channel_method = noise_channel

@@ -700,3 +701,2 @@

elif isinstance(noise_channel, tuple):
noise_channel_method = noise_channel[0]

@@ -703,0 +703,0 @@

@@ -73,3 +73,3 @@ # oscillator.py

# —Oscillator class———————————————————————————————————————————————————————————————————
# -Oscillator class-------------------------------------------------------------------

@@ -190,3 +190,3 @@

# —KerrOscillator class———————————————————————————————————————————————————————————————————
# -KerrOscillator class-------------------------------------------------------------------

@@ -223,3 +223,2 @@

) -> None:
self.K: float = K # type:ignore

@@ -226,0 +225,0 @@

@@ -118,2 +118,3 @@ # param_sweep.py

_lookup_exists = False
_parameters = descriptors.WatchedProperty(Parameters, "PARAMETERSWEEP_UPDATE")

@@ -608,3 +609,3 @@ _evals_count = descriptors.WatchedProperty(int, "PARAMETERSWEEP_UPDATE")

self._preslicing_reset()
self.reset_preslicing()

@@ -733,3 +734,3 @@ if not as_specdata:

self._preslicing_reset()
self.reset_preslicing()

@@ -892,3 +893,3 @@ if coloring == "plain":

if set to False (default), bare product states and dressed eigenstates are
identified if `\|<psi_bare\|psi_dressed>\|^2 > 0.5`; if True,
identified if `|<psi_bare|psi_dressed>|^2 > 0.5`; if True,
then identification will always take place based on which bare product state

@@ -962,3 +963,3 @@ has the maximum overlap

self._out_of_sync = False
self._preslicing_reset()
self.reset_preslicing()

@@ -1002,2 +1003,3 @@ dispatch.CENTRAL_DISPATCH.register("PARAMETERSWEEP_UPDATE", self)

self._lookup_exists = True
if self._deepcopy:

@@ -1334,2 +1336,3 @@ stored_hilbertspace = copy.deepcopy(self.hilbertspace)

) -> None:
self._lookup_exists = True
self._parameters = Parameters(paramvals_by_name)

@@ -1336,0 +1339,0 @@ self._hilbertspace = hilbertspace

@@ -33,2 +33,3 @@ # qubit_base.py

import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

@@ -40,2 +41,3 @@ import scipy as sp

from numpy import ndarray
from scipy.sparse import csc_matrix, dia_matrix

@@ -52,3 +54,3 @@ import scqubits.core.constants as constants

from scqubits.core.storage import DataStore, SpectrumData
from scqubits.settings import IN_IPYTHON
from scqubits.settings import IN_IPYTHON, matplotlib_settings
from scqubits.utils.cpu_switch import get_map_method

@@ -78,3 +80,3 @@ from scqubits.utils.misc import InfoBar, process_which

# —Generic quantum system container and Qubit base class——————————————————————————————
# -Generic quantum system container and Qubit base class------------------------------

@@ -236,3 +238,3 @@

# —QubitBaseClass———————————————————————————————————————————————————————————————————————————————————————————————————————
# -QubitBaseClass-------------------------------------------------------------------------------------------------------

@@ -258,3 +260,3 @@

evals = sp.linalg.eigh(
hamiltonian_mat, eigvals_only=True, eigvals=(0, evals_count - 1)
hamiltonian_mat, eigvals_only=True, subset_by_index=(0, evals_count - 1)
)

@@ -266,3 +268,3 @@ return np.sort(evals)

evals, evecs = sp.linalg.eigh(
hamiltonian_mat, eigvals_only=False, eigvals=(0, evals_count - 1)
hamiltonian_mat, eigvals_only=False, subset_by_index=(0, evals_count - 1)
)

@@ -375,2 +377,79 @@ evals, evecs = order_eigensystem(evals, evecs)

def process_op(
self,
native_op: Union[ndarray, csc_matrix],
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> Union[ndarray, csc_matrix]:
"""Processes the operator `native_op`: either hand back `native_op` unchanged, or transform it into the
energy eigenbasis. (Native basis refers to the basis used internally by each qubit, e.g., charge basis in the
case of `Transmon`.
Parameters
----------
native_op:
operator in native basis
energy_esys:
If `False` (default), returns operator in the native basis
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis
if energy_esys is the energy eigenspectrum, in the form of a tuple containing two ndarrays
(eigenvalues and energy eigenvectors), returns operator in the energy eigenbasis,
and does not have to recalculate eigenspectrum.
Returns
-------
`native_op` either unchanged or transformed into eigenenergy basis
"""
if isinstance(energy_esys, bool):
if not energy_esys:
return native_op
esys = self.eigensys(evals_count=self.truncated_dim)
else:
esys = energy_esys
evectors = esys[1][:, : self.truncated_dim]
return get_matrixelement_table(native_op, evectors)
def process_hamiltonian(
self,
native_hamiltonian: Union[ndarray, csc_matrix],
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> Union[ndarray, csc_matrix]:
"""Return qubit Hamiltonian in chosen basis: either return unchanged (i.e., in native basis) or transform
into eigenenergy basis
Parameters
----------
native_hamiltonian:
Hamiltonian in native basis
energy_esys:
If `False` (default), returns Hamiltonian in the native basis
If `True`, the energy eigenspectrum is computed, returns Hamiltonian in the energy eigenbasis
if energy_esys is the energy eigenspectrum, in the form of a tuple containing two ndarrays
(eigenvalues and energy eigenvectors), returns Hamiltonian in the energy eigenbasis,
and does not have to recalculate eigenspectrum.
Returns
-------
Hamiltonian, either unchanged in native basis, or transformed into eigenenergy basis
"""
if isinstance(energy_esys, bool):
if not energy_esys:
return native_hamiltonian
esys = self.eigensys(evals_count=self.truncated_dim)
else:
esys = energy_esys
evals = esys[0][: self.truncated_dim]
if isinstance(native_hamiltonian, ndarray):
return np.diag(evals)
return dia_matrix(evals).tocsc()
def anharmonicity(self) -> float:
"""Returns the qubit's anharmonicity, (E_2 - E_1) - (E_1 - E_0)."""
energies = self.eigenvals(evals_count=3)
return energies[2] - 2 * energies[1] + energies[0]
def E01(self) -> float:
"""Returns the qubit's fundamental energy splitting, E_1 - E_0."""
energies = self.eigenvals(evals_count=2)
return energies[1] - energies[0]
@overload

@@ -749,8 +828,11 @@ def matrixelement_table(

paramval_before = getattr(self, param_name)
assert spectrumdata.state_table is not None
for index, paramval in enumerate(param_vals):
evecs = spectrumdata.state_table[index]
setattr(self, param_name, paramval)
matelem_table[index] = self.matrixelement_table(
operator, evecs=evecs, evals_count=evals_count
)
setattr(self, param_name, paramval_before)

@@ -760,2 +842,3 @@ spectrumdata.matrixelem_table = matelem_table

@mpl.rc_context(matplotlib_settings)
def plot_evals_vs_paramvals(

@@ -802,2 +885,3 @@ self,

@mpl.rc_context(matplotlib_settings)
def plot_dispersion_vs_paramvals(

@@ -878,2 +962,3 @@ self,

@mpl.rc_context(matplotlib_settings)
def plot_matrixelements(

@@ -932,2 +1017,3 @@ self,

@mpl.rc_context(matplotlib_settings)
def plot_matelem_vs_paramvals(

@@ -1009,3 +1095,3 @@ self,

# —QubitBaseClass1d——————————————————————————————————————————————————————————————————
# -QubitBaseClass1d------------------------------------------------------------------

@@ -1055,2 +1141,3 @@

@mpl.rc_context(matplotlib_settings)
def plot_wavefunction(

@@ -1057,0 +1144,0 @@ self,

@@ -14,2 +14,3 @@ # spec_lookup.py

import itertools
import numbers

@@ -38,4 +39,2 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union

from scqubits.core.param_sweep import Parameters
from scqubits.core.qubit_base import QuantumSystem
from scqubits.io_utils.fileio_qutip import QutipEigenstates
from scqubits.utils.typedefs import QuantumSys

@@ -61,188 +60,2 @@

class SpectrumLookupAdapter:
"""Bridges earlier functionality provided by the `SpectrumLookup` class to
universal use of the `SpectrumLookupMixin` class (originally developed for
`ParameterSweep` and then extended to `HilbertSpace`)."""
def __init__(self, hilbertspace):
self.hilbertspace = hilbertspace
@property
def _out_of_sync(self):
return self.hilbertspace._out_of_sync
@_out_of_sync.setter
def _out_of_sync(self, value: bool):
self.hilbertspace._out_of_sync = value
@utils.DeprecationMessage(
"<HilbertSpace>.lookup.run is deprecated. "
"Use <HilbertSpace>.generate_lookup instead."
)
def run(self) -> None:
self.hilbertspace.generate_lookup()
@utils.check_sync_status
@utils.DeprecationMessage(
"<HilbertSpace>.lookup.dressed_index is "
"deprecated. Use <HilbertSpace>.dressed_index instead."
)
def dressed_index(self, bare_labels: Tuple[int, ...]) -> Union[int, None]:
"""
For given bare product state return the corresponding dressed-state index.
Parameters
----------
bare_labels:
bare_labels = (index, index2, ...)
Returns
-------
dressed state index closest to the specified bare state
"""
return self.hilbertspace.dressed_index(bare_labels)
@utils.check_sync_status
@utils.DeprecationMessage(
"<HilbertSpace>.lookup.bare_index is "
"deprecated. Use <HilbertSpace>.bare_index instead."
)
def bare_index(self, dressed_index: int) -> Union[Tuple[int, ...], None]:
"""
For given dressed index, look up the corresponding bare index.
Returns
-------
Bare state specification in tuple form. Example: (1,0,3) means subsystem 1
is in bare state 1, subsystem 2 in bare state 0, and subsystem 3 in bare
state 3.
"""
return self.hilbertspace.bare_index(dressed_index)
@utils.check_sync_status
@utils.DeprecationMessage(
"<HilbertSpace>.lookup.dressed_eigenstates is "
"deprecated. Use <HilbertSpace>['evecs'] instead."
)
def dressed_eigenstates(self) -> List["QutipEigenstates"]:
"""
Return the list of dressed eigenvectors
Returns
-------
dressed eigenvectors for the external parameter fixed to the value
indicated by the provided index
"""
return self.hilbertspace["evecs"]
@utils.check_sync_status
@utils.DeprecationMessage(
"<HilbertSpace>.lookup.dressed_eigenenergies is "
"deprecated. Use <HilbertSpace>['evals'] instead."
)
def dressed_eigenenergies(self) -> ndarray:
"""
Return the array of dressed eigenenergies
Returns
-------
dressed eigenenergies for the external parameter fixed to the value
indicated by the provided index
"""
return self.hilbertspace["evals"]
@utils.check_sync_status
@utils.DeprecationMessage(
"<HilbertSpace>.lookup.energy_bare_index is "
"deprecated. Use "
"<HilbertSpace>.energy_by_bare_index instead."
)
def energy_bare_index(self, bare_tuple: Tuple[int, ...]) -> Union[float, None]:
"""
Look up dressed energy most closely corresponding to the given bare-state labels
Parameters
----------
bare_tuple:
bare state indices
Returns
-------
dressed energy, if lookup successful
"""
return self.hilbertspace.energy_by_bare_index(bare_tuple)
@utils.check_sync_status
@utils.DeprecationMessage(
"<HilbertSpace>.lookup.energy_dressed_index is "
"deprecated. Use "
"<HilbertSpace>.energy_by_dressed_index instead."
)
def energy_dressed_index(self, dressed_index: int) -> float:
"""
Look up the dressed eigenenergy belonging to the given dressed index.
Parameters
----------
dressed_index:
index of dressed state of interest
Returns
-------
dressed energy
"""
return self.hilbertspace.energy_by_dressed_index(dressed_index)
@utils.check_sync_status
@utils.DeprecationMessage(
"<HilbertSpace>.lookup.bare_eigenstates is deprecated. "
"Use <HilbertSpace>.bare_eigenstates instead."
)
def bare_eigenstates(self, subsys: "QuantumSystem") -> ndarray:
"""
Return ndarray of bare eigenstates for given subsystem.
Eigenstates are expressed in the basis internal to the subsystem.
"""
return self.hilbertspace.bare_eigenstates(subsys)
@utils.check_sync_status
@utils.DeprecationMessage(
"<HilbertSpace>.lookup.bare_eigenenergies is deprecated. "
"Use <HilbertSpace>.bare_eigenvals instead."
)
def bare_eigenenergies(self, subsys: "QuantumSystem") -> ndarray:
"""
Return list of bare eigenenergies for given subsystem.
Parameters
----------
subsys:
Hilbert space subsystem for which bare eigendata is to be looked up
Returns
-------
bare eigenenergies for the specified subsystem
"""
return self.hilbertspace.bare_eigenvals(subsys)
@utils.DeprecationMessage(
"<HilbertSpace>.lookup.bare_productstate is deprecated. "
"Use <HilbertSpace>.bare_productstate instead."
)
def bare_productstate(self, bare_index: Tuple[int, ...]) -> Qobj:
"""
Return the bare product state specified by `bare_index`.
Parameters
----------
bare_index:
Returns
-------
ket in full Hilbert space
"""
return self.hilbertspace.bare_productstate(bare_index)
class SpectrumLookupMixin(MixinCompatible):

@@ -255,2 +68,17 @@ """

_inside_hilbertspace = False
def __init_subclass__(cls):
super().__init_subclass__()
if cls.__name__ == "HilbertSpace":
cls._inside_hilbertspace = True
else:
cls._inside_hilbertspace = False
def reset_preslicing(self):
if self._inside_hilbertspace:
self._current_param_indices = 0
else:
self._current_param_indices = slice(None, None, None)
@property

@@ -336,2 +164,3 @@ def _bare_product_states_labels(self) -> List[Tuple[int, ...]]:

@utils.check_lookup_exists
@utils.check_sync_status

@@ -364,2 +193,3 @@ def dressed_index(

@utils.check_lookup_exists
@utils.check_sync_status

@@ -439,2 +269,3 @@ def bare_index(

@utils.check_lookup_exists
@utils.check_sync_status

@@ -446,3 +277,3 @@ def energy_by_bare_index(

param_indices: Optional[NpIndices] = None,
) -> NamedSlotsNdarray: # the return value may also be np.nan
) -> Union[float, NamedSlotsNdarray]: # the return value may also be np.nan
"""

@@ -469,3 +300,3 @@ Look up dressed energy most closely corresponding to the given bare-state labels

return np.nan # type:ignore
if isinstance(dressed_index, int):
if isinstance(dressed_index, numbers.Number):
energy = self["evals"][param_indices + (dressed_index,)]

@@ -477,3 +308,3 @@ if subtract_ground:

dressed_index = np.asarray(dressed_index)
energies = np.empty_like(dressed_index)
energies = np.empty_like(dressed_index, dtype=np.float_)
it = np.nditer(dressed_index, flags=["multi_index", "refs_ok"])

@@ -494,2 +325,3 @@ sliced_energies = self["evals"][param_indices]

@utils.check_lookup_exists
@utils.check_sync_status

@@ -501,3 +333,3 @@ def energy_by_dressed_index(

param_indices: Optional[Tuple[int, ...]] = None,
) -> float:
) -> Union[float, NamedSlotsNdarray]:
"""

@@ -526,2 +358,3 @@ Look up the dressed eigenenergy belonging to the given dressed index,

@utils.check_lookup_exists
@utils.check_sync_status

@@ -536,9 +369,10 @@ def bare_eigenstates(

Eigenstates are expressed in the basis internal to the subsystems. Usually to be
with pre-slicing.
used with pre-slicing when part of `ParameterSweep`.
"""
param_indices_tuple = self.set_npindextuple(param_indices)
subsys_index = self.hilbertspace.get_subsys_index(subsys)
self._current_param_indices = slice(None, None, None)
self.reset_preslicing()
return self["bare_evecs"][subsys_index][param_indices_tuple]
@utils.check_lookup_exists
@utils.check_sync_status

@@ -568,3 +402,3 @@ def bare_eigenvals(

subsys_index = self.hilbertspace.get_subsys_index(subsys)
self._current_param_indices = slice(None, None, None)
self.reset_preslicing()
return self["bare_evals"][subsys_index][param_indices_tuple]

@@ -571,0 +405,0 @@

@@ -29,3 +29,3 @@ # storage.py

# —WaveFunction class———————————————————————————————————————————————————————————————————
# -WaveFunction class-------------------------------------------------------------------

@@ -78,3 +78,3 @@

# —WaveFunctionOnGrid class—————————————————————————————————————————————————————————————
# -WaveFunctionOnGrid class-------------------------------------------------------------

@@ -104,3 +104,3 @@

# —BaseData class———————————————————————————————————————————————————————————————————————
# -BaseData class-----------------------------------------------------------------------

@@ -166,3 +166,3 @@

# —SpectrumData class———————————————————————————————————————————————————————————————————
# -SpectrumData class-------------------------------------------------------------------

@@ -169,0 +169,0 @@

@@ -42,3 +42,3 @@ # transmon.py

# —Cooper pair box / transmon——————————————————————————————————————————————
# -Cooper pair box / transmon----------------------------------------------

@@ -153,28 +153,162 @@

def n_operator(self) -> ndarray:
"""Returns charge operator `n` in the charge basis"""
@staticmethod
def find_EJ_EC(
E01: float, anharmonicity: float, ng=0, ncut=30
) -> Tuple[float, float]:
"""
Finds the EJ and EC values given a qubit splitting `E01` and `anharmonicity`.
Parameters
----------
E01:
qubit transition energy
anharmonicity:
absolute qubit anharmonicity, (E2-E1) - (E1-E0)
ng:
offset charge (default: 0)
ncut:
charge number cutoff (default: 30)
Returns
-------
A tuple of the EJ and EC values representing the best fit.
"""
tmon = Transmon(EJ=10.0, EC=0.1, ng=ng, ncut=ncut)
start_EJ_EC = np.array([tmon.EJ, tmon.EC])
def cost_func(EJ_EC: Tuple[float, float]) -> float:
EJ, EC = EJ_EC
tmon.EJ = EJ
tmon.EC = EC
energies = tmon.eigenvals(evals_count=3)
computed_E01 = energies[1] - energies[0]
computed_anharmonicity = energies[2] - energies[1] - computed_E01
cost = (E01 - computed_E01) ** 2
cost += (anharmonicity - computed_anharmonicity) ** 2
return cost
return sp.optimize.minimize(cost_func, start_EJ_EC).x
def n_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns charge operator n in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns charge operator n in the charge basis.
If `True`, energy eigenspectrum is computed, returns charge operator n in the energy eigenbasis.
If `energy_esys = esys`, where `esys` is a tuple containing two ndarrays (eigenvalues and energy
eigenvectors), returns charge operator n in the energy eigenbasis, and does not have to recalculate the
eigenspectrum.
Returns
-------
Charge operator n in chosen basis as ndarray.
For `energy_esys=True`, n has dimensions of `truncated_dim` x `truncated_dim`.
If an actual eigensystem is handed to `energy_sys`, then `n` has dimensions of m x m,
where m is the number of given eigenvectors.
"""
diag_elements = np.arange(-self.ncut, self.ncut + 1, 1)
return np.diag(diag_elements)
native = np.diag(diag_elements)
return self.process_op(native_op=native, energy_esys=energy_esys)
def exp_i_phi_operator(self) -> ndarray:
"""Returns operator :math:`e^{i\\varphi}` in the charge basis"""
def exp_i_phi_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator :math:`e^{i\\varphi}` in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator :math:`e^{i\\varphi}` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator :math:`e^{i\\varphi}` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator :math:`e^{i\\varphi}` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator :math:`e^{i\\varphi}` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`e^{i\\varphi}` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`e^{i\\varphi}` has dimensions of m x m,
for m given eigenvectors.
"""
dimension = self.hilbertdim()
entries = np.repeat(1.0, dimension - 1)
exp_op = np.diag(entries, -1)
return exp_op
return self.process_op(native_op=exp_op, energy_esys=energy_esys)
def cos_phi_operator(self) -> ndarray:
"""Returns operator :math:`\\cos \\varphi` in the charge basis"""
def cos_phi_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator :math:`\\cos \\varphi` in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator :math:`\\cos \\varphi` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator :math:`\\cos \\varphi` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator :math:`\\cos \\varphi` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator :math:`\\cos \\varphi` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`\\cos \\varphi` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\cos \\varphi` has dimensions of m x m,
for m given eigenvectors.
"""
cos_op = 0.5 * self.exp_i_phi_operator()
cos_op += cos_op.T
return cos_op
return self.process_op(native_op=cos_op, energy_esys=energy_esys)
def sin_phi_operator(self) -> ndarray:
"""Returns operator :math:`\\sin \\varphi` in the charge basis"""
def sin_phi_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator :math:`\\sin \\varphi` in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator :math:`\\sin \\varphi` in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator :math:`\\sin \\varphi` in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator :math:`\\sin \\varphi` in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator :math:`\\sin \\varphi` in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless energy_esys is specified, :math:`\\sin \\varphi` has dimensions of truncated_dim
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\sin \\varphi` has dimensions of m x m,
for m given eigenvectors.
"""
sin_op = -1j * 0.5 * self.exp_i_phi_operator()
sin_op += sin_op.conjugate().T
return sin_op
return self.process_op(native_op=sin_op, energy_esys=energy_esys)
def hamiltonian(self) -> ndarray:
"""Returns Hamiltonian in charge basis"""
def hamiltonian(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns Hamiltonian in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns Hamiltonian in the charge basis.
If `True`, the energy eigenspectrum is computed; returns Hamiltonian in the energy eigenbasis.
If `energy_esys = esys`, where `esys` is a tuple containing two ndarrays (eigenvalues and energy
eigenvectors); then return the Hamiltonian in the energy eigenbasis, do not recalculate eigenspectrum.
Returns
-------
Hamiltonian in chosen basis as ndarray. For `energy_esys=False`, the Hamiltonian has dimensions of
`truncated_dim` x `truncated_dim`. For `energy_sys=esys`, the Hamiltonian has dimensions of m x m,
for m given eigenvectors.
"""
dimension = self.hilbertdim()

@@ -190,14 +324,56 @@ hamiltonian_mat = np.diag(

hamiltonian_mat[ind + 1, ind] = -self.EJ / 2.0
return hamiltonian_mat
return self.process_hamiltonian(
native_hamiltonian=hamiltonian_mat, energy_esys=energy_esys
)
def d_hamiltonian_d_ng(self) -> ndarray:
"""Returns operator representing a derivative of the Hamiltonian with respect to
charge offset `ng`."""
return -8 * self.EC * self.n_operator()
def d_hamiltonian_d_ng(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
charge offset `ng` in the charge or eigenenergy basis.
def d_hamiltonian_d_EJ(self) -> ndarray:
"""Returns operator representing a derivative of the Hamiltonian with respect
to EJ."""
return -self.cos_phi_operator()
Parameters
----------
energy_esys:
If `False` (default), returns operator in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where `esys` is a tuple containing two ndarrays (eigenvalues and energy
eigenvectors), returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m,
for m given eigenvectors.
"""
native = -8 * self.EC * self.n_operator(energy_esys=energy_esys)
return self.process_op(native_op=native, energy_esys=energy_esys)
def d_hamiltonian_d_EJ(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
EJ in the charge or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where `esys` is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m,
for m given eigenvectors.
"""
native = -self.cos_phi_operator()
return self.process_op(native_op=native, energy_esys=energy_esys)
def hilbertdim(self) -> int:

@@ -409,3 +585,3 @@ """Returns Hilbert space dimension"""

# — Flux-tunable Cooper pair box / transmon———————————————————————————————————————————
# - Flux-tunable Cooper pair box / transmon-------------------------------------------

@@ -509,6 +685,31 @@

def d_hamiltonian_d_flux(self) -> ndarray:
"""Returns operator representing a derivative of the Hamiltonian with respect
to `flux`."""
return (
def d_hamiltonian_d_flux(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
`flux` in the charge or eigenenergy basis.
Here, the derivative is taken with respect to flux before the qubit's :math:`\phi` degree of
freedom in the Hamiltonian is shifted by a flux-dependent quantity :math:`\varphi_{0}`
(see Eq. 2.17 and surrounding text in PRA 76, 042319 (2007)). Then only after the flux
derivative is taken, both the Hamiltonian as well as its flux derivative are assumed to
be shifted by :math:`\varphi_{0}`.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the charge basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis as ndarray. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m,
for m given eigenvectors.
"""
native = (
np.pi

@@ -524,3 +725,10 @@ * self.EJmax

* self.cos_phi_operator()
- np.pi * self.EJmax * self.d
/ np.sqrt(
np.cos(np.pi * self.flux) ** 2
+ self.d**2 * np.sin(np.pi * self.flux) ** 2
)
* self.sin_phi_operator()
)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -527,0 +735,0 @@ def _compute_dispersion(

@@ -297,6 +297,8 @@ # zeropi_full.py

def hamiltonian(
self, return_parts: bool = False
) -> Union[csc_matrix, Tuple[csc_matrix, ndarray, ndarray, float]]:
"""Returns Hamiltonian in basis obtained by discretizing phi, employing charge basis for theta, and Fock
basis for zeta.
self,
return_parts: bool = False,
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> Union[csc_matrix, Tuple[csc_matrix, ndarray, ndarray, ndarray]]:
r"""Returns Hamiltonian in basis obtained by discretizing :math:`\phi`, employing
charge basis for :math:`\theta`, and Fock basis for :math:`\zeta`, or in the eigenenergy basis.

@@ -306,3 +308,17 @@ Parameters

return_parts:
If set to true, `hamiltonian` returns [hamiltonian, evals, evecs, g_coupling_matrix]
If set to true, `hamiltonian` returns
[hamiltonian, evals, evecs, g_coupling_matrix]
energy_esys:
If `False` (default), returns Hamiltonian in native Hamiltonian basis
If `True`, the energy eigenspectrum is computed, returns Hamiltonian in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns Hamiltonian in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Hamiltonian in chosen basis as csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, the Hamiltonian has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m,
for m given eigenvectors.
"""

@@ -338,44 +354,122 @@ zeropi_dim = self.zeropi_cutoff

) + sparse.kron(zeropi_coupling.conjugate().T, op.creation_sparse(zeta_dim))
hmtocsc = hamiltonian_mat.tocsc()
if return_parts:
return hamiltonian_mat.tocsc(), zeropi_evals, zeropi_evecs, gmat
return (
self.process_hamiltonian(
native_hamiltonian=hmtocsc, energy_esys=energy_esys
),
zeropi_evals,
zeropi_evecs,
gmat,
)
return self.process_hamiltonian(
native_hamiltonian=hmtocsc, energy_esys=energy_esys
)
return hamiltonian_mat.tocsc()
def d_hamiltonian_d_flux(
self,
zeropi_evecs: ndarray = None,
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> Union[ndarray, csc_matrix]:
r"""
Calculates a derivative of the Hamiltonian w.r.t flux, at the current value of flux,
as stored in the object. The returned operator is in the product basis or eigenenergy basis.
def d_hamiltonian_d_flux(self, zeropi_evecs: ndarray = None) -> csc_matrix:
r"""Calculates a derivative of the Hamiltonian w.r.t flux, at the current value of flux,
as stored in the object. The returned operator is in the product basis
Helper method _zeropi_operator_in_product_basis is employed which converts
a zeropi operator into one in the product basis. If user already has zeropi eigenvectors, user can input
as zeropi_evecs=ndarray.
The flux is assumed to be given in the units of the ratio \Phi_{ext}/\Phi_0.
So if \frac{\partial H}{ \partial \Phi_{\rm ext}}, is needed, the expr returned
by this function, needs to be multiplied by 1/\Phi_0.
Parameters
----------
zeropi_evecs:
If None (default), helper method _zeropi_operator_in_product_basis calculates zeropi eigenvectors and uses
them to convert operator to the product basis, if product basis is chosen.
If zeropi_evecs = zeropievecs, where zeropievecs is an ndarray, and product basis is chosen,
helper method _zeropi_operator_in_product_basis uses zeropievecs to convert the operator
to the product basis.
energy_esys:
If `False` (default), returns operator in the product basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
matrix representing the derivative of the Hamiltonian
Operator in chosen basis. If product basis is chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return self._zeropi_operator_in_product_basis(
native = self._zeropi_operator_in_product_basis(
self._zeropi.d_hamiltonian_d_flux(), zeropi_evecs=zeropi_evecs
)
return self.process_op(native_op=native, energy_esys=energy_esys)
def d_hamiltonian_d_EJ(self, zeropi_evecs: ndarray = None) -> csc_matrix:
r"""Calculates a derivative of the Hamiltonian w.r.t EJ.
def d_hamiltonian_d_EJ(
self,
zeropi_evecs: ndarray = None,
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> Union[ndarray, csc_matrix]:
r"""
Calculates a derivative of the Hamiltonian w.r.t EJ. The returned operator is in the
product basis or eigenenergy basis.
Helper method _zeropi_operator_in_product_basis is employed which converts
a zeropi operator into one in the product basis. If user already has zeropi eigenvectors, user can input
as zeropi_evecs=ndarray.
Parameters
----------
zeropi_evecs:
If None (default), helper method _zeropi_operator_in_product_basis calculates zeropi eigenvectors and uses
them to convert operator to the product basis, if product basis is chosen.
If zeropi_evecs = zeropievecs, where zeropievecs is an ndarray, and product basis is chosen,
helper method _zeropi_operator_in_product_basis uses zeropievecs to convert the operator
to the product basis.
energy_esys:
If `False` (default), returns operator in the product basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
matrix representing the derivative of the Hamiltonian
Operator in chosen basis. If product basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return self._zeropi_operator_in_product_basis(
native = self._zeropi_operator_in_product_basis(
self._zeropi.d_hamiltonian_d_EJ(), zeropi_evecs=zeropi_evecs
)
return self.process_op(native_op=native, energy_esys=energy_esys)
def d_hamiltonian_d_ng(self) -> csc_matrix:
r"""Calculates a derivative of the Hamiltonian w.r.t ng.
as stored in the object.
def d_hamiltonian_d_ng(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Calculates a derivative of the Hamiltonian w.r.t ng.
Returns matrix representing a derivative of the Hamiltonian in the native Hamiltonian basis
or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
matrix representing the derivative of the Hamiltonian
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return -8 * self.EC * self.n_theta_operator()
native = -8 * self.EC * self.n_theta_operator()
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -412,29 +506,118 @@ def _zeropi_operator_in_product_basis(

def i_d_dphi_operator(self, zeropi_evecs: ndarray = None) -> csc_matrix:
def i_d_dphi_operator(
self,
zeropi_evecs: ndarray = None,
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> Union[ndarray, csc_matrix]:
r"""
Operator :math:`i d/d\phi`.
Returns :math:`i d/d\phi` operator in the product or eigenenergy basis.
Helper method _zeropi_operator_in_product_basis is employed which converts
a zeropi operator into one in the product basis. If user already has zeropi eigenvectors, user can input
as zeropi_evecs=ndarray.
Parameters
----------
zeropi_evecs:
If None (default), helper method _zeropi_operator_in_product_basis calculates zeropi eigenvectors and uses
them to convert operator to the product basis, if product basis is chosen.
If zeropi_evecs = zeropievecs, where zeropievecs is an ndarray, and product basis is chosen,
helper method _zeropi_operator_in_product_basis uses zeropievecs to convert the operator
to the product basis.
energy_esys:
If `False` (default), returns operator in the product basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If product basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return self._zeropi_operator_in_product_basis(
native = self._zeropi_operator_in_product_basis(
self._zeropi.i_d_dphi_operator(), zeropi_evecs=zeropi_evecs
)
return self.process_op(native_op=native, energy_esys=energy_esys)
def n_theta_operator(self, zeropi_evecs: ndarray = None) -> csc_matrix:
def n_theta_operator(
self,
zeropi_evecs: ndarray = None,
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> Union[ndarray, csc_matrix]:
r"""
Operator :math:`n_\theta`.
Returns :math:`n_\theta` operator in the product or eigenenergy basis.
Helper method _zeropi_operator_in_product_basis is employed which converts
a zeropi operator into one in the product basis. If user already has zeropi eigenvectors, user can input
as zeropi_evecs=ndarray.
Parameters
----------
zeropi_evecs:
If None (default), helper method _zeropi_operator_in_product_basis calculates zeropi eigenvectors and uses
them to convert operator to the product basis, if product basis is chosen.
If zeropi_evecs = zeropievecs, where zeropievecs is an ndarray, and product basis is chosen,
helper method _zeropi_operator_in_product_basis uses zeropievecs to convert the operator
to the product basis.
energy_esys:
If `False` (default), returns operator in the product basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If product basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return self._zeropi_operator_in_product_basis(
native = self._zeropi_operator_in_product_basis(
self._zeropi.n_theta_operator(), zeropi_evecs=zeropi_evecs
)
return self.process_op(native_op=native, energy_esys=energy_esys)
def phi_operator(self, zeropi_evecs: ndarray = None) -> csc_matrix:
def phi_operator(
self,
zeropi_evecs: ndarray = None,
energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False,
) -> Union[ndarray, csc_matrix]:
r"""
Operator :math:`\phi`.
Returns :math:`\phi` operator in the product or eigenenergy basis.
Helper method _zeropi_operator_in_product_basis is employed which converts
a zeropi operator into one in the product basis. If user already has zeropi eigenvectors, user can input
as zeropi_evecs=ndarray.
Parameters
----------
zeropi_evecs:
If None (default), helper method _zeropi_operator_in_product_basis calculates zeropi eigenvectors and uses
them to convert operator to the product basis, if product basis is chosen.
If zeropi_evecs = zeropievecs, where zeropievecs is an ndarray, and product basis is chosen,
helper method _zeropi_operator_in_product_basis uses zeropievecs to convert the operator
to the product basis.
energy_esys:
If `False` (default), returns operator in the product basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
scipy.sparse.csc_matrix
Operator in chosen basis. If product basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return self._zeropi_operator_in_product_basis(
native = self._zeropi_operator_in_product_basis(
self._zeropi.phi_operator(), zeropi_evecs=zeropi_evecs
)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -503,5 +686,7 @@ def hilbertdim(self) -> int:

) -> ndarray:
"""Returns a matrix of coupling strengths g_{ll'} [cmp. Dempster et al., text above Eq. (17)], using the states
from 'zeropi_states'. If `zeropi_states==None`, then a set of `self.zeropi` eigenstates is calculated. Only in
that case is `which` used for the eigenstate number (and hence the coupling matrix size).
"""Returns a matrix of coupling strengths g_{ll'} [cmp. Dempster et al., text
above Eq. (17)], using the states from 'zeropi_states'. If
`zeropi_states==None`, then a set of `self.zeropi` eigenstates is calculated.
Only in that case is `which` used for the eigenstate number (and hence the
coupling matrix size).
"""

@@ -508,0 +693,0 @@ if evals_count is None:

@@ -370,14 +370,34 @@ # zeropi.py

def hamiltonian(self) -> csc_matrix:
"""Calculates Hamiltonian in basis obtained by discretizing phi and employing
charge basis for theta.
def hamiltonian(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> csc_matrix:
r"""
Calculates Hamiltonian in basis obtained by discretizing :math:`\phi` and employing
charge basis for :math:`\theta` or in the eigenenergy basis. Returns matrix representing
the potential energy operator.
Parameters
----------
energy_esys:
If `False` (default), returns Hamiltonian in basis obtained by discretizing :math:`\phi` and employing
charge basis for :math:`\theta`.
If `True`, the energy eigenspectrum is computed, returns Hamiltonian in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns Hamiltonian in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
matrix representing the potential energy operator
Hamiltonian in chosen basis as csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, the Hamiltonian has dimensions of `truncated_dim`
x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m,
for m given eigenvectors.
"""
return self.sparse_kinetic_mat() + self.sparse_potential_mat()
hamiltonian_mat = self.sparse_kinetic_mat() + self.sparse_potential_mat()
return self.process_hamiltonian(
native_hamiltonian=hamiltonian_mat, energy_esys=energy_esys
)
def sparse_d_potential_d_flux_mat(self) -> csc_matrix:
r"""Calculates a of the potential energy w.r.t flux, at the current value of
r"""
Calculates a derivative of the potential energy w.r.t flux, at the current value of
flux, as stored in the object.

@@ -405,15 +425,30 @@

def d_hamiltonian_d_flux(self) -> csc_matrix:
r"""Calculates a derivative of the Hamiltonian w.r.t flux, at the current value
of flux, as stored in the object.
def d_hamiltonian_d_flux(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Calculates a derivative of the Hamiltonian w.r.t flux, at the current value
of flux, as stored in the object. The flux is assumed to be given in the units
of the ratio :math:`\Phi_{ext}/\Phi_0`.
Returns matrix representing a derivative of the Hamiltonian in the native Hamiltonian basis
or eigenenergy basis.
The flux is assumed to be given in the units of the ratio \Phi_{ext}/\Phi_0.
So if \frac{\partial H}{ \partial \Phi_{\rm ext}}, is needed, the expr returned
by this function, needs to be multiplied by 1/\Phi_0.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
matrix representing the derivative of the Hamiltonian
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return self.sparse_d_potential_d_flux_mat()
native = self.sparse_d_potential_d_flux_mat()
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -433,20 +468,55 @@ def sparse_d_potential_d_EJ_mat(self) -> csc_matrix:

def d_hamiltonian_d_EJ(self) -> csc_matrix:
r"""Calculates a derivative of the Hamiltonian w.r.t EJ.
def d_hamiltonian_d_EJ(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Calculates a derivative of the Hamiltonian w.r.t EJ.
Returns matrix representing a derivative of the Hamiltonian in the native Hamiltonian basis
or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
matrix representing the derivative of the Hamiltonian
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return self.sparse_d_potential_d_EJ_mat()
native = self.sparse_d_potential_d_EJ_mat()
return self.process_op(native_op=native, energy_esys=energy_esys)
def d_hamiltonian_d_ng(self) -> csc_matrix:
r"""Calculates a derivative of the Hamiltonian w.r.t ng.
as stored in the object.
def d_hamiltonian_d_ng(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Calculates a derivative of the Hamiltonian w.r.t ng as stored in the object.
Returns matrix representing a derivative of the Hamiltonian in the native Hamiltonian basis
or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
matrix representing the derivative of the Hamiltonian
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return -8 * self.EC * self.n_theta_operator()
native = -8 * self.EC * self.n_theta_operator()
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -488,11 +558,48 @@ def _identity_phi(self) -> csc_matrix:

def phi_operator(self) -> csc_matrix:
def phi_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Operator :math:`\phi`.
Returns :math:`\phi` operator in the native or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return sparse.kron(self._phi_operator(), self._identity_theta(), format="csc")
native = sparse.kron(self._phi_operator(), self._identity_theta(), format="csc")
return self.process_op(native_op=native, energy_esys=energy_esys)
def n_theta_operator(self) -> csc_matrix:
def n_theta_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Operator :math:`n_\theta`.
Returns :math:`n_\theta` operator in the native or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""

@@ -504,3 +611,4 @@ dim_theta = 2 * self.ncut + 1

).tocsc()
return sparse.kron(self._identity_phi(), n_theta_matrix, format="csc")
native = sparse.kron(self._identity_phi(), n_theta_matrix, format="csc")
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -549,9 +657,28 @@ def _sin_phi_operator(self, x: float = 0) -> csc_matrix:

def cos_theta_operator(self) -> csc_matrix:
def cos_theta_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Operator :math:`\cos(\theta)`.
Returns :math:`\cos(\theta)` operator in the native or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return sparse.kron(
native = sparse.kron(
self._identity_phi(), self._cos_theta_operator(), format="csc"
)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -568,6 +695,6 @@ def _sin_theta_operator(self) -> csc_matrix:

sparse.dia_matrix(
([1.0] * dim_theta, [1]), shape=(dim_theta, dim_theta)
([1.0] * dim_theta, [-1]), shape=(dim_theta, dim_theta)
)
- sparse.dia_matrix(
([1.0] * dim_theta, [-1]), shape=(dim_theta, dim_theta)
([1.0] * dim_theta, [1]), shape=(dim_theta, dim_theta)
)

@@ -578,9 +705,28 @@ ).tocsc()

def sin_theta_operator(self) -> csc_matrix:
def sin_theta_operator(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> Union[ndarray, csc_matrix]:
r"""
Operator :math:`\sin(\theta)`.
Returns :math:`\sin(\theta)` operator in the native or eigenenergy basis.
Parameters
----------
energy_esys:
If `False` (default), returns operator in the native basis.
If `True`, the energy eigenspectrum is computed, returns operator in the energy eigenbasis.
If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors),
returns operator in the energy eigenbasis, and does not have to recalculate eigenspectrum.
Returns
-------
Operator in chosen basis. If native basis chosen, operator
returned as a csc_matrix. If the eigenenergy basis is chosen,
unless `energy_esys` is specified, operator has dimensions of `truncated_dim`
x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen,
operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray.
"""
return sparse.kron(
native = sparse.kron(
self._identity_phi(), self._sin_theta_operator(), format="csc"
)
return self.process_op(native_op=native, energy_esys=energy_esys)

@@ -587,0 +733,0 @@ def plot_potential(

@@ -15,4 +15,6 @@ # explorer_panels.py

# import matplotlib as mlp
import numpy as np
from matplotlib import rc_context
from matplotlib.axes import Axes

@@ -27,2 +29,3 @@ from matplotlib.figure import Figure

from scqubits.core.oscillator import Oscillator
from scqubits.settings import matplotlib_settings
from scqubits.utils.misc import tuple_to_short_str

@@ -35,2 +38,3 @@

@rc_context(matplotlib_settings)
def display_bare_spectrum(

@@ -61,2 +65,3 @@ sweep: "ParameterSweep",

@rc_context(matplotlib_settings)
def display_anharmonicity(

@@ -82,2 +87,3 @@ sweep: "ParameterSweep",

@rc_context(matplotlib_settings)
def display_matrixelements(

@@ -111,2 +117,3 @@ sweep: "ParameterSweep",

@rc_context(matplotlib_settings)
def display_matrixelement_sweep(

@@ -158,2 +165,3 @@ sweep: "ParameterSweep",

@rc_context(matplotlib_settings)
def display_bare_wavefunctions(

@@ -180,2 +188,3 @@ sweep: "ParameterSweep",

@rc_context(matplotlib_settings)
def display_transitions(

@@ -206,2 +215,3 @@ sweep: "ParameterSweep",

@rc_context(matplotlib_settings)
def display_cross_kerr(

@@ -219,3 +229,3 @@ sweep: "ParameterSweep",

title = f"ac Stark: {subsys1.id_str} + {subsys2.id_str}"
ylabel = f"ac Stark shift $\chi^{{{subsys1_index},{subsys2_index}}}$"
ylabel = rf"ac Stark shift $\chi^{{{subsys1_index},{subsys2_index}}}$"
levels_list = [1]

@@ -263,2 +273,3 @@ kerr_data = sweep["chi"][subsys1_index, subsys2_index]

@rc_context(matplotlib_settings)
def display_self_kerr(

@@ -265,0 +276,0 @@ sweep: "ParameterSweep",

@@ -96,28 +96,37 @@ """

# Matplotlib options -------------------------------------------------------------------
# set custom matplotlib color cycle
mpl.rcParams["axes.prop_cycle"] = cycler(
color=[
"#016E82",
"#333795",
"#2E5EAC",
"#4498D3",
"#CD85B9",
"#45C3D1",
"#AA1D3F",
"#F47752",
"#19B35A",
"#EDE83B",
"#ABD379",
"#F9E6BE",
]
)
# set matplotlib defaults for use in @mpl.rc_context
off_black = "0.2"
matplotlib_settings = {
"axes.prop_cycle": cycler(
color=[
"#016E82",
"#333795",
"#2E5EAC",
"#4498D3",
"#CD85B9",
"#45C3D1",
"#AA1D3F",
"#F47752",
"#19B35A",
"#EDE83B",
"#ABD379",
"#F9E6BE",
]
),
"font.family": "IBM Plex Sans, Roboto, Arial, Helvetica, DejaVu Sans",
"font.size": 11,
"font.weight": 500,
"axes.labelsize": 11,
"axes.titlesize": 11,
"xtick.labelsize": 10,
"ytick.labelsize": 10,
"xtick.labelcolor": off_black,
"ytick.labelcolor": off_black,
"xtick.color": off_black,
"ytick.color": off_black,
"axes.labelcolor": off_black,
"axes.edgecolor": off_black,
"axes.titlecolor": off_black
}
# set matplotlib defaults
mpl.rcParams["font.family"] = "sans-serif"
mpl.rcParams["font.sans-serif"] = "Roboto, Arial, Helvetica, DejaVu Sans"
mpl.rcParams["font.size"] = 11
mpl.rcParams["axes.labelsize"] = 11
mpl.rcParams["axes.titlesize"] = 11
mpl.rcParams["xtick.labelsize"] = 10
mpl.rcParams["ytick.labelsize"] = 10

@@ -124,0 +133,0 @@ # toggle top and right axes on and off

@@ -18,2 +18,3 @@ # test_hilbertspace.py

import scqubits as scq
import qutip as qt

@@ -387,1 +388,88 @@ from scqubits.core.hilbert_space import HilbertSpace

hilbertspace_new = scq.HilbertSpace.create()
def test_HilbertSpace_op_in_dressed_basis(self):
E_osc_a = 4.0
E_osc_b = 3.2
g = 0.01
Delta = E_osc_a - E_osc_b
truncated_dim = 4
theta = 0.5 * np.arctan(2 * g / Delta)
osc_a = scq.Oscillator(E_osc=E_osc_a, truncated_dim=truncated_dim)
osc_b = scq.Oscillator(E_osc=E_osc_b, truncated_dim=truncated_dim)
hilbert_space = scq.HilbertSpace([osc_a, osc_b])
hilbert_space.add_interaction(
g=g,
op1=osc_a.creation_operator,
op2=osc_b.annihilation_operator,
add_hc=True,
)
hilbert_space.generate_lookup()
hilbert_space.standardize_eigenvector_phases()
# analytic answer for the dressed operator based on a Bogoliubov transformation
a_id_wrap = scq.utils.spectrum_utils.identity_wrap(
osc_a.annihilation_operator(), osc_a, hilbert_space.subsystem_list
)
b_id_wrap = scq.utils.spectrum_utils.identity_wrap(
osc_b.annihilation_operator(), osc_b, hilbert_space.subsystem_list
)
analytic_op = np.cos(theta) * a_id_wrap - np.sin(theta) * b_id_wrap
# need to order this operator according to the dressed indices for later
# comparison with operators expressed in the dressed basis
ordered_bare_indices = [
hilbert_space.bare_index(idx) for idx in range(truncated_dim**2)
]
ordered_basis_states = [
qt.tensor(qt.basis(truncated_dim, idx_a), qt.basis(truncated_dim, idx_b))
for (idx_a, idx_b) in ordered_bare_indices
]
# consider only matrix elements unaffected by the truncation level
analytic_op_ordered = qt.Qobj(
analytic_op.transform(ordered_basis_states)[0:10, 0:10]
)
op1 = qt.Qobj(
hilbert_space.op_in_dressed_eigenbasis(op=osc_a.annihilation_operator)[
0:10, 0:10
]
)
op2 = qt.Qobj(
hilbert_space.op_in_dressed_eigenbasis(
op=(osc_a.annihilation_operator(), osc_a)
)[0:10, 0:10]
)
op3 = qt.Qobj(
hilbert_space.op_in_dressed_eigenbasis(
op=(osc_a.annihilation_operator(), osc_a), op_in_bare_eigenbasis=True
)[0:10, 0:10]
)
op4 = qt.Qobj(
hilbert_space.op_in_dressed_eigenbasis(
op=(osc_a.annihilation_operator(), osc_a), op_in_bare_eigenbasis=False
)[0:10, 0:10]
)
assert analytic_op_ordered == op1
assert analytic_op_ordered == op2
assert analytic_op_ordered == op3
assert analytic_op_ordered == op4
def test_HilbertSpace_op_in_dressed_basis_native_vs_bare_basis(self):
E_osc = 4.0
g = 0.01
truncated_dim = 4
tmon = scq.Transmon(
EJ=10.0, EC=0.2, ng=0.0, ncut=15, truncated_dim=truncated_dim
)
osc = scq.Oscillator(E_osc=E_osc, truncated_dim=truncated_dim)
hilbert_space = scq.HilbertSpace([tmon, osc])
hilbert_space.add_interaction(
g=g,
op1=tmon.n_operator,
op2=osc.annihilation_operator,
add_hc=True,
)
hilbert_space.generate_lookup()
op1 = hilbert_space.op_in_dressed_eigenbasis(op=tmon.n_operator)
n_op_bare_eigenbasis_v2 = tmon.n_operator(energy_esys=True)
op2 = hilbert_space.op_in_dressed_eigenbasis(
op=(n_op_bare_eigenbasis_v2, tmon), op_in_bare_eigenbasis=True
)
assert op1 == op2

@@ -39,2 +39,3 @@ # explorer_widget.py

from scqubits.utils import misc as utils
from scqubits.settings import matplotlib_settings

@@ -94,3 +95,2 @@ if TYPE_CHECKING:

width=str(pixels) + "px",
align="top",
border="1px solid lightgrey",

@@ -169,2 +169,3 @@ padding="10px 10px 10px 10px",

@matplotlib.rc_context(matplotlib_settings)
def build_figure_and_axes_table(self) -> Tuple[Figure, np.ndarray]:

@@ -430,2 +431,3 @@ # the %inline and %widget backends somehow scale differently; try to compensate

@matplotlib.rc_context(matplotlib_settings)
def build_ui_figure_display(self):

@@ -445,2 +447,3 @@ if _HAS_WIDGET_BACKEND:

@matplotlib.rc_context(matplotlib_settings)
def display_panel(

@@ -662,2 +665,3 @@ self,

@matplotlib.rc_context(matplotlib_settings)
def update_layout_and_plots(self: "Explorer", change):

@@ -696,2 +700,3 @@ panels = self.get_panels_list()

@matplotlib.rc_context(matplotlib_settings)
def update_plots(self: "Explorer", change):

@@ -822,6 +827,3 @@ if not hasattr(self, "fig"):

self.ui["transitions"]["initial_bare_dressed_toggle"] = ToggleButtons(
options=["bare", "dressed"],
value="bare",
description="",
disable=False,
options=["bare", "dressed"], value="bare", description=""
)

@@ -828,0 +830,0 @@ self.ui["transitions"][

@@ -68,3 +68,2 @@ # qubit_widget.py

description="",
disabled=False,
layout=ipywidgets.Layout(width="150px"),

@@ -71,0 +70,0 @@ )

@@ -165,2 +165,16 @@ # misc.py

def check_lookup_exists(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
if not self._lookup_exists:
raise Exception(
"Lookup data not found. For HilbertSpace: data must be generated with .generate_lookup(). "
"For ParameterSweep: data should be automatically generated unless disabled manually. In "
"the latter case, apply .run()."
)
return func(self, *args, **kwargs)
return wrapper
class DeprecationMessage:

@@ -167,0 +181,0 @@ """Decorator class, producing an adjustable warning and info upon usage of the

@@ -61,3 +61,3 @@ # plot_defaults.py

# If energy spacing is used for scaling, fill no more than this fraction of the
# If energy spacing is used for scaling, fill no more than this fraction of the
# spacing.

@@ -79,4 +79,4 @@ FILLING_FRACTION = 0.9

e_range = e_max - e_min
y_min = np.min(potential_vals)
y_max = e_max + 0.3 * e_range
y_min = np.min(potential_vals) # lowest value of potential energy
y_max = e_max + 0.3 * e_range # maximum eigenenergy plus padding
y_range = y_max - y_min

@@ -83,0 +83,0 @@

@@ -27,2 +27,3 @@ # plot_utils.py

from scqubits.utils import plot_defaults as defaults
from scqubits.settings import matplotlib_settings

@@ -50,2 +51,3 @@ if TYPE_CHECKING:

@mpl.rc_context(matplotlib_settings)
def _extract_kwargs_options(

@@ -83,2 +85,3 @@ kwargs: Dict[str, Any], plot_type: str, direct_plot_options: Dict[str, Any] = None

@mpl.rc_context(matplotlib_settings)
def _process_options(

@@ -129,2 +132,3 @@ figure: Figure, axes: Axes, opts: Dict[str, Any] = None, **kwargs

@mpl.rc_context(matplotlib_settings)
def _process_special_option(figure: Figure, axes: Axes, key: str, value: Any) -> None:

@@ -148,2 +152,3 @@ """Processes a single 'special' option, i.e., one internal to scqubits and not to be

@mpl.rc_context(matplotlib_settings)
def despine_axes(axes: Axes) -> None:

@@ -159,2 +164,3 @@ # Hide the right and top spines

@mpl.rc_context(matplotlib_settings)
def scale_wavefunctions(

@@ -175,2 +181,3 @@ wavefunc_list: List["WaveFunction"],

@mpl.rc_context(matplotlib_settings)
def plot_wavefunction_to_axes(

@@ -189,2 +196,3 @@ axes: Axes, wavefunction: "WaveFunction", energy_offset: float, **kwargs

@mpl.rc_context(matplotlib_settings)
def plot_potential_to_axes(

@@ -210,2 +218,3 @@ axes: Axes,

@mpl.rc_context(matplotlib_settings)
def add_numbers_to_axes(

@@ -228,2 +237,3 @@ axes: Axes, matrix: ndarray, modefunc: Callable, fontsize: int = 8

@mpl.rc_context(matplotlib_settings)
def color_normalize(vals, mode: str) -> Tuple[float, float, mpl.colors.Normalize]:

@@ -230,0 +240,0 @@ minval = min(vals)

@@ -36,2 +36,3 @@ # plotting.py

)
from scqubits.settings import matplotlib_settings

@@ -49,2 +50,3 @@ if TYPE_CHECKING:

@mpl.rc_context(matplotlib_settings)
def wavefunction1d(

@@ -94,2 +96,3 @@ wavefuncs: Union["WaveFunction", "List[WaveFunction]"],

@mpl.rc_context(matplotlib_settings)
def wavefunction1d_nopotential(

@@ -133,2 +136,3 @@ wavefuncs: Union["WaveFunction", "List[WaveFunction]"],

@mpl.rc_context(matplotlib_settings)
def wavefunction1d_discrete(wavefunc: "WaveFunction", **kwargs) -> Tuple[Figure, Axes]:

@@ -163,2 +167,3 @@ """

@mpl.rc_context(matplotlib_settings)
def wavefunction2d(

@@ -217,2 +222,3 @@ wavefunc: "WaveFunctionOnGrid", zero_calibrate: bool = False, **kwargs

@mpl.rc_context(matplotlib_settings)
def contours(

@@ -270,2 +276,3 @@ x_vals: Union[List[float], np.ndarray],

@mpl.rc_context(matplotlib_settings)
def matrix(

@@ -309,2 +316,3 @@ data_matrix: np.ndarray, mode: str = "abs", show_numbers: bool = False, **kwargs

@mpl.rc_context(matplotlib_settings)
def matrix_skyscraper(

@@ -368,2 +376,6 @@ matrix: np.ndarray, mode: str = "abs", **kwargs

axes.tick_params(axis='x', pad=-5)
axes.tick_params(axis='y', pad=-5)
axes.tick_params(axis='z', pad=-2)
_process_options(fig, axes, opts=defaults.matrix(), **kwargs)

@@ -374,2 +386,3 @@

@mpl.rc_context(matplotlib_settings)
def matrix2d(

@@ -425,3 +438,3 @@ matrix: np.ndarray,

box_height_inches = fig_height / matrix.shape[0]
font_size = min(box_width_inches, box_height_inches) * 12
font_size = min(box_width_inches, box_height_inches) * 11
add_numbers_to_axes(axes, matrix, modefunction, fontsize=font_size)

@@ -444,2 +457,3 @@

@mpl.rc_context(matplotlib_settings)
def data_vs_paramvals(

@@ -503,2 +517,3 @@ xdata: np.ndarray,

@mpl.rc_context(matplotlib_settings)
def evals_vs_paramvals(

@@ -550,2 +565,3 @@ specdata: "SpectrumData",

@mpl.rc_context(matplotlib_settings)
def matelem_vs_paramvals(

@@ -591,3 +607,3 @@ specdata: "SpectrumData",

for (row, col) in index_pairs:
for row, col in index_pairs:
y_vals = modefunction(specdata.matrixelem_table[:, row, col])

@@ -604,4 +620,4 @@ axes.plot(

else:
axes.legend(loc="center left", bbox_to_anchor=(1, 0.5))
axes.legend(loc="center left", bbox_to_anchor=(1, 0.5), frameon=False)
_process_options(fig, axes, opts=defaults.matelem_vs_paramvals(specdata), **kwargs)
return fig, axes

@@ -121,4 +121,4 @@ # spectrum_utils.py

Summing up to the midpoint only is to address the danger that the sum is
actually zero, which may is the case for odd wavefunctions taken over an interval
Summing up to the midpoint only is to address the danger that the sum is
actually zero, which may be the case for odd wavefunctions taken over an interval
centered at zero.

@@ -125,0 +125,0 @@ """

# THIS FILE IS GENERATED FROM scqubits SETUP.PY
short_version = '3.1.0'
version = '3.1.0'
short_version = '3.1.1'
version = '3.1.1'
release = True

@@ -56,3 +56,3 @@ """scqubits: superconducting qubits in Python

MINOR = 1
MICRO = 0
MICRO = 1
ISRELEASED = True

@@ -59,0 +59,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display