scqubits
Advanced tools
| # 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 @@ |
+18
-9
@@ -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 | ||
| 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> | ||
| New J. Phys. 24 103020 (2022).<br> | ||
| https://iopscience.iop.org/article/10.1088/1367-2630/ac94f2 | ||
| Download and Installation | ||
@@ -38,0 +47,0 @@ ------------------------- |
+3
-2
| 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 |
+24
-8
@@ -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 |
+36
-12
@@ -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 @@ """ |
+29
-15
@@ -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 @@ ), |
+186
-46
@@ -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" |
+410
-218
@@ -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) |
+93
-22
@@ -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 |
+4
-5
@@ -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;"> | ||
| 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
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
8479081
4.05%138
14.05%27236
7.93%