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.1
to
3.2.0
+650
scqubits/core/diag.py
# diag.py
#
# This file is part of scqubits: a Python package for superconducting qubits,
# Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/
#
# Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
############################################################################
from numpy import ndarray
from typing import Any, Dict, List, Optional, Tuple, Union
from qutip import Qobj
from scipy.sparse import csc_matrix
from scqubits.io_utils.fileio_qutip import QutipEigenstates
from scqubits.utils.spectrum_utils import order_eigensystem, has_degeneracy
import copy
import numpy as np
import scipy as sp
import scqubits.settings as settings
import warnings
def _dict_merge(
d: Dict[str, Any],
d_other: Dict[str, Any],
exclude: Union[List[str], None] = None,
overwrite=False,
) -> Dict[str, Any]:
"""
Selective dictionary merge. This function makes a copy of the given
dictionary `d` and selectively updates/adds entries from `d_other`,
as long as the keys are not given in `exclude`.
Whether entries in `d` are overwritten by entries in `d_other` is
determined by the value of the `overwrite` parameter
Parameters
----------
d: dict
dictionary
d_other:
second dictionary to be merged with the first
exclude: dict
list of potential keys in d_other to be excluded from being added to resulting merge
overwrite: bool
determines if keys already in d should be overwritten by those in d_other
Returns
----------
merged dictionary
"""
exclude = [] if exclude is None else exclude
d_new = copy.deepcopy(d)
for key in d_other:
if key not in exclude and (overwrite or key not in d):
d_new[key] = d_other[key]
return d_new
def _cast_matrix(
matrix: Union[ndarray, csc_matrix, Qobj], cast_to: str, force_cast: bool = True
) -> Union[ndarray, csc_matrix, Qobj]:
"""
Casts a given matrix into a required form ('sparse' or 'dense')
as defined by `cast_to` parameter.
Note that in some cases casting may not be explicitly needed,
for example: the sparse matrix routines can can often accept
dense matrices. The parameter `force_cast` determines if the
casing should be always done, or only where it is necessary.
Parameters
----------
matrix: Qobj, ndarray or csc_matrx
matrix given as an ndarray, Qobj, or scipy's sparse matrix format
cast_to: str
string representing the format that matrix should be cast into: 'sparse' or 'dense'
force_cast: bool
determines of casting should be always performed or only where necessary
Returns
----------
matrix in the right sparse or dense form
"""
m = matrix
if cast_to == "sparse":
if isinstance(matrix, Qobj):
m = csc_matrix(matrix.data)
elif force_cast and isinstance(matrix, ndarray):
m = csc_matrix(matrix)
elif cast_to == "dense":
if isinstance(matrix, Qobj):
m = matrix.full()
elif force_cast and sp.sparse.issparse(matrix):
m = matrix.toarray()
else:
raise ValueError("Can only matrix to 'sparse' or 'dense' forms.")
return m
def _convert_evecs_to_qobjs(evecs: ndarray, matrix_qobj, wrap: bool = False) -> ndarray:
"""
Converts an `ndarray` containing eigenvectors (that would be typically
returned from a diagonalization routine, such as `eighs` or `eigh`),
to a numpy array of qutip's Qobjs.
Potentially also wraps those into `scqubits.io_utils.fileio_qutip.QutipEigenstates`.
Parameters
----------
evecs:
ndarray of eigenvectors (as columns)
matrix_qobj:
matrix in the qutipQbj form; if given, used to extract the tensor product structure
wrap:
determines if we wrap results in QutipEigenstates
Returns
----------
eigenvectors represented in terms of Qobjs
"""
evecs_count = evecs.shape[1]
evec_dims = [matrix_qobj.dims[0], [1] * len(matrix_qobj.dims[0])]
evecs_qobj = np.empty((evecs_count,), dtype=object)
for i in range(evecs_count):
v = Qobj(evecs[:, i], dims=evec_dims, type="ket")
evecs_qobj[i] = v / v.norm()
# Optionally, we wrap the resulting array in QutipEigenstates as is done in HilbertSpace.
if wrap:
evecs_qobj = evecs_qobj.view(QutipEigenstates)
return evecs_qobj
### scipy based routines ####
def evals_scipy_dense(
matrix: Union[ndarray, csc_matrix, Qobj], evals_count: int, **kwargs
) -> ndarray:
"""
Diagonalization based on scipy's (dense) `eigh` function.
Only evals are returned.
Parameters
----------
matrix:
ndarray or qutip.Qobj to be diagonalized
evals_count:
how many eigenvalues should be returned
kwargs:
optional settings that are passed onto the diagonalization routine
Returns
----------
eigenvalues of matrix
"""
m = _cast_matrix(matrix, "dense")
evals = sp.linalg.eigh(
m, subset_by_index=(0, evals_count - 1), eigvals_only=True, **kwargs
)
return evals
def esys_scipy_dense(
matrix, evals_count, **kwargs
) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]:
"""
Diagonalization based on scipy's (dense) eigh function.
Both evals and evecs are returned.
Parameters
----------
matrix:
ndarray or qutip.Qobj to be diagonalized
evals_count:
how many eigenvalues/vectors should be returned
kwargs:
optional settings that are passed onto the diagonalization routine
Returns
----------
a tuple of eigenvalues and eigenvectors. Eigenvectors are Qobjs if matrix is a Qobj instance
"""
m = _cast_matrix(matrix, "dense")
evals, evecs = sp.linalg.eigh(m, subset_by_index=(0, evals_count - 1), **kwargs)
evecs = (
_convert_evecs_to_qobjs(evecs, matrix) if isinstance(matrix, Qobj) else evecs
)
return evals, evecs
def evals_scipy_sparse(
matrix: Union[ndarray, csc_matrix, Qobj], evals_count: int, **kwargs
) -> ndarray:
"""
Diagonalization based on scipy's (sparse) `eigsh` function.
Only evals are returned.
Note the convoluted convention when it comes to ordering and how it is related
to the presence of `return_eigenvectors` parameter. See here for details:
https://github.com/scipy/scipy/issues/9082
Parameters
----------
matrix:
ndarray or qutip.Qobj to be diagonalized
evals_count:
how many eigenvalues should be returned
kwargs:
optional settings that are passed onto the diagonalization routine
Returns
----------
eigenvalues of matrix
"""
m = _cast_matrix(matrix, "sparse")
options = _dict_merge(
dict(
which="SA",
v0=settings.RANDOM_ARRAY[: matrix.shape[0]],
return_eigenvectors=False,
),
kwargs,
overwrite=True,
)
evals = sp.sparse.linalg.eigsh(m, k=evals_count, **options)
# have to reverse order if return_eigenvectors=False and which="SA"
return evals[::-1]
def esys_scipy_sparse(
matrix: Union[ndarray, csc_matrix, Qobj], evals_count: int, **kwargs
) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]:
"""
Diagonalization based on scipy's (sparse) `eigsh` function.
Both evals and evecs are returned.
Note the convoluted convention when it comes to ordering and how it is related
to the presence of `return_eigenvectors` parameter. See here for details:
https://github.com/scipy/scipy/issues/9082
This function ensures that:
1. We always use the same "random" starting vector v0. Otherwise results show
random behavior (small deviations between different runs, problem for pytests)
2. We test for degenerate eigenvalues. If there are any, we orthogonalize the
eigenvectors properly.
TODO:
Right now, this is essentially a copy/paste of spectrum_utils.eigsh_safe().
When the dust settles, should combine both into one.
Parameters
----------
matrix:
ndarray or qutip.Qobj to be diagonalized
evals_count:
how many eigenvalues/vectors should be returned
kwargs:
optional settings that are passed onto the diagonalization routine
Returns
----------
a tuple of eigenvalues and eigenvectors. Eigenvectors are Qobjs if matrix is a Qobj instance
"""
m = _cast_matrix(matrix, "sparse")
options = _dict_merge(
dict(
which="SA",
v0=settings.RANDOM_ARRAY[: matrix.shape[0]],
return_eigenvectors=True,
),
kwargs,
overwrite=True,
)
evals, evecs = sp.sparse.linalg.eigsh(m, k=evals_count, **options)
if has_degeneracy(evals):
evecs, _ = sp.linalg.qr(evecs, mode="economic")
evecs = (
_convert_evecs_to_qobjs(evecs, matrix) if isinstance(matrix, Qobj) else evecs
)
return evals, evecs
### primme based routines ####
def evals_primme_sparse(
matrix: Union[ndarray, csc_matrix, Qobj], evals_count: int, **kwargs
) -> ndarray:
"""
Diagonalization based on primme's (sparse) `eigsh` function.
Only evals are returned.
Requires that the primme library is installed.
Parameters
----------
matrix:
ndarray or qutip.Qobj to be diagonalized
evals_count:
how many eigenvalues/vectors should be returned
kwargs:
optional settings that are passed onto the diagonalization routine
Returns
----------
eigenvalues of matrix
"""
try:
import primme
except:
raise ImportError("Module primme is not installed.")
m = _cast_matrix(matrix, "sparse")
options = _dict_merge(
dict(
which="SA",
return_eigenvectors=False,
),
kwargs,
overwrite=True,
)
evals = primme.eigsh(m, k=evals_count, **options)
return evals
def esys_primme_sparse(
matrix: Union[ndarray, csc_matrix, Qobj], evals_count: int, **kwargs
) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]:
"""
Diagonalization based on primme's (sparse) `eigsh` function.
Both evals and evecs are returned.
Requires that the primme library is installed.
Parameters
----------
matrix:
ndarray or qutip.Qobj to be diagonalized
evals_count:
how many eigenvalues/vectors should be returned
kwargs:
optional settings that are passed onto the diagonalization routine
Returns
----------
a tuple of eigenvalues and eigenvectors. Eigenvectors are Qobjs if matrix is a Qobj instance
"""
try:
import primme
except:
raise ImportError("Module primme is not installed.")
m = _cast_matrix(matrix, "sparse")
options = _dict_merge(
dict(
which="SA",
return_eigenvectors=True,
),
kwargs,
overwrite=True,
)
evals, evecs = primme.eigsh(m, k=evals_count, **options)
evecs = (
_convert_evecs_to_qobjs(evecs, matrix) if isinstance(matrix, Qobj) else evecs
)
return evals, evecs
### cupy based routines ####
def evals_cupy_dense(
matrix: Union[ndarray, csc_matrix, Qobj], evals_count: int, **kwargs
) -> ndarray:
"""
Diagonalization based on cupy's (dense) `eighvalsh` function
Only evals are returned.
Requires that the cupy library is installed.
Parameters
----------
matrix:
ndarray or qutip.Qobj to be diagonalized
evals_count:
how many eigenvalues/vectors should be returned
kwargs:
optional settings that are passed onto the diagonalization routine
Returns
----------
eigenvalues of matrix
"""
try:
import cupy as cp
except:
raise ImportError("Module cupy is not installed.")
m = _cast_matrix(matrix, "dense")
evals_gpu = cp.linalg.eigvalsh(cp.asarray(m), **kwargs)
cp.cuda.Stream.null.synchronize() # wait for GPU to finish
return evals_gpu[:evals_count].get()
def esys_cupy_dense(
matrix: Union[ndarray, csc_matrix, Qobj], evals_count: int, **kwargs
) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]:
"""
Diagonalization based on cupy's (dense) `eigh` function.
Both evals and evecs are returned.
Requires that the cupy library is installed.
Parameters
----------
matrix:
ndarray or qutip.Qobj to be diagonalized
evals_count:
how many eigenvalues/vectors should be returned
kwargs:
optional settings that are passed onto the diagonalization routine
Returns
----------
a tuple of eigenvalues and eigenvectors. Eigenvectors are Qobjs if matrix is a Qobj instance
"""
try:
import cupy as cp
except:
raise ImportError("Module cupy is not installed.")
m = _cast_matrix(matrix, "dense")
evals_gpu, evecs_gpu = cp.linalg.eigh(cp.asarray(m), **kwargs)
cp.cuda.Stream.null.synchronize() # wait for GPU to finish
evals, evecs = evals_gpu[:evals_count].get(), evecs_gpu[:, :evals_count].get()
evecs = (
_convert_evecs_to_qobjs(evecs, matrix) if isinstance(matrix, Qobj) else evecs
)
return evals, evecs
def evals_cupy_sparse(
matrix: Union[ndarray, csc_matrix, Qobj], evals_count: int, **kwargs
) -> ndarray:
"""
Diagonalization based on cupy's (sparse) `eigsh` function.
Only evals are returned.
Requires that the cupy (and cupyx) library is installed.
Parameters
----------
matrix:
ndarray or qutip.Qobj to be diagonalized
evals_count:
how many eigenvalues/vectors should be returned
kwargs:
optional settings that are passed onto the diagonalization routine
Returns
----------
eigenvalues of matrix
"""
try:
import cupy as cp
from cupyx.scipy.sparse import csc_matrix as cp_csc_matrix
from cupyx.scipy.sparse.linalg import eigsh
except:
raise ImportError("Module cupyx (part of cupy) is not installed.")
m = cp_csc_matrix(_cast_matrix(matrix, "sparse"))
options = _dict_merge(
dict(
which="SA",
return_eigenvectors=False,
),
kwargs,
overwrite=True,
)
evals_gpu = eigsh(m, k=evals_count, **options)
# return evals_gpu.get()[::-1]
return evals_gpu.get()
def esys_cupy_sparse(
matrix: Union[ndarray, csc_matrix, Qobj], evals_count: int, **kwargs
) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]:
"""
Diagonalization based on cupy's (sparse) eigsh function.
Both evals and evecs are returned.
Requires that the cupy library is installed.
Parameters
----------
matrix:
ndarray or qutip.Qobj to be diagonalized
evals_count:
how many eigenvalues/vectors should be returned
kwargs:
optional settings that are passed onto the diagonalization routine
Returns
----------
a tuple of eigenvalues and eigenvectors. Eigenvectors are Qobjs if matrix is a Qobj instance
"""
try:
import cupy as cp
from cupyx.scipy.sparse import csc_matrix as cp_csc_matrix
from cupyx.scipy.sparse.linalg import eigsh
except:
raise ImportError("Module cupyx (part of cupy) is not installed.")
m = cp_csc_matrix(_cast_matrix(matrix, "sparse"))
options = _dict_merge(
dict(
which="SA",
return_eigenvectors=True,
),
kwargs,
overwrite=True,
)
evals_gpu, evecs_gpu = eigsh(m, k=evals_count, **options)
evals, evecs = evals_gpu.get(), evecs_gpu.get()
evecs = (
_convert_evecs_to_qobjs(evecs, matrix) if isinstance(matrix, Qobj) else evecs
)
return evals, evecs
# Default values of various noise constants and parameters.
DIAG_METHODS = {
# scipy dense
"evals_scipy_dense": evals_scipy_dense,
"esys_scipy_dense": esys_scipy_dense,
# scipy sparse
"evals_scipy_sparse": evals_scipy_sparse,
"esys_scipy_sparse": esys_scipy_sparse,
"evals_scipy_sparse_SM": lambda matrix, evals_count, **kwargs: evals_scipy_sparse(
matrix, evals_count, **_dict_merge(dict(which="SM"), kwargs, overwrite=True)
),
"esys_scipy_sparse_SM": lambda matrix, evals_count, **kwargs: esys_scipy_sparse(
matrix, evals_count, **_dict_merge(dict(which="SM"), kwargs, overwrite=True)
),
"evals_scipy_sparse_LA_shift-inverse": lambda matrix, evals_count, **kwargs: evals_scipy_sparse(
matrix,
evals_count,
**_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True)
),
"esys_scipy_sparse_LA_shift-inverse": lambda matrix, evals_count, **kwargs: esys_scipy_sparse(
matrix,
evals_count,
**_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True)
),
"evals_scipy_sparse_LM_shift-inverse": lambda matrix, evals_count, **kwargs: evals_scipy_sparse(
matrix,
evals_count,
**_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True)
),
"esys_scipy_sparse_LM_shift-inverse": lambda matrix, evals_count, **kwargs: esys_scipy_sparse(
matrix,
evals_count,
**_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True)
),
# primme sparse
"evals_primme_sparse": evals_primme_sparse,
"esys_primme_sparse": esys_primme_sparse,
"evals_primme_sparse_SM": lambda matrix, evals_count, **kwargs: evals_primme_sparse(
matrix=matrix,
evals_count=evals_count,
**_dict_merge(dict(which="SM"), kwargs, overwrite=True)
),
"esys_primme_sparse_SM": lambda matrix, evals_count, **kwargs: esys_primme_sparse(
matrix, evals_count, **_dict_merge(dict(which="SM"), kwargs, overwrite=True)
),
"evals_primme_sparse_LA_shift-inverse": lambda matrix, evals_count, **kwargs: evals_primme_sparse(
matrix=matrix,
evals_count=evals_count,
**_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True)
),
"esys_primme_sparse_LA_shift-inverse": lambda matrix, evals_count, **kwargs: esys_primme_sparse(
matrix=matrix,
evals_count=evals_count,
**_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True)
),
"evals_primme_sparse_LM_shift-inverse": lambda matrix, evals_count, **kwargs: evals_primme_sparse(
matrix=matrix,
evals_count=evals_count,
**_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True)
),
"esys_primme_sparse_LM_shift-inverse": lambda matrix, evals_count, **kwargs: esys_primme_sparse(
matrix=matrix,
evals_count=evals_count,
**_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True)
),
# cupy dense
"evals_cupy_dense": evals_cupy_dense,
"esys_cupy_dense": esys_cupy_dense,
# cupy sparse
"evals_cupy_sparse": evals_cupy_sparse,
"esys_cupy_sparse": esys_cupy_sparse,
}

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 not supported yet

# explorer_settings.py
#
# This file is part of scqubits: a Python package for superconducting qubits,
# Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/
#
# Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
############################################################################
import itertools
from typing import TYPE_CHECKING, Any, Dict
import scqubits as scq
import scqubits.ui.gui_custom_widgets as ui
from scqubits.ui.gui_defaults import PlotType, mode_dropdown_list
from scqubits.utils import misc as utils
if TYPE_CHECKING:
from scqubits import Explorer
from scqubits.explorer.explorer_widget import PlotID
try:
from IPython.display import HTML, display, notebook
except ImportError:
_HAS_IPYTHON = False
else:
_HAS_IPYTHON = True
try:
import ipyvuetify as v
import ipywidgets
from scqubits.ui.gui_custom_widgets import flex_row
except ImportError:
_HAS_IPYVUETIFY = False
else:
_HAS_IPYVUETIFY = True
class ExplorerSettings:
"""
Generates the UI for Explorer settings.
Parameters
----------
explorer:
the `Explorer` object of interest
Attributes
----------
ui:
dictionary of all UI elements
"""
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY)
def __init__(self, explorer: "Explorer"):
self.explorer = explorer
self.ui: Dict[str, Any] = {}
self.ui["level_slider"]: Dict[PlotID, v.VuetifyWidget] = {}
self.ui["Transitions"]: Dict[str, v.VuetifyWidget] = {}
self.ui["kerr"]: Dict[str, v.VuetifyWidget] = {}
for plot_id in self.explorer.ui["panel_switch_by_plot_id"].keys():
self.ui[plot_id] = self.build_settings_ui(plot_id)
self.ui["dialogs"] = {
plot_id: v.Dialog(
v_model=False,
width="40%",
children=[
v.Card(
children=[
v.Toolbar(
children=[
v.ToolbarTitle(
children=[f"Plot settings: {str(plot_id)}"]
)
],
color="deep-purple accent-4",
dark=True,
),
v.CardText(children=[ui.flex_row(self.ui[plot_id])]),
]
)
],
)
for plot_id in self.explorer.ui["panel_switch_by_plot_id"].keys()
}
def __getitem__(self, item):
return self.ui[item]
def build_settings_ui(self, plot_id: "PlotID"):
subsys = plot_id.subsystems
plot_type = plot_id.plot_type
if plot_type is PlotType.ENERGY_SPECTRUM:
subsys = subsys[0]
subsys_index = self.explorer.sweep.get_subsys_index(subsys)
evals_count = self.explorer.sweep.subsys_evals_count(subsys_index)
self.ui["level_slider"][plot_id] = ui.NumberEntryWidget(
num_type=int,
label="Highest level",
v_min=1,
v_max=evals_count,
v_model=evals_count,
text_kwargs={
"style_": "min-width: 140px; max-width: 200px;",
"dense": True,
},
slider_kwargs={
"style_": "min-width: 110px; max-width: 230px",
"dense": True,
},
)
ui_subtract_ground_switch = v.Switch(
label="Subtract E\u2080", v_model=True, width=300
)
self.ui["level_slider"][plot_id].observe(
self.explorer.update_plots, names="v_model"
)
ui_subtract_ground_switch.observe(
self.explorer.update_plots, names="v_model"
)
return [
self.ui["level_slider"][plot_id].widget(),
ui_subtract_ground_switch,
]
if plot_type is PlotType.WAVEFUNCTIONS:
subsys = subsys[0]
if isinstance(
subsys, (scq.FluxQubit, scq.ZeroPi, scq.Cos2PhiQubit) # scq.Bifluxon
):
ui_wavefunction_selector = ui.InitializedSelect(
label="Display wavefunctions",
items=list(range(subsys.truncated_dim)),
v_model=0,
)
else:
ui_wavefunction_selector = ui.InitializedSelect(
label="Display wavefunctions",
multiple=True,
items=list(range(subsys.truncated_dim)),
v_model=list(range(subsys.truncated_dim)),
)
ui_mode_dropdown = ui.InitializedSelect(
items=mode_dropdown_list,
v_model=mode_dropdown_list[0],
label="Plot amplitude as",
)
ui_wavefunction_selector.observe(
self.explorer.update_plots, names="v_model"
)
ui_mode_dropdown.observe(self.explorer.update_plots, names="v_model")
return [ui_wavefunction_selector, ui_mode_dropdown]
if plot_type is PlotType.MATRIX_ELEMENTS:
subsys = subsys[0]
ui_mode_dropdown = ui.InitializedSelect(
items=mode_dropdown_list,
label="Plot matrix elements as",
v_model=mode_dropdown_list[2],
)
op_names = subsys.get_operator_names()
ui_operator_dropdown = ui.InitializedSelect(
items=op_names, label="Operator", v_model=op_names[0]
)
ui_mode_dropdown.observe(self.explorer.update_plots, names="v_model")
ui_operator_dropdown.observe(self.explorer.update_plots, names="v_model")
return [ui_mode_dropdown, ui_operator_dropdown]
if plot_type is PlotType.MATRIX_ELEMENT_SCAN:
subsys = subsys[0]
ui_mode_dropdown = ui.InitializedSelect(
items=mode_dropdown_list,
label="Plot matrix elements as",
v_model=mode_dropdown_list[2],
)
op_names = subsys.get_operator_names()
ui_operator_dropdown = ui.InitializedSelect(
items=op_names, label="Operator", v_model=op_names[0]
)
ui_mode_dropdown.observe(self.explorer.update_plots, names="v_model")
ui_operator_dropdown.observe(self.explorer.update_plots, names="v_model")
return [ui_mode_dropdown, ui_operator_dropdown]
if plot_type is PlotType.TRANSITIONS:
self.ui["Transitions"]["initial_state_inttexts"] = [
ui.ValidatedNumberField(
label=subsys.id_str,
num_type=int,
v_min=0,
v_max=subsys.truncated_dim,
v_model=0,
style_="display: inherit; width: 65px;",
class_="ml-4",
)
for subsys in self.explorer.sweep.hilbertspace
]
self.ui["Transitions"]["initial_dressed_inttext"] = ui.ValidatedNumberField(
label="Dressed state",
class_="ml-4 align-bottom",
num_type=int,
v_min=0,
v_max=self.explorer.sweep.hilbertspace.dimension,
v_model=0,
style_="display: none; width: 65px;",
)
self.ui["Transitions"]["photons_inttext"] = ui.ValidatedNumberField(
num_type=int,
class_="ml-3",
v_model=1,
v_min=1,
v_max=5,
label="Photon number",
style_="max-width: 120px",
)
self.ui["Transitions"]["highlight_selectmultiple"] = ui.InitializedSelect(
multiple=True,
label="",
items=self.explorer.subsys_names,
v_model=[self.explorer.subsys_names[0]],
width=185,
)
self.ui["Transitions"]["initial_bare_dressed_toggle"] = v.RadioGroup(
v_model="bare",
children=[
v.Radio(label="by bare product label", value="bare"),
v.Radio(label="by dressed index", value="dressed"),
],
)
self.ui["Transitions"]["sidebands_switch"] = v.Switch(
label="Show sidebands", v_model=False, width=250
)
for inttext in self.ui["Transitions"]["initial_state_inttexts"]:
inttext.observe(self.explorer.update_plots, names="num_value")
self.ui["Transitions"]["initial_dressed_inttext"].observe(
self.explorer.update_plots, names="num_value"
)
self.ui["Transitions"]["photons_inttext"].observe(
self.explorer.update_plots, names="num_value"
)
self.ui["Transitions"]["highlight_selectmultiple"].observe(
self.explorer.update_plots, names="v_model"
)
self.ui["Transitions"]["sidebands_switch"].observe(
self.explorer.update_plots, names="v_model"
)
self.ui["Transitions"]["initial_bare_dressed_toggle"].observe(
self.explorer.bare_dressed_toggle, names="v_model"
)
initial_state_selection = ui.flex_row(
[
v.Text(children=["Initial state"]),
self.ui["Transitions"]["initial_bare_dressed_toggle"],
*self.ui["Transitions"]["initial_state_inttexts"],
self.ui["Transitions"]["initial_dressed_inttext"],
]
)
photon_options_selection = v.Container(
class_="d-flex flex-row",
children=[
v.Text(children=["Single vs. multi-photon transitions"]),
self.ui["Transitions"]["photons_inttext"],
],
)
transition_highlighting = v.Container(
class_="d-flex flex-row",
children=[
v.Text(children=["Highlight:"]),
self.ui["Transitions"]["highlight_selectmultiple"],
],
)
return [
v.Container(
class_="d-flex flex-column",
children=[
initial_state_selection,
photon_options_selection,
self.ui["Transitions"]["sidebands_switch"],
transition_highlighting,
],
),
]
if plot_type is PlotType.SELF_KERR and isinstance(subsys[0], scq.Oscillator):
return []
if plot_type is PlotType.SELF_KERR and not isinstance(
subsys[0], scq.Oscillator
):
subsys = subsys[0]
ui_kerr_selector = ui.InitializedSelect(
label="Selected pair of levels",
items=list(
itertools.combinations_with_replacement(
list(range(subsys.truncated_dim)), 2
)
),
v_model=[[1, 1], [1, 2], [2, 2]],
multiple=True,
)
ui_kerr_selector.observe(self.explorer.update_plots, names="v_model")
return [ui_kerr_selector]
if plot_type is PlotType.CROSS_KERR:
return []
if plot_type is PlotType.AC_STARK:
self.ui["kerr"]["ac_stark_ell"] = ui.InitializedSelect(
v_model=1,
items=list(range(1, subsys[0].truncated_dim)),
label="qubit level",
)
self.ui["kerr"]["ac_stark_ell"].observe(
self.explorer.update_plots, names="v_model"
)
return [
v.Container(
class_="d-flex flex-column",
children=[self.ui["kerr"]["ac_stark_ell"]],
)
]
return []
# explorer_widget.py
#
# This file is part of scqubits: a Python package for superconducting qubits,
# Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/
#
# Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
############################################################################
import itertools
from typing import TYPE_CHECKING, Any, Dict, List, Tuple
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.axes import Axes
from matplotlib.figure import Figure
import scqubits as scq
import scqubits.ui.gui_custom_widgets as ui
import scqubits.ui.gui_defaults as gui_defaults
from scqubits.core.param_sweep import ParameterSlice
from scqubits.core.qubit_base import QuantumSystem, QubitBaseClass
from scqubits.explorer import explorer_panels as panels
from scqubits.explorer.explorer_settings import ExplorerSettings
from scqubits.settings import matplotlib_settings
from scqubits.ui.gui_defaults import (
NAV_COLOR,
PlotType,
default_panels,
mode_dropdown_dict,
supported_panels,
)
from scqubits.utils import misc as utils
from scqubits.utils.misc import _HAS_WIDGET_BACKEND
if TYPE_CHECKING:
from scqubits.core.param_sweep import ParameterSweep
try:
from IPython.display import HTML, display, notebook
except ImportError:
_HAS_IPYTHON = False
else:
_HAS_IPYTHON = True
try:
import ipyvuetify as v
import ipywidgets
from scqubits.ui.gui_custom_widgets import flex_row
except ImportError:
_HAS_IPYVUETIFY = False
else:
_HAS_IPYVUETIFY = True
class PlotID:
"""Class for storing plot identifiers. Used for plot panel selection."""
SEP = " | "
def __init__(self, plot_type: PlotType, subsystems: List[QuantumSystem]):
self.plot_type: PlotType = plot_type
self.subsystems: List[QuantumSystem] = subsystems
def __repr__(self):
return f"PlotID({self.plot_type}, {self.subsystems})"
def __str__(self):
subsys_names = self.subsys_ids()
if len(subsys_names) == 1:
return f"{self.plot_type.value}{self.SEP}{subsys_names[0]}"
return f"{self.plot_type.value}{self.SEP}{','.join(subsys_names)}"
def is_composite(self) -> bool:
return len(self.subsystems) > 1
def subsys_ids(self) -> List[str]:
return [subsys.id_str for subsys in self.subsystems]
def is_default_active(self) -> bool:
if self.is_composite():
return self.plot_type in default_panels["Composite"]
subsys_type_str = type(self.subsystems[0]).__name__
return self.plot_type in default_panels[subsys_type_str]
class Explorer:
"""
Generates the UI for exploring `ParameterSweep` objects.
Parameters
----------
sweep:
the `ParameterSweep` object to be visualized.
ncols:
number of columns used in plot panel display
Attributes
----------
sweep:
the `ParameterSweep` object to be visualized.
ncols:
number of columns used in plot panel display
subsystems:
list of subsystems in the `ParameterSweep` object
subsys_names:
list of subsystem names
ui:
dictionary of all UI elements
settings: ExplorerSettings
settings for the Explorer
Plot panels are labeled by PlotID instances where supported plot names are
- "Energy spectrum"
- "Wavefunctions" (only for subsystems who inherit from `QubitBaseClass`)
- "Matrix elements"
- "Anharmonicity" (for subsystems who inherit from `QubitBaseClass`)
- "Transitions"
- "Self-Kerr"
- "Cross-Kerr"
- "ac Stark"
"""
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY)
def __init__(self, sweep: scq.ParameterSweep, ncols: int = 2):
"""Set up all widget GUI elements and class attributes."""
self.sweep = sweep
self.ncols = ncols # number of columns used for axes in the figure display
self.subsystems: List[QuantumSystem] = self.sweep.hilbertspace.subsystem_list
self.subsys_names: List[str] = [subsys.id_str for subsys in self.subsystems]
utils.check_matplotlib_compatibility()
# == GUI elements =========================================================
self.ui: Dict[str, Any] = {}
self.build_panel_switches()
self.ui["add_plot_dialog"] = self.build_ui_add_plot_dialog()
self.ui["sweep_param_dropdown"] = ui.InitializedSelect(
class_="px-2",
style_="max-width: 200px;",
label="Active Sweep Parameter",
items=list(self.sweep.param_info.keys()),
)
self.ui["sweep_param_dropdown"].observe(
self.update_parameter_sliders, names="v_model"
)
# self.ui["sweep_value_slider"] = ui.DiscreteSetSlider(
# param_name=self.ui["sweep_param_dropdown"].v_model,
# param_vals=self.param_vals,
# filled=False,
# class_="px-3",
# style_="max-width: 300px; padding-top: 10px",
# )
#
#
# self.ui["sweep_value_slider"].observe(self.update_plots, names="v_model")
self.ui["param_sliders"] = self.create_sliders()
self.update_parameter_sliders(None)
self.ui["top_bar"] = v.Sheet(
class_="d-flex flex-row m-0 pt-3 align-bottom",
height=70,
flat=True,
width="100%",
color=NAV_COLOR,
children=[
v.Card(
class_="p-2 mx-4",
color=NAV_COLOR,
elevation=0,
children=[gui_defaults.icons["scq-logo.png"]],
),
self.ui["sweep_param_dropdown"],
self.ui["param_sliders_container"],
],
)
self.plot_collection = ui.PlotPanelCollection(
ncols=self.ncols,
plot_choice_dialog=self.show_plot_choice_dialog,
toggle_switches_by_plot_id=self.ui["panel_switch_by_plot_id"],
plot_settings_dialog=self.plot_settings_dialog,
)
self.settings = ExplorerSettings(self)
self.explorer_display = v.Container(
class_="d-flex flex-column mx-0 px-0",
children=[
self.ui["top_bar"],
self.plot_collection.show(),
self.ui["add_plot_dialog"],
*self.settings["dialogs"].values(),
],
)
self.create_initial_plot_panels()
display(self.explorer_display)
@property
def fixed_param(self):
"""Return the currently selected sweep parameter."""
return self.ui["sweep_param_dropdown"].v_model
@property
def param_vals(self):
"""Return the selected value of the current sweep parameter."""
return self.sweep.param_info[self.fixed_param]
def show_plot_choice_dialog(self, *args) -> None:
self.ui["add_plot_dialog"].v_model = True
def close_plot_choice_dialog(self, *args) -> None:
self.ui["add_plot_dialog"].v_model = False
def plot_settings_dialog(self, plot_id: PlotID) -> None:
self.settings["dialogs"][plot_id].v_model = True
def update_switches(self) -> None:
for switch in self.ui["panel_switch_by_plot_id"].values():
switch.v_model = switch.ref in self.plot_collection.id_list()
def build_panel_switches(self) -> None:
# The panel switches reflect the set of panels that are currently displayed
ui_panel_switch_by_plot_id: Dict[PlotID, ui.LinkedSwitch] = {}
ui_panel_switches_by_subsys_name: Dict[str, List[ui.LinkedSwitch]] = {
"Composite": []
}
# Add default panels for individual subsystems
for subsys in self.subsystems:
ui_panel_switches_by_subsys_name[subsys.id_str] = []
subsys_type_str = type(subsys).__name__
for plot_type in supported_panels[subsys_type_str]:
plot_id = PlotID(plot_type, [subsys])
ui_panel_switch_by_plot_id[plot_id] = ui.LinkedSwitch(
v_model=plot_id.is_default_active(),
label=plot_type.value,
ref=plot_id,
dense=True,
width=185,
)
ui_panel_switches_by_subsys_name[subsys.id_str] += [
ui_panel_switch_by_plot_id[plot_id]
]
# Add composite panels
plot_id = PlotID(PlotType.TRANSITIONS, self.subsystems)
ui_panel_switch_by_plot_id[plot_id] = ui.LinkedSwitch(
v_model=plot_id.is_default_active(),
label="Transitions",
ref=plot_id,
dense=True,
width=185,
)
ui_panel_switches_by_subsys_name["Composite"] += [
ui_panel_switch_by_plot_id[plot_id]
]
for subsys1, subsys2 in itertools.combinations(self.subsystems, 2):
is_oscillator1 = isinstance(subsys1, scq.Oscillator)
is_oscillator2 = isinstance(subsys2, scq.Oscillator)
if any([is_oscillator1, is_oscillator2]) and not all(
[is_oscillator1, is_oscillator2]
):
plot_type = PlotType.AC_STARK
else:
plot_type = PlotType.CROSS_KERR
plot_id = PlotID(plot_type, [subsys1, subsys2])
ui_panel_switch_by_plot_id[plot_id] = ui.LinkedSwitch(
v_model=plot_id.is_default_active(),
label=f"{plot_type.value}: {subsys1.id_str}, {subsys2.id_str}",
ref=plot_id,
dense=True,
width=185,
)
ui_panel_switches_by_subsys_name["Composite"] += [
ui_panel_switch_by_plot_id[plot_id]
]
self.ui["panel_switch_by_plot_id"] = ui_panel_switch_by_plot_id
self.ui["panel_switches_by_subsys_name"] = ui_panel_switches_by_subsys_name
self.ui["panel_switches"] = {}
for subsys_name in self.ui["panel_switches_by_subsys_name"].keys():
self.ui["panel_switches"][subsys_name] = v.Container(
class_="d-flex flex-column",
dense=True,
children=self.ui["panel_switches_by_subsys_name"][subsys_name],
)
for switch in self.ui["panel_switch_by_plot_id"].values():
switch.observe(self.on_toggle_event, names="v_model")
def build_ui_add_plot_dialog(self) -> v.Dialog:
return v.Dialog(
v_model=False,
width="75%",
children=[
v.Card(
children=[
v.Toolbar(
children=[v.ToolbarTitle(children=["Choose Plot"])],
color="deep-purple accent-4",
dark=True,
),
ui.flex_row(
[
ui.flex_column(
[
v.CardTitle(children="Composite-system plots"),
self.ui["panel_switches"]["Composite"],
]
),
v.Divider(vertical=True),
ui.flex_column(
[
v.CardTitle(
children="Subsystem-specific plots"
),
ui.flex_row(
[
ui.flex_column(
[
v.CardTitle(
style_="font-weight: normal;",
children=subsys_name,
),
self.ui["panel_switches"][
subsys_name
],
]
)
for subsys_name in self.subsys_names
]
),
]
),
]
),
v.CardActions(
class_="justify-center",
children=[
ui.LinkedButton(
onclick=self.close_plot_choice_dialog,
children=["FINISH"],
)
],
),
]
)
],
)
@matplotlib.rc_context(matplotlib_settings)
def build_panel(
self,
plot_id: PlotID,
param_slice: ParameterSlice,
fig_ax: Tuple[Figure, Axes],
):
if plot_id.plot_type is PlotType.ENERGY_SPECTRUM:
panel_widget = self.settings[plot_id]
return panels.display_bare_spectrum(
self.sweep,
plot_id.subsystems[0],
param_slice,
fig_ax,
subtract_ground=panel_widget[1].v_model,
evals_count=self.settings["level_slider"][plot_id].num_value,
)
elif plot_id.plot_type is PlotType.WAVEFUNCTIONS and isinstance(
plot_id.subsystems[0], QubitBaseClass
):
ui_wavefunction_selector, ui_mode_dropdown = self.settings[plot_id]
return panels.display_bare_wavefunctions(
self.sweep,
plot_id.subsystems[0],
param_slice,
fig_ax,
mode=mode_dropdown_dict[ui_mode_dropdown.v_model],
which=ui_wavefunction_selector.v_model,
)
elif plot_id.plot_type is PlotType.MATRIX_ELEMENTS and isinstance(
plot_id.subsystems[0], QubitBaseClass
):
ui_mode_dropdown, opname_dropdown = self.settings[plot_id]
return panels.display_matrixelements(
sweep=self.sweep,
subsys=plot_id.subsystems[0],
operator_name=opname_dropdown.v_model,
mode_str=mode_dropdown_dict[ui_mode_dropdown.v_model],
param_slice=param_slice,
fig_ax=fig_ax,
)
elif plot_id.plot_type is PlotType.MATRIX_ELEMENT_SCAN and isinstance(
plot_id.subsystems[0], QubitBaseClass
):
ui_mode_dropdown, opname_dropdown = self.settings[plot_id]
return panels.display_matrixelement_sweep(
sweep=self.sweep,
subsys=plot_id.subsystems[0],
operator_name=opname_dropdown.v_model,
mode_str=mode_dropdown_dict[ui_mode_dropdown.v_model],
param_slice=param_slice,
fig_ax=fig_ax,
)
elif plot_id.plot_type is PlotType.ANHARMONICITY:
return panels.display_anharmonicity(
self.sweep, plot_id.subsystems[0], param_slice, fig_ax
)
elif plot_id.plot_type is PlotType.TRANSITIONS:
if (
self.settings["Transitions"]["initial_bare_dressed_toggle"].v_model
== "bare"
):
initial_state = tuple(
int(inttext.v_model)
for inttext in self.settings["Transitions"][
"initial_state_inttexts"
]
)
else:
initial_state = int(
self.settings["Transitions"]["initial_dressed_inttext"].v_model
)
subsys_name_tuple = self.settings["Transitions"][
"highlight_selectmultiple"
].v_model
if subsys_name_tuple == ():
subsys_list = None
else:
subsys_list = [
self.sweep.subsys_by_id_str(subsys_name)
for subsys_name in subsys_name_tuple
]
sidebands = self.settings["Transitions"]["sidebands_switch"].v_model
photon_number = int(self.settings["Transitions"]["photons_inttext"].v_model)
return panels.display_transitions(
self.sweep,
photon_number,
subsys_list,
initial_state,
sidebands,
param_slice,
fig_ax,
)
elif plot_id.plot_type is PlotType.SELF_KERR:
if self.settings[plot_id]: # has settings, so must be qubit-mode self-Kerr
ui_state_selection = self.settings[plot_id][0]
which = ui_state_selection.v_model
return panels.display_qubit_self_kerr(
sweep=self.sweep,
subsys=plot_id.subsystems[0],
param_slice=param_slice,
fig_ax=fig_ax,
which=which,
)
return panels.display_self_kerr(
sweep=self.sweep,
subsys=plot_id.subsystems[0],
param_slice=param_slice,
fig_ax=fig_ax,
)
elif plot_id.plot_type is PlotType.CROSS_KERR:
return panels.display_cross_kerr(
sweep=self.sweep,
subsys1=plot_id.subsystems[0],
subsys2=plot_id.subsystems[1],
param_slice=param_slice,
fig_ax=fig_ax,
)
elif plot_id.plot_type is PlotType.AC_STARK:
return panels.display_cross_kerr(
sweep=self.sweep,
subsys1=plot_id.subsystems[0],
subsys2=plot_id.subsystems[1],
param_slice=param_slice,
fig_ax=fig_ax,
which=self.settings.ui["kerr"]["ac_stark_ell"].v_model,
)
raise NotImplementedError(f"Plot type {plot_id} not implemented.")
@property
def active_switches_by_plot_id(self) -> Dict[PlotID, "ui.LinkedSwitch"]:
"""Returns a dictionary labeling all selected switches by their plot_id names."""
return {
plot_id: switch
for plot_id, switch in self.ui["panel_switch_by_plot_id"].items()
if switch.v_model
}
@property
def selected_plot_id_list(self) -> List[PlotID]:
"""Returns a list of strings capturing the names of all panels selected via
the switches."""
return list(self.active_switches_by_plot_id.keys())
def create_sliders(self) -> Dict[str, "v.VuetifyWidget"]:
"""Returns a list of selection sliders, one for each parameter that is part
of the underlying ParameterSweep object."""
slider_by_name = {
param_name: ui.DiscreteSetSlider(
param_name=param_name,
param_vals=param_array.tolist(),
filled=False,
class_="px-3",
style_="max-width: 300px; padding-top: 10px",
)
for param_name, param_array in self.sweep.param_info.items()
}
for slider in slider_by_name.values():
slider.observe(self.update_plots, names="v_model")
return slider_by_name
@property
def fixed_params(self) -> Dict[str, float]:
sliders = self.ui["fixed_param_sliders"]
return {
param_name: slider.current_value() for param_name, slider in sliders.items()
}
@property
def axes_list(self) -> List[Axes]:
return self.plot_collection.axes_list()
@matplotlib.rc_context(matplotlib_settings)
def create_initial_plot_panels(self):
for plot_id in self.selected_plot_id_list:
param_slice = self.parameter_slice
fig = matplotlib.pyplot.figure()
axes = fig.subplots()
fig.set_figwidth(4)
fig.set_figheight(4)
fig, axes = self.build_panel(
plot_id, param_slice=param_slice, fig_ax=(fig, axes)
)
self.plot_collection.new_plot_panel(plot_id, fig, axes)
@matplotlib.rc_context(matplotlib_settings)
def on_toggle_event(self, change):
toggled_panel_id = change["owner"].ref
if change["new"]:
param_slice = self.parameter_slice
fig = matplotlib.pyplot.figure()
axes = fig.subplots()
fig.set_figwidth(4)
fig.set_figheight(4)
fig, axes = self.build_panel(
toggled_panel_id, param_slice=param_slice, fig_ax=(fig, axes)
)
self.plot_collection.new_plot_panel(toggled_panel_id, fig, axes)
else:
self.plot_collection.close_panel_by_id(toggled_panel_id)
def update_parameter_sliders(self, change):
current_sweep_param = self.ui["sweep_param_dropdown"].v_model
self.ui["fixed_param_sliders"] = self.ui["param_sliders"].copy()
self.ui["fixed_param_sliders"].pop(current_sweep_param)
self.ui["sweep_value_slider"] = self.ui["param_sliders"][current_sweep_param]
self.ui["param_sliders_container"] = ui.flex_row(
[
self.ui["sweep_value_slider"],
v.Text(children="Fixed:" if self.ui["fixed_param_sliders"] else ""),
*self.ui["fixed_param_sliders"].values(),
]
)
if "top_bar" in self.ui:
self.ui["top_bar"].children = self.ui["top_bar"].children[:-1] + [
self.ui["param_sliders_container"]
]
self.update_plots(None)
def bare_dressed_toggle(self, change):
if (
self.settings.ui["Transitions"]["initial_bare_dressed_toggle"].v_model
== "bare"
):
self.settings.ui["Transitions"][
"initial_dressed_inttext"
].style_ = "display: none; max-width: 100px;"
for inttext in self.settings.ui["Transitions"]["initial_state_inttexts"]:
inttext.style_ = "display: inline-block; max-width: 100px;"
else:
self.settings.ui["Transitions"][
"initial_dressed_inttext"
].style_ = "display: inherit; max-width: 100px;"
for inttext in self.settings.ui["Transitions"]["initial_state_inttexts"]:
inttext.style_ = "display: none; max-width: 80px;"
self.update_plots(change)
@property
def parameter_slice(self):
return ParameterSlice(
self.ui["sweep_param_dropdown"].v_model,
self.ui["sweep_value_slider"].current_value(),
self.fixed_params,
list(self.sweep.param_info.keys()),
)
@matplotlib.rc_context(matplotlib_settings)
def update_plots(self: "Explorer", change):
param_val = self.ui["sweep_value_slider"].current_value()
panel_ids = self.selected_plot_id_list
param_slice = ParameterSlice(
self.ui["sweep_param_dropdown"].v_model,
param_val,
self.fixed_params,
list(self.sweep.param_info.keys()),
)
for axes in self.axes_list:
for item in axes.lines + axes.collections + axes.texts:
item.remove()
axes.set_prop_cycle(None)
axes.relim()
axes.autoscale_view()
for index, panel_id in enumerate(panel_ids):
fig = self.plot_collection.panel_by_id[panel_id].fig
ax = self.plot_collection.panel_by_id[panel_id].axes
output_widget = self.plot_collection.panel_by_id[panel_id].output
self.build_panel(panel_id, param_slice=param_slice, fig_ax=(fig, ax))
ax.title.set_text("") # title is instead displayed in card header
if not _HAS_WIDGET_BACKEND:
with output_widget:
output_widget.clear_output(wait=True)
display(fig)
else:
fig.canvas.draw_idle()

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

# gui_custom_widgets.py
#
# This file is part of scqubits: a Python package for superconducting qubits,
# Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/
#
# Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
############################################################################
import collections
from typing import (
TYPE_CHECKING,
Callable,
Dict,
List,
Optional,
OrderedDict,
)
import matplotlib as mp
import scqubits.utils.misc as utils
try:
import ipyvuetify as v
import ipywidgets
import traitlets
except ImportError:
_HAS_IPYVUETIFY = False
else:
_HAS_IPYVUETIFY = True
try:
from IPython.display import display
except ImportError:
_HAS_IPYTHON = False
else:
_HAS_IPYTHON = True
if TYPE_CHECKING:
from scqubits.explorer.explorer_widget import PlotID
if _HAS_IPYTHON and _HAS_IPYVUETIFY:
class ValidatedNumberField(v.TextField):
_typecheck_func: callable = None
_type = None
num_value = None # must determine appropriate traitlet type dynamically
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def __init__(
self,
v_model,
num_type=None,
v_min=None,
v_max=None,
step=None,
filled=True,
**kwargs,
):
self.name = kwargs.pop("name", None)
self._type = num_type if num_type is not None else type(v_model)
if num_type == float:
TraitletClass = traitlets.Float
self._typecheck_func = utils.is_string_float
self.step = step if step is not None else 0.1
elif num_type == int:
TraitletClass = traitlets.Int
self._typecheck_func = utils.is_string_int
self.step = step if step is not None else 1
else:
raise Exception(f"Not a supported number type: {num_type}")
self.add_traits(
num_value=TraitletClass(read_only=True).tag(sync=True),
v_min=TraitletClass(allow_none=True).tag(sync=True),
v_max=TraitletClass(allow_none=True).tag(sync=True),
)
self.v_min = v_min
self.v_max = v_max
super().__init__(v_model=v_model, filled=filled, **kwargs)
@traitlets.validate("v_model")
def _validate_v_model(self, state):
if self.is_valid():
self.error = False
self.rules = []
else:
self.error = True
self.rules = ["invalid"]
return state["value"]
@traitlets.observe("v_model")
def _observe_v_model(self, change):
if not self.error:
self.set_trait("num_value", self._type(change["new"]))
def is_valid(self):
if (
not self._typecheck_func(self.v_model)
or (
self.v_min not in [None, ""]
and self._type(self.v_model) < self.v_min
)
or (
self.v_max not in [None, ""]
and self._type(self.v_model) > self.v_max
)
):
return False
return True
class NumberEntryWidget(ValidatedNumberField):
"""A widget consisting of a text field and a slider, linked to each other. The text field acts as the main
class, while the slider is stored as a class attribute and displayed alongside.
"""
def __init__(
self,
label,
v_model=None,
num_type=float,
step=None,
v_min=None,
v_max=None,
s_min=None,
s_max=None,
text_kwargs=None,
slider_kwargs=None,
):
text_kwargs = text_kwargs or {}
slider_kwargs = slider_kwargs or {}
super().__init__(
label=label,
v_model=v_model,
num_type=num_type,
step=step,
v_min=v_min,
v_max=v_max,
**text_kwargs,
)
if "style_" not in slider_kwargs:
slider_kwargs["style_"] = "max-width: 240px; min-width: 220px;"
if "class_" not in slider_kwargs:
slider_kwargs["class_"] = "pt-3"
self.slider = v.Slider(
min=s_min,
max=s_max,
step=step,
v_model=v_model,
thumb_label=True,
**slider_kwargs,
)
self.slider.on_event("change", self.slider_click)
self.observe(self.update_slider, names="num_value")
ipywidgets.jslink((self, "v_max"), (self.slider, "max"))
ipywidgets.jslink((self, "v_min"), (self.slider, "min"))
ipywidgets.jslink((self, "disabled"), (self.slider, "disabled"))
def _ipython_display_(self):
display(self.widget())
def slider_click(self, *args):
self.v_model = self.slider.v_model
def widget(self):
return v.Container(
class_="d-flex flex-row ml-2 pb-0 pt-1",
style_="min-width: 220px; max-width: 220px",
children=[self, self.slider],
)
def update_slider(self, *args):
if self.error:
return
if self.num_value > self.slider.max:
self.slider.color = "red"
self.slider.v_model = self.slider.max
elif self.num_value < self.slider.min:
self.slider.color = "red"
self.slider.v_model = self.slider.min
else:
self.slider.color = ""
self.slider.v_model = self.num_value
def update_text(self, *args):
self.v_model = self.slider.v_model
class InitializedSelect(v.Select):
def __init__(self, **kwargs):
if "v_model" not in kwargs and "items" in kwargs:
kwargs["v_model"] = kwargs["items"][0]
super().__init__(**kwargs)
class LinkedButton(v.Btn):
def __init__(self, ref=None, **kwargs):
onclick = kwargs.pop("onclick", None)
super().__init__(**kwargs)
self.ref = ref
if onclick:
self.on_event("click", onclick)
class LinkedSwitch(v.Switch):
def __init__(self, ref, **kwargs):
super().__init__(**kwargs)
self.ref = ref
class vTooltipBtn(v.Tooltip):
def __init__(self, tooltip, bottom=False, left=True, **kwargs):
self.btn = LinkedButton(v_on="tooltip.on", **kwargs)
super().__init__(
bottom=bottom,
left=left,
v_slots=[
{"name": "activator", "variable": "tooltip", "children": self.btn}
],
children=[tooltip],
)
class ClickChip(v.Chip):
def __init__(self, **kwargs):
onclick_close = kwargs.pop("click_close", None)
onclick = kwargs.pop("onclick", None)
super().__init__(**kwargs)
if onclick_close:
self.on_event("click:close", onclick_close)
if onclick:
self.on_event("click", onclick)
class DiscreteSetSlider(v.Slider):
def __init__(self, param_name, param_vals, **kwargs):
self.val_count = len(param_vals)
self.param_name = param_name
self.param_vals = param_vals
super().__init__(
min=0, max=(self.val_count - 1), step=1, v_model=0, **kwargs
)
self.label = f"{self.param_name}={self.current_value():.3f}"
self.observe(self.update_textfield, names="v_model")
def current_value(self):
return self.param_vals[int(self.v_model)]
def update_textfield(self, *args):
self.label = f"{self.param_name}={self.current_value():.3f}"
class IconButton(LinkedButton):
def __init__(self, icon_name, **kwargs):
super().__init__(
**kwargs,
min_width=40,
width=40,
height=40,
elevation="0",
children=[v.Icon(children=[icon_name])],
)
def flex_row(widgets: List[v.VuetifyWidget], class_="", **kwargs) -> v.Container:
return v.Container(
class_="d-flex flex-row " + class_, children=widgets, **kwargs
)
def flex_column(widgets: List[v.VuetifyWidget], class_="", **kwargs) -> v.Container:
return v.Container(
class_="d-flex flex-column " + class_, children=widgets, **kwargs
)
class PanelBase:
def __init__(self, panel_id=None, content_list=None, width="49.5%"):
content_list = content_list if content_list else []
self.content_row = v.Container(
children=content_list,
class_="d-flex flex-row",
style_="justify-content: center",
)
self.card = v.Card(
id=str(panel_id),
max_width=width,
min_width=width,
elevation=2,
class_="mx-1 my-1",
children=[self.content_row],
)
self.panel_id = panel_id
def set_content(self, content_list):
self.content_row.children = content_list
class ClosablePanel(PanelBase):
def __init__(self, panel_id: "PlotID" = None, content_list=None, width="49.5%"):
super().__init__(panel_id=panel_id, content_list=content_list, width=width)
self.btn = v.Btn(
class_="mx-1",
icon=True,
size="xx-small",
elevation=0,
children=[v.Icon(children="mdi-close-circle")],
)
self.settings_btn = LinkedButton(
# class_="mx-1",
style_="margin-left: auto;",
icon=True,
size="xx-small",
elevation=0,
children=[v.Icon(children="mdi-settings")],
ref=panel_id,
)
self.title = v.CardTitle(
style_="margin-left: auto;", children=[v.Html(children=[str(panel_id)])]
)
self.card.children = [
v.Container(
class_="d-flex flex-row justify-center",
children=[self.title, self.settings_btn, self.btn],
),
self.content_row,
]
class ClosablePlotPanel(ClosablePanel):
def __init__(
self,
fig: mp.figure.Figure,
axes: mp.axes.Axes,
panel_id: Optional["PlotID"] = None,
width="49%",
):
self.fig = fig
self.axes = axes
title = self.axes.title.get_text()
self.axes.title.set_text("")
self.output = ipywidgets.Output(
layout=ipywidgets.Layout(object_fit="contain")
)
super().__init__(content_list=[self.output], panel_id=panel_id, width=width)
self.title.children = [
v.Html(
style_="font-weight: normal; color: 0.2;",
tag="div",
children=[title],
)
]
class PlotPanelCollection:
def __init__(
self,
toggle_switches_by_plot_id: Dict["PlotID", v.Switch],
ncols: int = 2,
plot_choice_dialog: Callable = None,
plot_settings_dialog: Callable = None,
):
self.ncols = ncols
self.plot_choice_dialog = plot_choice_dialog
self.plot_settings_dialog = plot_settings_dialog
self.panel_by_btn: OrderedDict[
v.Btn, ClosablePlotPanel
] = collections.OrderedDict()
self.panel_by_id: OrderedDict[
str, ClosablePlotPanel
] = collections.OrderedDict()
self.toggle_switches_by_plot_id = toggle_switches_by_plot_id
self.container = v.Container(
children=[],
style_="background: #eeeeee; position: relative; top: -70px",
class_="d-flex flex-row flex-wrap p-0 m-0",
width="100%",
)
def choose_plot(self, *args, **kwargs):
if self.plot_choice_dialog:
self.plot_choice_dialog()
def axes_list(self) -> List[mp.axes.Axes]:
return [panel.axes for panel in self.panel_by_btn.values()]
def card_list(self) -> List[v.Card]:
"""Returns the list of all the cards in the deck.
Returns
------
A list of the cards in the deck
"""
cards = [panel.card for panel in self.panel_by_btn.values()]
return cards
def id_list(self) -> List[str]:
"""Returns the list of all the ids in the deck.
Returns
------
A list of the ids in the deck
"""
return list(self.panel_by_id.keys())
def setup_card(self, new_panel: ClosablePanel):
"""Sets up a new card, connecting its close button to the card collection's `close_card` method."""
card = new_panel.card
card.id = new_panel.panel_id
btn = new_panel.btn
self.panel_by_btn[btn] = new_panel
self.panel_by_id[new_panel.panel_id] = new_panel
btn.on_event("click", self.close_panel)
new_panel.settings_btn.on_event("click", self.settings_dialog)
self.container.children = self.card_list()
def new_plot_panel(self, panel_id: "PlotID", fig, axes):
"""Adds a new plot card to the grid."""
closable_panel = ClosablePlotPanel(
fig, axes, panel_id=panel_id, width=f"{100 / self.ncols - 1}%"
)
self.setup_card(closable_panel)
with closable_panel.output:
mp.pyplot.show()
def close_panel(self, close_btn: LinkedButton, *args, **kwargs):
"""Remove card from dictionary and from the grid."""
panel_id = self.panel_by_btn[close_btn].panel_id
if self.toggle_switches_by_plot_id[panel_id].v_model:
self.toggle_switches_by_plot_id[panel_id].v_model = False
else:
self.panel_by_btn.pop(close_btn)
self.panel_by_id.pop(panel_id)
self.container.children = self.card_list()
def close_panel_by_id(self, panel_id: "PlotID"):
self.close_panel(self.panel_by_id[panel_id].btn)
def settings_dialog(self, settings_btn: LinkedButton, *args, **kwargs):
"""Bring up plot panel settings dialog."""
self.plot_settings_dialog(settings_btn.ref)
def resize_all(self, width=None, height=None):
"""Resizes all cards in the grid.
Parameters
----------
width:
width of the cards in the grid
height:
height of the cards in the grid
"""
for card in self.card_list():
if width:
card.width = width
if height:
card.height = height
def change_cols(self, ncols: int):
"""
Changes the number of columns in the grid by setting `explorer.cols` and resizing all widgets in the grid.
Parameters
----------
ncols:
number of columns in the grid
"""
self.ncols = ncols
self.resize_all(width=f"{100 / self.ncols - 1}%")
def show(self):
"""
The show function is the main function of a component. It returns
a VDOM object that will be rendered in the browser. The show function
is called every time an event occurs, and it's return value is used to
update the DOM.
Returns
-------
A container with the full widget.
"""
return v.Container(
class_="mx-0 px-0 my-0 py-0",
width="100%",
children=[
LinkedButton(
fab=True,
position="fixed",
class_="mx-2 my-2",
style_="z-index: 1000",
onclick=self.choose_plot,
children=[v.Icon(children="mdi-plus")],
),
self.container,
],
)
# gui_navbar.py
#
# This file is part of scqubits: a Python package for superconducting qubits,
# Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/
#
# Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
############################################################################
from typing import Tuple
import scqubits.ui.gui_custom_widgets as ui
import scqubits.utils.misc as utils
from scqubits.ui import gui_defaults as gui_defaults
from scqubits.ui.gui_custom_widgets import flex_column
from scqubits.ui.gui_defaults import NAV_COLOR
try:
import ipyvuetify as v
import ipywidgets
except ImportError:
_HAS_IPYVUETIFY = False
else:
_HAS_IPYVUETIFY = True
try:
from IPython.display import display
except ImportError:
_HAS_IPYTHON = False
else:
_HAS_IPYTHON = True
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def create_navbar() -> Tuple[v.Card, dict]:
# Navigation bar elements are:
# CHOOSE_QUBIT
# CHOOSE_PLOT
# AUTO_UPDATING
# DO_UPDATE
icons = gui_defaults.icons
navbar_elements = {}
navbar_elements["CHOOSE_QUBIT"] = v.Select(
v_model=gui_defaults.supported_qubits[0],
items=gui_defaults.supported_qubits,
filled=True,
label="Qubit",
dense=True,
)
navbar_elements["CHOOSE_PLOT"] = v.BtnToggle(
v_model=0,
mandatory=True,
class_="p-0 mx-0 my-2",
color=NAV_COLOR,
children=[
flex_column(
[
ui.vTooltipBtn(
color=NAV_COLOR,
class_="my-4",
children=[icons[icon_filename]],
tooltip=plot_type_name,
width=50,
elevation=0,
)
for plot_type_name, icon_filename in gui_defaults.gui_plot_choice_dict.items()
],
class_="px-0",
style_=f"background-color: {NAV_COLOR}",
)
],
)
navbar_elements["AUTO_UPDATING"] = v.Switch(
v_model=True,
width=150,
label="Auto update",
)
navbar_elements["DO_UPDATE"] = v.Btn(
children=[v.Icon(children=["mdi-refresh"])],
fab=True,
color="orange",
small=True,
disabled=True,
elevation="0",
class_="px-2",
)
navbar_elements["HEADER"] = v.Sheet(
class_="d-flex flex-row m-0 pt-3",
style_="padding-right: 50%",
height=70,
flat=True,
width="100%",
color=NAV_COLOR,
children=[
v.Card(
class_="p-2 mx-4",
color=NAV_COLOR,
elevation=0,
children=[icons["scq-logo.png"]],
),
navbar_elements["CHOOSE_QUBIT"],
],
)
nav_drawer = v.NavigationDrawer(
v_model="drawer",
permanent=True,
elevation="0",
color=NAV_COLOR,
floating=True,
width=90,
height=800,
children=[
v.List(
nav=True,
dense=True,
children=[
v.ListItem(
color=NAV_COLOR,
class_="align-items-bottom p-0 m-0",
children=[navbar_elements["CHOOSE_PLOT"]],
)
],
)
],
)
return nav_drawer, navbar_elements
# gui_setup.py
#
# This file is part of scqubits: a Python package for superconducting qubits,
# Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/
#
# Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
############################################################################
from pathlib import Path
from typing import Any, Dict
import numpy as np
import scqubits as scq
import scqubits.core.noise as noise
import scqubits.utils.misc as utils
from scqubits.core.qubit_base import QubitBaseClass
from scqubits.ui import gui_custom_widgets as ui
from scqubits.ui import gui_defaults as gui_defaults
try:
import ipyvuetify as v
import ipywidgets
except ImportError:
_HAS_IPYVUETIFY = False
else:
_HAS_IPYVUETIFY = True
try:
from IPython.display import display
except ImportError:
_HAS_IPYTHON = False
else:
_HAS_IPYTHON = True
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def init_save_btn():
return v.Btn(
class_="ml-5 pmr-2",
height=40,
width=40,
min_width=40,
children=[v.Icon(children=["mdi-download"])],
)
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def init_filename_textfield():
return v.TextField(
class_="ml-3 pl-3",
style_="max-width: 600px",
v_model=str(Path.cwd().joinpath("plot.pdf")),
label="Save As",
)
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def init_noise_param_floattextfield(noise_param: str) -> ui.ValidatedNumberField:
"""
Creates a `ValidatedNumberField` widget for each noise parameter.
The function takes one argument, the name of the noise parameter, and returns a `ValidatedNumberField` widget set to
the current value of that noise parameter in the `NOISE_PARAMS` dictionary.
Parameters
----------
noise_param:
Name of the noise parameter that is being modified
Returns
-------
`ValidatedNumberField` widget
"""
return ui.ValidatedNumberField(
num_type=float,
v_model=noise.NOISE_PARAMS[noise_param],
name=noise_param,
label=noise_param,
step=0.001,
style_="max-width: 180px",
)
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def init_dict_v_plot_options(
active_qubit, active_defaults, scan_params
) -> Dict[str, v.VuetifyWidget]:
"""Creates all the widgets that will be used for general plotting options."""
operator_names = active_qubit.get_operator_names()
noise_channels = active_qubit.supported_noise_channels()
file = open(active_qubit._image_filename, "rb")
image = file.read()
dict_v_plot_options = {
"info_panel": ipywidgets.Image(
value=image, format="jpg", layout=ipywidgets.Layout(width="100%")
),
"scan_param": v.Select(
items=scan_params,
outlined=True,
v_model=active_defaults["scan_param"],
label="Scan over",
),
"amplitude_mode": v.Select(
v_model=gui_defaults.mode_dropdown_list[0],
items=gui_defaults.mode_dropdown_list,
label="Plot as:",
style_="width: 250px",
),
"operator_choice": v.Select(
items=operator_names,
v_model=active_defaults["operator"],
label="Operator",
),
"noise_channel_multiselect": v.Select(
multiple=True,
dense=True,
items=noise_channels,
v_model=noise_channels,
single_line=True,
label="Noise channels",
),
"highest_state": v.Slider(
thumb_label="always",
thumb_size="24",
min=1,
max=10,
style_="min-width: 200px",
v_model=5,
label="Max level",
),
"show_matelem_numbers": v.Switch(v_model=False, label="Show values"),
"show3d_matelem": v.Switch(v_model=True, label="Show 3D"),
"subtract_ground": v.Switch(
v_model=True,
label="Subtract E\u2080",
),
"i_text": ui.ValidatedNumberField(
v_model=1, num_type=int, name="i", v_min=0, dense=True
),
"j_text": ui.ValidatedNumberField(
v_model=0, num_type=int, name="j", v_min=0, dense=True
),
"t1_checkbox": v.Switch(v_model=False, label="Effective T1", dense=True),
"t2_checkbox": v.Switch(v_model=False, label="Effective T2", dense=True),
}
if active_qubit._sys_type in ("Transmon", "TunableTransmon", "Fluxonium"):
dict_v_plot_options["manual_wf_scaling"] = v.Switch(
v_model=False, label="Manual Scaling"
)
dict_v_plot_options["multi_state_selector"] = v.Select(
multiple=True,
items=list(range(0, 10)),
v_model=[0, 1, 2, 3, 4],
label="States",
style_="width: 250px",
)
dict_v_plot_options["wavefunction_scale_slider"] = v.Slider(
min=0.1,
max=4.0,
step=0.05,
v_model=active_defaults["scale"],
disabled=True,
label="\u03c8 ampl.",
style_="width: 200px",
)
else:
dict_v_plot_options["wavefunction_state_slider"] = v.Slider(
min=0,
max=9,
v_model=5,
style_="width: 200px",
thumb_label="always",
thumb_size="24",
label="State no.",
)
literature_params_dropdown_list = ["User specified"]
if active_qubit._sys_type in gui_defaults.paramvals_from_papers.keys():
literature_params_dropdown_list.extend(
gui_defaults.paramvals_from_papers[active_qubit._sys_type].keys()
)
dict_v_plot_options["literature_params"] = v.Select(
class_="ml-3 pl-3 pt-5 mb-0 pb-0",
label="Select qubit parameters",
filled=True,
items=literature_params_dropdown_list,
v_model=literature_params_dropdown_list[0],
)
dict_v_plot_options["link_HTML"] = v.Html(
class_="pl-3 pb-5 pt-0 mt-0", tag="a", children=[""]
)
return dict_v_plot_options
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def init_dict_v_noise_params(active_qubit) -> Dict[str, v.VuetifyWidget]:
"""Creates all the widgets associated with coherence times plots"""
dict_v_noise_params = {}
noise_params = ["T", "omega_low", "omega_high", "t_exp"]
noise_channels = active_qubit.supported_noise_channels()
if "tphi_1_over_f_flux" in noise_channels:
noise_params.append("A_flux")
if "tphi_1_over_f_cc" in noise_channels:
noise_params.append("A_cc")
if "tphi_1_over_f_ng" in noise_channels:
noise_params.append("A_ng")
if "t1_charge_impedance" in noise_channels or "t1_flux_bias_line" in noise_channels:
noise_params.append("R_0")
if "t1_flux_bias_line" in noise_channels:
noise_params.append("M")
if "t1_quasiparticle_tunneling" in noise_channels:
noise_params.append("x_qp")
noise_params.append("Delta")
for noise_param in noise_params:
dict_v_noise_params[noise_param] = init_noise_param_floattextfield(noise_param)
return dict_v_noise_params
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def init_qubit_params_widgets_dict(
qubit: QubitBaseClass,
qubit_params: Dict[str, float],
defaults: Dict[str, Any],
) -> Dict[str, v.VuetifyWidget]:
"""Creates all the widgets associated with the parameters of the
chosen qubit.
"""
dict_v_qubit_params = {}
for param_name, param_val in qubit_params.items():
if isinstance(param_val, int):
kwargs = defaults.get(param_name) or defaults["int"]
dict_v_qubit_params[param_name] = ui.NumberEntryWidget(
num_type=int,
label=f"{param_name}",
v_model=param_val,
text_kwargs={
"style_": "min-width: 80px; max-width: 90px;",
"dense": True,
},
slider_kwargs={
"style_": "min-width: 110px; max-width: 130px",
"dense": True,
},
s_min=0,
s_max=None,
**kwargs,
)
else:
kwargs = defaults.get(param_name) or defaults["float"]
dict_v_qubit_params[param_name] = ui.NumberEntryWidget(
num_type=float,
label=f"{param_name}",
step=gui_defaults.STEP,
v_model=param_val,
text_kwargs={
"style_": "min-width: 80px; max-width: 90px;",
"dense": True,
},
slider_kwargs={
"style_": "min-width: 110px; max-width: 130px",
"dense": True,
},
**kwargs,
)
if isinstance(qubit, (scq.ZeroPi, scq.FullZeroPi)): # scq.Bifluxon
grid_min = qubit.grid.min_val
grid_max = qubit.grid.max_val
dict_v_qubit_params["grid"] = v.RangeSlider(
min=-12 * np.pi,
max=12 * np.pi,
v_model=[grid_min, grid_max],
step=0.05,
thumb_label=True,
thumb_size="24",
style_="width: 200px",
label="Grid range",
)
return dict_v_qubit_params
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def init_ranges_widgets_dict(
qubit, dict_v_plot_options, dict_v_qubit_params
) -> Dict[str, Any]:
"""Creates all the widgets associated with changing the ranges of
certain qubit plot options as well as all of the qubit's parameters.
"""
dict_v_ranges = {}
total_dict = {
**dict_v_plot_options,
**dict_v_qubit_params,
}
for widget_name, widget in total_dict.items():
if widget_name == "noise_channel_multiselect":
continue
if isinstance(widget, v.Slider) and isinstance(widget.v_model, int):
widget_min_text = ui.ValidatedNumberField(
v_model=widget.min,
num_type=int,
label="min",
name="min",
v_min=0,
style_="width: 80px",
class_="px-3",
)
widget_max_text = ui.ValidatedNumberField(
v_model=widget.max,
num_type=int,
label="max",
name="max",
v_min=0,
style_="width: 80px",
class_="px-3",
)
elif isinstance(widget, ui.NumberEntryWidget) and isinstance(
widget.v_model, int
):
widget_min_text = ui.ValidatedNumberField(
v_model=widget.v_min,
num_type=int,
label="min",
name="min",
v_min=0,
style_="width: 80px",
class_="px-3",
)
widget_max_text = ui.ValidatedNumberField(
v_model=widget.v_max,
num_type=int,
label="max",
name="max",
v_min=0,
style_="width: 80px",
class_="px-3",
)
elif isinstance(widget, ui.NumberEntryWidget) and isinstance(
widget.v_model, float
):
widget_min_text = ui.ValidatedNumberField(
v_model=widget.v_min,
num_type=float,
step=0.01,
label="min",
style_="width: 80px",
class_="px-3",
)
widget_max_text = ui.ValidatedNumberField(
v_model=widget.v_max,
num_type=float,
step=0.01,
label="max",
style_="width: 80px",
class_="px-3",
)
elif isinstance(widget, (v.Slider, v.RangeSlider)) and isinstance(
widget.v_model, float
):
widget_min_text = ui.ValidatedNumberField(
v_model=widget.min,
num_type=float,
step=0.01,
label="min",
style_="width: 80px",
class_="px-3",
)
widget_max_text = ui.ValidatedNumberField(
v_model=widget.max,
num_type=float,
step=0.01,
label="max",
style_="width: 80px",
class_="px-3",
)
elif isinstance(widget, v.Select) and widget.multiple:
min_val = widget.items[0]
max_val = widget.items[-1]
widget_min_text = ui.ValidatedNumberField(
v_model=min_val,
num_type=type(widget.items[-1]),
name="min",
label="min",
style_="width: 80px",
class_="px-3",
)
widget_max_text = ui.ValidatedNumberField(
v_model=max_val,
num_type=type(widget.items[-1]),
name="max",
label="max",
style_="width: 80px",
class_="px-3",
)
else:
continue
dict_v_ranges[widget_name] = {
"min": widget_min_text,
"max": widget_max_text,
}
if isinstance(
qubit,
(scq.Transmon, scq.TunableTransmon, scq.Fluxonium, scq.FluxQubit),
):
widget_min_text = ui.ValidatedNumberField(
v_model=qubit._default_grid.min_val,
num_type=float,
label="min",
step=0.01,
style_="width: 80px",
class_="px-3",
)
widget_max_text = ui.ValidatedNumberField(
v_model=qubit._default_grid.max_val,
num_type=float,
label="max",
step=0.01,
style_="width: 80px",
class_="px-3",
)
dict_v_ranges["phi"] = {
"min": widget_min_text,
"max": widget_max_text,
}
elif isinstance(qubit, (scq.ZeroPi,)): # scq.Bifluxon)):
widget_min_text = ui.ValidatedNumberField(
v_model=qubit._default_grid.min_val,
num_type=float,
label="min",
step=0.01,
style_="width: 80px",
class_="px-3",
)
widget_max_text = ui.ValidatedNumberField(
v_model=qubit._default_grid.max_val,
num_type=float,
label="max",
step=0.01,
style_="width: 80px",
class_="px-3",
)
dict_v_ranges["theta"] = {
"min": widget_min_text,
"max": widget_max_text,
}
elif isinstance(qubit, scq.Cos2PhiQubit):
default_grids = {
"phi": qubit._default_phi_grid,
"theta": qubit._default_theta_grid,
"zeta": qubit._default_zeta_grid,
}
for param, param_grid in default_grids.items():
widget_min_text = ui.ValidatedNumberField(
v_model=param_grid.min_val, num_type=float, label="min", step=0.01
)
widget_max_text = ui.ValidatedNumberField(
v_model=param_grid.max_val, num_type=float, label="max", step=0.01
)
dict_v_ranges[param] = {
"min": widget_min_text,
"max": widget_max_text,
}
return dict_v_ranges

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

+1
-0

@@ -9,1 +9,2 @@ include README.md

graft scqubits/core/qubit_img
graft scqubits/ui/icons
h5py>=2.10
pytest
ipywidgets
ipyvuetify
pathos>=0.3.0
dill
traitlets
typing_extensions
+1
-2
Metadata-Version: 2.1
Name: scqubits
Version: 3.1.1
Version: 3.2.0
Summary: scqubits: superconducting qubits in Python

@@ -29,3 +29,2 @@ Home-page: https://scqubits.readthedocs.io

Provides-Extra: pathos
Provides-Extra: fitting
License-File: LICENSE

@@ -32,0 +31,0 @@

@@ -11,3 +11,12 @@ scqubits: superconducting qubits in Python

<br>
> **Join the scqubits mailing list!** Receive information about new releases
> and opportunities to contribute
> to new developments.
> |[SIGN UP](https://sites.northwestern.edu/koch/scqubits-news-sign-up/)|
> |---------------------------------------------------------------------|
<br>
scqubits is an open-source Python library for simulating superconducting qubits. It is meant to give the user

@@ -24,14 +33,14 @@ a convenient way to obtain energy spectra of common superconducting qubits, plot energy levels as a function of

> Peter Groszkowski and Jens Koch,<br>
> *scqubits: a Python package for superconducting qubits*,<br>
> Quantum 5, 583 (2021).<br>
> https://quantum-journal.org/papers/q-2021-11-17-583/
>
> Sai Pavan Chitta, Tianpu Zhao, Ziwen Huang, Ian Mondragon-Shem, and Jens Koch,<br>
> *Computer-aided quantization and numerical analysis of superconducting circuits*,<br>
> arXiv:2206.08320 (2022).<br>
> https://arxiv.org/abs/2206.08320
&nbsp; &nbsp; Peter Groszkowski and Jens Koch,<br>
&nbsp; &nbsp; *scqubits: a Python package for superconducting qubits*,<br>
&nbsp; &nbsp; Quantum 5, 583 (2021).<br>
&nbsp; &nbsp; https://quantum-journal.org/papers/q-2021-11-17-583/
&nbsp; &nbsp; Sai Pavan Chitta, Tianpu Zhao, Ziwen Huang, Ian Mondragon-Shem, and Jens Koch,<br>
&nbsp; &nbsp; *Computer-aided quantization and numerical analysis of superconducting circuits*,<br>
&nbsp; &nbsp; New J. Phys. 24 103020 (2022).<br>
&nbsp; &nbsp; https://iopscience.iop.org/article/10.1088/1367-2630/ac94f2
Download and Installation

@@ -38,0 +47,0 @@ -------------------------

cycler
matplotlib>=3.0.0
cython>=0.29.20,<3.0.0
matplotlib>=3.5
numpy>=1.14.2

@@ -9,2 +10,2 @@ pyyaml

tqdm
typing_extensions
typing_extensions
Metadata-Version: 2.1
Name: scqubits
Version: 3.1.1
Version: 3.2.0
Summary: scqubits: superconducting qubits in Python

@@ -29,3 +29,2 @@ Home-page: https://scqubits.readthedocs.io

Provides-Extra: pathos
Provides-Extra: fitting
License-File: LICENSE

@@ -32,0 +31,0 @@

cycler
matplotlib>=3.0.0
cython<3.0.0,>=0.29.20
matplotlib>=3.5
numpy>=1.14.2

@@ -14,5 +15,2 @@ pyyaml

[fitting]
lmfit
[graphics]

@@ -19,0 +17,0 @@ matplotlib-label-lines>=0.3.6

@@ -24,2 +24,3 @@ LICENSE

scqubits/core/descriptors.py
scqubits/core/diag.py
scqubits/core/discretization.py

@@ -44,13 +45,17 @@ scqubits/core/flux_qubit.py

scqubits/core/zeropi_full.py
scqubits/core/qubit_img/cos2phi-qubit.jpg
scqubits/core/qubit_img/fixed-transmon.jpg
scqubits/core/qubit_img/flux-qubit.jpg
scqubits/core/qubit_img/fluxonium.jpg
scqubits/core/qubit_img/fullzeropi.jpg
scqubits/core/qubit_img/kerr-oscillator.jpg
scqubits/core/qubit_img/oscillator.jpg
scqubits/core/qubit_img/tunable-transmon.jpg
scqubits/core/qubit_img/zeropi.jpg
scqubits/core/qubit_img/Bifluxon.jpg
scqubits/core/qubit_img/Cos2PhiQubit.jpg
scqubits/core/qubit_img/FluxQubit.jpg
scqubits/core/qubit_img/Fluxonium.jpg
scqubits/core/qubit_img/FullZeroPi.jpg
scqubits/core/qubit_img/KerrOscillator.jpg
scqubits/core/qubit_img/Oscillator.jpg
scqubits/core/qubit_img/Snailmon.jpg
scqubits/core/qubit_img/Transmon.jpg
scqubits/core/qubit_img/TunableTransmon.jpg
scqubits/core/qubit_img/ZeroPi.jpg
scqubits/explorer/__init__.py
scqubits/explorer/explorer_panels.py
scqubits/explorer/explorer_settings.py
scqubits/explorer/explorer_widget.py
scqubits/io_utils/__init__.py

@@ -98,2 +103,6 @@ scqubits/io_utils/fileio.py

scqubits/tests/data/fullzeropi_2.hdf5
scqubits/tests/data/snailmon_1.hdf5
scqubits/tests/data/snailmon_2.hdf5
scqubits/tests/data/snailmon_4.hdf5
scqubits/tests/data/snailmon_5.hdf5
scqubits/tests/data/transmon_1.hdf5

@@ -110,7 +119,15 @@ scqubits/tests/data/transmon_2.hdf5

scqubits/ui/__init__.py
scqubits/ui/explorer_widget.py
scqubits/ui/gui.py
scqubits/ui/gui_custom_widgets.py
scqubits/ui/gui_defaults.py
scqubits/ui/gui_navbar.py
scqubits/ui/gui_setup.py
scqubits/ui/hspace_widget.py
scqubits/ui/qubit_widget.py
scqubits/ui/icons/En.png
scqubits/ui/icons/Me.png
scqubits/ui/icons/MeS.png
scqubits/ui/icons/T1.png
scqubits/ui/icons/psi.png
scqubits/ui/icons/scq-logo.png
scqubits/utils/__init__.py

@@ -117,0 +134,0 @@ scqubits/utils/cpu_switch.py

@@ -30,6 +30,11 @@ # scqubits: superconducting qubits in Python

from scqubits.core.discretization import Grid1d
from scqubits.core.diag import DIAG_METHODS
from scqubits.core.flux_qubit import FluxQubit
from scqubits.core.fluxonium import Fluxonium
from scqubits.core.generic_qubit import GenericQubit
from scqubits.core.hilbert_space import HilbertSpace, InteractionTerm
from scqubits.core.hilbert_space import (
HilbertSpace,
InteractionTerm,
InteractionTermStr,
)
from scqubits.core.noise import calc_therm_ratio

@@ -54,2 +59,7 @@ from scqubits.core.oscillator import KerrOscillator, Oscillator

# diagonalization
import scqubits.core.diag as diag
from scqubits.core.diag import (
DIAG_METHODS,
)

@@ -65,11 +75,17 @@ # Import of custom-circuit modules needs to take place after other imports to

try:
from scqubits.ui.explorer_widget import Explorer
from scqubits.explorer.explorer_widget import Explorer
from scqubits.ui.gui import GUI
except NameError:
warnings.warn(
"scqubits: could not import GUI/Explorer - consider installing ipywidgets "
"(optional dependency)?",
ImportWarning,
)
except (ImportError, NameError):
def Explorer(*args, **kwargs):
warnings.warn(
"scqubits: could not create Explorer - did you install the optional dependency ipyvuetify?"
)
def GUI(*args, **kwargs):
warnings.warn(
"scqubits: could not create GUI - did you install the optional dependency ipyvuetify?"
)
# for showing scqubits info

@@ -76,0 +92,0 @@ from scqubits.utils.misc import about, cite

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

from types import MethodType
from typing import Optional
from weakref import WeakKeyDictionary

@@ -72,3 +73,3 @@

def register(
self, event: str, who: "DispatchClient", callback: MethodType = None
self, event: str, who: "DispatchClient", callback: Optional[MethodType] = None
) -> None:

@@ -75,0 +76,0 @@ """

@@ -14,3 +14,2 @@ # circuit-utils.py

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

@@ -20,3 +19,2 @@

import sympy as sm
from numpy import ndarray

@@ -23,0 +21,0 @@ from scipy import sparse

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

import math
import os

@@ -449,7 +448,7 @@ from abc import ABC, abstractmethod

ncut:
cutoff of charge basis, -ncut <= :math:`n_\theta` <= ncut
cutoff in charge basis, -ncut <= :math:`n_\theta` <= ncut
zeta_cut:
number of harmonic oscillator basis for :math:`\zeta` variable
number of harmonic oscillator basis states for :math:`\zeta` variable
phi_cut:
number of harmonic oscillator basis for :math:`\phi` variable
number of harmonic oscillator basis states for :math:`\phi` variable
truncated_dim:

@@ -490,4 +489,15 @@ desired dimension of the truncated quantum system; expected: truncated_dim > 1

id_str: Optional[str] = None,
evals_method: Optional[str] = None,
evals_method_options: Optional[dict] = None,
esys_method: Optional[str] = None,
esys_method_options: Optional[dict] = None,
) -> None:
base.QuantumSystem.__init__(self, id_str=id_str)
base.QubitBaseClass.__init__(
self,
id_str=id_str,
evals_method=evals_method,
evals_method_options=evals_method_options,
esys_method=esys_method,
esys_method_options=esys_method_options,
)
self.EJ = EJ

@@ -509,6 +519,2 @@ self.ECJ = ECJ

self._default_theta_grid = discretization.Grid1d(-0.5 * np.pi, 1.5 * np.pi, 100)
self._image_filename = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"qubit_img/cos2phi-qubit.jpg",
)

@@ -515,0 +521,0 @@ @staticmethod

@@ -15,3 +15,3 @@ # descriptors.py

from typing import Any, Generic, Type, TypeVar
from typing import Any, Generic, Optional, Type, TypeVar

@@ -66,4 +66,4 @@ from scqubits.core.central_dispatch import DispatchClient

event: str,
inner_object_name: str = None,
attr_name: str = None,
inner_object_name: Optional[str] = None,
attr_name: Optional[str] = None,
) -> None:

@@ -70,0 +70,0 @@ self.event = event

@@ -13,4 +13,2 @@ # flux_qubit.py

import os
from abc import ABC, abstractmethod

@@ -341,4 +339,15 @@ from typing import Any, Dict, List, Optional, Tuple, Union

id_str: Optional[str] = None,
evals_method: Optional[str] = None,
evals_method_options: Optional[dict] = None,
esys_method: Optional[str] = None,
esys_method_options: Optional[dict] = None,
) -> None:
base.QuantumSystem.__init__(self, id_str=id_str)
base.QubitBaseClass.__init__(
self,
id_str=id_str,
evals_method=evals_method,
evals_method_options=evals_method_options,
esys_method=esys_method,
esys_method_options=esys_method_options,
)
self.EJ1 = EJ1

@@ -360,5 +369,2 @@ self.EJ2 = EJ2

) # for plotting in phi_j basis
self._image_filename = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "qubit_img/flux-qubit.jpg"
)

@@ -391,2 +397,3 @@ @staticmethod

"tphi_1_over_f_cc",
"tphi_1_over_f_flux",
# 'tphi_1_over_f_ng1',

@@ -416,3 +423,6 @@ # 'tphi_1_over_f_ng2',

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

@@ -424,3 +434,6 @@ return np.sort(evals)

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

@@ -633,2 +646,44 @@ evals, evecs = spec_utils.order_eigensystem(evals, evecs)

def d_hamiltonian_d_flux(
self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False
) -> ndarray:
"""
Returns the operator representing a derivative of the Hamiltonian with respect to flux
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 = (
2j
* np.pi
* (
-0.5
* self.EJ3
* (
np.exp(1j * 2 * np.pi * self.flux)
* np.kron(self._exp_i_phi_operator(), self._exp_i_phi_operator().T)
)
+ 0.5
* self.EJ3
* (
np.exp(-1j * 2 * np.pi * self.flux)
* np.kron(self._exp_i_phi_operator().T, self._exp_i_phi_operator())
)
)
)
return self.process_op(native_op=native, energy_esys=energy_esys)
def _n_operator(self) -> ndarray:

@@ -635,0 +690,0 @@ diag_elements = np.arange(-self.ncut, self.ncut + 1, dtype=np.complex_)

@@ -15,3 +15,2 @@ # fluxonium.py

import math
import os

@@ -81,4 +80,15 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union

id_str: Optional[str] = None,
evals_method: Optional[str] = None,
evals_method_options: Optional[dict] = None,
esys_method: Optional[str] = None,
esys_method_options: Optional[dict] = None,
) -> None:
base.QuantumSystem.__init__(self, id_str=id_str)
base.QubitBaseClass.__init__(
self,
id_str=id_str,
evals_method=evals_method,
evals_method_options=evals_method_options,
esys_method=esys_method,
esys_method_options=esys_method_options,
)
self.EJ = EJ

@@ -91,5 +101,2 @@ self.EC = EC

self._default_grid = discretization.Grid1d(-4.5 * np.pi, 4.5 * np.pi, 151)
self._image_filename = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "qubit_img/fluxonium.jpg"
)

@@ -96,0 +103,0 @@ @staticmethod

@@ -42,2 +42,3 @@ # hilbert_space.py

import scqubits.core.descriptors as descriptors
import scqubits.core.diag as diag
import scqubits.core.oscillator as osc

@@ -58,2 +59,3 @@ import scqubits.core.spec_lookup as spec_lookup

if settings.IN_IPYTHON:

@@ -96,5 +98,6 @@ from tqdm.notebook import tqdm

g_strength = descriptors.WatchedProperty(complex, "INTERACTIONTERM_UPDATE")
operator_list = descriptors.WatchedProperty(
List[Tuple[int, Union[ndarray, csc_matrix]]], "INTERACTIONTERM_UPDATE"
)
List[Tuple[int, Union[ndarray, csc_matrix, Callable]]], "INTERACTIONTERM_UPDATE"
) # Each item in the operator_list is a tuple (subsys_index, operator)
add_hc = descriptors.WatchedProperty(bool, "INTERACTIONTERM_UPDATE")

@@ -105,3 +108,3 @@

g_strength: Union[float, complex],
operator_list: List[Tuple[int, Union[ndarray, csc_matrix]]],
operator_list: List[Tuple[int, Union[ndarray, csc_matrix, Callable]]],
add_hc: bool = False,

@@ -168,15 +171,51 @@ ) -> None:

def id_wrap_all_ops(
operator_list: List[Tuple[int, Union[ndarray, csc_matrix]]],
operator_list: List[Tuple[int, Union[ndarray, csc_matrix, Callable]]],
subsystem_list: List[QuantumSys],
bare_esys: Optional[Dict[int, ndarray]] = None,
) -> list:
) -> List[Qobj]:
"""
Returns a list of identity-wrapped operators, one for each operator in
operator_list. Note: at this point, any callable operator is actually evaluated.
Parameters
----------
operator_list:
list of tuples (subsys_index, operator)
subsystem_list:
list of all quantum systems in HilbertSpace calling ``hamiltonian``,
needed for identity wrapping
bare_esys:
optionally, the bare eigensystems for each subsystem can be provided to
speed up computation; these are provided in dict form via <subsys>: esys)
Returns
-------
list of identity-wrapped operators
"""
id_wrapped_operators = []
for subsys_index, operator in operator_list:
if bare_esys is not None and subsys_index in bare_esys:
evecs = bare_esys[subsys_index][1]
esys = bare_esys[subsys_index]
evecs = esys[1]
else:
esys = None
evecs = None
if callable(operator):
try:
operator = operator(energy_esys=esys)
except TypeError:
operator = operator()
op_in_eigenbasis = bool(esys)
else:
op_in_eigenbasis = False
id_wrapped_operators.append(
spec_utils.identity_wrap(
operator, subsystem_list[subsys_index], subsystem_list, evecs=evecs
operator,
subsystem_list[subsys_index],
subsystem_list,
evecs=evecs,
op_in_eigenbasis=op_in_eigenbasis,
)

@@ -351,2 +390,6 @@ )

ignore_low_overlap: bool = False,
evals_method: Optional[str] = None,
evals_method_options: Optional[dict] = None,
esys_method: Optional[str] = None,
esys_method_options: Optional[dict] = None,
) -> None:

@@ -378,2 +421,7 @@ if has_duplicate_id_str(subsystem_list):

self.evals_method = evals_method
self.evals_method_options = evals_method_options
self.esys_method = esys_method
self.esys_method_options = esys_method_options
# The following attributes are for compatibility with SpectrumLookupMixin

@@ -551,3 +599,3 @@ self._data: Dict[str, Any] = {}

"""Returns total dimension of joint Hilbert space"""
return np.prod(np.asarray(self.subsystem_dims)) # type:ignore
return np.prod(np.asarray(self.subsystem_dims)).item()

@@ -630,4 +678,4 @@ @property

) -> ndarray:
"""Calculates eigenvalues of the full Hamiltonian using
`qutip.Qobj.eigenenergies()`.
"""Calculates eigenvalues of the full Hamiltonian. Qutip's `qutip.Qobj.eigenenergies()` is
used by default, unless `self.evals_method` has been set to something other than `None`.

@@ -642,5 +690,26 @@ Parameters

"""
# hamiltonian_mat = self.hamiltonian(bare_esys=bare_esys) # type:ignore
# return hamiltonian_mat.eigenenergies(eigvals=evals_count)
hamiltonian_mat = self.hamiltonian(bare_esys=bare_esys) # type:ignore
return hamiltonian_mat.eigenenergies(eigvals=evals_count)
if not hasattr(self, "evals_method") or self.evals_method is None:
evals = hamiltonian_mat.eigenenergies(eigvals=evals_count)
else:
diagonalizer = (
diag.DIAG_METHODS[self.evals_method]
if isinstance(self.evals_method, str)
else self.evals_method
)
evals = diagonalizer(
hamiltonian_mat,
evals_count=evals_count,
**(
{}
if self.evals_method_options is None
else self.evals_method_options
),
)
return evals
def eigensys(

@@ -651,4 +720,5 @@ self,

) -> Tuple[ndarray, QutipEigenstates]:
"""Calculates eigenvalues and eigenvectors of the full Hamiltonian using
`qutip.Qobj.eigenstates()`.
"""Calculates eigenvalues and eigenvectors of the full Hamiltonian. Qutip's
`qutip.Qobj.eigenenergies()` is used by default, unless `self.evals_method`
has been set to something other than `None`.

@@ -667,5 +737,23 @@ Parameters

"""
hamiltonian_mat = self.hamiltonian(bare_esys=bare_esys) # type:ignore
evals, evecs = hamiltonian_mat.eigenstates(eigvals=evals_count)
if not hasattr(self, "esys_method") or self.esys_method is None:
evals, evecs = hamiltonian_mat.eigenstates(eigvals=evals_count)
else:
diagonalizer = (
diag.DIAG_METHODS[self.esys_method]
if isinstance(self.esys_method, str)
else self.esys_method
)
evals, evecs = diagonalizer(
hamiltonian_mat,
evals_count=evals_count,
**(
{} if self.esys_method_options is None else self.esys_method_options
),
)
evecs = evecs.view(scqubits.io_utils.fileio_qutip.QutipEigenstates)
return evals, evecs

@@ -898,3 +986,3 @@

leave=False,
disable=(num_cpus > 1),
disable=(num_cpus > 1) or settings.PROGRESSBAR_DISABLED,
),

@@ -924,3 +1012,3 @@ )

leave=False,
disable=(num_cpus > 1),
disable=(num_cpus > 1) or settings.PROGRESSBAR_DISABLED,
),

@@ -948,3 +1036,7 @@ )

def op_in_dressed_eigenbasis(self, **kwargs) -> Qobj:
def op_in_dressed_eigenbasis(
self,
op: Union[Tuple[Union[np.ndarray, csc_matrix], QuantumSys], Callable],
**kwargs,
) -> Qobj:
"""

@@ -969,18 +1061,19 @@ Express a subsystem operator in the dressed eigenbasis of the full system

"""
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
if isinstance(op, tuple):
op_matrix, subsys = op
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
return self._op_matrix_to_dressed_eigenbasis(
op_matrix, subsys_index, op_in_bare_eigenbasis
)
def _op_in_dressed_eigenbasis(
self, op: ndarray, subsys_index: int, op_in_bare_eigenbasis: bool = False
assert callable(op)
subsys_index = self.get_subsys_index(op.__self__)
return self._op_callable_to_dressed_eigenbasis(op, subsys_index)
def _op_matrix_to_dressed_eigenbasis(
self,
op: Union[np.ndarray, csc_matrix],
subsys_index: int,
op_in_bare_eigenbasis,
) -> Qobj:

@@ -999,2 +1092,16 @@ bare_evecs = self._data["bare_evecs"][subsys_index][0]

def _op_callable_to_dressed_eigenbasis(
self, op: Callable, subsys_index: int
) -> 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,
evecs=bare_evecs,
)
dressed_evecs = self._data["evecs"][0]
dressed_op = id_wrapped_op.transform(dressed_evecs)
return dressed_op
###################################################################################

@@ -1048,2 +1155,7 @@ # HilbertSpace: add interaction and parsing arguments to .add_interaction

Parameters
----------
check_validity:
optional bool indicating whether to check the validity of the interaction;
switch this off for speed if you are sure the interaction is valid
id_str:

@@ -1077,6 +1189,6 @@ optional string by which this instance can be referred to in `HilbertSpace`

_ = self.interaction_hamiltonian()
except:
except Exception as inst:
self.interaction_list.pop()
del self._interaction_term_by_id_str[id_str]
raise ValueError("Invalid Interaction Term")
raise ValueError(f"Invalid Interaction Term. Exception: {inst}")

@@ -1092,3 +1204,3 @@ def _parse_interactiontermstr(self, **kwargs) -> InteractionTermStr:

raise TypeError("Unexpected keyword argument {}.".format(key))
operator_list.append(self._parse_op_by_name(kwargs[key]))
operator_list.append(self._parse_str_based_op(kwargs[key]))

@@ -1107,3 +1219,3 @@ return InteractionTermStr(expr, operator_list, const=const, add_hc=add_hc)

raise TypeError("Unexpected keyword argument {}.".format(key))
subsys_index, op = self._parse_op(kwargs[key])
subsys_index, op = self._parse_non_strbased_op(kwargs[key])
operator_list.append((subsys_index, op))

@@ -1120,22 +1232,33 @@

def _parse_op_by_name(
self, op_by_name
) -> Tuple[int, str, Union[ndarray, csc_matrix, dia_matrix]]:
if not isinstance(op_by_name, tuple):
raise TypeError("Cannot interpret specified operator {}".format(op_by_name))
if len(op_by_name) == 3:
def _parse_str_based_op(
self,
keyword_value: Union[Tuple[str, ndarray, QuantumSys], Tuple[str, Callable]],
) -> Tuple[int, str, Union[ndarray, csc_matrix, dia_matrix, Callable]]:
if not isinstance(keyword_value, tuple):
raise TypeError(
"Cannot interpret specified operator {}".format(keyword_value)
)
if len(keyword_value) == 3:
# format expected: (<op name as str>, <op as array>, <subsys as QuantumSystem>)
return self.get_subsys_index(op_by_name[2]), op_by_name[0], op_by_name[1]
return (
self.get_subsys_index(keyword_value[2]),
keyword_value[0],
keyword_value[1],
)
# format expected (<op name as str)>, <QuantumSystem.method callable>)
return (
self.get_subsys_index(op_by_name[1].__self__),
op_by_name[0],
op_by_name[1](),
self.get_subsys_index(keyword_value[1].__self__),
keyword_value[0],
keyword_value[1],
)
def _parse_op(
self, op: Union[Callable, Tuple[Union[ndarray, csc_matrix], QuantumSys]]
) -> Tuple[int, Union[ndarray, csc_matrix]]:
def _parse_non_strbased_op(
self,
op: Union[Callable, Tuple[Union[ndarray, csc_matrix], QuantumSys]],
) -> Tuple[int, Union[ndarray, csc_matrix, Callable]]:
if callable(op):
return self.get_subsys_index(op.__self__), op() # type:ignore
return (
self.get_subsys_index(op.__self__),
op,
) # store op here, not op() [v3.2]
if not isinstance(op, tuple):

@@ -1142,0 +1265,0 @@ raise TypeError("Cannot interpret specified operator {}".format(op))

@@ -19,4 +19,4 @@ # noise.py

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

@@ -38,3 +38,2 @@ import scipy as sp

# flag that lets us show a warning about the default t1 behavior

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

# If axes was given in fig_as, it should support the plot structure
# If axes was given in fig_ax, it should support the plot structure
# consistent with plot_grid, otherwise the plotting routine below, will fail

@@ -232,0 +231,0 @@ fig, axes = kwargs.get("fig_ax") or plt.subplots(*plot_grid, figsize=figsize)

@@ -13,3 +13,3 @@ # operators.py

from typing import Union
from typing import Optional, Union

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

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

@@ -77,3 +77,5 @@

def number(dimension: int, prefactor: Union[float, complex] = None) -> ndarray:
def number(
dimension: int, prefactor: Optional[Union[float, complex]] = None
) -> ndarray:
"""Number operator matrix of size dimension x dimension in sparse matrix

@@ -102,3 +104,3 @@ representation. An additional prefactor can be directly included in the

def number_sparse(
dimension: int, prefactor: Union[float, complex] = None
dimension: int, prefactor: Optional[Union[float, complex]] = None
) -> csc_matrix:

@@ -105,0 +107,0 @@ """Number operator matrix of size dimension x dimension in sparse matrix

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

import os
from typing import Any, Dict, Optional, Tuple, Union

@@ -152,3 +151,3 @@

def annihilation_operator(self) -> ndarray:
"""Returns the creation operator"""
"""Returns the annihilation operator"""
return op.annihilation(self.truncated_dim)

@@ -170,3 +169,3 @@

+ "in order to use the phi_operator() method. This can be done by either\n"
+ "passing it to the class constructor, or by setting it afterwords."
+ "passing it to the class constructor, or by setting it afterwards."
)

@@ -186,3 +185,3 @@ a = op.annihilation(self.truncated_dim)

+ "in order to use the n_operator() method. This can be done by either\n"
+ "passing it to the class constructor, or by setting it afterwords."
+ "passing it to the class constructor, or by setting it afterwards."
)

@@ -235,3 +234,3 @@ a = op.annihilation(self.truncated_dim)

self._image_filename = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "qubit_img/kerr-oscillator.jpg"
os.path.dirname(os.path.abspath(__file__)), "qubit_img/KerrOscillator.jpg"
)

@@ -238,0 +237,0 @@

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

import numpy as np
from matplotlib.axes import Axes

@@ -40,2 +39,3 @@ from matplotlib.figure import Figure

from qutip import Qobj
from scipy.sparse import csc_matrix
from typing_extensions import Literal

@@ -50,3 +50,2 @@

import scqubits.utils.plotting as plot
from scqubits import settings as settings

@@ -72,3 +71,3 @@ from scqubits.core.hilbert_space import HilbertSpace

from scqubits.utils.typedefs import GIndexTuple, NpIndices, QubitList
from scqubits.utils.typedefs import GIndexTuple, NpIndices

@@ -80,2 +79,5 @@ BareLabel = Tuple[int, ...]

_faulty_interactionterm_warning_issued = False # flag to ensure single-time warning
class ParameterSlice:

@@ -130,2 +132,3 @@ """

_out_of_sync = False
_out_of_sync_warning_issued = False
_current_param_indices: NpIndices

@@ -381,3 +384,3 @@

if subsystems is None:
return self.hilbertspace.subsys_list
return self.hilbertspace.subsystem_list
if isinstance(subsystems, list):

@@ -963,3 +966,2 @@ return subsystems

self._num_cpus = num_cpus
self.tqdm_disabled = settings.PROGRESSBAR_DISABLED or (num_cpus > 1)

@@ -972,5 +974,28 @@ self._out_of_sync = False

global _faulty_interactionterm_warning_issued
if self.faulty_interactionterm_suspected() and not _faulty_interactionterm_warning_issued:
warnings.warn(
"The interactions specified for this HilbertSpace object involve coupling operators stored as fixed "
"matrices. This may be unintended, as the operators of quantum systems (specifically, their "
"representation with respect to some basis) may change as a function of sweep parameters. \nFor that "
"reason, it is recommended to use coupling operators specified as callable functions.\n",
UserWarning,
)
_faulty_interactionterm_warning_issued = True
if autorun:
self.run()
@property
def tqdm_disabled(self) -> bool:
return settings.PROGRESSBAR_DISABLED or (self._num_cpus > 1)
def faulty_interactionterm_suspected(self) -> bool:
"""Check if any interaction terms are specified as fixed matrices"""
for interactionterm in self._hilbertspace.interaction_list:
for idx_operator in interactionterm.operator_list:
if isinstance(idx_operator[1], (ndarray, Qobj, csc_matrix)):
return True
return False
def cause_dispatch(self) -> None:

@@ -1006,2 +1031,4 @@ initial_parameters = tuple(paramvals[0] for paramvals in self._parameters)

# generate one dispatch before temporarily disabling CENTRAL_DISPATCH
self._out_of_sync = False
self._out_of_sync_warning_issued = False

@@ -1103,3 +1130,2 @@ self._lookup_exists = True

multi_cpu = self._num_cpus > 1
target_map = cpu_switch.get_map_method(self._num_cpus)

@@ -1125,3 +1151,3 @@

leave=False,
disable=multi_cpu,
disable=self.tqdm_disabled,
)

@@ -1179,3 +1205,2 @@

"""
multi_cpu = self._num_cpus > 1
target_map = cpu_switch.get_map_method(self._num_cpus)

@@ -1202,3 +1227,3 @@ total_count = np.prod(self._parameters.counts)

leave=False,
disable=multi_cpu,
disable=self.tqdm_disabled,
)

@@ -1205,0 +1230,0 @@ )

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

import inspect
import os

@@ -33,4 +34,4 @@ from abc import ABC, ABCMeta, abstractmethod

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

@@ -46,2 +47,3 @@ import scipy as sp

import scqubits.core.descriptors as descriptors
import scqubits.core.diag as diag
import scqubits.core.units as units

@@ -123,2 +125,7 @@ import scqubits.settings as settings

self._id_str = id_str or self._autogenerate_id_str()
self._image_filename = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"qubit_img",
type(self).__name__ + ".jpg",
)

@@ -175,3 +182,14 @@ def __init_subclass__(cls):

object."""
return {name: getattr(self, name) for name in self._init_params}
EXCLUDE = [
"evals_method",
"evals_method_options",
"esys_method",
"esys_method_options",
]
initdata = {
name: getattr(self, name)
for name in self._init_params
if name not in EXCLUDE
}
return initdata

@@ -210,3 +228,3 @@ @abstractmethod

def widget(self, params: Dict[str, Any] = None):
def widget(self, params: Optional[Dict[str, Any]] = None):
"""Use ipywidgets to modify parameters of class instance"""

@@ -216,3 +234,3 @@ init_params = params or self.get_initdata()

ui.create_widget(
self.set_params, init_params, image_filename=self._image_filename
self.set_params_from_gui, init_params, image_filename=self._image_filename
)

@@ -226,2 +244,10 @@

def set_params_from_gui(self, change):
"""
Set new parameters through the provided dictionary.
"""
param_name = change["owner"].name
param_val = change["owner"].num_value
setattr(self, param_name, param_val)
def set_params(self, **kwargs):

@@ -256,2 +282,16 @@ """

def __init__(
self,
id_str: Union[str, None],
evals_method: Union[str, None] = None,
evals_method_options: Union[Dict, None] = None,
esys_method: Union[str, None] = None,
esys_method_options: Union[Dict, None] = None,
):
super().__init__(id_str=id_str)
self.evals_method = evals_method
self.evals_method_options = evals_method_options
self.esys_method = esys_method
self.esys_method_options = esys_method_options
@abstractmethod

@@ -264,3 +304,6 @@ def hamiltonian(self):

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

@@ -272,3 +315,6 @@ return np.sort(evals)

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

@@ -282,3 +328,3 @@ evals, evecs = order_eigensystem(evals, evecs)

evals_count: int = 6,
filename: str = None,
filename: Optional[str] = None,
return_spectrumdata: "Literal[False]" = False,

@@ -300,3 +346,3 @@ ) -> ndarray:

evals_count: int = 6,
filename: str = None,
filename: Optional[str] = None,
return_spectrumdata: bool = False,

@@ -322,3 +368,15 @@ ) -> Union[SpectrumData, ndarray]:

"""
evals = self._evals_calc(evals_count)
if not hasattr(self, "evals_method") or self.evals_method is None:
evals = self._evals_calc(evals_count)
else:
diagonalizer = (
diag.DIAG_METHODS[self.evals_method]
if isinstance(self.evals_method, str)
else self.evals_method
)
options = (
{} if self.esys_method_options is None else self.esys_method_options
)
evals = diagonalizer(self.hamiltonian(), evals_count, **options)
if filename or return_spectrumdata:

@@ -336,3 +394,3 @@ specdata = SpectrumData(

evals_count: int = 6,
filename: str = None,
filename: Optional[str] = None,
return_spectrumdata: "Literal[False]" = False,

@@ -354,3 +412,3 @@ ) -> Tuple[ndarray, ndarray]:

evals_count: int = 6,
filename: str = None,
filename: Optional[str] = None,
return_spectrumdata: bool = False,

@@ -369,4 +427,3 @@ ) -> Union[Tuple[ndarray, ndarray], SpectrumData]:

(default value = None)
return_spectrumdata:
if set to true, the returned data is provided as a SpectrumData object
return_spectrumdata: if set to true, the returned data is provided as a SpectrumData object
(default value = False)

@@ -378,3 +435,15 @@

"""
evals, evecs = self._esys_calc(evals_count)
if not hasattr(self, "esys_method") or self.esys_method is None:
evals, evecs = self._esys_calc(evals_count)
else:
diagonalizer = (
diag.DIAG_METHODS[self.esys_method]
if isinstance(self.esys_method, str)
else self.esys_method
)
options = (
{} if self.esys_method_options is None else self.esys_method_options
)
evals, evecs = diagonalizer(self.hamiltonian(), evals_count, **options)
if filename or return_spectrumdata:

@@ -1152,3 +1221,3 @@ specdata = SpectrumData(

phi_grid: Grid1d = None,
scaling: float = None,
scaling: Optional[float] = None,
**kwargs,

@@ -1155,0 +1224,0 @@ ) -> Tuple[Figure, Axes]:

@@ -123,3 +123,3 @@ # spec_lookup.py

``param_indices``, create an array of the dressed-state indices in an order
that corresponds one to one to the bare product states with largest overlap
that corresponds one-to-one to the bare product states with largest overlap
(whenever possible).

@@ -131,7 +131,21 @@

indices of the parameter values
Length of tuple must match the number of parameters in the `ParameterSweep` object inheriting from
`SpectrumLookupMixin`.
Returns
-------
dressed-state indices
1d array of dressed-state indices
Dimensions: (`self.hilbertspace.dimension`,)
Array which contains the dressed-state indices in an order that corresponds to the canonically ordered bare
product state basis, i.e. (0,0,0), (0,0,1), (0,0,2), ..., (0,1,0), (0,1,1), (0,1,2), ... etc.
For example, for two subsystems with two states each, the array [0, 2, 1, 3] would mean:
(0,0) corresponds to the dressed state 0,
(0,1) corresponds to the dressed state 2,
(1,0) corresponds to the dressed state 1,
(1,1) corresponds to the dressed state 3.
"""
# Overlaps between dressed energy eigenstates and bare product states, <e1, e2, ...| E_j>
# Since the Hamiltonian matrix is explicitly constructed in the bare product states basis, this is just the same
# as the matrix of eigenvectors handed back when diagonalizing the Hamiltonian matrix.
overlap_matrix = spec_utils.convert_evecs_to_ndarray(

@@ -157,2 +171,5 @@ self._data["evecs"][param_indices]

) -> NpIndexTuple:
"""
Convert the NpIndices parameter indices to a tuple of NpIndices.
"""
param_indices = param_indices or self._current_param_indices

@@ -168,3 +185,3 @@ if not isinstance(param_indices, tuple):

bare_labels: Tuple[int, ...],
param_indices: Optional[NpIndices] = None,
param_npindices: Optional[NpIndices] = None,
) -> Union[ndarray, int, None]:

@@ -178,10 +195,14 @@ """

bare_labels = (index, index2, ...)
param_indices:
Dimension: (`self.hilbertspace.subsystem_count`,)
param_npindices:
indices of parameter values of interest
Depending on the nature of the slice, this can be a single parameter point or multiple ones.
Returns
-------
dressed state index closest to the specified bare state
dressed state index closest to the specified bare state with excitation numbers given by `bare_labels`.
If `param_npindices` spans multiple parameter points, then this returns a corresponding 1d array of
length dictated by the number of parameter points.
"""
param_indices = self.set_npindextuple(param_indices)
param_npindices = self.set_npindextuple(param_npindices)
try:

@@ -191,4 +212,5 @@ lookup_position = self._bare_product_states_labels.index(bare_labels)

return None
return self._data["dressed_indices"][param_indices + (lookup_position,)]
return self._data["dressed_indices"][param_npindices + (lookup_position,)]
@utils.check_lookup_exists

@@ -275,3 +297,3 @@ @utils.check_sync_status

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

@@ -287,3 +309,3 @@ """

whether to subtract the ground state energy
param_indices:
param_npindices:
indices specifying the set of parameters

@@ -295,4 +317,4 @@

"""
param_indices = self.set_npindextuple(param_indices)
dressed_index = self.dressed_index(bare_tuple, param_indices)
param_npindices = self.set_npindextuple(param_npindices)
dressed_index = self.dressed_index(bare_tuple, param_npindices)

@@ -302,5 +324,5 @@ if dressed_index is None:

if isinstance(dressed_index, numbers.Number):
energy = self["evals"][param_indices + (dressed_index,)]
energy = self["evals"][param_npindices + (dressed_index,)]
if subtract_ground:
energy -= self["evals"][param_indices + (0,)]
energy -= self["evals"][param_npindices + (0,)]
return energy

@@ -311,3 +333,3 @@

it = np.nditer(dressed_index, flags=["multi_index", "refs_ok"])
sliced_energies = self["evals"][param_indices]
sliced_energies = self["evals"][param_npindices]

@@ -314,0 +336,0 @@ for location in it:

@@ -69,2 +69,19 @@ # storage.py

"""
self.amplitudes *= self.amplitude_scale_factor(potential_vals)
def amplitude_scale_factor(self, potential_vals: np.ndarray) -> float:
"""
Returnn scale factor that converts the dimensionless amplitude to a (pseudo-)energy that allows us to plot
wavefunctions and potential energies in the same plot.
Parameters
----------
potential_vals:
array of potential energy values (that determine the energy range on the y axis
Returns
-------
scale factor
"""
FILL_FACTOR = 0.1

@@ -75,3 +92,3 @@ energy_range = np.max(potential_vals) - np.min(potential_vals)

return 0.0
self.amplitudes *= FILL_FACTOR * energy_range / amplitude_range
return FILL_FACTOR * energy_range / amplitude_range

@@ -78,0 +95,0 @@

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

import math
import os

@@ -43,3 +42,3 @@ from typing import Any, Dict, List, Optional, Tuple, Union

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

@@ -85,4 +84,15 @@

id_str: Optional[str] = None,
evals_method: Optional[str] = None,
evals_method_options: Optional[dict] = None,
esys_method: Optional[str] = None,
esys_method_options: Optional[dict] = None,
) -> None:
base.QuantumSystem.__init__(self, id_str=id_str)
base.QubitBaseClass.__init__(
self,
id_str=id_str,
evals_method=evals_method,
evals_method_options=evals_method_options,
esys_method=esys_method,
esys_method_options=esys_method_options,
)
self.EJ = EJ

@@ -95,5 +105,2 @@ self.EC = EC

self._default_n_range = (-5, 6)
self._image_filename = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "qubit_img/fixed-transmon.jpg"
)

@@ -637,4 +644,15 @@ @staticmethod

id_str: Optional[str] = None,
evals_method: Optional[str] = None,
evals_method_options: Optional[dict] = None,
esys_method: Optional[str] = None,
esys_method_options: Optional[dict] = None,
) -> None:
base.QuantumSystem.__init__(self, id_str=id_str)
base.QubitBaseClass.__init__(
self,
id_str=id_str,
evals_method=evals_method,
evals_method_options=evals_method_options,
esys_method=esys_method,
esys_method_options=esys_method_options,
)
self.EJmax = EJmax

@@ -649,5 +667,2 @@ self.EC = EC

self._default_n_range = (-5, 6)
self._image_filename = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "qubit_img/tunable-transmon.jpg"
)

@@ -690,10 +705,9 @@ @property

) -> ndarray:
"""
Returns operator representing a derivative of the Hamiltonian with respect to
r"""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
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}`.

@@ -727,3 +741,5 @@

* self.cos_phi_operator()
- np.pi * self.EJmax * self.d
- np.pi
* self.EJmax
* self.d
/ np.sqrt(

@@ -730,0 +746,0 @@ np.cos(np.pi * self.flux) ** 2

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

from typing import List
from typing import List, Optional

@@ -35,3 +35,8 @@ # Currently set units, referred to elsewhere as "system units" (must be one of the units

def get_units() -> str:
"""Get system units."""
"""The get_units function returns the current units of the system.
Returns
-------
The current units as a string
"""
return _current_units

@@ -41,3 +46,14 @@

def set_units(units: str) -> str:
"""Set system units."""
"""The set_units function is used to set the system units for all qubit instances.
The default unit system is GHz, but this can be changed by calling `set_units()` with one of the `_supported_units`
Parameters
----------
units:
Specify the units that are to be used in the system
Returns
-------
The units that were set
"""
# Importing here avoids a cyclic import problem.

@@ -69,3 +85,3 @@ from scqubits.core.qubit_base import QuantumSystem

def get_units_time_label(units: str = None) -> str:
def get_units_time_label(units: Optional[str] = None) -> str:
"""Get a latex representation of 1/units"""

@@ -95,3 +111,3 @@ units = units or _current_units

----------
value: float
value:
a frequency or angular frequency assumed to be in system units.

@@ -101,3 +117,2 @@

-------
float:
frequency or angular frequency converted to Hz or 2pi/s

@@ -115,8 +130,8 @@ """

----------
value: float
value:
a frequency or angular frequency assumed to be in standard units
(`[Hz]` or `2\pi / [s]`)
Returns
-------
float:
frequency or angular frequency converted to system units

@@ -128,7 +143,16 @@

def units_scale_factor(units: str = None) -> float:
def units_scale_factor(units: Optional[str] = None) -> float:
"""The units_scale_factor function returns a numerical scaling factor that converts from Hz to the `units` given as
a string argument. If no argument is given, the current units stored in `_current_units` are used. If the units are
not supported, a `ValueError` is raised.
Parameters
----------
units:
units to convert to (optional; if none given, default to `_current_units`)
Returns
-------
A numerical scaling factor that converts from Hz to `units` or `_current_units`.
"""
Return a numerical scaling factor that converts form Hz to `units`.
(given as argument or, by default, stored in `_current_units`) .
"""
units = _current_units if units is None else units

@@ -135,0 +159,0 @@

@@ -13,4 +13,2 @@ # zeropi_full.py

import os
from typing import Any, Dict, List, Optional, Tuple, Union

@@ -31,3 +29,2 @@

import scqubits.io_utils.fileio_serializers as serializers
import scqubits.settings as settings
import scqubits.ui.qubit_widget as ui

@@ -195,6 +192,10 @@ import scqubits.utils.spectrum_utils as spec_utils

) # used for file IO Serializable purposes; remove ECS as init parameter
self._image_filename = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "qubit_img/fullzeropi.jpg"
)
# This class does not yet support custom diagonalization options, but these
# still have to be defined
self.evals_method = None
self.evals_method_options = None
self.esys_method = None
self.esys_method_options = None
dispatch.CENTRAL_DISPATCH.register("GRID_UPDATE", self)

@@ -249,14 +250,11 @@

ui.create_widget(
self.set_params, init_params, image_filename=self._image_filename
self.set_params_from_gui, init_params, image_filename=self._image_filename
)
def set_params(self, **kwargs) -> None:
phi_grid = discretization.Grid1d(
kwargs.pop("grid_min_val"),
kwargs.pop("grid_max_val"),
kwargs.pop("grid_pt_count"),
)
self.grid = phi_grid
def set_params_from_gui(self, **kwargs) -> None:
for param_name, param_val in kwargs.items():
setattr(self, param_name, param_val)
if "grid_" in param_name:
setattr(self.grid, param_name[5:], param_val)
else:
setattr(self, param_name, param_val)

@@ -641,4 +639,4 @@ def receive(self, event: str, sender: object, **kwargs) -> None:

k=evals_count,
sigma=0.0,
which="LM",
# sigma=0.0,
which="SA",
return_eigenvectors=False,

@@ -656,4 +654,4 @@ )

k=evals_count,
sigma=0.0,
which="LM",
# sigma=0.0,
which="SA",
return_eigenvectors=True,

@@ -660,0 +658,0 @@ )

@@ -13,3 +13,2 @@ # zeropi.py

import os
import warnings

@@ -34,3 +33,2 @@

import scqubits.io_utils.fileio_serializers as serializers
import scqubits.settings as settings
import scqubits.ui.qubit_widget as ui

@@ -51,3 +49,3 @@ import scqubits.utils.plotting as plot

# -Symmetric 0-pi qubit, phi discretized, theta in charge basis-------------------------
# -Symmetric 0-pi qubit, phi discretized, theta in charge basis

@@ -131,4 +129,15 @@

id_str: Optional[str] = None,
evals_method: Optional[str] = None,
evals_method_options: Optional[dict] = None,
esys_method: Optional[str] = None,
esys_method_options: Optional[dict] = None,
) -> None:
base.QuantumSystem.__init__(self, id_str=id_str)
base.QubitBaseClass.__init__(
self,
id_str=id_str,
evals_method=evals_method,
evals_method_options=evals_method_options,
esys_method=esys_method,
esys_method_options=esys_method_options,
)

@@ -161,5 +170,2 @@ self.EJ = EJ

) # used in for file Serializable purposes; remove ECS as init parameter
self._image_filename = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "qubit_img/zeropi.jpg"
)
dispatch.CENTRAL_DISPATCH.register("GRID_UPDATE", self)

@@ -210,6 +216,6 @@

ui.create_widget(
self.set_params, init_params, image_filename=self._image_filename
self.set_params_from_gui, init_params, image_filename=self._image_filename
)
def set_params(self, **kwargs) -> None:
def set_params_from_gui(self, **kwargs) -> None:
phi_grid = discretization.Grid1d(

@@ -216,0 +222,0 @@ kwargs.pop("grid_min_val"),

@@ -13,5 +13,6 @@ # explorer_panels.py

from typing import TYPE_CHECKING, List, Tuple, Union
import itertools
# import matplotlib as mlp
from typing import TYPE_CHECKING, List, Optional, Tuple, Union
import numpy as np

@@ -23,2 +24,4 @@

import scqubits
import scqubits.core.constants
import scqubits.core.units as units

@@ -35,3 +38,3 @@ import scqubits.utils.plotting as plot

from scqubits.core.param_sweep import ParameterSlice, ParameterSweep
from scqubits.core.qubit_base import QuantumSystem, QubitBaseClass, QubitBaseClass1d
from scqubits.core.qubit_base import QuantumSystem, QubitBaseClass

@@ -47,5 +50,5 @@

subtract_ground: bool = False,
) -> None:
) -> Tuple[Figure, Axes]:
subsys_index = sweep.get_subsys_index(subsys)
title = "bare spectrum: {}".format(subsys.id_str)
title = "Bare Spectrum: {}\n".format(subsys.id_str)

@@ -64,2 +67,3 @@ evals_count = evals_count or -1

axes.axvline(param_slice.param_val, color="gray", linestyle=":")
return fig, axes

@@ -79,3 +83,3 @@

title = "anharmonicity: {}".format(subsys.id_str)
title = "Anharmonicity: {}".format(subsys.id_str)
fig, axes = anharmonicity.plot( # type:ignore

@@ -87,2 +91,3 @@ title=title,

axes.axvline(param_slice.param_val, color="gray", linestyle=":")
return fig, axes

@@ -105,3 +110,3 @@

title = "{}: {}".format(subsys.id_str, operator_name)
title = f"{subsys.id_str}: matrix elements (fixed)"
fig, axes = subsys.plot_matrixelements(

@@ -113,7 +118,18 @@ operator_name,

show3d=False,
show_numbers=True,
show_colorbar=False,
show_numbers=False,
show_colorbar=True,
fig_ax=fig_ax,
title=title,
)
axes.text(
0.5,
1.05,
f"{scqubits.core.constants.MODE_STR_DICT[mode_str](operator_name)}",
fontsize=9,
fontweight=300,
horizontalalignment="center",
verticalalignment="bottom",
transform=axes.transAxes,
)
return fig, axes

@@ -129,3 +145,3 @@

fig_ax: Tuple[Figure, Axes],
) -> None:
) -> Tuple[Figure, Axes]:
subsys_index = sweep.get_subsys_index(subsys)

@@ -162,3 +178,3 @@ evals = sweep["bare_evals"][subsys_index][param_slice.fixed]

title = "{}: {}".format(subsys.id_str, operator_name)
title = f"{subsys.id_str}: matrix elements (sweep)"
fig, axes = plot.matelem_vs_paramvals(

@@ -168,2 +184,3 @@ specdata, mode=mode_str, fig_ax=fig_ax, title=title

axes.axvline(param_slice.param_val, color="gray", linestyle=":")
return fig, axes

@@ -177,3 +194,5 @@

fig_ax: Tuple[Figure, Axes],
) -> None:
mode="real",
which=-1,
) -> Tuple[Figure, Axes]:
subsys_index = sweep.get_subsys_index(subsys)

@@ -188,5 +207,5 @@

title = "wavefunctions: {}".format(subsys.id_str)
__ = subsys.plot_wavefunction(
which=-1, esys=(evals, evecs), title=title, fig_ax=fig_ax
title = "Wavefunctions: {}".format(subsys.id_str)
return subsys.plot_wavefunction(
which=which, esys=(evals, evecs), mode=mode, title=title, fig_ax=fig_ax
)

@@ -204,7 +223,7 @@

fig_ax: Tuple[Figure, Axes],
) -> None:
if len(subsys_list) == 1:
title = r"{}-photon {} transitions".format(photon_number, subsys_list[0].id_str)
) -> Tuple[Figure, Axes]:
if photon_number == 1:
title = r"Transition Spectrum"
else:
title = r"{}-photon transitions".format(photon_number)
title = r"{}-photon Transitions".format(photon_number)
sliced_sweep = sweep if param_slice.fixed == tuple() else sweep[param_slice.fixed]

@@ -221,3 +240,20 @@ fig, axes = sliced_sweep.plot_transitions(

info_string = "Highlighted: transitions for "
for sys in subsys_list:
info_string += sys.id_str + ", "
info_string += "with sidebands" if sidebands else "no sidebands"
axes.text(
0.5,
1.05,
info_string,
fontsize=9,
fontweight=300,
horizontalalignment="center",
verticalalignment="bottom",
transform=axes.transAxes,
)
return fig, axes
@rc_context(matplotlib_settings)

@@ -230,3 +266,4 @@ def display_cross_kerr(

fig_ax: Tuple[Figure, Axes],
) -> None:
which: Optional[Union[int, Tuple[int, int]]] = None,
) -> Tuple[Figure, Axes]:
subsys1_index = sweep.get_subsys_index(subsys1)

@@ -236,13 +273,13 @@ subsys2_index = sweep.get_subsys_index(subsys2)

if type_list.count(Oscillator) == 1:
title = f"ac Stark: {subsys1.id_str} + {subsys2.id_str}"
ylabel = rf"ac Stark shift $\chi^{{{subsys1_index},{subsys2_index}}}$"
levels_list = [1]
title = f"AC Stark: {subsys1.id_str} + {subsys2.id_str}"
ylabel = rf"AC Stark Shift $\chi^{{{subsys1_index},{subsys2_index}}}_\ell$"
level = which or 1
kerr_data = sweep["chi"][subsys1_index, subsys2_index]
if param_slice.fixed != tuple():
kerr_data = kerr_data[param_slice.fixed]
label_list = []
kerr_datasets = [kerr_data[..., level] for level in levels_list]
label_list = [level]
kerr_datasets = [kerr_data[..., level]]
elif type_list.count(Oscillator) == 2:
title = r"cross-Kerr: {} - {}".format(subsys1.id_str, subsys2.id_str)
ylabel = r"Kerr coefficient $K_{{},{}}$".format(subsys1_index, subsys2_index)
title = r"Cross-Kerr: {} - {}".format(subsys1.id_str, subsys2.id_str)
ylabel = r"Kerr Coefficient $K_{{},{}}$".format(subsys1_index, subsys2_index)
level_pairs = [(1, 1)]

@@ -257,4 +294,4 @@ kerr_data = sweep["kerr"][subsys1_index, subsys2_index]

else:
title = "cross-Kerr: {} \u2194 {}".format(subsys1.id_str, subsys2.id_str)
ylabel = r"Kerr coefficient $\Lambda^{{{},{}}}_{{ll'}}$".format(
title = "Cross-Kerr: {} \u2194 {}".format(subsys1.id_str, subsys2.id_str)
ylabel = r"Kerr Coefficient $\Lambda^{{{},{}}}_{{ll'}}$".format(
subsys1_index, subsys2_index

@@ -280,6 +317,7 @@ )

axes.axvline(param_slice.param_val, color="gray", linestyle=":")
return fig, axes
@rc_context(matplotlib_settings)
def display_self_kerr(
def display_qubit_self_kerr(
sweep: "ParameterSweep",

@@ -289,11 +327,9 @@ subsys: "QuantumSystem",

fig_ax: Tuple[Figure, Axes],
) -> None:
which: Optional[List[Tuple[int, int]]] = None,
) -> Tuple[Figure, Axes]:
subsys_index = sweep.get_subsys_index(subsys)
title = r"self-Kerr: {}".format(subsys.id_str)
if isinstance(subsys, Oscillator):
ylabel = "Kerr coefficient $K_{{{}}}$".format(subsys_index)
else:
ylabel = r"Kerr coefficient $\Lambda^{{{},{}}}_{{ll'}}$".format(
subsys_index, subsys_index
)
title = r"Self-Kerr: {}".format(subsys.id_str)
ylabel = r"Kerr coefficient $\Lambda^{{{},{}}}_{{ll}}$".format(
subsys_index, subsys_index
)

@@ -304,7 +340,10 @@ kerr_data = sweep["kerr"][subsys_index, subsys_index]

level_pairs = [(1, 1), (2, 2)]
if not which:
level_pairs = list(itertools.combinations(list(range(subsys.truncated_dim)), 2))
else:
level_pairs = which
kerr_datasets = []
for level1, level2 in level_pairs:
kerr_datasets.append(kerr_data[..., level1, level2])
for pair in level_pairs:
kerr_datasets.append(kerr_data[..., pair[0], pair[1]])
kerr_datasets = np.asarray(kerr_datasets).T

@@ -315,3 +354,3 @@ kerr_namedarray = NamedSlotsNdarray(kerr_datasets, kerr_data.param_info)

title=title,
label_list=["11", "22"],
label_list=level_pairs,
ylabel=ylabel + "[{}]".format(units.get_units()),

@@ -321,1 +360,26 @@ fig_ax=fig_ax,

axes.axvline(param_slice.param_val, color="gray", linestyle=":")
return fig, axes
@rc_context(matplotlib_settings)
def display_self_kerr(
sweep: "ParameterSweep",
subsys: Union[scqubits.Oscillator, scqubits.KerrOscillator],
param_slice: "ParameterSlice",
fig_ax: Tuple[Figure, Axes],
) -> Tuple[Figure, Axes]:
subsys_index = sweep.get_subsys_index(subsys)
title = r"Self-Kerr: {}".format(subsys.id_str)
ylabel = "Kerr coefficient $K_{{{}}}$".format(subsys_index)
kerr_data = sweep["kerr"][subsys_index, subsys_index]
if param_slice.fixed != tuple():
kerr_data = kerr_data[param_slice.fixed]
fig, axes = kerr_data.plot(
title=title,
ylabel=ylabel + "[{}]".format(units.get_units()),
fig_ax=fig_ax,
)
axes.axvline(param_slice.param_val, color="gray", linestyle=":")
return fig, axes

@@ -25,3 +25,2 @@ # fileio_backends.py

import numpy as np
from numpy import ndarray

@@ -34,3 +33,2 @@

import h5py
from h5py import AttributeManager, File, Group

@@ -37,0 +35,0 @@ except ImportError:

@@ -133,2 +133,13 @@ # fileio_serializers.py

def _add_boundmethod_attribute(
name: str,
obj: Any,
attributes: Dict[str, Any],
ndarrays: Dict[str, ndarray],
objects: Dict[str, object],
) -> Tuple[Dict, Dict, Dict]:
attributes[name] = obj()
return attributes, ndarrays, objects
TO_ATTRIBUTE = (Expr, str, Number, dict, OrderedDict, list, tuple, bool, np.bool_)

@@ -152,2 +163,4 @@ TO_NDARRAY = (np.ndarray,)

return _add_ndarray
if callable(entity) and "_operator" in entity.__name__:
return _add_boundmethod_attribute
# no match, try treating as object, though this may fail

@@ -154,0 +167,0 @@ return _add_object

@@ -18,3 +18,3 @@ # fileio.py

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

@@ -158,4 +158,4 @@ from numpy import ndarray

file_name: str,
file_handle: "h5py.Group" = None,
get_external_reader: Callable = None,
file_handle: Optional["h5py.Group"] = None,
get_external_reader: Optional[Callable] = None,
) -> Union["CSVReader", "H5Reader"]:

@@ -162,0 +162,0 @@ """

@@ -30,5 +30,5 @@ """

from typing import Any, Type, Union
from typing import Any, Optional, Type, Union
import matplotlib as mpl
import matplotlib.font_manager as mpl_font
import numpy as np

@@ -45,3 +45,3 @@

lineno: int,
line: str = None,
line: Optional[str] = None,
) -> str:

@@ -98,4 +98,17 @@ return "{}: {}\n {}: {}".format(category.__name__, message, filename, lineno)

# Matplotlib options -------------------------------------------------------------------
# select fonts
FONT_SELECTED = None
try:
font_names = mpl_font.get_font_names()
for font in ["IBM Plex Sans", "Roboto", "Arial", "Helvetica"]:
if font in font_names:
FONT_SELECTED = font
break
if not FONT_SELECTED:
FONT_SELECTED = "sans-serif"
except AttributeError:
FONT_SELECTED = "sans-serif"
# set matplotlib defaults for use in @mpl.rc_context
off_black = "0.2"
OFF_BLACK = "0.2"
matplotlib_settings = {

@@ -118,3 +131,3 @@ "axes.prop_cycle": cycler(

),
"font.family": "IBM Plex Sans, Roboto, Arial, Helvetica, DejaVu Sans",
"font.family": FONT_SELECTED,
"font.size": 11,

@@ -124,11 +137,12 @@ "font.weight": 500,

"axes.titlesize": 11,
"axes.titleweight": 500,
"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
"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,
}

@@ -144,5 +158,5 @@

# global random number generator for consistent initial state vector v0 in ARPACK
SEED = 63142
RNG = np.random.default_rng(seed=SEED)
RANDOM_ARRAY = RNG.random(size=10000000)
_SEED = 63142
_RNG = np.random.default_rng(seed=_SEED)
RANDOM_ARRAY = _RNG.random(size=10000000)

@@ -149,0 +163,0 @@ # toggle fuzzy value-based slicing and warnings about it on and off

@@ -206,2 +206,4 @@ # conftest.py --- for use with pytest

def test_plot_wavefunction(self, io_type):
if "plot_wavefunction" not in dir(self.qbt_type):
pytest.skip("This is expected, no reason for concern.")
testname = self.file_str + "_1." + io_type

@@ -267,7 +269,7 @@ specdata = SpectrumData.create_from_file(DATADIR + testname)

def test_plot_potential(self, io_type):
if "plot_potential" not in dir(self.qbt_type):
pytest.skip("This is expected, no reason for concern.")
testname = self.file_str + "_1.hdf5"
specdata = SpectrumData.create_from_file(DATADIR + testname)
self.qbt = self.qbt_type(**specdata.system_params)
if "plot_potential" not in dir(self.qbt):
pytest.skip("This is expected, no reason for concern.")
self.qbt.plot_potential()
import os
import numpy as np
import scqubits as scq
from scqubits import truncation_template
from scqubits.core.circuit_utils import example_circuit

@@ -8,0 +5,0 @@ TESTDIR, _ = os.path.split(scq.__file__)

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

ncut=40,
truncated_dim=4, # after diagonalization, we will keep 3 levels
truncated_dim=5,
)

@@ -35,3 +35,3 @@

resonator = scq.Oscillator(E_osc=4.5, truncated_dim=4) # up to 3 photons (0,1,2,3)
resonator = scq.Oscillator(E_osc=4.5, truncated_dim=4)

@@ -38,0 +38,0 @@ hilbertspace = scq.HilbertSpace([tmon1, tmon2, resonator])

@@ -16,5 +16,5 @@ # test_hilbertspace.py

import pytest
import qutip as qt
import scqubits as scq
import qutip as qt

@@ -21,0 +21,0 @@ from scqubits.core.hilbert_space import HilbertSpace

@@ -73,4 +73,5 @@ # test_noise.py

1685277.68169804,
66760.19238822,
np.inf,
842638.84084902,
61859.237880050845,
]

@@ -77,0 +78,0 @@ ),

@@ -13,16 +13,58 @@ # gui_defaults.py

import numpy as np
import base64
import collections
import enum
import os
EL_range = {"min": 1e-5, "max": 10.0}
EJ_range = {"min": 1e-5, "max": 70.0}
EC_range = {"min": 1e-5, "max": 10.0}
flux_range = {"min": 0.0, "max": 1.0}
ng_range = {"min": 0.0, "max": 1.0}
int_range = {"min": 1, "max": 30}
float_range = {"min": 0.0, "max": 30.0}
ncut_range = {"min": 10, "max": 50}
try:
import ipyvuetify as v
import ipywidgets
except ImportError:
_HAS_IPYVUETIFY = False
else:
_HAS_IPYVUETIFY = True
gui_plot_choice_dict = collections.OrderedDict(
[
("Energy spectrum", "En.png"),
("Wavefunctions", "psi.png"),
("Matrix elements", "Me.png"),
("Matrix-element sweep", "MeS.png"),
("Coherence times", "T1.png"),
]
)
gui_sweep_plots = [0, 3, 4]
gui_plot_icon_filenames = list(gui_plot_choice_dict.values())
gui_icon_filenames = gui_plot_icon_filenames + ["scq-logo.png"]
gui_plot_type_names = list(gui_plot_choice_dict.keys())
icons = {}
if _HAS_IPYVUETIFY:
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icons")
for name in gui_icon_filenames:
full_path = os.path.join(path, name)
file = open(full_path, "rb")
image = file.read()
image_base64 = base64.b64encode(image).decode("ascii")
icons[name] = v.Img(src=f"data:image/png;base64,{image_base64}", width=50)
STEP = 1e-2
EL_range = {"v_min": STEP, "v_max": 10.0}
EJ_range = {"v_min": STEP, "v_max": 70.0}
EC_range = {"v_min": STEP, "v_max": 10.0}
flux_range = {"v_min": 0.0, "v_max": 1.0}
ng_range = {"v_min": 0.0, "v_max": 1.0}
int_range = {"v_min": 1, "v_max": 30}
float_range = {"v_min": 0.0, "v_max": 30.0}
ncut_range = {"v_min": 6, "v_max": 50}
global_defaults = {
"mode_wavefunc": "real",
"mode_matrixelem": "abs",
"mode_wavefunc": "Re(·)",
"mode_matrixelem": "|·|",
"ng": ng_range,

@@ -34,3 +76,3 @@ "flux": flux_range,

"float": float_range,
"scale": 1,
"scale": 1.0,
"num_sample": 150,

@@ -50,4 +92,4 @@ }

"operator": "n_operator",
"EJmax": EJ_range,
"d": {"min": 0.0, "max": 1.0},
"EJ_max": EJ_range,
"d": {"v_min": 0.0, "v_max": 1.0},
"ncut": ncut_range,

@@ -61,3 +103,3 @@ }

"EL": EL_range,
"cutoff": {"min": 10, "max": 120},
"cutoff": {"v_min": 10, "v_max": 120},
}

@@ -84,2 +126,29 @@

snail_EJ_range = {"v_min": STEP, "v_max": 2e3}
snail_EC_range = {"v_min": STEP, "v_max": 300.0}
snailmon_defaults = {
**global_defaults,
"scan_param": "flux",
"operator": "n_1_operator",
"ncut": ncut_range,
"EJ1": snail_EJ_range,
"EJ2": snail_EJ_range,
"EJ3": snail_EJ_range,
"EJ4": snail_EJ_range,
"ECJ1": snail_EC_range,
"ECJ2": snail_EC_range,
"ECJ3": snail_EC_range,
"ECJ4": snail_EC_range,
"ECg1": snail_EC_range,
"ECg2": snail_EC_range,
"ECg3": snail_EC_range,
"ECg4": snail_EC_range,
"ng1": ng_range,
"ng2": ng_range,
"ng3": ng_range,
"scale": None,
"num_sample": 100,
}
zeropi_defaults = {

@@ -92,4 +161,4 @@ **global_defaults,

"ECJ": EC_range,
"dEJ": {"min": 0.0, "max": 1.0},
"dCJ": {"min": 0.0, "max": 1.0},
"dEJ": {"v_min": 0.0, "v_max": 1.0},
"dCJ": {"v_min": 0.0, "v_max": 1.0},
"scale": None,

@@ -106,8 +175,8 @@ "num_sample": 50,

"ECJ": EC_range,
"dEJ": {"min": 0.0, "max": 1.0},
"dCJ": {"min": 0.0, "max": 1.0},
"dEL": {"min": 0.0, "max": 1.0},
"dC": {"min": 0.0, "max": 1.0},
"zeropi_cutoff": {"min": 5, "max": 30},
"zeta_cutoff": {"min": 5, "max": 30},
"dEJ": {"v_min": 0.0, "v_max": 1.0},
"dCJ": {"v_min": 0.0, "v_max": 1.0},
"dEL": {"v_min": 0.0, "v_max": 1.0},
"dC": {"v_min": 0.0, "v_max": 1.0},
"zeropi_cutoff": {"v_min": 5, "v_max": 30},
"zeta_cutoff": {"v_min": 5, "v_max": 30},
"scale": None,

@@ -123,8 +192,8 @@ "num_sample": 50,

"ECJ": EC_range,
"dEJ": {"min": 0, "max": 0.99},
"dL": {"min": 0, "max": 0.99},
"dCJ": {"min": 0, "max": 0.99},
"dEJ": {"v_min": 0, "v_max": 0.99},
"dL": {"v_min": 0, "v_max": 0.99},
"dCJ": {"v_min": 0, "v_max": 0.99},
"ncut": ncut_range,
"zeta_cut": {"min": 10, "max": 50},
"phi_cut": {"min": 5, "max": 30},
"zeta_cut": {"v_min": 10, "v_max": 50},
"phi_cut": {"v_min": 5, "v_max": 30},
"scale": None,

@@ -134,2 +203,14 @@ "num_sample": 50,

bifluxon_defaults = {
**global_defaults,
"scan_param": "flux",
"operator": "n_theta_operator",
"ncut": ncut_range,
"EL": EL_range,
"ECL": EC_range,
"dEJ": {"v_min": 0.0, "v_max": 1.0},
"scale": None,
"num_sample": 50,
}
qubit_defaults = {

@@ -143,2 +224,4 @@ "Transmon": transmon_defaults,

"Cos2PhiQubit": cos2phiqubit_defaults,
"Snailmon": snailmon_defaults,
"Bifluxon": bifluxon_defaults,
}

@@ -209,2 +292,3 @@

"EJ": 6.0,
"EJ": 6.0,
"ECJ": 2.28,

@@ -235,12 +319,26 @@ "EC": 0.184,

},
"Bifluxon": {
"Kalashnikov et al., PRX Quantum 1, 010307 (2020)": {
"params": {
"EJ": 27.2,
"EL": 0.94,
"EC": 7.7,
"ECL": 10.0,
"dEJ": 0.0,
},
"link": "https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.1.010307",
},
},
}
# Plot categories available in the single-qubit GUI
plot_choices = [
"Energy spectrum",
"Wavefunctions",
"Matrix elements",
"Matrix element scan",
"Matrix elements",
"Coherence times",
]
# The following qubits are supported by the GUI
supported_qubits = [

@@ -254,32 +352,74 @@ "Transmon",

"Cos2PhiQubit",
# "Snailmon",
# "Bifluxon",
]
slow_qubits = ["FluxQubit", "ZeroPi", "FullZeroPi", "Cos2PhiQubit"]
subsys_panel_names = [
"Energy spectrum",
"Wavefunctions",
"Matrix elements",
"Anharmonicity",
"Self-Kerr",
# The following qubits are supported by the GUI, but are slow, so auto-updating is disabled by default
slow_qubits = [
"FluxQubit",
"ZeroPi",
"FullZeroPi",
"Cos2PhiQubit",
# "Snailmon",
# "Bifluxon",
]
composite_panel_names = ["Transitions", "Cross-Kerr, ac-Stark", "Custom data"]
common_panels = ["Energy spectrum", "Wavefunctions"]
# Explorer plot names
class PlotType(enum.Enum):
ENERGY_SPECTRUM = "Energy spectrum"
WAVEFUNCTIONS = "Wavefunctions"
MATRIX_ELEMENTS = "Matrix elements (fixed)"
MATRIX_ELEMENT_SCAN = "Matrix elements (sweep)"
ANHARMONICITY = "Anharmonicity"
SELF_KERR = "Self-Kerr"
TRANSITIONS = "Transitions"
CROSS_KERR = "Cross-Kerr"
AC_STARK = "ac Stark"
mode_dropdown_list = [
("Re(·)", "real"),
("Im(·)", "imag"),
("|·|", "abs"),
("|\u00B7|\u00B2", "abs_sqr"),
]
# Plot types associated with individual subsystems (used in Explorer class)
subsys_plot_types = (
PlotType.ENERGY_SPECTRUM,
PlotType.WAVEFUNCTIONS,
PlotType.MATRIX_ELEMENTS,
PlotType.MATRIX_ELEMENT_SCAN,
PlotType.ANHARMONICITY,
PlotType.SELF_KERR,
)
# Plot names for composite-system plots (used in Explorer class)
composite_plot_types = [PlotType.TRANSITIONS, PlotType.CROSS_KERR, PlotType.AC_STARK]
# Plots that are activated for all `supported_qubits` when entering the Explorer class
common_panels = [PlotType.ENERGY_SPECTRUM, PlotType.WAVEFUNCTIONS]
# Options for plotting complex-valued data
mode_dropdown_dict = {
"Re(·)": "real",
"Im(·)": "imag",
"|·|": "abs",
"|\u00B7|\u00B2": "abs_sqr",
}
mode_dropdown_list = list(mode_dropdown_dict.keys())
# Default panels for each qubit type, used as default in Explorer class
default_panels = {qubit_name: common_panels for qubit_name in supported_qubits}
default_panels["Oscillator"] = []
default_panels["KerrOscillator"] = []
default_panels["Composite"] = ["Transitions"]
default_panels["Composite"] = [PlotType.TRANSITIONS]
# Supported panels for each qubit type, used in Explorer class
supported_panels = {qubit_name: subsys_plot_types for qubit_name in supported_qubits}
supported_panels["Oscillator"] = [PlotType.ENERGY_SPECTRUM, PlotType.SELF_KERR]
supported_panels["KerrOscillator"] = [PlotType.ENERGY_SPECTRUM]
supported_panels["Composite"] = composite_plot_types
# Default plot options used in Explorer class
PLOT_HEIGHT = "500px"
FIG_WIDTH_INCHES = 6
FIG_DPI = 150
NAV_COLOR = "#deebf9"

@@ -16,3 +16,4 @@ # hspace_widget.py

from typing import List, Union
from typing import Dict, List, Union
from typing_extensions import Literal # for Python 3.7 compatibility

@@ -24,8 +25,13 @@ import numpy as np

from scqubits.ui.gui_defaults import NAV_COLOR
try:
import ipyvuetify as v
import ipywidgets
from scqubits.ui.gui_custom_widgets import ClickChip, ValidatedNumberField
except ImportError:
_HAS_IPYWIDGETS = False
_HAS_IPYVUETIFY = False
else:
_HAS_IPYWIDGETS = True
_HAS_IPYVUETIFY = True

@@ -49,127 +55,115 @@ try:

class HilbertSpaceUi:
"""Class for setup and display of the ipywidget used for creation of a
"""Class for setup and display of the widget used for creation of a
HilbertSpace object."""
@utils.Required(ipywidgets=_HAS_IPYWIDGETS)
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY)
def __init__(self):
"""Set up all widget GUI elements and class attributes."""
self.status_output = None
self.status_output = v.Container(children=[], id="status_output")
self.subsys_candidates_dict = self.get_subsys_candidates()
self.interactions_count = 0
self.current_interaction_key = ""
self.interactions_dict = {}
self.current_interaction_idx = None
# self.interaction_terms_dict = collections.OrderedDict()
self.op1subsys_widget: List[v.Select] = []
self.op2subsys_widget: List[v.Select] = []
self.op1_ddown_widget: List[v.Select] = []
self.op2_ddown_widget: List[v.Select] = []
self.g_widget: List[ValidatedNumberField] = []
self.addhc_widget: List[v.Select] = []
self.string_expr_widget: List[v.TextField] = []
self.interact_box1: List[v.Container] = []
self.interact_box2: List[v.Container] = []
# == subsystems panel =========================================================
label = ipywidgets.Label(value="Select subsystems (Ctrl-Click)")
self.subsys_refresh_button = ipywidgets.Button(
icon="refresh", layout=ipywidgets.Layout(width="35px")
self.subsys_refresh_button = v.Btn(
children=[v.Icon(children=["mdi-refresh"])],
width=40,
min_width=40,
height=40,
class_="ml-2 mt-2",
)
self.subsys_toprow = ipywidgets.HBox([label, self.subsys_refresh_button])
self.subsys_widget = ipywidgets.SelectMultiple(
options=list(self.subsys_candidates_dict.keys()),
rows=10,
description="",
disabled=False,
layout=ipywidgets.Layout(width="220px"),
self.subsys_widget = v.Select(
class_="px-2",
v_model=[],
items=list(self.subsys_candidates_dict.keys()),
menu_props={"closeOnContentClick": True},
attach=True,
chips=True,
multiple=True,
clear=True,
outlined=True,
filled=True,
height=40,
label="Select Subsystem(s)",
# style_="width: 70%",
)
self.subsys_box = ipywidgets.VBox([self.subsys_toprow, self.subsys_widget])
# == InteractionTerms list panel ==============================================
label = ipywidgets.Label(value="Interaction term(s) ")
self.interact_new_button = ipywidgets.Button(
icon="plus", layout=ipywidgets.Layout(width="35px")
self.interact_new_button = v.Btn(
children=[v.Icon(children=["mdi-plus"])],
width=40,
min_width=40,
height=40,
class_="ml-2 align-self-center",
)
self.interact_del_button = ipywidgets.Button(
icon="remove", layout=ipywidgets.Layout(width="35px")
self.interact_chipgroup = v.ChipGroup(
v_model=None,
mandatory=True,
children=[],
id="interact_display",
class_="ml-2 align-self-center",
color="primary",
active_class="text-primary",
)
self.interact_buttons = ipywidgets.HBox(
[label, self.interact_new_button, self.interact_del_button]
)
self.interact_list_widget = ipywidgets.Select(
options=[],
rows=10,
description="",
disabled=False,
layout=ipywidgets.Layout(width="200px"),
)
self.interact_list_box = ipywidgets.VBox(
[self.interact_buttons, self.interact_list_widget]
)
# == Panel for specifying an InteractionTerm ==================================
self.op1subsys_widget = ipywidgets.Dropdown(
options=self.subsys_widget.value, description="subsys1", disabled=False
)
self.op2subsys_widget = ipywidgets.Dropdown(
options=self.subsys_widget.value, description="subsys2", disabled=False
)
self.op1_ddown_widget = ipywidgets.Dropdown(
options=self.possible_operators(self.op1subsys_widget.value),
description="op1",
disabled=False,
)
self.op2_ddown_widget = ipywidgets.Dropdown(
options=self.possible_operators(self.op2subsys_widget.value),
description="op2",
disabled=False,
)
self.g_widget = ipywidgets.FloatText(description="g_strength")
self.addhc_widget = ipywidgets.Dropdown(
description="add_hc", options=["False", "True"]
)
self.interact_box1 = ipywidgets.VBox(
[
ipywidgets.Label(value="Specify interaction"),
self.op1subsys_widget,
self.op1_ddown_widget,
self.op2subsys_widget,
self.op2_ddown_widget,
self.g_widget,
self.addhc_widget,
]
)
self.tabs_select_interact_type = []
self.string_expr_widget = ipywidgets.Text(
description="expr", placeholder="e.g., EJ * cos(op1 - op2)"
# == Central run button ==================================
self.run_button = v.Btn(
children=["Create HilbertSpace"],
class_="m-2",
style_="align: bottom;",
disabled=True,
)
self.interact_box2 = ipywidgets.VBox(
[
ipywidgets.Label(value="Specify interaction"),
self.string_expr_widget,
self.op1subsys_widget,
self.op1_ddown_widget,
self.op2subsys_widget,
self.op2_ddown_widget,
self.addhc_widget,
]
)
self.tabs_select_interact_type = ipywidgets.Tab(
layout=ipywidgets.Layout(width="350px")
self.edit_interaction_card = v.Card(
class_="d-flex flex-column align-self-center mb-2",
width=450,
children=[
v.CardTitle(children=["Edit Interaction Terms"]),
v.Container(
class_="d-flex flex-row",
children=[
self.interact_new_button,
self.interact_chipgroup,
],
),
v.Container(), # SLOT 2 -- for self.tabs_select_interact_type[idx],
],
id="edit_interaction_card",
style_="display: none !important",
)
self.tabs_select_interact_type.children = [
self.interact_box1,
self.interact_box2,
]
self.tabs_select_interact_type.set_title(0, "g * op1 * op2")
self.tabs_select_interact_type.set_title(1, "Python expr")
self.tabs_select_interact_type.layout.display = "none"
# == Central run button, status output field ==================================
self.run_button = ipywidgets.Button(
description="Create HilbertSpace object",
layout=ipywidgets.Layout(width="200px"),
# == Wrap everything into boxes ===============================================
self.all_panels = v.Card(
class_="d-flex flex-column",
children=[
v.CardTitle(children=["Create Hilbert Space"]),
v.Container(
class_="d-flex flex-row align-self-left my-0 py-0",
children=[self.subsys_widget, self.subsys_refresh_button],
style_="width: 50%",
),
self.edit_interaction_card,
self.run_button,
],
)
self.status_output = ipywidgets.Output()
# == Wrap everything into boxes ===============================================
self.all_panels = ipywidgets.HBox(
[self.subsys_box, self.interact_list_box, self.tabs_select_interact_type],
layout=ipywidgets.Layout(grid_gap="50px"),
self.ui = v.Container(
class_="d-flex flex-column", children=[self.all_panels, self.status_output]
)
self.ui = ipywidgets.VBox(
[self.all_panels, self.run_button, self.status_output]
)

@@ -185,43 +179,197 @@ # == Make GUI connections =====================================================

return [
method_name + "()"
method_name
for method_name in dir(main.__dict__[subsystem]) # type:ignore
if "_operator" in method_name and method_name[0] != "_"
if "_operator" in method_name
and method_name[0] != "_"
and "get_" not in method_name
]
def current_interaction_type(self) -> str:
def new_interact_entry_widget(self):
self.op1subsys_widget.append(
v.Select(
v_model=None,
items=self.subsys_widget.v_model,
label="subsys1",
outlined=True,
dense=True,
)
)
self.op2subsys_widget.append(
v.Select(
v_model=None,
items=self.subsys_widget.v_model,
label="subsys2",
outlined=True,
dense=True,
)
)
self.op1_ddown_widget.append(
v.Select(
v_model=None,
items=[],
label="op1",
outlined=True,
dense=True,
)
)
self.op2_ddown_widget.append(
v.Select(
v_model=None,
items=[],
label="op2",
outlined=True,
dense=True,
)
)
self.g_widget.append(
ValidatedNumberField(
v_model="0",
num_type=float,
label="g_strength",
style_="",
outlined=True,
dense=True,
filled=False,
)
)
self.addhc_widget.append(
v.Select(
v_model="False",
label="add_hc",
items=["False", "True"],
outlined=True,
dense=True,
)
)
self.interact_box1.append(
v.Container(
class_="d-flex flex-column",
children=[
self.op1subsys_widget[-1],
self.op1_ddown_widget[-1],
self.op2subsys_widget[-1],
self.op2_ddown_widget[-1],
self.g_widget[-1],
self.addhc_widget[-1],
],
id="interact_box1",
)
)
self.string_expr_widget.append(
v.TextField(
v_model="", label="expr", placeholder="e.g., EJ * cos(op1 - op2)"
)
)
self.interact_box2.append(
v.Container(
class_="d-flex flex-column",
children=[
self.string_expr_widget[-1],
self.op1subsys_widget[-1],
self.op1_ddown_widget[-1],
self.op2subsys_widget[-1],
self.op2_ddown_widget[-1],
self.addhc_widget[-1],
],
id="interact_box2",
)
)
self.tabs_select_interact_type.append(
v.Tabs(
v_model="tab",
align_with_title=True,
grow=True,
background_color=NAV_COLOR,
children=[
v.Tab(children=["g * op1 * op2"]),
v.Tab(children=["Python expr"]),
v.TabItem(
key="g * op1 * op2",
children=[self.interact_box1[-1]],
style_="background-color: " + NAV_COLOR,
),
v.TabItem(
key="Python expr",
children=[self.interact_box2[-1]],
style_="background-color: " + NAV_COLOR,
),
],
)
)
def on_op_subsys1_selected(*args):
value = self.op1subsys_widget[self.current_interaction_idx].v_model
self.op1_ddown_widget[
self.current_interaction_idx
].items = self.possible_operators(value)
def on_op_subsys2_selected(*args):
value = self.op2subsys_widget[self.current_interaction_idx].v_model
self.op2_ddown_widget[
self.current_interaction_idx
].items = self.possible_operators(value)
self.op1subsys_widget[-1].observe(on_op_subsys1_selected, names="v_model")
self.op2subsys_widget[-1].observe(on_op_subsys2_selected, names="v_model")
def get_interaction_type(self, idx) -> str:
interaction_types = {0: "InteractionTerm", 1: "InteractionTermStr"}
tab_index = self.tabs_select_interact_type.selected_index
tab_index = self.tabs_select_interact_type[idx].v_model
return interaction_types[tab_index]
def connect_ui(self):
def on_subsys_selected(change):
self.op1subsys_widget.options = change["new"]
self.op2subsys_widget.options = change["new"]
def on_op_subsys1_selected(change):
self.op1_ddown_widget.options = self.possible_operators(
self.op1subsys_widget.value
def on_interact_chipgroup_change(self, *args, **kwargs):
active_term = self.interact_chipgroup.v_model
if active_term == self.current_interaction_idx:
return
if active_term is not None:
self.current_interaction_idx = int(
self.interact_chipgroup.children[active_term].children[0][5:]
)
else:
self.current_interaction_idx = None
self.retrieve_and_display_interact_data()
def on_op_subsys2_selected(change):
self.op2_ddown_widget.options = self.possible_operators(
self.op2subsys_widget.value
def update_chipgroup_display(self):
self.interact_chipgroup.children = [
ClickChip(
children=[f"term {idx}"],
class_="align-self-center",
close=True,
click_close=self.del_interaction_term,
)
for idx, _ in enumerate(self.interact_box1)
]
def on_interact_list_changed(change):
self.current_interaction_key = change["new"]
self.current_interact_change()
if self.current_interaction_idx is not None:
self.interact_chipgroup.v_model = self.current_interaction_idx
else:
self.interact_chipgroup.v_model = None
def connect_ui(self):
def on_subsys_selected(*args):
for idx, _ in enumerate(self.interact_box1):
self.op1subsys_widget[idx].items = self.subsys_widget.v_model
self.op2subsys_widget[idx].items = self.subsys_widget.v_model
if self.subsys_widget.v_model:
self.edit_interaction_card.style_ = ""
self.run_button.disabled = False
else:
self.edit_interaction_card.style_ = "display: none !important;"
self.run_button.disabled = True
def refresh_subsys_list(*args):
self.subsys_widget.options = list(self.get_subsys_candidates().keys())
self.subsys_widget.items = list(self.get_subsys_candidates().keys())
self.subsys_widget.observe(on_subsys_selected, "value")
self.interact_list_widget.observe(on_interact_list_changed, "value")
self.subsys_widget.observe(on_subsys_selected, names="v_model")
self.op1subsys_widget.observe(on_op_subsys1_selected, "value")
self.op2subsys_widget.observe(on_op_subsys2_selected, "value")
self.interact_chipgroup.observe(
self.on_interact_chipgroup_change, names="v_model"
)
self.subsys_refresh_button.on_click(refresh_subsys_list)
self.interact_new_button.on_click(self.new_interaction_term)
self.interact_del_button.on_click(self.del_interaction_term)
self.subsys_refresh_button.on_event("click", refresh_subsys_list)
self.interact_new_button.on_event("click", self.new_interaction_term)

@@ -233,3 +381,3 @@ def get_subsys_candidates(self):

for name, subsys in main.__dict__.items()
if isinstance(subsys, QuantumSystem)
if isinstance(subsys, QuantumSystem) and name[0] != "_"
}

@@ -239,49 +387,58 @@ return candidates_dict

def finish(self, callback_func, *args, **kwargs):
main = importlib.import_module("__main__")
interaction_list = self.validated_interact_list()
if interaction_list is False:
return None
with self.status_output:
print("HilbertSpace instance created.")
callback_func(self.subsystem_list(), interaction_list)
self.status_output.children = [
v.Alert(
children=["HilbertSpace instance created."],
color="blue",
text=True,
dense=True,
type="info",
dismissible=True,
)
]
subsys_list = [
main.__dict__[subsys_name] for subsys_name in self.subsys_widget.v_model
]
callback_func(subsys_list, interaction_list)
def set_data(self, **kwargs):
self.set_interact_term(**kwargs)
def set_interact_term(self, **kwargs):
if self.current_interaction_key:
self.interactions_dict[self.current_interaction_key] = kwargs
def new_interaction_term(self, *args):
self.interactions_count += 1
self.current_interaction_key = "term {}".format(self.interactions_count)
self.interactions_dict[
self.current_interaction_key
] = self.empty_interaction_term()
self.interact_list_widget.options = list(self.interactions_dict.keys())
self.tabs_select_interact_type.layout.display = "flex"
self.new_interact_entry_widget()
self.update_chipgroup_display()
self.retrieve_and_display_interact_data()
self.interact_chipgroup.v_model = self.current_interaction_idx
def del_interaction_term(self, *args):
if len(list(self.interactions_dict.keys())) > 0:
del self.interactions_dict[self.current_interaction_key]
if self.interactions_dict:
self.current_interaction_key = list(self.interactions_dict.keys())[0]
self.interact_list_widget.options = list(self.interactions_dict.keys())
self.current_interact_change()
idx = self.current_interaction_idx
if self.interact_box1:
del self.op1subsys_widget[idx]
del self.op2subsys_widget[idx]
del self.op1_ddown_widget[idx]
del self.op2_ddown_widget[idx]
del self.g_widget[idx]
del self.addhc_widget[idx]
del self.string_expr_widget[idx]
del self.interact_box1[idx]
del self.interact_box2[idx]
del self.tabs_select_interact_type[idx]
if self.interact_box1:
self.current_interaction_idx = 0
self.retrieve_and_display_interact_data()
self.update_chipgroup_display()
else:
self.current_interaction_key = ""
self.interact_list_widget.options = []
self.tabs_select_interact_type.layout.display = "none"
self.current_interaction_idx = None
self.edit_interaction_card.children[2].style_ = "display: none !important"
self.update_chipgroup_display()
def current_interact_change(self, *args):
if not self.current_interaction_key:
def retrieve_and_display_interact_data(self, *args):
if self.current_interaction_idx is None:
self.edit_interaction_card.children[2].style_ = "display: none !import"
return
key = self.current_interaction_key
interact_params = self.interactions_dict[key]
self.op1subsys_widget.value = interact_params["subsys1"]
self.op1_ddown_widget.value = interact_params["op1"]
self.op2subsys_widget.value = interact_params["subsys2"]
self.op2_ddown_widget.value = interact_params["op2"]
self.g_widget.value = interact_params["g_strength"]
self.addhc_widget.value = interact_params["add_hc"]
self.string_expr_widget.value = interact_params["string_expr"]
idx = self.current_interaction_idx
self.edit_interaction_card.children[2].children = [
self.tabs_select_interact_type[idx]
]
self.edit_interaction_card.children[2].style_ = ""

@@ -300,12 +457,12 @@ @staticmethod

def widgets_dict(self):
def widgets_dict(self, idx: int) -> Dict[str, "v.VuetifyWidget"]:
return {
"subsys_list": self.subsys_widget,
"subsys1": self.op1subsys_widget,
"op1": self.op1_ddown_widget,
"subsys2": self.op2subsys_widget,
"op2": self.op2_ddown_widget,
"g_strength": self.g_widget,
"add_hc": self.addhc_widget,
"string_expr": self.string_expr_widget,
"subsys1": self.op1subsys_widget[idx],
"op1": self.op1_ddown_widget[idx],
"subsys2": self.op2subsys_widget[idx],
"op2": self.op2_ddown_widget[idx],
"g_strength": self.g_widget[idx],
"add_hc": self.addhc_widget[idx],
"string_expr": self.string_expr_widget[idx],
}

@@ -316,30 +473,51 @@

return [
eval(subsys_name, main.__dict__) for subsys_name in self.subsys_widget.value
eval(subsys_name, main.__dict__) for subsys_name in self.subsys_widget.items
]
def validated_interact_list(self):
self.status_output.clear_output()
def validated_interact_list(
self,
) -> Union[Literal[False], list]:
self.status_output.children = []
main = importlib.import_module("__main__")
subsysname_list = self.subsys_widget.value
subsysname_list = self.subsys_widget.v_model
interaction_list = []
for interaction_term in self.interactions_dict.values():
for param_name in ["subsys1", "subsys2"]:
if not interaction_term[param_name]:
with self.status_output:
print("Error: {} not specified.".format(param_name))
for idx, _ in enumerate(self.interact_box1):
interaction_term = self.widgets_dict(idx)
for subsys in ["subsys1", "subsys2"]:
if not interaction_term[subsys]:
self.status_output.children = [
v.Alert(
children=[f"Error: {subsys} not specified."],
text=True,
dense=True,
type="error",
dismissible=True,
)
]
return False
if interaction_term[param_name] not in subsysname_list:
with self.status_output:
print(
"Error: subsystem operator '{}' is not consistent "
"with HilbertSpace subsysname_list.".format(
interaction_term[param_name]
)
if interaction_term[subsys].v_model not in subsysname_list:
self.status_output.children = [
v.Alert(
children=[
f"Error: subsystem operator '{interaction_term[subsys].v_model}' is not consistent with"
f" HilbertSpace subsysname_list."
],
text=True,
dense=True,
type="error",
dismissible=True,
)
]
return False
operator_str_list = [interaction_term["op1"], interaction_term["op2"]]
operator_str_list = [
interaction_term["op1"].v_model,
interaction_term["op2"].v_model,
]
for subsys_str, operator_str in zip(
[interaction_term["subsys1"], interaction_term["subsys2"]],
[
interaction_term["subsys1"].v_model,
interaction_term["subsys2"].v_model,
],
operator_str_list,

@@ -350,31 +528,48 @@ ):

except (AttributeError, SyntaxError, NameError):
with self.status_output:
print(
"Error: operator {} is not defined or has a "
"syntax error.".format(operator_str)
self.status_output.children = [
v.Alert(
children=[
f"Error: operator {subsys_str}.{operator_str} is not defined or has a syntax error."
],
text=True,
dense=True,
type="error",
dismissible=True,
)
]
return False
if not isinstance(instance, (np.ndarray, csc_matrix, Qobj)):
with self.status_output:
print(
"Type mismatch: '{}' is not a valid operator.".format(
operator_str
)
if not isinstance(
instance, (np.ndarray, csc_matrix, Qobj)
) and not callable(instance):
self.status_output.children = [
v.Alert(
children=[
f"Error (type mismatch): '{operator_str}' is not a valid operator."
],
text=True,
dense=True,
type="error",
dismissible=True,
)
]
return False
subsys1_index = subsysname_list.index(interaction_term["subsys1"])
subsys2_index = subsysname_list.index(interaction_term["subsys2"])
op1_str = f"""{interaction_term["subsys1"]}.{operator_str_list[0]}"""
op2_str = f"""{interaction_term["subsys2"]}.{operator_str_list[1]}"""
subsys1_index = subsysname_list.index(interaction_term["subsys1"].v_model)
subsys2_index = subsysname_list.index(interaction_term["subsys2"].v_model)
op1_str = (
f"""{interaction_term["subsys1"].v_model}.{operator_str_list[0]}"""
)
op2_str = (
f"""{interaction_term["subsys2"].v_model}.{operator_str_list[1]}"""
)
op1 = eval(op1_str, main.__dict__)
op2 = eval(op2_str, main.__dict__)
if self.current_interaction_type() == "InteractionTerm":
if self.get_interaction_type(idx) == "InteractionTerm":
operator_list = [(subsys1_index, op1), (subsys2_index, op2)]
interaction_list.append(
scqubits.InteractionTerm(
g_strength=interaction_term["g_strength"],
g_strength=interaction_term["g_strength"].num_value,
operator_list=operator_list,
add_hc=(interaction_term["add_hc"] == "True"),
add_hc=(interaction_term["add_hc"].v_model == "True"),
)

@@ -385,3 +580,3 @@ )

scqubits.InteractionTermStr(
expr=self.string_expr_widget.value,
expr=interaction_term["string_expr"].v_model,
operator_list=[

@@ -391,3 +586,3 @@ (subsys1_index, "op1", op1),

],
add_hc=(interaction_term["add_hc"] == "True"),
add_hc=(interaction_term["add_hc"].v_model == "True"),
)

@@ -398,3 +593,3 @@ )

@utils.Required(ipywidgets=_HAS_IPYWIDGETS, IPython=_HAS_IPYTHON)
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def create_hilbertspace_widget(callback_func):

@@ -405,3 +600,2 @@ """

Parameters

@@ -415,6 +609,4 @@ ----------

out = ipywidgets.interactive_output(ui_view.set_data, ui_view.widgets_dict())
finish_func = functools.partial(ui_view.finish, callback_func)
ui_view.run_button.on_click(finish_func)
display(ui_view.ui, out)
ui_view.run_button.on_event("click", finish_func)
display(ui_view.ui)

@@ -13,3 +13,3 @@ # qubit_widget.py

from typing import Any, Callable, Dict
from typing import Any, Callable, Dict, Optional

@@ -19,8 +19,11 @@ import scqubits.core.units as units

try:
import ipyvuetify
import ipywidgets
from scqubits.ui.gui_custom_widgets import ValidatedNumberField
except ImportError:
_HAS_IPYWIDGETS = False
_HAS_IPYVUETIFY = False
else:
_HAS_IPYWIDGETS = True
_HAS_IPYVUETIFY = True

@@ -35,8 +38,10 @@ try:

@utils.Required(ipywidgets=_HAS_IPYWIDGETS, IPython=_HAS_IPYTHON)
@utils.Required(ipyvuetify=_HAS_IPYVUETIFY, IPython=_HAS_IPYTHON)
def create_widget(
callback_func: Callable, init_params: Dict[str, Any], image_filename: str = None
callback_func: Callable,
init_params: Dict[str, Any],
image_filename: Optional[str] = None,
) -> None:
"""
Displays ipywidgets for initialization of a QuantumSystem object.
Displays ipyvuetify for initialization of a QuantumSystem object.

@@ -57,24 +62,23 @@ Parameters

label_str = name
# NOTE: This will break if names of energy parameters in future qubits do not start with 'E'
# NOTE: This will break if names of energy parameters in future qubits
# do not start with 'E'
if name[0] == "E":
label_str += f" [{units.get_units()}]"
elif name == "flux":
label_str += r" [$\Phi_0$]"
label = ipywidgets.Label(value=label_str)
if isinstance(value, float):
enter_widget = ipywidgets.FloatText
else:
enter_widget = ipywidgets.IntText
label_str += r" [Φ₀]"
widgets[name] = enter_widget(
value=value,
description="",
layout=ipywidgets.Layout(width="150px"),
widgets[name] = ValidatedNumberField(
v_model=value,
num_type=type(value),
placeholder=f"enter appropriate value for {label_str}",
label=label_str,
name=name,
outlined=True,
filled=True,
dense=True,
style_="width: 30%;",
class_="ml-2 py-0",
)
box_list.append(
ipywidgets.HBox(
[label, widgets[name]],
layout=ipywidgets.Layout(justify_content="flex-end"),
)
)
widgets[name].observe(callback_func, names="v_model")
box_list.append(widgets[name])

@@ -84,12 +88,22 @@ if image_filename:

image = file.read()
image_widget = ipywidgets.Image(
value=image, format="jpg", layout=ipywidgets.Layout(width="700px")
image_widget = ipyvuetify.Container(
children=[ipywidgets.Image(value=image, format="jpg")],
class_="ml-3 mt-0 pt-0",
style_="width: 65%; max-width:1000px",
)
ui_widget = ipywidgets.HBox(
[ipywidgets.VBox(box_list), ipywidgets.VBox([image_widget])]
ui_widget = ipyvuetify.Row(
children=[
ipyvuetify.Row(
children=box_list,
style_="max-width: 22%; align-content: flex-start;",
),
image_widget,
],
class_="ml-3 mt-4",
)
else:
ui_widget = ipywidgets.VBox(box_list)
ui_widget = ipyvuetify.Row(children=box_list, align_items="flex-start")
out = ipywidgets.interactive_output(callback_func, widgets)
display(ui_widget, out)
display(ui_widget)

@@ -19,9 +19,13 @@ # misc.py

from collections.abc import Sequence
from distutils.version import StrictVersion
from io import StringIO
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
import matplotlib
import numpy as np
import qutip as qt
import scipy as sp
from matplotlib import get_backend as get_matplotlib_backend
import scqubits.settings
from scqubits.settings import IN_IPYTHON

@@ -101,3 +105,3 @@

self.num_cpus = num_cpus
self.tqdm_bar: Optional[tqdm] = None
self.tqdm_bar = None

@@ -107,3 +111,3 @@ def __enter__(self) -> None:

total=0,
disable=(self.num_cpus == 1),
disable=(self.num_cpus == 1) or scqubits.settings.PROGRESSBAR_DISABLED,
leave=False,

@@ -115,3 +119,4 @@ desc=self.desc,

def __exit__(self, *args) -> None:
self.tqdm_bar.close()
if self.tqdm_bar:
self.tqdm_bar.close()

@@ -133,2 +138,3 @@

self.requirements_names = list(requirements.keys())
self.missing_imports = [name for name in requirements if not requirements[name]]

@@ -141,9 +147,23 @@ def __call__(self, func: Callable, *args, **kwargs) -> Callable:

else:
raise Exception(
"ImportError: use of this method requires the optional package(s):"
" {}. If you wish to use this functionality, the corresponding"
" package(s) must be installed manually. (Installation via `conda"
" install -c conda-forge <packagename>` or `pip install"
" <packagename>` is recommended.)".format(self.requirements_names)
)
with warnings.catch_warnings():
if self.missing_imports == ["ipyvuetify"]:
warnings.warn(
"Starting with v3.2, scqubits uses the optional package 'ipyuetify' for graphical "
"user interfaces. To use this functionality, add the package via "
"`conda install -c conda-forge ipyvuetify` or `pip install ipyvuetify`.\n"
"For use with jupyter lab, additionally execute "
"`jupyter labextension install jupyter-vuetify`.\n",
category=Warning,
)
else:
warnings.warn(
"use of this method requires the optional package(s):"
" {}. If you wish to use this functionality, the corresponding"
" package(s) must be installed manually. (Installation via `conda"
" install -c conda-forge <packagename>` or `pip install"
" <packagename>` is recommended.)".format(
self.requirements_names
),
category=Warning,
)

@@ -156,7 +176,7 @@ return decorated_func

def wrapper(self, *args, **kwargs):
if self._out_of_sync:
if self._out_of_sync and not self._out_of_sync_warning_issued:
with warnings.catch_warnings():
warnings.simplefilter("always")
warnings.warn(
"[scqubits] Some system parameters have been changed and"
"[scqubits] Some quantum system parameters have been changed and"
" generated spectrum data could be outdated, potentially leading to"

@@ -167,2 +187,3 @@ " incorrect results. Spectral data can be refreshed via"

)
self._out_of_sync_warning_issued = True
return func(self, *args, **kwargs)

@@ -263,2 +284,16 @@

def to_list(obj: Any) -> List[Any]:
"""
Converts object to a list: if the input is already a list, it will return the same list. If the input is
a numpy array, it will convert to a python list. Otherwise, it will return
the original object in a single-elemented python list.
Parameters
----------
obj:
Specify the type of object that is being passed into the function
Returns
-------
a list of the object passed in
"""
if isinstance(obj, list):

@@ -271,3 +306,3 @@ return obj

def about(print_info=True):
def about(print_info=True) -> Optional[str]:
"""Prints or returns a string with basic information about

@@ -341,3 +376,3 @@ scqubits as well as installed version of various packages

def is_float_string(the_string: str) -> bool:
def is_string_float(the_string: str) -> bool:
try:

@@ -350,2 +385,10 @@ float(the_string)

def is_string_int(the_string: str) -> bool:
try:
int(the_string)
return True
except ValueError:
return False
def list_intersection(list1: list, list2: list) -> list:

@@ -372,3 +415,3 @@ return list(set(list1) & set(list2))

def flatten_list_recursive(S):
def flatten_list_recursive(some_list: list) -> list:
"""

@@ -379,4 +422,3 @@ Flattens a list of lists recursively.

----------
nested_list:
some_list:
A list of lists, which can hold any class instance.

@@ -388,10 +430,39 @@

"""
if S == []:
return S
if isinstance(S[0], list):
return flatten_list_recursive(S[0]) + flatten_list_recursive(S[1:])
return S[:1] + flatten_list_recursive(S[1:])
if some_list == []:
return some_list
if isinstance(some_list[0], list):
return flatten_list_recursive(some_list[0]) + flatten_list_recursive(
some_list[1:]
)
return some_list[:1] + flatten_list_recursive(some_list[1:])
def number_of_lists_in_list(list_object: list) -> int:
"""
Takes a list as an argument and returns the number of lists in that list. (Counts lists at root level only, no
recursion.)
Parameters
----------
list_object:
List to be analyzed
Returns
-------
The number of lists in the list
"""
return sum([1 for element in list_object if type(element) == list])
def check_matplotlib_compatibility():
if _HAS_WIDGET_BACKEND and StrictVersion(matplotlib.__version__) < StrictVersion(
"3.5.1"
):
warnings.warn(
"The widget backend requires Matplotlib >=3.5.1 for proper functioning",
UserWarning,
)
MATPLOTLIB_WIDGET_BACKEND = "module://ipympl.backend_nbagg"
_HAS_WIDGET_BACKEND = get_matplotlib_backend() == MATPLOTLIB_WIDGET_BACKEND

@@ -26,4 +26,4 @@ # plot_utils.py

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

@@ -53,3 +53,5 @@ if TYPE_CHECKING:

def _extract_kwargs_options(
kwargs: Dict[str, Any], plot_type: str, direct_plot_options: Dict[str, Any] = None
kwargs: Dict[str, Any],
plot_type: str,
direct_plot_options: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:

@@ -87,3 +89,3 @@ """

def _process_options(
figure: Figure, axes: Axes, opts: Dict[str, Any] = None, **kwargs
figure: Figure, axes: Axes, opts: Optional[Dict[str, Any]] = None, **kwargs
) -> None:

@@ -168,4 +170,7 @@ """

) -> List["WaveFunction"]:
scale_factors = np.array(
[wavefunc.amplitude_scale_factor(potential_vals) for wavefunc in wavefunc_list]
)
for wavefunc in wavefunc_list:
wavefunc.rescale_to_potential(potential_vals)
wavefunc.rescale(np.max(scale_factors))
adaptive_scalefactor = scaling or defaults.set_wavefunction_scaling(

@@ -172,0 +177,0 @@ wavefunc_list, potential_vals

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

from scqubits.settings import matplotlib_settings
from scqubits.utils.plot_utils import (

@@ -37,3 +38,2 @@ _extract_kwargs_options,

)
from scqubits.settings import matplotlib_settings

@@ -370,5 +370,5 @@ if TYPE_CHECKING:

axes.tick_params(axis='x', pad=-5)
axes.tick_params(axis='y', pad=-5)
axes.tick_params(axis='z', pad=-2)
axes.tick_params(axis="x", pad=-5)
axes.tick_params(axis="y", pad=-5)
axes.tick_params(axis="z", pad=-2)

@@ -419,8 +419,14 @@ _process_options(fig, axes, opts=defaults.matrix(), **kwargs)

# add colorbar with normalized range
fig.colorbar(
mpl.cm.ScalarMappable(norm=nrm, cmap=plt.cm.viridis),
ax=axes,
fraction=0.046,
pad=0.04,
)
if hasattr(axes, "colorbar"): # update existing colorbar
axes.colorbar.update_normal(
mpl.cm.ScalarMappable(norm=nrm, cmap=plt.cm.viridis)
)
else: # create new colorbar
cb = fig.colorbar(
mpl.cm.ScalarMappable(norm=nrm, cmap=plt.cm.viridis),
ax=axes,
fraction=0.046,
pad=0.04,
)
axes.colorbar = cb
cax = axes.matshow(modefunction(matrix), cmap=plt.cm.viridis, interpolation=None)

@@ -583,5 +589,5 @@

"""
assert specdata.matrixelem_table is not None, (
"SpectrumData is missing matrix " "element data!"
)
assert (
specdata.matrixelem_table is not None
), "SpectrumData is missing matrix element data!"
fig, axes = kwargs.get("fig_ax") or plt.subplots()

@@ -588,0 +594,0 @@ x_vals = specdata.param_vals

@@ -15,3 +15,3 @@ # spectrum_utils.py

from typing import TYPE_CHECKING, List, Optional, Tuple, Union
from typing import TYPE_CHECKING, Callable, List, Optional, Tuple, Union

@@ -37,5 +37,5 @@ import numpy as np

def eigsh_safe(*args, **kwargs):
"""Wrapper method for scipy.sparse.linalg.eigsh
This ensures:
1. Always use the same "random" starting vector v0. Otherwise results show
"""Wrapper method for `scipy.sparse.linalg.eigsh` which ensures the following.
1. Always use the same "random" starting vector v0. Otherwise, results show
random behavior (small deviations between different runs, problem for pytests)

@@ -82,3 +82,5 @@ 2. Test for degenerate eigenvalues. If there are any, need to orthogonalize the

def extract_phase(complex_array: np.ndarray, position: Optional[int] = None) -> float:
def extract_phase(
complex_array: np.ndarray, position: Optional[Tuple[int, ...]] = None
) -> float:
"""Extracts global phase from `complex_array` at given `position`. If position is

@@ -400,7 +402,7 @@ not specified, the `position` is set as follows. Find the maximum between the

def identity_wrap(
operator: Union[str, ndarray, Qobj],
operator: Union[str, ndarray, Qobj, Callable],
subsystem: "QuantumSys",
subsys_list: List["QuantumSys"],
op_in_eigenbasis: bool = False,
evecs: ndarray = None,
evecs: Optional[ndarray] = None,
) -> Qobj:

@@ -437,2 +439,9 @@ """Takes the `operator` belonging to `subsystem` and "wraps" it in identities.

"""
if not isinstance(operator, qt.Qobj) and callable(operator):
try:
operator = operator(energy_esys=(None, evecs))
except TypeError:
operator = operator()
op_in_eigenbasis = True
subsys_operator = convert_operator_to_qobj(

@@ -439,0 +448,0 @@ operator, subsystem, op_in_eigenbasis, evecs # type:ignore

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

@@ -55,4 +55,4 @@ """scqubits: superconducting qubits in Python

MAJOR = 3
MINOR = 1
MICRO = 1
MINOR = 2
MICRO = 0
ISRELEASED = True

@@ -71,7 +71,6 @@

"h5-support": ["h5py (>=2.10)"],
"pathos": ["pathos", "dill"],
"fitting": ["lmfit"],
"pathos": ["pathos", "dill"]
}
TESTS_REQUIRE = ["h5py (>=2.7.1)", "pathos", "dill", "ipywidgets", "pytest", "lmfit"]
TESTS_REQUIRE = ["h5py (>=2.7.1)", "pathos", "dill", "ipywidgets", "pytest"]

@@ -78,0 +77,0 @@ PACKAGES = [

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

# explorer_widget.py
#
# This file is part of scqubits: a Python package for superconducting qubits,
# Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/
#
# Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
############################################################################
import warnings
from distutils.version import StrictVersion
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import get_backend as get_matplotlib_backend
from matplotlib.axes import Axes
from matplotlib.figure import Figure
import scqubits as scq
from scqubits.core.param_sweep import ParameterSlice
from scqubits.core.qubit_base import QubitBaseClass
from scqubits.explorer import explorer_panels as panels
from scqubits.ui.gui_defaults import (
composite_panel_names,
default_panels,
mode_dropdown_list,
subsys_panel_names,
)
from scqubits.utils import misc as utils
from scqubits.settings import matplotlib_settings
if TYPE_CHECKING:
from scqubits.core.param_sweep import ParameterSweep
try:
from IPython.display import display
except ImportError:
_HAS_IPYTHON = False
else:
_HAS_IPYTHON = True
try:
import ipywidgets
except ImportError:
_HAS_IPYWIDGETS = False
else:
_HAS_IPYWIDGETS = True
from ipywidgets import (
HTML,
BoundedIntText,
Button,
Checkbox,
Dropdown,
FloatSlider,
HBox,
IntSlider,
Label,
Layout,
Output,
Select,
SelectionSlider,
SelectMultiple,
Tab,
ToggleButtons,
VBox,
)
SEP = " | "
MATPLOTLIB_WIDGET_BACKEND = "module://ipympl.backend_nbagg"
_HAS_WIDGET_BACKEND = get_matplotlib_backend() == MATPLOTLIB_WIDGET_BACKEND
@utils.Required(ipywidgets=_HAS_IPYWIDGETS)
def width(pixels: int, justify_content: Optional[str] = None) -> Layout:
if justify_content:
return Layout(width=str(pixels) + "px", justify_content=justify_content)
return Layout(width=str(pixels) + "px")
@utils.Required(ipywidgets=_HAS_IPYWIDGETS)
def boxed(pixels: int = 900) -> Layout:
return Layout(
width=str(pixels) + "px",
border="1px solid lightgrey",
padding="10px 10px 10px 10px",
)
class Explorer:
"""
Generates the UI for exploring `ParameterSweep` objects.
Parameters
----------
sweep:
the `ParameterSweep` object to be visualized.
"""
@utils.Required(ipywidgets=_HAS_IPYWIDGETS)
def __init__(self, sweep: scq.ParameterSweep):
"""Set up all widget GUI elements and class attributes."""
self.gui_display = ipywidgets.VBox()
self.gui_display.layout.width = "100%"
self.gui_display.layout.height = "1000px"
if _HAS_WIDGET_BACKEND and StrictVersion(
matplotlib.__version__
) < StrictVersion("3.5.1"):
warnings.warn(
"The widget backend requires Matplotlib >=3.5.1 for proper functioning",
UserWarning,
)
self.sweep = sweep
self.subsys_names = [subsys.id_str for subsys in self.sweep.hilbertspace]
self.subsys_types = {
subsys.id_str: type(subsys).__name__ for subsys in self.sweep.hilbertspace
}
self.subsys_types["Composite"] = "Composite" # for use in default_panels
self.ncols = 2 # number of columns used for axes in the figure display
self.fig, self.axes_table = self.build_figure_and_axes_table()
self.figwidth: float
self.figheight: float
# == GUI elements =========================================================
self.ui_hbox: Dict[str, HBox] = {}
self.ui_vbox: Dict[str, VBox] = {}
self.ui: Dict[str, Any] = {}
# +--------+---------+
# | TAB1 | TAB2 |
# +--------+---------+self.ui_main_tab-----------------------------------------+
# | |
# | self.ui_hbox["panels_select_tab"] |
# | - or - |
# | self.ui_vbox["panel_settings_tab"] |
# | |
# +----------------+-----------------------------------------------------------+
# | self.ui_hbox["main_display"] |
# | | |
# | | |
# | | |
# | self.ui_vbox | self["ui_figure_display"] = self.fig.canvas |
# | ["parameters- | - or - |
# | panel_left"] | = Output() |
# | | |
# | | |
# | | |
# +----------------+-----------------------------------------------------------+
self.ui_main_tab = self.build_ui_main_tab()
self.ui_hbox["main_display"] = self.build_ui_main_display()
self.gui_display.children = [self.ui_main_tab, self.ui_hbox["main_display"]]
display(self.gui_display)
@matplotlib.rc_context(matplotlib_settings)
def build_figure_and_axes_table(self) -> Tuple[Figure, np.ndarray]:
# the %inline and %widget backends somehow scale differently; try to compensate
self.figwidth = 6.4
self.figheight = 2.6
plt.ioff()
fig = plt.figure(figsize=(self.figwidth, self.figheight))
fig.canvas.toolbar_position = "right"
fig.canvas.header_visible = False
fig.canvas.footer_visible = False
plt.ion()
axes_table = np.array([])
return fig, axes_table
# +--------+---------+
# | TAB1 | TAB2 |
# +--------+---------+self.ui_main_tab-----------------------------------------+
# | |
# | self.ui_hbox["panels_select_tab"] |
# | - or - |
# | self.ui_vbox["panel_settings_tab"] |
# | |
# +----------------+-----------------------------------------------------------+
def build_ui_main_tab(self) -> Tab:
self.ui_hbox["panels_select_tab"] = self.build_ui_panels_select_tab()
self.ui_vbox["panel_settings_tab"] = self.build_ui_settings_tab()
main_tab = Tab(
children=[
self.ui_hbox["panels_select_tab"],
self.ui_vbox["panel_settings_tab"],
]
)
main_tab.set_title(0, "Choose panels")
main_tab.set_title(1, "Panel settings")
return main_tab
# +----------------+-----------------------------------------------------------+
# | self.ui_hbox["main_display"] |
# | | |
# | | |
# | | |
# | self.ui_vbox | self["ui_figure_display"] = self.fig.canvas |
# | ["parameters- | - or - |
# | panel_left"] | = Output() |
# | | |
# | | |
# | | |
# +----------------+-----------------------------------------------------------+
def build_ui_main_display(self) -> HBox:
self.ui_vbox["parameters_panel_left"] = self.build_ui_parameters_panel_left()
self.ui["figure_display"] = self.build_ui_figure_display()
self.update_layout_and_plots(None)
return HBox([self.ui_vbox["parameters_panel_left"], self.ui["figure_display"]])
# +--------+
# | TAB1 |
# +-----------------------------------------------------------------------------+
# | subsys_dropdown html_label +-------------------------+ BTN |
# | | | |
# | checkbox checkbox | panels_list | |
# | checkbox checkbox | | |
# | ... ... +-------------------------+ |
# +-----------------------------------------------------------------------------+
def build_ui_panels_select_tab(self) -> HBox:
self.ui["subsys_dropdown"] = Dropdown(
options=self.subsys_names, layout=width(165)
)
self.ui["subsys_dropdown"].observe(self.on_subsys_change, "value")
ui_panels_checkboxes: Dict[str, Dict[str, Checkbox]] = {}
for subsys_name in self.subsys_names:
ui_panels_checkboxes[subsys_name] = {
panel_name: Checkbox(
value=self.get_toggle_value_default(subsys_name, panel_name),
description=panel_name,
layout=width(185),
style={"description_width": "initial"},
)
for panel_name in subsys_panel_names
}
ui_panels_checkboxes["Composite"] = {
panel_name: Checkbox(
value=self.get_toggle_value_default("Composite", panel_name),
description=panel_name,
layout=width(185),
style={"description_width": "initial"},
)
for panel_name in composite_panel_names
}
self.ui["panels_checkboxes"] = ui_panels_checkboxes
for subsys_name in self.subsys_names:
self.ui_vbox[subsys_name] = VBox(
[
self.ui["panels_checkboxes"][subsys_name][panel_name]
for panel_name in subsys_panel_names
]
)
self.ui_vbox["current_subsys"] = VBox(
children=self.ui_vbox[self.ui["subsys_dropdown"].value].children
)
self.ui_vbox["Composite"] = VBox(
[
self.ui["panels_checkboxes"]["Composite"][panel_name]
for panel_name in composite_panel_names
],
)
for _, checkbox_dict in self.ui["panels_checkboxes"].items():
for checkbox in checkbox_dict.values():
checkbox.observe(self.on_toggle_event, "value")
self.ui["strings_to_panel_checkboxes"] = {}
for name in self.ui["panels_checkboxes"].keys():
for panel_name in self.ui["panels_checkboxes"][name].keys():
string_id = name + SEP + panel_name
checkbox = self.ui["panels_checkboxes"][name][panel_name]
self.ui["strings_to_panel_checkboxes"][string_id] = checkbox
html_label = HTML(
"""<p style="border:1px; border-style:solid; border-color:lightgrey;
padding-left: 1em;">
&nbsp;Multi-system
</p>"""
)
self.ui_hbox["choose_panels_checkboxes_group"] = HBox(
[
VBox([self.ui["subsys_dropdown"], self.ui_vbox["current_subsys"]]),
VBox([html_label, self.ui_vbox["Composite"]]),
],
layout=width(400, justify_content="space-between"),
)
self.ui["panels_list"] = Select(options=self.selected_as_strings(), rows=8)
self.ui["delete_btn"] = Button(icon="trash", layout=width(35))
self.ui["delete_btn"].on_click(self.delete_panel)
self.ui_vbox["panels_list_group"] = VBox(
[HBox([self.ui["panels_list"], self.ui["delete_btn"]])]
)
self.ui_hbox["panels_select_tab_content"] = HBox(
[
self.ui_hbox["choose_panels_checkboxes_group"],
self.ui_vbox["panels_list_group"],
],
layout=width(800, justify_content="space-between"),
)
return self.ui_hbox["panels_select_tab_content"]
# +--------+
# | TAB2 |
# +-----------------------------------------------------------------------------+
# | panel_choice ---------+ |
# | panels_choice_dropdown| |
# | |
# | panel_settings ------------------------------+ |
# | ui["settings"][subsys_name][panel_name] |
# | |
# +-----------------------------------------------------------------------------+
def build_ui_settings_tab(self) -> VBox:
self.ui["subsys_panel_settings"] = {
subsys_name: {
panel_name: self.build_ui_settings_subsys(subsys_index, panel_name)
for panel_name in subsys_panel_names
}
for subsys_index, subsys_name in enumerate(self.subsys_names)
}
self.ui["transitions"] = {}
self.ui["composite_panel_settings"] = {
"Composite": {
panel_name: self.build_ui_settings_composite(panel_name)
for panel_name in composite_panel_names
}
}
self.ui["settings"] = {
**self.ui["subsys_panel_settings"],
**self.ui["composite_panel_settings"],
}
self.ui["panels_choice_dropdown"] = Dropdown(
options=self.get_panels_list(), layout=width(250)
)
self.ui["panels_choice_dropdown"].observe(self.activate_settings, "value")
if self.ui["panels_choice_dropdown"].value:
subsys_name, panel_name = self.ui["panels_choice_dropdown"].value.split(SEP)
self.ui_hbox["panel_settings"] = HBox(
children=self.ui["settings"][subsys_name][panel_name]
)
else:
self.ui_hbox["panel_settings"] = HBox([])
self.ui_hbox["panel_choice"] = HBox([self.ui["panels_choice_dropdown"]])
return VBox(
[
self.ui_hbox["panel_choice"],
HTML("<br>"),
self.ui_hbox["panel_settings"],
],
layout=Layout(width="100%"), # width(900),
)
# +--parameters_panel_left-----+
# | |
# | Active Sweep Parameter |
# | "sweep_param_dropdown" |
# | |
# | Sample Value |
# | "sweep_value_slider" |
# | |
# | Fixed Parameter(s) |
# | "fixed_param_sliders" |
# | ... |
# | |
# +----------------------------+
def build_ui_parameters_panel_left(self) -> VBox:
self.ui["sweep_param_dropdown"] = Dropdown(
options=self.sweep.param_info.keys(), layout=width(150)
)
self.ui["sweep_param_dropdown"].observe(self.update_fixed_sliders, "value")
self.ui["sweep_value_slider"] = SelectionSlider(
description=self.ui["sweep_param_dropdown"].value,
options=self.sweep.param_info[self.ui["sweep_param_dropdown"].value],
continuous_update=False,
style={"description_width": "initial"},
)
self.ui["sweep_value_slider"].layout.object_fit = "contain"
self.ui["sweep_value_slider"].layout.width = "95%"
self.ui["sweep_value_slider"].observe(self.update_plots, "value")
self.ui["fixed_param_sliders"] = None
self.ui_vbox["fixed_param_sliders"] = VBox([])
self.update_fixed_sliders(None)
return VBox(
[
VBox(
[
HTML("<br>Active sweep parameter"),
self.ui["sweep_param_dropdown"],
],
layout=width(185),
),
VBox([HTML("<br>Sample value"), self.ui["sweep_value_slider"]]),
HTML("<br>"),
self.ui_vbox["fixed_param_sliders"],
],
layout=boxed(260),
)
@matplotlib.rc_context(matplotlib_settings)
def build_ui_figure_display(self):
if _HAS_WIDGET_BACKEND:
out = self.fig.canvas
self.fig.tight_layout()
else:
out = Output(layout=width(750))
out.layout.object_fit = "contain"
out.layout.width = "100%"
with out:
out.clear_output(wait=True)
self.fig.tight_layout()
display(self.fig)
return out
@matplotlib.rc_context(matplotlib_settings)
def display_panel(
self,
full_panel_name: str,
param_slice: ParameterSlice,
fig_ax: Tuple[Figure, Axes],
):
subsys_name, panel_name = full_panel_name.split(SEP)
if subsys_name == "Composite":
subsys = None
else:
subsys = self.sweep.subsys_by_id_str(subsys_name)
if panel_name == "Energy spectrum":
panel_widget = self.ui["subsys_panel_settings"][subsys_name][panel_name]
panels.display_bare_spectrum(
self.sweep,
subsys,
param_slice,
fig_ax,
subtract_ground=panel_widget[1].value,
evals_count=panel_widget[0].value,
)
elif panel_name == "Wavefunctions" and isinstance(subsys, QubitBaseClass):
panels.display_bare_wavefunctions(self.sweep, subsys, param_slice, fig_ax)
elif panel_name == "Matrix elements" and isinstance(subsys, QubitBaseClass):
panel_widgets = self.ui["subsys_panel_settings"][subsys_name][panel_name]
(
opname_dropdown,
matrixscan_toggle,
mode_dropdown,
) = panel_widgets
if matrixscan_toggle.value == "fixed":
panels.display_matrixelements(
sweep=self.sweep,
subsys=subsys,
operator_name=opname_dropdown.value,
mode_str=mode_dropdown.value,
param_slice=param_slice,
fig_ax=fig_ax,
)
else:
panels.display_matrixelement_sweep(
sweep=self.sweep,
subsys=subsys,
operator_name=opname_dropdown.value,
mode_str=mode_dropdown.value,
param_slice=param_slice,
fig_ax=fig_ax,
)
elif panel_name == "Anharmonicity":
panels.display_anharmonicity(self.sweep, subsys, param_slice, fig_ax)
elif panel_name == "Transitions":
if self.ui["transitions"]["initial_dressed_inttext"].disabled:
initial_state = tuple(
inttext.value
for inttext in self.ui["transitions"]["initial_state_inttexts"]
)
else:
initial_state = self.ui["transitions"]["initial_dressed_inttext"].value
subsys_name_tuple = self.ui["transitions"]["highlight_selectmultiple"].value
if subsys_name_tuple == ():
subsys_list = None
else:
subsys_list = [
self.sweep.subsys_by_id_str(subsys_name)
for subsys_name in subsys_name_tuple
]
sidebands = self.ui["transitions"]["sidebands_checkbox"].value
photon_number = self.ui["transitions"]["photons_inttext"].value
panels.display_transitions(
self.sweep,
photon_number,
subsys_list,
initial_state,
sidebands,
param_slice,
fig_ax,
)
elif panel_name == "Self-Kerr":
panels.display_self_kerr(
sweep=self.sweep,
subsys=subsys,
param_slice=param_slice,
fig_ax=fig_ax,
)
elif panel_name == "Cross-Kerr, ac-Stark":
panels.display_cross_kerr(
sweep=self.sweep,
subsys1=self.sweep.get_subsys(0),
subsys2=self.sweep.get_subsys(1),
param_slice=param_slice,
fig_ax=fig_ax,
)
elif panel_name == "Custom data":
pass
@property
def all_selected(self) -> Dict[str, Checkbox]:
"""Returns a dictionary labeling all selected checkboxes by their names."""
return {
name: [
panel
for panel in self.ui["panels_checkboxes"][name].keys()
if self.ui["panels_checkboxes"][name][panel].value
]
for name in self.ui["panels_checkboxes"].keys()
}
def selected_as_strings(self) -> List[str]:
"""Returns a list of strings capturing the names of all panels selected via
the checkboxes."""
all_selected = self.all_selected
selected = []
for name in all_selected.keys():
for panel in all_selected[name]:
selected.append(name + SEP + panel)
return selected
def create_sliders(self) -> List[SelectionSlider]:
"""Returns a list of selection sliders, one for each parameter that is part
of the underlying ParameterSweep object."""
sliders = [
SelectionSlider(
description=param_name,
options=param_array,
continuous_update=False,
layout=Layout(width="95%", object_fit="contain"),
style={"description_width": "initial"},
)
for param_name, param_array in self.sweep.param_info.items()
if param_name != self.ui["sweep_param_dropdown"].value
]
for slider in sliders:
slider.observe(self.update_plots, "value")
return sliders
@property
def fixed_params(self) -> Dict[str, float]:
sliders = self.ui["fixed_param_sliders"]
return {slider.description: slider.value for slider in sliders}
def on_toggle_event(self, change):
self.ui["panels_list"].options = self.selected_as_strings()
self.ui["panels_choice_dropdown"].options = self.selected_as_strings()
self.update_layout_and_plots(change)
def on_subsys_change(self, change):
self.ui_vbox["current_subsys"].children = self.ui_vbox[
self.ui["subsys_dropdown"].value
].children
def activate_settings(self, change):
if self.ui["panels_choice_dropdown"].value:
subsys_name, panel_name = self.ui["panels_choice_dropdown"].value.split(SEP)
self.ui_hbox["panel_settings"].children = [
*self.ui["settings"][subsys_name][panel_name]
]
def delete_panel(self, change):
btn_string = self.ui["panels_list"].value
toggle_btn = self.ui["strings_to_panel_checkboxes"][btn_string]
toggle_btn.value = False # this triggers an on_toggle_event
def get_toggle_value_default(self, subsys_name, panel_name):
sys_type = self.subsys_types[subsys_name]
return panel_name in default_panels[sys_type]
def get_panels_list(self):
panels_list: List[str] = []
for subsys_name, btn_dict in self.ui["panels_checkboxes"].items():
for btn_name, btn in btn_dict.items():
if btn.value:
panels_list.append(subsys_name + SEP + btn_name)
return panels_list
def update_fixed_sliders(self, change):
self.ui["fixed_param_sliders"] = self.create_sliders()
self.ui_vbox["fixed_param_sliders"].children = [
HTML("Fixed parameter(s)"),
*self.ui["fixed_param_sliders"],
]
self.ui["sweep_value_slider"].description = self.ui[
"sweep_param_dropdown"
].value
self.ui["sweep_value_slider"].options = self.sweep.param_info[
self.ui["sweep_param_dropdown"].value
]
def bare_dressed_toggle(self, change):
if self.ui["transitions"]["initial_bare_dressed_toggle"].value == "bare":
self.ui["transitions"]["initial_dressed_inttext"].disabled = True
for inttext in self.ui["transitions"]["initial_state_inttexts"]:
inttext.disabled = False
else:
self.ui["transitions"]["initial_dressed_inttext"].disabled = False
for inttext in self.ui["transitions"]["initial_state_inttexts"]:
inttext.disabled = True
self.update_plots(change)
def fig_ax_by_index(self, index):
row_index = index // self.ncols
col_index = index % self.ncols
return self.fig, self.axes_table[row_index, col_index]
@property
def parameter_slice(self):
return ParameterSlice(
self.ui["sweep_param_dropdown"].value,
self.ui["sweep_value_slider"].value,
self.fixed_params,
list(self.sweep.param_info.keys()),
)
@matplotlib.rc_context(matplotlib_settings)
def update_layout_and_plots(self: "Explorer", change):
panels = self.get_panels_list()
nrows = len(panels) // self.ncols
if len(panels) % self.ncols != 0:
nrows += 1
for axes in self.fig.axes:
self.fig.delaxes(axes)
plt.ioff()
if len(panels) > 0:
self.axes_table = self.fig.subplots(
ncols=self.ncols,
nrows=nrows,
squeeze=False,
)
self.fig.set_size_inches(1.1 * self.figwidth, 0.9 * self.figheight * nrows)
unfilled_cols_in_last_row = (self.ncols - len(panels) % self.ncols) % self.ncols
if unfilled_cols_in_last_row != 0:
for col in range(self.ncols - unfilled_cols_in_last_row, self.ncols):
self.axes_table[-1, col].remove()
# self.panel_count = len(panels)
self.update_plots(None)
plt.ion()
if not _HAS_WIDGET_BACKEND:
with self.ui["figure_display"]:
self.ui["figure_display"].clear_output(wait=True)
self.fig.tight_layout()
display(self.fig)
@matplotlib.rc_context(matplotlib_settings)
def update_plots(self: "Explorer", change):
if not hasattr(self, "fig"):
return
param_val = self.ui["sweep_value_slider"].value
panels = self.get_panels_list()
param_slice = ParameterSlice(
self.ui["sweep_param_dropdown"].value,
param_val,
self.fixed_params,
list(self.sweep.param_info.keys()),
)
for axes in self.axes_table.flatten():
for item in axes.lines + axes.collections:
item.remove()
axes.set_prop_cycle(None)
axes.relim()
axes.autoscale_view()
for index, full_panel_name in enumerate(panels):
self.display_panel(
full_panel_name,
param_slice=param_slice,
fig_ax=self.fig_ax_by_index(index),
)
if not _HAS_WIDGET_BACKEND:
with self.ui["figure_display"]:
self.ui["figure_display"].clear_output(wait=True)
self.fig.tight_layout()
display(self.fig)
else:
self.fig.canvas.draw_idle()
self.fig.tight_layout()
def build_ui_settings_subsys(self, subsys_index: int, panel_name: str):
subsys = self.sweep.get_subsys(subsys_index)
if panel_name == "Energy spectrum":
evals_count = self.sweep.subsys_evals_count(subsys_index)
ui_level_slider = IntSlider(
description="Highest level",
min=1,
max=evals_count,
value=evals_count,
continuous_update=False,
layout=width(300),
)
ui_subtract_ground_checkbox = Checkbox(
description="subtract lowest energy", value=True, layout=width(300)
)
ui_level_slider.observe(self.update_plots, "value")
ui_subtract_ground_checkbox.observe(self.update_plots, "value")
return [ui_level_slider, ui_subtract_ground_checkbox]
if panel_name == "Wavefunctions":
if isinstance(subsys, (scq.FluxQubit, scq.ZeroPi, scq.Cos2PhiQubit)):
self.ui["wavefunction_selector"] = Select(
options=list(range(subsys.truncated_dim)), rows=6
)
else:
self.ui["wavefunction_selector"] = SelectMultiple(
options=list(range(subsys.truncated_dim)), rows=6
)
self.ui["mode_dropdown"] = Dropdown(
options=mode_dropdown_list,
description="Plot as:",
)
return [self.ui["wavefunction_selector"], self.ui["mode_dropdown"]]
if panel_name == "Matrix elements":
ui_mode_dropdown = Dropdown(
options=mode_dropdown_list, description="Plot as:", value="abs"
)
ui_matrixscan_toggle = ToggleButtons(options=["fixed", "sweep"])
ui_matrixscan_toggle.style.button_width = "55px"
ui_operator_dropdown = Dropdown(
options=subsys.get_operator_names(),
description="Operator",
)
ui_mode_dropdown.observe(self.update_plots, "value")
ui_operator_dropdown.observe(self.update_plots, "value")
ui_matrixscan_toggle.observe(self.update_layout_and_plots, "value")
return [ui_operator_dropdown, ui_matrixscan_toggle, ui_mode_dropdown]
return [HBox()]
def build_ui_settings_composite(self, panel_name: str):
if panel_name == "Transitions":
self.ui["transitions"]["initial_state_inttexts"] = [
BoundedIntText(
description="",
min=0,
max=subsys.truncated_dim,
value=0,
continuous_update=False,
layout=width(35),
)
for subsys in self.sweep.hilbertspace
]
self.ui["transitions"]["initial_dressed_inttext"] = BoundedIntText(
description="",
min=0,
max=self.sweep.hilbertspace.dimension,
value=0,
continuous_update=False,
layout=width(35),
disabled=True,
)
self.ui["transitions"]["photons_inttext"] = BoundedIntText(
value=1, min=1, max=5, description="", layout=width(35)
)
self.ui["transitions"]["highlight_selectmultiple"] = SelectMultiple(
description="",
options=self.subsys_names,
value=[self.subsys_names[0]],
rows=4,
layout=width(185),
)
self.ui["transitions"]["initial_bare_dressed_toggle"] = ToggleButtons(
options=["bare", "dressed"], value="bare", description=""
)
self.ui["transitions"][
"initial_bare_dressed_toggle"
].style.button_width = "45px"
self.ui["transitions"]["sidebands_checkbox"] = Checkbox(
description="show sidebands", value=False, layout=width(250)
)
for inttext in self.ui["transitions"]["initial_state_inttexts"]:
inttext.observe(self.update_plots, "value")
self.ui["transitions"]["initial_dressed_inttext"].observe(
self.update_plots, "value"
)
self.ui["transitions"]["photons_inttext"].observe(
self.update_plots, "value"
)
self.ui["transitions"]["highlight_selectmultiple"].observe(
self.update_plots, "value"
)
self.ui["transitions"]["sidebands_checkbox"].observe(
self.update_plots, "value"
)
self.ui["transitions"]["initial_bare_dressed_toggle"].observe(
self.bare_dressed_toggle, "value"
)
initial_state_selection = HBox(
[
Label("Initial state "),
*self.ui["transitions"]["initial_state_inttexts"],
self.ui["transitions"]["initial_bare_dressed_toggle"],
self.ui["transitions"]["initial_dressed_inttext"],
],
layout=Layout(width="400px", justify_content="flex-end"),
)
photon_options_selection = HBox(
[
Label("photons"),
self.ui["transitions"]["photons_inttext"],
self.ui["transitions"]["sidebands_checkbox"],
],
layout=Layout(width="400px", justify_content="flex-end"),
)
transition_highlighting = HBox(
[
Label("Highlight:"),
self.ui["transitions"]["highlight_selectmultiple"],
],
layout=Layout(width="400px", justify_content="flex-end"),
)
return [
initial_state_selection,
VBox([photon_options_selection, transition_highlighting]),
]

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 too big to display