scqubits
Advanced tools
| # test_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. | ||
| ############################################################################ | ||
| import importlib | ||
| import numpy as np | ||
| import pytest | ||
| import scipy as sp | ||
| import scqubits as scq | ||
| import warnings | ||
| import scqubits.utils.spectrum_utils as spec_utils | ||
| from scqubits import Fluxonium, Transmon, HilbertSpace | ||
| diag_methods = scq.DIAG_METHODS.keys() | ||
| def _library_installed(library): | ||
| """Check whether the given `library` is installed or not.""" | ||
| try: | ||
| importlib.import_module(library) | ||
| return True | ||
| except ( | ||
| ImportError | ||
| ): # `ModuleNotFoundError` is a subclass of `ImportError`. Here, the general class of errors are checked. | ||
| return False | ||
| def _get_library_methods(library, evals=True): | ||
| """Get all diagonalization methods for a given `library`.""" | ||
| library_diag_methods = [ | ||
| method | ||
| for method in diag_methods | ||
| if method.split("_")[1] == library | ||
| and method.split("_")[0] == ("evals" if evals else "esys") | ||
| # NOTE: shift-inverse currently does not match with the default, so are omitted. Needs investigation. | ||
| and "_shift-inverse" not in method | ||
| # This is a temporary omission until this bug is fixed. Possible solution: add a constant (EJ) to the Hamiltonian. | ||
| and "_SM" not in method | ||
| ] | ||
| return library_diag_methods | ||
| optional_libraries = ("cupy", "jax", "primme") | ||
| def test_custom_diagonalization_raises_error_if_esys_method_passed_to_evals_method(): | ||
| """Test if an error is raised if an esys method is passed to `evals_method`.""" | ||
| esys_method = "esys_scipy_dense" | ||
| with pytest.raises(ValueError) as exc_info: | ||
| Fluxonium( | ||
| EJ=8.9, EC=2.5, EL=0.5, flux=0.5, cutoff=120, evals_method=esys_method | ||
| ) | ||
| assert ( | ||
| "Invalid `evals_method`: expect one of `evals` methods, got one of `esys` methods." | ||
| in exc_info.exconly() | ||
| ) | ||
| def test_custom_diagonalization_raises_error_if_evals_method_passed_to_esys_method(): | ||
| """Test if an error is raised if an evals method is passed to `esys_method`.""" | ||
| evals_method = "evals_scipy_dense" | ||
| with pytest.raises(ValueError) as exc_info: | ||
| Fluxonium( | ||
| EJ=8.9, EC=2.5, EL=0.5, flux=0.5, cutoff=120, esys_method=evals_method | ||
| ) | ||
| assert ( | ||
| "Invalid `esys_method`: expect one of `esys` methods, got one of `evals` methods." | ||
| in exc_info.exconly() | ||
| ) | ||
| def test_custom_diagonalization_raises_error_if_nonexistent_method_provided_evals(): | ||
| """Test if an error is raised if a non-existent evals method is used.""" | ||
| evals_method = "non_existent_method" | ||
| fluxonium = Fluxonium( | ||
| EJ=8.9, EC=2.5, EL=0.5, flux=0.5, cutoff=120, evals_method=evals_method | ||
| ) | ||
| with pytest.raises(ValueError) as exc_info: | ||
| fluxonium.eigenvals() | ||
| assert ( | ||
| f"Invalid {evals_method} `evals_method`, does not exist in available custom diagonalization methods." | ||
| in exc_info.exconly() | ||
| ) | ||
| def test_custom_diagonalization_raises_error_if_nonexistent_method_provided_esys(): | ||
| """Test if an error is raised if a non-existent esys method is used.""" | ||
| esys_method = "non_existent_method" | ||
| fluxonium = Fluxonium( | ||
| EJ=8.9, EC=2.5, EL=0.5, flux=0.5, cutoff=120, esys_method=esys_method | ||
| ) | ||
| with pytest.raises(ValueError) as exc_info: | ||
| fluxonium.eigensys() | ||
| assert ( | ||
| f"Invalid {esys_method} `esys_method`, does not exist in available custom diagonalization methods." | ||
| in exc_info.exconly() | ||
| ) | ||
| @pytest.mark.parametrize( | ||
| "library", | ||
| [ | ||
| pytest.param( | ||
| library, | ||
| marks=pytest.mark.skipif( | ||
| not _library_installed(library), | ||
| reason=f"Package {library} not installed; skipping test", | ||
| ), | ||
| ) | ||
| for library in optional_libraries | ||
| ], | ||
| ) | ||
| def test_custom_diagonalization_evals_method_matches_default(library): | ||
| """Test custom diagonalization gives the same eigenvalues as using the default method.""" | ||
| if library == "jax": | ||
| # To evade np.allclose errors when not using 64-bit calculations. | ||
| from jax import config | ||
| config.update("jax_enable_x64", True) | ||
| try: | ||
| importlib.import_module(library) | ||
| except ImportError: | ||
| warnings.warn(f"Package {library} not installed; skipping test", ImportWarning) | ||
| return | ||
| library_diag_methods = _get_library_methods(library, evals=True) | ||
| for method in library_diag_methods: | ||
| fluxonium = Fluxonium( | ||
| EJ=8.9, EC=2.5, EL=0.5, flux=0.5, cutoff=120, evals_method=method | ||
| ) | ||
| evals = fluxonium.eigenvals() | ||
| fluxonium.evals_method = None | ||
| evals_default = fluxonium.eigenvals() | ||
| assert np.allclose(evals, evals_default) | ||
| @pytest.mark.parametrize( | ||
| "library", | ||
| [ | ||
| pytest.param( | ||
| library, | ||
| marks=pytest.mark.skipif( | ||
| not _library_installed(library), | ||
| reason=f"Package {library} not installed; skipping test", | ||
| ), | ||
| ) | ||
| for library in optional_libraries | ||
| ], | ||
| ) | ||
| def test_custom_diagonalization_matches_default_with_composite_systems(library): | ||
| """Test custom diagonalization gives same eigenvalues as using the default method for composite systems.""" | ||
| if library == "jax": | ||
| # To evade np.allclose errors when not using 64-bit calculations. | ||
| from jax import config | ||
| config.update("jax_enable_x64", True) | ||
| try: | ||
| importlib.import_module(library) | ||
| except ImportError: | ||
| warnings.warn(f"Package {library} not installed; skipping test", ImportWarning) | ||
| return | ||
| library_diag_methods = _get_library_methods(library, evals=True) | ||
| for method in library_diag_methods: | ||
| tmon = Transmon(EJ=30.02, EC=1.2, ng=0.0, ncut=101) | ||
| fluxonium = Fluxonium( | ||
| EJ=8.9, EC=2.5, EL=0.5, flux=0.5, cutoff=110, evals_method=method | ||
| ) | ||
| hs = HilbertSpace([tmon, fluxonium], evals_method=method) | ||
| hs.add_interaction( | ||
| g_strength=0.250, op1=tmon.n_operator, op2=fluxonium.n_operator, add_hc=True | ||
| ) | ||
| # Get the eigenvalues using the respective custom methods defined above. | ||
| evals_hs = hs.eigenvals(evals_count=10) | ||
| evals_fluxonium = fluxonium.eigenvals(evals_count=10) | ||
| # Get the eigenvalues using the default method. | ||
| fluxonium.evals_method = None | ||
| hs.evals_method = None | ||
| evals_default_hs = hs.eigenvals(evals_count=10) | ||
| evals_default_fluxonium = fluxonium.eigenvals(evals_count=10) | ||
| assert np.allclose(evals_hs, evals_default_hs) | ||
| assert np.allclose(evals_fluxonium, evals_default_fluxonium) | ||
| def test_custom_diagonalization_matches_default_using_custom_procedure(): | ||
| """Test custom diagonalization gives same result as using the default method when using a custom procedure.""" | ||
| def custom_esys(matrix, evals_count, **kwargs): | ||
| evals, evecs = sp.linalg.eigh( | ||
| matrix, subset_by_index=(0, evals_count - 1), eigvals_only=False, **kwargs | ||
| ) | ||
| return evals, evecs | ||
| tmon = Transmon( | ||
| EJ=30.02, | ||
| EC=1.2, | ||
| ng=0.0, | ||
| ncut=501, | ||
| esys_method=custom_esys, | ||
| esys_method_options=dict(driver="evr"), # set a custom driver | ||
| ) | ||
| evals, evecs = tmon.eigensys() | ||
| tmon.esys_method = None | ||
| tmon.esys_method_options = None | ||
| evals_default, evecs_default = tmon.eigensys() | ||
| assert np.all( | ||
| [np.allclose(evecs[:, i], evecs_default[:, i]) for i, _ in enumerate(evals)] | ||
| ) | ||
| assert np.allclose(evals, evals_default) | ||
| @pytest.mark.parametrize( | ||
| "library", | ||
| [ | ||
| pytest.param( | ||
| library, | ||
| marks=pytest.mark.skipif( | ||
| not _library_installed(library), | ||
| reason=f"Package {library} not installed; skipping test", | ||
| ), | ||
| ) | ||
| for library in optional_libraries | ||
| ], | ||
| ) | ||
| def test_custom_diagonalization_evals_are_same_using_eigenvals_and_eigensys_default( | ||
| library, | ||
| ): | ||
| """Test eigenvalues are the same when using either `eigenvals` or `eigensys`.""" | ||
| if library == "jax": | ||
| # To evade np.allclose errors when not using 64-bit calculations. | ||
| from jax import config | ||
| config.update("jax_enable_x64", True) | ||
| try: | ||
| importlib.import_module(library) | ||
| except ImportError: | ||
| warnings.warn(f"Package {library} not installed; skipping test", ImportWarning) | ||
| return | ||
| library_diag_methods = _get_library_methods(library, evals=True) | ||
| for method in library_diag_methods: | ||
| fluxonium = Fluxonium( | ||
| EJ=8.9, | ||
| EC=2.5, | ||
| EL=0.5, | ||
| flux=0.5, | ||
| cutoff=120, | ||
| evals_method=method, | ||
| evals_method_options=dict(tol=1e-4), # custom tolerance | ||
| ) | ||
| evals = fluxonium.eigenvals() | ||
| evals_sys, _ = fluxonium.eigensys() | ||
| assert np.allclose(evals, evals_sys) | ||
| @pytest.mark.parametrize( | ||
| "library", | ||
| [ | ||
| pytest.param( | ||
| library, | ||
| marks=[ | ||
| pytest.mark.skipif( | ||
| not _library_installed(library), | ||
| reason=f"Package {library} not installed; skipping test", | ||
| ), | ||
| ], | ||
| ) | ||
| for library in optional_libraries | ||
| ], | ||
| ) | ||
| def test_custom_diagonalization_esys_method_matches_default(library): | ||
| """Test custom diagonalization gives same result as using the default method.""" | ||
| if library == "jax": | ||
| # To evade np.allclose errors when not using 64-bit calculations. | ||
| from jax import config | ||
| config.update("jax_enable_x64", True) | ||
| try: | ||
| importlib.import_module(library) | ||
| except ImportError: | ||
| warnings.warn(f"Package {library} not installed; skipping test", ImportWarning) | ||
| return | ||
| library_diag_methods = _get_library_methods(library, evals=False) | ||
| for method in library_diag_methods: | ||
| tmon = Transmon(EJ=30.02, EC=1.2, ng=0.0, ncut=301, esys_method=method) | ||
| evals, evecs = tmon.eigensys() | ||
| tmon.esys_method = None | ||
| evals_default, evecs_default = tmon.eigensys() | ||
| if method in []: | ||
| # These cases are currently failing. | ||
| warnings.warn(f"Skipping (known) failing test of {method}", Warning) | ||
| with pytest.raises(AssertionError): | ||
| assert np.all( | ||
| [ | ||
| np.allclose( | ||
| spec_utils.standardize_phases(evecs[:, i]), | ||
| spec_utils.standardize_phases(evecs_default[:, i]), | ||
| atol=1e-7, | ||
| ) | ||
| for i, _ in enumerate(evals) | ||
| ] | ||
| ) | ||
| else: | ||
| assert np.all( | ||
| [ | ||
| np.allclose( | ||
| spec_utils.standardize_phases(evecs[:, i]), | ||
| spec_utils.standardize_phases(evecs_default[:, i]), | ||
| atol=1e-7, | ||
| ) | ||
| # There may be a slight numerical difference in the last eigenvector. | ||
| # We allow for that here and neglect testing the last entry | ||
| # (various discussions about this online). | ||
| for i, _ in enumerate(evals[:-1]) | ||
| ] | ||
| ) | ||
| # There may be a slight numerical difference in the last eigenvalue. | ||
| # We allow for that here and neglect testing the last entry | ||
| # (various discussions about this online). | ||
| assert np.allclose(evals[:-1], evals_default[:-1]) |
@@ -5,4 +5,3 @@ h5py>=2.10 | ||
| ipyvuetify | ||
| pathos>=0.3.0 | ||
| traitlets | ||
| typing_extensions | ||
| typing_extensions |
+9
-7
| Metadata-Version: 2.1 | ||
| Name: scqubits | ||
| Version: 4.1.0 | ||
| Version: 4.2.0 | ||
| Summary: scqubits: superconducting qubits in Python | ||
@@ -24,15 +24,17 @@ Home-page: https://scqubits.readthedocs.io | ||
| Classifier: Operating System :: Microsoft :: Windows | ||
| Requires-Python: >=3.7 | ||
| Requires-Python: >=3.9 | ||
| License-File: LICENSE | ||
| Requires-Dist: numpy>=1.14.2 | ||
| Requires-Dist: scipy<=1.13.1,>=1.5; sys_platform == "darwin" and python_version >= "3.10" | ||
| Requires-Dist: scipy>=1.5; sys_platform == "darwin" and python_version < "3.10" | ||
| Requires-Dist: scipy>=1.5; sys_platform != "darwin" | ||
| Requires-Dist: cycler | ||
| Requires-Dist: cython>=0.29.20 | ||
| Requires-Dist: matplotlib>=3.5.1 | ||
| Requires-Dist: numpy>=1.14.2 | ||
| Requires-Dist: qutip>=4.3.1; python_version >= "3.9" | ||
| Requires-Dist: qutip<5.0; python_version < "3.9" | ||
| Requires-Dist: scipy>=1.5 | ||
| Requires-Dist: dill | ||
| Requires-Dist: qutip>=4.3.1 | ||
| Requires-Dist: sympy | ||
| Requires-Dist: tqdm | ||
| Requires-Dist: typing_extensions | ||
| Requires-Dist: pathos | ||
| Requires-Dist: dill | ||
| Provides-Extra: graphics | ||
@@ -39,0 +41,0 @@ Requires-Dist: matplotlib-label-lines>=0.3.6; extra == "graphics" |
+1
-1
@@ -47,3 +47,3 @@ scqubits: superconducting qubits in Python | ||
| For Python 3.7, 3.8, 3.9, and 3.10: installation via conda is supported. | ||
| For Python 3.9 - 3.12: installation via conda is supported. | ||
| ``` | ||
@@ -50,0 +50,0 @@ conda install -c conda-forge scqubits |
+9
-5
@@ -0,11 +1,15 @@ | ||
| numpy>=1.14.2 | ||
| scipy>=1.5,<=1.13.1 ; sys_platform == 'darwin' and python_version >= '3.10' | ||
| scipy>=1.5 ; sys_platform == 'darwin' and python_version < '3.10' | ||
| scipy>=1.5 ; sys_platform != 'darwin' | ||
| cycler | ||
| cython>=0.29.20 | ||
| matplotlib>=3.5.1 | ||
| numpy>=1.14.2 | ||
| qutip>=4.3.1; python_version >= "3.9" | ||
| qutip<5.0; python_version < "3.9" | ||
| scipy>=1.5 | ||
| dill | ||
| qutip>=4.3.1 | ||
| sympy | ||
| tqdm | ||
| typing_extensions | ||
| pathos | ||
| dill | ||
| Metadata-Version: 2.1 | ||
| Name: scqubits | ||
| Version: 4.1.0 | ||
| Version: 4.2.0 | ||
| Summary: scqubits: superconducting qubits in Python | ||
@@ -24,15 +24,17 @@ Home-page: https://scqubits.readthedocs.io | ||
| Classifier: Operating System :: Microsoft :: Windows | ||
| Requires-Python: >=3.7 | ||
| Requires-Python: >=3.9 | ||
| License-File: LICENSE | ||
| Requires-Dist: numpy>=1.14.2 | ||
| Requires-Dist: scipy<=1.13.1,>=1.5; sys_platform == "darwin" and python_version >= "3.10" | ||
| Requires-Dist: scipy>=1.5; sys_platform == "darwin" and python_version < "3.10" | ||
| Requires-Dist: scipy>=1.5; sys_platform != "darwin" | ||
| Requires-Dist: cycler | ||
| Requires-Dist: cython>=0.29.20 | ||
| Requires-Dist: matplotlib>=3.5.1 | ||
| Requires-Dist: numpy>=1.14.2 | ||
| Requires-Dist: qutip>=4.3.1; python_version >= "3.9" | ||
| Requires-Dist: qutip<5.0; python_version < "3.9" | ||
| Requires-Dist: scipy>=1.5 | ||
| Requires-Dist: dill | ||
| Requires-Dist: qutip>=4.3.1 | ||
| Requires-Dist: sympy | ||
| Requires-Dist: tqdm | ||
| Requires-Dist: typing_extensions | ||
| Requires-Dist: pathos | ||
| Requires-Dist: dill | ||
| Provides-Extra: graphics | ||
@@ -39,0 +41,0 @@ Requires-Dist: matplotlib-label-lines>=0.3.6; extra == "graphics" |
@@ -0,17 +1,21 @@ | ||
| numpy>=1.14.2 | ||
| cycler | ||
| cython>=0.29.20 | ||
| matplotlib>=3.5.1 | ||
| numpy>=1.14.2 | ||
| scipy>=1.5 | ||
| dill | ||
| qutip>=4.3.1 | ||
| sympy | ||
| tqdm | ||
| typing_extensions | ||
| pathos | ||
| dill | ||
| [:python_version < "3.9"] | ||
| qutip<5.0 | ||
| [:sys_platform != "darwin"] | ||
| scipy>=1.5 | ||
| [:python_version >= "3.9"] | ||
| qutip>=4.3.1 | ||
| [:sys_platform == "darwin" and python_version < "3.10"] | ||
| scipy>=1.5 | ||
| [:sys_platform == "darwin" and python_version >= "3.10"] | ||
| scipy<=1.13.1,>=1.5 | ||
| [explorer] | ||
@@ -18,0 +22,0 @@ ipywidgets>=7.5 |
@@ -9,3 +9,2 @@ LICENSE | ||
| scqubits/py.typed | ||
| scqubits/qubit_base_OLD.py | ||
| scqubits/settings.py | ||
@@ -76,2 +75,3 @@ scqubits/testing.py | ||
| scqubits/tests/test_cos2phiqubit.py | ||
| scqubits/tests/test_diag.py | ||
| scqubits/tests/test_explorer.py | ||
@@ -78,0 +78,0 @@ scqubits/tests/test_fluxonium.py |
@@ -45,7 +45,5 @@ import pyparsing as pp | ||
| # - Branch types *************************************************** | ||
| branch_type_names = ["C", "L"] | ||
| # build up dictionary of branch types | ||
| # allow, for example, both "JJ" as well as just JJ | ||
| BRANCH_TYPES = {name: QM + name + QM for name in branch_type_names} | ||
| # allow, for example, both "JJ" as well as just JJ using QM | ||
| BRANCH_TYPES = {name: QM + name + QM for name in ["C", "L"]} | ||
| for BTYPE in BRANCH_TYPES.values(): | ||
@@ -61,2 +59,4 @@ BTYPE.set_results_name("branch_type") | ||
| # - Mutual inductance ************ | ||
| BRANCH_TYPES["ML"] = QM + "ML" + QM | ||
@@ -77,3 +77,3 @@ # - Units: prefixes etc. ************************************************** | ||
| energy_names = ["EJ", "EC", "EL"] | ||
| energy_names = ["EJ", "EC", "EL", "EML"] | ||
@@ -87,2 +87,3 @@ | ||
| UNITS["EL"] += UNITS_FREQ_ENERGY ^ Literal("H") # Henry | ||
| UNITS["EML"] += UNITS_FREQ_ENERGY ^ Literal("H") # Henry | ||
| for name, unit in UNITS.items(): | ||
@@ -171,4 +172,17 @@ unit.leave_whitespace() # allow only "kHz", not "k Hz" | ||
| BRANCHES = Or([BRANCH_JJ, BRANCH_C, BRANCH_L]) | ||
| BRANCH_EM = ( | ||
| BEG | ||
| + BRANCH_TYPES["ML"]("BRANCH_TYPE") | ||
| + CM | ||
| + INT("branch1") | ||
| + CM | ||
| + INT("branch2") | ||
| + CM | ||
| + PARAMS["EML"]("EML") | ||
| + AUX_PARAM | ||
| + END | ||
| ) | ||
| BRANCHES = Or([BRANCH_JJ, BRANCH_C, BRANCH_L, BRANCH_EM]) | ||
| # uncomment to create a html describing the grammar of this language | ||
@@ -175,0 +189,0 @@ # BRANCHES.create_diagram("branches.html") |
+37
-14
@@ -596,6 +596,7 @@ # circuit.py | ||
| subsystem_trunc_dims: Optional[list] = None, | ||
| closure_branches: Optional[List[Branch]] = None, | ||
| closure_branches: Optional[List[Union[Branch, Dict[Branch, float]]]] = None, | ||
| ext_basis: Optional[str] = None, | ||
| use_dynamic_flux_grouping: Optional[bool] = None, | ||
| generate_noise_methods: bool = False, | ||
| subsys_dict: Optional[Dict[str, Any]] = None, | ||
| ): | ||
@@ -619,5 +620,5 @@ """ | ||
| closure_branches: | ||
| List of branches where external flux variables will be specified, by default | ||
| `None` which then chooses closure branches by an internally generated | ||
| spanning tree. For this option, Circuit should be initialized with `use_dynamic_flux_grouping` set to False. | ||
| Each element of the list corresponds to one external flux variable. If the element is a branch | ||
| the external flux will be associated with that branch. If the element is a dictionary, the external flux variable | ||
| will be distributed across the branches according to the dictionary with the factor given as a key value. | ||
| ext_basis: | ||
@@ -631,2 +632,4 @@ can be "discretized" or "harmonic" which chooses whether to use discretized | ||
| set to False by default. Indicates if the noise methods should be generated for the circuit instance. | ||
| subsys_dict: | ||
| User provided dictionary with two keys "systems_sym" and "interaction_sym" defining the symbolic Hamiltonians and interactions for the subsystems. By default set to None, and is internally generated. | ||
| Raises | ||
@@ -644,2 +647,3 @@ ------ | ||
| old_ext_basis = self.ext_basis | ||
| old_subsys_dict = subsys_dict | ||
| if hasattr(self, "symbolic_circuit"): | ||
@@ -664,2 +668,3 @@ old_transformation_matrix = self.transformation_matrix | ||
| generate_noise_methods=generate_noise_methods, | ||
| subsys_dict=subsys_dict, | ||
| ) | ||
@@ -679,2 +684,3 @@ else: | ||
| ext_basis=ext_basis, | ||
| subsys_dict=subsys_dict, | ||
| ) | ||
@@ -698,2 +704,3 @@ except: | ||
| generate_noise_methods=old_generate_noise_methods, | ||
| subsys_dict=old_subsys_dict, | ||
| ) | ||
@@ -705,2 +712,3 @@ else: | ||
| ext_basis=old_ext_basis, | ||
| subsys_dict=old_subsys_dict, | ||
| ) | ||
@@ -739,2 +747,3 @@ raise Exception("Configure failed due to incorrect parameters.") | ||
| subsystem_trunc_dims: Optional[list] = None, | ||
| subsys_dict: Optional[Dict[str, Any]] = None, | ||
| ext_basis: Optional[str] = None, | ||
@@ -755,2 +764,4 @@ ): | ||
| method `truncation_template`, by default `None` | ||
| subsys_dict: | ||
| User provided dictionary with two keys "systems_sym" and "interaction_sym" defining the symbolic Hamiltonians and interactions for the subsystems. By default set to None, and is internally generated. | ||
| ext_basis: | ||
@@ -874,3 +885,3 @@ can be "discretized" or "harmonic" which chooses whether to use discretized | ||
| self.generate_hamiltonian_sym_for_numerics() | ||
| self.generate_subsystems() | ||
| self.generate_subsystems(subsys_dict=subsys_dict) | ||
| self.ext_basis = ( | ||
@@ -895,5 +906,6 @@ self.get_ext_basis() | ||
| subsystem_trunc_dims: Optional[list] = None, | ||
| closure_branches: Optional[List[Branch]] = None, | ||
| closure_branches: Optional[List[Union[Branch, Dict[Branch, float]]]] = None, | ||
| ext_basis: Optional[str] = None, | ||
| use_dynamic_flux_grouping: Optional[bool] = None, | ||
| subsys_dict: Optional[Dict[str, Any]] = None, | ||
| generate_noise_methods: bool = False, | ||
@@ -918,5 +930,5 @@ ): | ||
| closure_branches: | ||
| List of branches where external flux variables will be specified, by default | ||
| `None` which then chooses closure branches by an internally generated | ||
| spanning tree. | ||
| Each element of the list corresponds to one external flux variable. If the element is a branch | ||
| the external flux will be associated with that branch. If the element is a dictionary, the external flux variable | ||
| will be distributed across the branches according to the dictionary with the factor given as a key value. | ||
| ext_basis: | ||
@@ -926,2 +938,4 @@ can be "discretized" or "harmonic" which chooses whether to use discretized, or can be a list of lists of lists, when hierarchical diagonalization is used. | ||
| set to False by default. Indicates if the flux allocation is done by assuming that flux is time dependent. When set to True, it disables the option to change the closure branches. | ||
| subsys_dict: | ||
| User provided dictionary with two keys "systems_sym" and "interaction_sym" defining the symbolic Hamiltonians and interactions for the subsystems. By default set to None, and is internally generated. | ||
| generate_noise_methods: | ||
@@ -1084,3 +1098,3 @@ set to False by default. Indicates if the noise methods should be generated for the circuit instance. | ||
| self.ext_basis = ext_basis or self.ext_basis | ||
| self.generate_subsystems() | ||
| self.generate_subsystems(subsys_dict=subsys_dict) | ||
| self.ext_basis = self.get_ext_basis() | ||
@@ -1254,8 +1268,17 @@ self.update_interactions() | ||
| """ | ||
| if not self.closure_branches: | ||
| return {} | ||
| list_closure_branches = { | ||
| element: idx | ||
| for idx, element in enumerate(self.closure_branches) | ||
| if isinstance(element, Branch) | ||
| } | ||
| return { | ||
| self._make_expr_human_readable(self.external_fluxes[ibranch]): ( | ||
| self.closure_branches[ibranch], | ||
| self.symbolic_circuit._find_loop(self.closure_branches[ibranch]), | ||
| self._make_expr_human_readable( | ||
| self.external_fluxes[list_closure_branches[branch]] | ||
| ): ( | ||
| branch, | ||
| self.symbolic_circuit._find_loop(branch), | ||
| ) | ||
| for ibranch in range(len(self.external_fluxes)) | ||
| for branch in list_closure_branches | ||
| } | ||
@@ -1262,0 +1285,0 @@ |
@@ -635,2 +635,5 @@ # diag.py | ||
| import jax | ||
| # jax defaults to single precision, but we need to default to double precision | ||
| jax.config.update("jax_enable_x64", True) | ||
| except: | ||
@@ -644,3 +647,6 @@ raise ImportError("Package jax is not installed.") | ||
| return evals[:evals_count] | ||
| # In eigh, the eigvals options is not currently implemented, although listed | ||
| # in the jax docs, hence we have to "manually" only return the number of | ||
| # evals that the user requested. We also "cast" to a numpy array via np.asarray. | ||
| return np.asarray(evals[:evals_count]) | ||
@@ -678,2 +684,5 @@ | ||
| import jax | ||
| # jax defaults to single precision, but we need to default to double precision | ||
| jax.config.update("jax_enable_x64", True) | ||
| except: | ||
@@ -686,4 +695,6 @@ raise ImportError("Package jax is not installed.") | ||
| # We explicitly cast to numpy arrays | ||
| evals, evecs = np.asarray(evals), np.asarray(evecs) | ||
| # In eigh, the eigvals options is not currently implemented, although listed | ||
| # in the jax docs, hence we only "manually" select the number of evals/evecs | ||
| # that the user requested. We also "cast" to a numpy array via np.asarray. | ||
| evals, evecs = np.asarray(evals[:evals_count]), np.asarray(evecs[:, :evals_count]) | ||
@@ -693,3 +704,3 @@ evecs = ( | ||
| ) | ||
| return evals[:evals_count], evecs[:, :evals_count] | ||
| return evals, evecs | ||
@@ -696,0 +707,0 @@ |
@@ -181,5 +181,5 @@ # discretization.py | ||
| if isinstance(prefactor, complex): | ||
| dtp = np.complex_ | ||
| dtp = np.complex128 | ||
| else: | ||
| dtp = np.float_ | ||
| dtp = np.float64 | ||
@@ -216,5 +216,5 @@ delta_x = self.grid_spacing() | ||
| if isinstance(prefactor, complex): | ||
| dtp = np.complex_ | ||
| dtp = np.complex128 | ||
| else: | ||
| dtp = np.float_ | ||
| dtp = np.float64 | ||
@@ -221,0 +221,0 @@ delta_x = self.grid_spacing() |
@@ -691,3 +691,3 @@ # flux_qubit.py | ||
| def _n_operator(self) -> ndarray: | ||
| diag_elements = np.arange(-self.ncut, self.ncut + 1, dtype=np.complex_) | ||
| diag_elements = np.arange(-self.ncut, self.ncut + 1, dtype=np.complex128) | ||
| return np.diag(diag_elements) | ||
@@ -697,3 +697,3 @@ | ||
| dim = 2 * self.ncut + 1 | ||
| off_diag_elements = np.ones(dim - 1, dtype=np.complex_) | ||
| off_diag_elements = np.ones(dim - 1, dtype=np.complex128) | ||
| e_iphi_matrix = np.diag(off_diag_elements, k=-1) | ||
@@ -700,0 +700,0 @@ return e_iphi_matrix |
@@ -419,3 +419,3 @@ # fluxonium.py | ||
| self, | ||
| esys: Optional[Tuple[ndarray, ndarray]], | ||
| esys: Optional[Tuple[ndarray, ndarray]] = None, | ||
| which: int = 0, | ||
@@ -446,3 +446,3 @@ phi_grid: "Grid1d" = None, | ||
| wavefunc_osc_basis_amplitudes = evecs[:, which] | ||
| phi_wavefunc_amplitudes = np.zeros(phi_grid.pt_count, dtype=np.complex_) | ||
| phi_wavefunc_amplitudes = np.zeros(phi_grid.pt_count, dtype=np.complex128) | ||
| phi_osc = self.phi_osc() | ||
@@ -449,0 +449,0 @@ for n in range(dim): |
@@ -921,3 +921,3 @@ # hilbert_space.py | ||
| index = range(dim) | ||
| diag_matrix = np.zeros((dim, dim), dtype=np.float_) | ||
| diag_matrix = np.zeros((dim, dim), dtype=np.float64) | ||
| diag_matrix[index, index] = diag_elements | ||
@@ -924,0 +924,0 @@ return spec_utils.identity_wrap(diag_matrix, subsystem, self.subsystem_list) |
@@ -593,3 +593,3 @@ # namedslots_array.py | ||
| io_attributes = None | ||
| if self.dtype in [np.float_, np.complex_, np.int_]: | ||
| if self.dtype in [np.float64, np.complex128, np.int_]: | ||
| io_ndarrays: Optional[Dict[str, ndarray]] = { | ||
@@ -596,0 +596,0 @@ "input_array": self.view(np.ndarray) |
@@ -30,3 +30,3 @@ # noise.py | ||
| from scipy.sparse import csc_matrix | ||
| from sympy import csc | ||
| from sympy import csc, sec | ||
@@ -102,7 +102,9 @@ import scqubits.core.units as units | ||
| "M": 400, # Mutual inductance between qubit and a flux line. Units: \Phi_0 / Ampere | ||
| "R_k": sp.constants.h | ||
| / sp.constants.e**2.0, # Normal quantum resistance, aka Klitzing constant. | ||
| # Note, in some papers a superconducting quantum | ||
| # resistance is used, and defined as: h/(2e)^2 | ||
| } | ||
| CONSTANTS = { | ||
| "R_k": sp.constants.h | ||
| / sp.constants.e**2.0, # Normal quantum resistance, aka Klitzing constant. | ||
| } | ||
@@ -924,2 +926,25 @@ | ||
| def transition_energy_derivative( | ||
| self, | ||
| ni, | ||
| nf, | ||
| esys, | ||
| hamiltonian_derivative, | ||
| ): | ||
| """ | ||
| Returns the first order and second order derivative of the nth eigenenergy | ||
| """ | ||
| eigs, evecs = esys | ||
| hamiltonian_derivative = ( | ||
| [hamiltonian_derivative] | ||
| if not isinstance(hamiltonian_derivative, list) | ||
| else hamiltonian_derivative | ||
| ) | ||
| deriv_lambda_1 = {} | ||
| for n in [ni, nf]: | ||
| deriv_lambda_1[n] = ( | ||
| evecs[:, n].conj().T @ hamiltonian_derivative[0] @ evecs[:, n] | ||
| ) | ||
| return np.array([deriv_lambda_1[ni] - deriv_lambda_1[nf]]) | ||
| def tphi_1_over_f( | ||
@@ -971,4 +996,16 @@ self, | ||
| evals, evecs = self.eigensys(evals_count=max(j, i) + 1) if esys is None else esys # type: ignore | ||
| esys = self.eigensys(evals_count=max(j, i) + 1) if esys is None else esys # type: ignore | ||
| if hasattr(self, "is_child"): # find if self is a Circuit object | ||
| energy_variations = self.transition_energy_derivative(i, j, esys, noise_op) | ||
| rate_squared = (2 * np.abs(np.log(p["omega_low"] * p["t_exp"]))) * ( | ||
| A_noise * np.abs(energy_variations[0]) | ||
| ) ** 2 | ||
| rate = np.sqrt(rate_squared) * 2 * np.pi | ||
| if get_rate: | ||
| return rate | ||
| else: | ||
| return 1 / rate if rate != 0 else np.inf | ||
| eigs, evecs = esys | ||
| if isinstance( | ||
@@ -1393,3 +1430,3 @@ noise_op, np.ndarray | ||
| # factor of 2 | ||
| Q_c = NOISE_PARAMS["R_k"] / (8 * np.pi * complex(Z_fun(omega)).real) | ||
| Q_c = CONSTANTS["R_k"] / (8 * np.pi * complex(Z_fun(omega)).real) | ||
| therm_ratio = calc_therm_ratio(omega, T) | ||
@@ -1690,3 +1727,3 @@ s = ( | ||
| np.sqrt(2 / np.pi) | ||
| * (8 / NOISE_PARAMS["R_k"]) | ||
| * (8 / CONSTANTS["R_k"]) | ||
| * (EJ_in_Hz / Delta_in_Hz) | ||
@@ -1716,6 +1753,10 @@ * (2 * Delta_in_Hz / omega_in_Hz) ** (3 / 2) | ||
| 2 | ||
| * sp.constants.hbar | ||
| * omega | ||
| / sp.constants.e**2 | ||
| * complex(y_qp_fun(omega, T)).real | ||
| * (1 / np.tanh(0.5 * therm_ratio)) | ||
| / (1 + np.exp(-therm_ratio)) | ||
| / ( | ||
| 1 + np.exp(-therm_ratio) | ||
| ) # the last two factors come by writing S(-omega) in terms of S(omega) form Smith et al. | ||
| ) | ||
@@ -1722,0 +1763,0 @@ |
@@ -70,3 +70,3 @@ # operators.py | ||
| """ | ||
| hubbardmat = sp.sparse.dok_matrix((dimension, dimension), dtype=np.float_) | ||
| hubbardmat = sp.sparse.dok_matrix((dimension, dimension), dtype=np.float64) | ||
| hubbardmat[j1, j2] = 1.0 | ||
@@ -95,3 +95,3 @@ return hubbardmat.asformat("csc") | ||
| """ | ||
| diag_elements = np.arange(dimension, dtype=np.float_) | ||
| diag_elements = np.arange(dimension, dtype=np.float64) | ||
| if prefactor: | ||
@@ -120,7 +120,7 @@ diag_elements *= prefactor | ||
| """ | ||
| diag_elements = np.arange(dimension, dtype=np.float_) | ||
| diag_elements = np.arange(dimension, dtype=np.float64) | ||
| if prefactor: | ||
| diag_elements *= prefactor | ||
| return sp.sparse.dia_matrix( | ||
| (diag_elements, [0]), shape=(dimension, dimension), dtype=np.float_ | ||
| (diag_elements, [0]), shape=(dimension, dimension), dtype=np.float64 | ||
| ).tocsc() | ||
@@ -127,0 +127,0 @@ |
@@ -137,3 +137,3 @@ # oscillator.py | ||
| evals_count = evals_count or _default_evals_count | ||
| evecs = np.zeros(shape=(self.truncated_dim, evals_count), dtype=np.float_) | ||
| evecs = np.zeros(shape=(self.truncated_dim, evals_count), dtype=np.float64) | ||
| np.fill_diagonal(evecs, 1.0) | ||
@@ -140,0 +140,0 @@ |
@@ -389,3 +389,3 @@ # param_sweep.py | ||
| self, | ||
| initial: Union[None, StateLabel], | ||
| initial: Optional[Union[StateLabel, List[Tuple[int]]]], | ||
| subsys_list: List[QuantumSystem], | ||
@@ -414,3 +414,3 @@ ) -> Tuple[bool, Callable, StateLabel]: | ||
| self, | ||
| final: Union[None, StateLabel], | ||
| final: Optional[Union[StateLabel, List[Tuple[int]]]], | ||
| initial: StateLabel, | ||
@@ -450,3 +450,3 @@ subsys_list: List[QuantumSystem], | ||
| ) -> None: | ||
| if np.isnan(initial_energies.toarray().astype(np.float_)).any(): | ||
| if np.isnan(initial_energies.toarray().astype(np.float64)).any(): | ||
| warnings.warn( | ||
@@ -504,4 +504,4 @@ "The initial state undergoes significant hybridization. " | ||
| subsystems: Optional[Union[QuantumSystem, List[QuantumSystem]]] = None, | ||
| initial: Optional[StateLabel] = None, | ||
| final: Optional[StateLabel] = None, | ||
| initial: Optional[Union[StateLabel, List[Tuple[int]]]] = None, | ||
| final: Optional[Union[StateLabel, List[Tuple[int]]]] = None, | ||
| sidebands: bool = False, | ||
@@ -517,4 +517,4 @@ photon_number: int = 1, | ||
| subsystems: Optional[Union[QuantumSystem, List[QuantumSystem]]] = None, | ||
| initial: Optional[StateLabel] = None, | ||
| final: Optional[StateLabel] = None, | ||
| initial: Optional[Union[StateLabel, List[Tuple[int]]]] = None, | ||
| final: Optional[Union[StateLabel, List[Tuple[int]]]] = None, | ||
| sidebands: bool = False, | ||
@@ -584,33 +584,39 @@ photon_number: int = 1, | ||
| subsys_list = self._process_subsystems_option(subsystems) | ||
| initial_states = initial if isinstance(initial, list) else [initial] | ||
| ( | ||
| initial_dressed, | ||
| initial_energy_lookup_func, | ||
| initial_state, | ||
| ) = self._process_initial_option(initial, subsys_list) | ||
| ( | ||
| final_dressed, | ||
| final_energy_lookup_func, | ||
| final_states_list, | ||
| ) = self._process_final_option(final, initial_state, subsys_list, sidebands) | ||
| final_states = final if isinstance(final, list) else [final] | ||
| transitions: List[Tuple[StateLabel, StateLabel]] = [] | ||
| transition_energies: List[NamedSlotsNdarray] = [] | ||
| for initial in initial_states: | ||
| initial_dressed, initial_energy_lookup_func, initial_state = ( | ||
| self._process_initial_option(initial, subsys_list) | ||
| ) | ||
| for final in final_states: | ||
| final_dressed, final_energy_lookup_func, final_states_list = ( | ||
| self._process_final_option( | ||
| final, initial_state, subsys_list, sidebands | ||
| ) | ||
| ) | ||
| param_indices = param_indices or self._current_param_indices | ||
| _ = self[param_indices] # trigger pre-slicing | ||
| param_indices = param_indices or self._current_param_indices | ||
| _ = self[param_indices] # trigger pre-slicing | ||
| initial_energies = initial_energy_lookup_func(initial_state) | ||
| if not initial_dressed: | ||
| self._validate_bare_initial(initial_state, initial_energies, param_indices) | ||
| initial_energies = initial_energy_lookup_func(initial_state) | ||
| if not initial_dressed: | ||
| self._validate_bare_initial( | ||
| initial_state, initial_energies, param_indices | ||
| ) | ||
| for final_state in final_states_list: | ||
| final_energies = final_energy_lookup_func(final_state) | ||
| diff_energies = (final_energies - initial_energies).astype(float) | ||
| diff_energies /= photon_number | ||
| if make_positive: | ||
| diff_energies = np.abs(diff_energies) | ||
| if not np.isnan(diff_energies).all(): # omit transitions with all nans | ||
| transitions.append((initial_state, final_state)) | ||
| transition_energies.append(diff_energies) | ||
| for final_state in final_states_list: | ||
| final_energies = final_energy_lookup_func(final_state) | ||
| diff_energies = (final_energies - initial_energies).astype(float) | ||
| diff_energies /= photon_number | ||
| if make_positive: | ||
| diff_energies = np.abs(diff_energies) | ||
| if not np.isnan( | ||
| diff_energies | ||
| ).all(): # omit transitions with all nans | ||
| transitions.append((initial_state, final_state)) | ||
| transition_energies.append(diff_energies) | ||
@@ -647,7 +653,60 @@ self.reset_preslicing() | ||
| def _validate_states( | ||
| self, | ||
| initial: Optional[Union[StateLabel, List[Tuple[int, ...]]]] = None, | ||
| final: Optional[Union[StateLabel, List[Tuple[int, ...]]]] = None, | ||
| ) -> None: | ||
| """ | ||
| Validates the conformity of initial and final state tuples with the dimensions and limits of | ||
| the subsystems defined in the hilbertspace. This method ensures that each state tuple, either | ||
| initial or final, is correctly structured and within the valid range for the quantum system's | ||
| dimensions. If the state tuples are not lists, they are converted into lists for validation. | ||
| Raises errors for any mismatch or exceeding values. | ||
| Parameters | ||
| ---------- | ||
| initial : Optional[Union[StateLabel, List[Tuple[int, ...]]]] | ||
| The initial state(s) to be validated. It can be a single state or a list of states. Each state | ||
| is either a `StateLabel` or a tuple representing the quantum state in terms of subsystem | ||
| excitation numbers. | ||
| final : Optional[Union[StateLabel, List[Tuple[int, ...]]]] | ||
| The final state(s) to be validated, structured similarly to the `initial` parameter. | ||
| Raises | ||
| ------ | ||
| ValueError | ||
| If any tuple length does not match the number of subsystems or if any tuple value exceeds | ||
| the maximum allowed dimension of the corresponding subsystem. Also raises an error if the | ||
| initial state values are greater than the final state values, which is not allowed in certain | ||
| quantum systems. | ||
| Returns | ||
| ------- | ||
| None | ||
| """ | ||
| initial = initial if isinstance(initial, list) else [initial] | ||
| final = final if isinstance(final, list) else [final] | ||
| for initial_tuple, final_tuple in itertools.product(initial, final): | ||
| if initial_tuple is not None: | ||
| if len(initial_tuple) != len(self.hilbertspace.subsystem_dims): | ||
| raise ValueError( | ||
| "Initial state tuple does not match the number of subsystems." | ||
| ) | ||
| if max(initial_tuple) >= max(self.hilbertspace.subsystem_dims): | ||
| raise ValueError( | ||
| "Initial state tuple exceeds subsystem dimensions." | ||
| ) | ||
| if final_tuple is not None: | ||
| if len(final_tuple) != len(self.hilbertspace.subsystem_dims): | ||
| raise ValueError( | ||
| "Final state tuple does not match the number of subsystems." | ||
| ) | ||
| if max(final_tuple) >= max(self.hilbertspace.subsystem_dims): | ||
| raise ValueError("Final state tuple exceeds subsystem dimensions.") | ||
| def plot_transitions( | ||
| self, | ||
| subsystems: Optional[Union[QuantumSystem, List[QuantumSystem]]] = None, | ||
| initial: Optional[StateLabel] = None, | ||
| final: Optional[StateLabel] = None, | ||
| initial: Optional[Union[StateLabel, List[Tuple[int, ...]]]] = None, | ||
| final: Optional[Union[StateLabel, List[Tuple[int, ...]]]] = None, | ||
| sidebands: bool = False, | ||
@@ -716,2 +775,4 @@ photon_number: int = 1, | ||
| """ | ||
| self._validate_states(initial, final) | ||
| param_indices = param_indices or self._current_param_indices | ||
@@ -725,3 +786,2 @@ if not self._slice_is_1d_sweep(param_indices): | ||
| ) | ||
| specdata_for_highlighting = self.transitions( | ||
@@ -728,0 +788,0 @@ subsystems=subsystems, |
@@ -39,2 +39,3 @@ # qubit_base.py | ||
| import scipy as sp | ||
| import qutip as qt | ||
@@ -44,3 +45,3 @@ from matplotlib.axes import Axes | ||
| from numpy import ndarray | ||
| from scipy.sparse import csc_matrix, dia_matrix | ||
| from scipy.sparse import csc_matrix, dia_matrix, spmatrix | ||
@@ -290,2 +291,12 @@ import scqubits.core.constants as constants | ||
| super().__init__(id_str=id_str) | ||
| if isinstance(evals_method, str): | ||
| if evals_method.split("_")[0] == "esys": | ||
| raise ValueError( | ||
| "Invalid `evals_method`: expect one of `evals` methods, got one of `esys` methods." | ||
| ) | ||
| if isinstance(esys_method, str): | ||
| if esys_method.split("_")[0] == "evals": | ||
| raise ValueError( | ||
| "Invalid `esys_method`: expect one of `esys` methods, got one of `evals` methods." | ||
| ) | ||
| self.evals_method = evals_method | ||
@@ -365,6 +376,10 @@ self.evals_method_options = evals_method_options | ||
| diagonalizer = ( | ||
| diag.DIAG_METHODS[self.evals_method] | ||
| diag.DIAG_METHODS.get(self.evals_method) | ||
| if isinstance(self.evals_method, str) | ||
| else self.evals_method | ||
| ) | ||
| if diagonalizer is None: | ||
| raise ValueError( | ||
| f"Invalid {self.evals_method} `evals_method`, does not exist in available custom diagonalization methods." | ||
| ) | ||
| options = ( | ||
@@ -427,6 +442,10 @@ {} if self.esys_method_options is None else self.esys_method_options | ||
| diagonalizer = ( | ||
| diag.DIAG_METHODS[self.esys_method] | ||
| diag.DIAG_METHODS.get(self.esys_method) | ||
| if isinstance(self.esys_method, str) | ||
| else self.esys_method | ||
| ) | ||
| if diagonalizer is None: | ||
| raise ValueError( | ||
| f"Invalid {self.esys_method} `esys_method`, does not exist in available custom diagonalization methods." | ||
| ) | ||
| options = ( | ||
@@ -544,3 +563,3 @@ {} if self.esys_method_options is None else self.esys_method_options | ||
| self, | ||
| operator: str, | ||
| operator: Union[str, ndarray, qt.Qobj, spmatrix], | ||
| evecs: ndarray = None, | ||
@@ -576,3 +595,6 @@ evals_count: int = 6, | ||
| _, evecs = self.eigensys(evals_count=evals_count) | ||
| operator_matrix = getattr(self, operator)() | ||
| if isinstance(operator, str): | ||
| operator_matrix = getattr(self, operator)() | ||
| else: | ||
| operator_matrix = operator | ||
| table = get_matrixelement_table(operator_matrix, evecs) | ||
@@ -861,3 +883,3 @@ if filename or return_datastore: | ||
| self, | ||
| operator: str, | ||
| operator: Union[str, ndarray, qt.Qobj, spmatrix], | ||
| param_name: str, | ||
@@ -897,3 +919,3 @@ param_vals: ndarray, | ||
| matelem_table = np.empty( | ||
| shape=(paramvals_count, evals_count, evals_count), dtype=np.complex_ | ||
| shape=(paramvals_count, evals_count, evals_count), dtype=np.complex128 | ||
| ) | ||
@@ -1035,3 +1057,3 @@ | ||
| self, | ||
| operator: str, | ||
| operator: Union[str, ndarray, qt.Qobj, spmatrix], | ||
| evecs: ndarray = None, | ||
@@ -1090,3 +1112,3 @@ evals_count: int = 6, | ||
| self, | ||
| operator: str, | ||
| operator: Union[str, ndarray, qt.Qobj, spmatrix], | ||
| param_name: str, | ||
@@ -1093,0 +1115,0 @@ param_vals: ndarray, |
@@ -319,3 +319,3 @@ # spec_lookup.py | ||
| dressed_index = np.asarray(dressed_index) | ||
| energies = np.empty_like(dressed_index, dtype=np.float_) | ||
| energies = np.empty_like(dressed_index, dtype=np.float64) | ||
| it = np.nditer(dressed_index, flags=["multi_index", "refs_ok"]) | ||
@@ -322,0 +322,0 @@ sliced_energies = self["evals"][param_npindices] |
@@ -512,3 +512,3 @@ # transmon.py | ||
| phi_basis_labels = phi_grid.make_linspace() | ||
| phi_wavefunc_amplitudes = np.empty(phi_grid.pt_count, dtype=np.complex_) | ||
| phi_wavefunc_amplitudes = np.empty(phi_grid.pt_count, dtype=np.complex128) | ||
| for k in range(phi_grid.pt_count): | ||
@@ -515,0 +515,0 @@ phi_wavefunc_amplitudes[k] = (1j**which / math.sqrt(2 * np.pi)) * np.sum( |
@@ -367,3 +367,3 @@ # zeropi_full.py | ||
| zeropi_diag_hamiltonian = sparse.dia_matrix( | ||
| (zeropi_dim, zeropi_dim), dtype=np.complex_ | ||
| (zeropi_dim, zeropi_dim), dtype=np.complex128 | ||
| ) | ||
@@ -379,6 +379,6 @@ zeropi_diag_hamiltonian.setdiag(zeropi_evals) | ||
| zeropi_diag_hamiltonian, | ||
| sparse.identity(zeta_dim, format="dia", dtype=np.complex_), | ||
| sparse.identity(zeta_dim, format="dia", dtype=np.complex128), | ||
| ) | ||
| hamiltonian_mat += sparse.kron( | ||
| sparse.identity(zeropi_dim, format="dia", dtype=np.complex_), | ||
| sparse.identity(zeropi_dim, format="dia", dtype=np.complex128), | ||
| zeta_diag_hamiltonian, | ||
@@ -388,3 +388,5 @@ ) | ||
| gmat = self.g_coupling_matrix(zeropi_evecs) | ||
| zeropi_coupling = sparse.dia_matrix((zeropi_dim, zeropi_dim), dtype=np.complex_) | ||
| zeropi_coupling = sparse.dia_matrix( | ||
| (zeropi_dim, zeropi_dim), dtype=np.complex128 | ||
| ) | ||
| for l1 in range(zeropi_dim): | ||
@@ -533,3 +535,3 @@ for l2 in range(zeropi_dim): | ||
| op_eigen_basis = sparse.dia_matrix( | ||
| (zeropi_dim, zeropi_dim), dtype=np.complex_ | ||
| (zeropi_dim, zeropi_dim), dtype=np.complex128 | ||
| ) # guaranteed to be zero? | ||
@@ -544,3 +546,3 @@ | ||
| op_eigen_basis, | ||
| sparse.identity(zeta_dim, format="csc", dtype=np.complex_), | ||
| sparse.identity(zeta_dim, format="csc", dtype=np.complex128), | ||
| format="csc", | ||
@@ -547,0 +549,0 @@ ) |
@@ -39,3 +39,3 @@ # test_circuit.py | ||
| """ | ||
| REFERENCE = "<bound method Printable.__str__ of EJ*cos(θ1 + θ3) + EJ*cos((2πΦ_{1}) + θ1 - 1.0*θ3) + 6.25625*\\dot{θ_1}**2 + 25.0*\\dot{θ_2}**2 + 0.00625*\\dot{θ_3}**2 - 0.036*θ2**2 - 0.004*θ2*θ3 - 0.009*θ3**2>" | ||
| REFERENCE = "<bound method Printable.__str__ of EJ*cos(θ1 + θ3) + EJ*cos(1.0*(2πΦ_{1}) + θ1 - 1.0*θ3) + 6.25625*\\dot{θ_1}**2 + 25.0*\\dot{θ_2}**2 + 0.00625*\\dot{θ_3}**2 - 0.036*θ2**2 - 0.004*θ2*θ3 - 0.009*θ3**2>" | ||
@@ -42,0 +42,0 @@ zero_pi = scq.Circuit(zp_yaml, from_file=False, ext_basis="discretized") |
@@ -62,3 +62,3 @@ # spectrum_utils.py | ||
| def order_eigensystem( | ||
| evals: np.ndarray, evecs: np.ndarray | ||
| evals: np.ndarray, evecs: np.ndarray, standardize_phase: bool = False | ||
| ) -> Tuple[np.ndarray, np.ndarray]: | ||
@@ -75,2 +75,4 @@ """Takes eigenvalues and corresponding eigenvectors and orders them (in place) | ||
| array containing eigenvectors; evecs[:, 0] is the first eigenvector etc. | ||
| standardize_phase: | ||
| if True, standardize the phase of the eigenvectors (default value = False) | ||
| """ | ||
@@ -80,2 +82,5 @@ ordered_evals_indices = evals.argsort() # sort manually | ||
| evecs[:] = evecs[:, ordered_evals_indices] | ||
| if standardize_phase: | ||
| for j in range(evecs.shape[1]): | ||
| evecs[:, j] = standardize_phases(evecs[:, j]) | ||
| return evals, evecs | ||
@@ -195,7 +200,4 @@ | ||
| if isinstance(operator, qt.Qobj): | ||
| states_in_columns = state_table.T | ||
| else: | ||
| states_in_columns = state_table | ||
| mtable = states_in_columns.conj().T @ operator @ states_in_columns | ||
| operator = Qobj_to_scipy_csc_matrix(operator) | ||
| mtable = state_table.conj().T @ operator @ state_table | ||
| return mtable | ||
@@ -303,3 +305,3 @@ | ||
| dimension = evecs_qutip[0].shape[0] | ||
| evecs_ndarray = np.empty((evals_count, dimension), dtype=np.complex_) | ||
| evecs_ndarray = np.empty((evals_count, dimension), dtype=np.complex128) | ||
| for index, eigenstate in enumerate(evecs_qutip): | ||
@@ -306,0 +308,0 @@ evecs_ndarray[index] = eigenstate.full()[:, 0] |
| # THIS FILE IS GENERATED FROM scqubits SETUP.PY | ||
| short_version = '4.1.0' | ||
| version = '4.1.0' | ||
| short_version = '4.2.0' | ||
| version = '4.2.0' | ||
| release = True |
+2
-2
@@ -56,3 +56,3 @@ """scqubits: superconducting qubits in Python | ||
| MAJOR = 4 | ||
| MINOR = 1 | ||
| MINOR = 2 | ||
| MICRO = 0 | ||
@@ -87,3 +87,3 @@ ISRELEASED = True | ||
| PYTHON_VERSION = ">=3.7" | ||
| PYTHON_VERSION = ">=3.9" | ||
@@ -90,0 +90,0 @@ |
| # qubit_base.py | ||
| # | ||
| # This file is part of scqubits. | ||
| # | ||
| # Copyright (c) 2019, 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. | ||
| ############################################################################ | ||
| """ | ||
| Provides the base classes for qubits | ||
| """ | ||
| import functools | ||
| import inspect | ||
| from abc import ABC, abstractmethod | ||
| import matplotlib.pyplot as plt | ||
| import numpy as np | ||
| import scipy as sp | ||
| import scqubits.core.constants as constants | ||
| import scqubits.settings as settings | ||
| import scqubits.ui.qubit_widget as ui | ||
| import scqubits.utils.plotting as plot | ||
| from scqubits.core.central_dispatch import DispatchClient | ||
| from scqubits.core.discretization import Grid1d | ||
| from scqubits.core.storage import SpectrumData, DataStore | ||
| from scqubits.settings import IN_IPYTHON | ||
| from scqubits.utils.cpu_switch import get_map_method | ||
| from scqubits.utils.misc import InfoBar, drop_private_keys, process_which | ||
| from scqubits.utils.plot_defaults import set_scaling | ||
| from scqubits.utils.spectrum_utils import ( | ||
| get_matrixelement_table, | ||
| order_eigensystem, | ||
| recast_esys_mapdata, | ||
| standardize_sign, | ||
| ) | ||
| if IN_IPYTHON: | ||
| from tqdm.notebook import tqdm | ||
| else: | ||
| from tqdm import tqdm | ||
| # To facilitate warnings in set_units, introduce a counter keeping track of the number of QuantumSystem instances | ||
| _QUANTUMSYSTEM_COUNTER = 0 | ||
| # —Generic quantum system container and Qubit base class————————————————————————————————— | ||
| class QuantumSystem(DispatchClient, ABC): | ||
| """Generic quantum system class""" | ||
| # see PEP 526 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations | ||
| truncated_dim: int | ||
| _image_filename: str | ||
| _evec_dtype: type | ||
| _sys_type: str | ||
| subclasses = [] | ||
| def __new__(cls, *args, **kwargs): | ||
| global _QUANTUMSYSTEM_COUNTER | ||
| _QUANTUMSYSTEM_COUNTER += 1 | ||
| return super().__new__(cls, *args, **kwargs) | ||
| def __del__(self): | ||
| global _QUANTUMSYSTEM_COUNTER | ||
| _QUANTUMSYSTEM_COUNTER -= 1 | ||
| def __init_subclass__(cls, **kwargs): | ||
| """Used to register all non-abstract subclasses as a list in `QuantumSystem.subclasses`.""" | ||
| super().__init_subclass__(**kwargs) | ||
| if not inspect.isabstract(cls): | ||
| cls.subclasses.append(cls) | ||
| def __repr__(self): | ||
| if hasattr(self, "_init_params"): | ||
| init_names = self._init_params | ||
| else: | ||
| init_names = list(inspect.signature(self.__init__).parameters.keys())[1:] | ||
| init_dict = {name: getattr(self, name) for name in init_names} | ||
| return type(self).__name__ + f"(**{init_dict!r})" | ||
| def __str__(self): | ||
| output = self._sys_type.upper() + "\n ———— PARAMETERS ————" | ||
| for param_name, param_val in drop_private_keys(self.__dict__).items(): | ||
| output += "\n" + str(param_name) + "\t: " + str(param_val) | ||
| output += "\nHilbert space dimension\t: " + str(self.hilbertdim()) | ||
| return output | ||
| @abstractmethod | ||
| def hilbertdim(self): | ||
| """Returns dimension of Hilbert space""" | ||
| @classmethod | ||
| def create(cls): | ||
| """Use ipywidgets to create a new class instance""" | ||
| init_params = cls.default_params() | ||
| instance = cls(**init_params) | ||
| instance.widget() | ||
| return instance | ||
| def widget(self, params=None): | ||
| """Use ipywidgets to modify parameters of class instance""" | ||
| init_params = params or self.get_initdata() | ||
| ui.create_widget( | ||
| self.set_params, init_params, image_filename=self._image_filename | ||
| ) | ||
| @staticmethod | ||
| @abstractmethod | ||
| def default_params(): | ||
| """Return dictionary with default parameter values for initialization of class instance""" | ||
| def set_params(self, **kwargs): | ||
| """ | ||
| Set new parameters through the provided dictionary. | ||
| Parameters | ||
| ---------- | ||
| kwargs: dict (str: Number) | ||
| """ | ||
| for param_name, param_val in kwargs.items(): | ||
| setattr(self, param_name, param_val) | ||
| def supported_noise_channels(self): | ||
| """ | ||
| Returns a list of noise channels this QuantumSystem supports. If none, return an empty list. | ||
| """ | ||
| return [] | ||
| # —QubitBaseClass——————————————————————————————————————————————————————————————————————————————————————————————————————— | ||
| class QubitBaseClass(QuantumSystem, ABC): | ||
| """Base class for superconducting qubit objects. Provide general mechanisms and routines | ||
| for plotting spectra, matrix elements, and writing data to files | ||
| """ | ||
| # see PEP 526 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations | ||
| truncated_dim: int | ||
| _default_grid: Grid1d | ||
| _evec_dtype: type | ||
| _sys_type: str | ||
| _init_params: list | ||
| @abstractmethod | ||
| def hamiltonian(self): | ||
| """Returns the Hamiltonian""" | ||
| def _evals_calc(self, evals_count): | ||
| hamiltonian_mat = self.hamiltonian() | ||
| evals = sp.linalg.eigh( | ||
| hamiltonian_mat, eigvals_only=True, eigvals=(0, evals_count - 1) | ||
| ) | ||
| return np.sort(evals) | ||
| def _esys_calc(self, evals_count): | ||
| hamiltonian_mat = self.hamiltonian() | ||
| evals, evecs = sp.linalg.eigh( | ||
| hamiltonian_mat, eigvals_only=False, eigvals=(0, evals_count - 1) | ||
| ) | ||
| evals, evecs = order_eigensystem(evals, evecs) | ||
| return evals, evecs | ||
| def eigenvals(self, evals_count=6, filename=None, return_spectrumdata=False): | ||
| """Calculates eigenvalues using `scipy.linalg.eigh`, returns numpy array of eigenvalues. | ||
| Parameters | ||
| ---------- | ||
| evals_count: int | ||
| number of desired eigenvalues/eigenstates (default value = 6) | ||
| filename: str, optional | ||
| path and filename without suffix, if file output desired (default value = None) | ||
| return_spectrumdata: bool, optional | ||
| if set to true, the returned data is provided as a SpectrumData object (default value = False) | ||
| Returns | ||
| ------- | ||
| ndarray or SpectrumData | ||
| eigenvalues as ndarray or in form of a SpectrumData object | ||
| """ | ||
| evals = self._evals_calc(evals_count) | ||
| if filename or return_spectrumdata: | ||
| specdata = SpectrumData( | ||
| energy_table=evals, system_params=self.get_initdata() | ||
| ) | ||
| if filename: | ||
| specdata.filewrite(filename) | ||
| return specdata if return_spectrumdata else evals | ||
| def eigensys(self, evals_count=6, filename=None, return_spectrumdata=False): | ||
| """Calculates eigenvalues and corresponding eigenvectors using `scipy.linalg.eigh`. Returns | ||
| two numpy arrays containing the eigenvalues and eigenvectors, respectively. | ||
| Parameters | ||
| ---------- | ||
| evals_count: int, optional | ||
| number of desired eigenvalues/eigenstates (default value = 6) | ||
| filename: str, optional | ||
| path and filename without suffix, if file output desired (default value = None) | ||
| return_spectrumdata: bool, optional | ||
| if set to true, the returned data is provided as a SpectrumData object (default value = False) | ||
| Returns | ||
| ------- | ||
| tuple(ndarray, ndarray) or SpectrumData | ||
| eigenvalues, eigenvectors as numpy arrays or in form of a SpectrumData object | ||
| """ | ||
| evals, evecs = self._esys_calc(evals_count) | ||
| if filename or return_spectrumdata: | ||
| specdata = SpectrumData( | ||
| energy_table=evals, system_params=self.get_initdata(), state_table=evecs | ||
| ) | ||
| if filename: | ||
| specdata.filewrite(filename) | ||
| return specdata if return_spectrumdata else (evals, evecs) | ||
| def matrixelement_table( | ||
| self, operator, evecs=None, evals_count=6, filename=None, return_datastore=False | ||
| ): | ||
| """Returns table of matrix elements for `operator` with respect to the eigenstates of the qubit. | ||
| The operator is given as a string matching a class method returning an operator matrix. | ||
| E.g., for an instance `trm` of Transmon, the matrix element table for the charge operator is given by | ||
| `trm.op_matrixelement_table('n_operator')`. | ||
| When `esys` is set to `None`, the eigensystem is calculated on-the-fly. | ||
| Parameters | ||
| ---------- | ||
| operator: str | ||
| name of class method in string form, returning operator matrix in qubit-internal basis. | ||
| evecs: ndarray, optional | ||
| if not provided, then the necessary eigenstates are calculated on the fly | ||
| evals_count: int, optional | ||
| number of desired matrix elements, starting with ground state (default value = 6) | ||
| filename: str, optional | ||
| output file name | ||
| return_datastore: bool, optional | ||
| if set to true, the returned data is provided as a DataStore object (default value = False) | ||
| Returns | ||
| ------- | ||
| ndarray | ||
| """ | ||
| if evecs is None: | ||
| _, evecs = self.eigensys(evals_count=evals_count) | ||
| operator_matrix = getattr(self, operator)() | ||
| table = get_matrixelement_table(operator_matrix, evecs) | ||
| if filename or return_datastore: | ||
| data_store = DataStore( | ||
| system_params=self.get_initdata(), matrixelem_table=table | ||
| ) | ||
| if filename: | ||
| data_store.filewrite(filename) | ||
| return data_store if return_datastore else table | ||
| def _esys_for_paramval(self, paramval, param_name, evals_count): | ||
| setattr(self, param_name, paramval) | ||
| return self.eigensys(evals_count) | ||
| def _evals_for_paramval(self, paramval, param_name, evals_count): | ||
| setattr(self, param_name, paramval) | ||
| return self.eigenvals(evals_count) | ||
| def get_spectrum_vs_paramvals( | ||
| self, | ||
| param_name, | ||
| param_vals, | ||
| evals_count=6, | ||
| subtract_ground=False, | ||
| get_eigenstates=False, | ||
| filename=None, | ||
| num_cpus=settings.NUM_CPUS, | ||
| ): | ||
| """Calculates eigenvalues/eigenstates for a varying system parameter, given an array of parameter values. | ||
| Returns a `SpectrumData` object with `energy_data[n]` containing eigenvalues calculated for | ||
| parameter value `param_vals[n]`. | ||
| Parameters | ||
| ---------- | ||
| param_name: str | ||
| name of parameter to be varied | ||
| param_vals: ndarray | ||
| parameter values to be plugged in | ||
| evals_count: int, optional | ||
| number of desired eigenvalues (sorted from smallest to largest) (default value = 6) | ||
| subtract_ground: bool, optional | ||
| if True, eigenvalues are returned relative to the ground state eigenvalue (default value = False) | ||
| get_eigenstates: bool, optional | ||
| return eigenstates along with eigenvalues (default value = False) | ||
| filename: str, optional | ||
| file name if direct output to disk is wanted | ||
| num_cpus: int, optional | ||
| number of cores to be used for computation (default value: settings.NUM_CPUS) | ||
| Returns | ||
| ------- | ||
| SpectrumData object | ||
| """ | ||
| previous_paramval = getattr(self, param_name) | ||
| tqdm_disable = num_cpus > 1 or settings.PROGRESSBAR_DISABLED | ||
| target_map = get_map_method(num_cpus) | ||
| if get_eigenstates: | ||
| func = functools.partial( | ||
| self._esys_for_paramval, param_name=param_name, evals_count=evals_count | ||
| ) | ||
| with InfoBar( | ||
| "Parallel computation of eigenvalues [num_cpus={}]".format(num_cpus), | ||
| num_cpus, | ||
| ): | ||
| # Note that it is useful here that the outermost eigenstate object is a list, | ||
| # as for certain applications the necessary hilbert space dimension can vary with paramvals | ||
| eigensystem_mapdata = list( | ||
| target_map( | ||
| func, | ||
| tqdm( | ||
| param_vals, | ||
| desc="Spectral data", | ||
| leave=False, | ||
| disable=tqdm_disable, | ||
| ), | ||
| ) | ||
| ) | ||
| eigenvalue_table, eigenstate_table = recast_esys_mapdata( | ||
| eigensystem_mapdata | ||
| ) | ||
| else: | ||
| func = functools.partial( | ||
| self._evals_for_paramval, param_name=param_name, evals_count=evals_count | ||
| ) | ||
| with InfoBar( | ||
| "Parallel computation of eigensystems [num_cpus={}]".format(num_cpus), | ||
| num_cpus, | ||
| ): | ||
| eigenvalue_table = list( | ||
| target_map( | ||
| func, | ||
| tqdm( | ||
| param_vals, | ||
| desc="Spectral data", | ||
| leave=False, | ||
| disable=tqdm_disable, | ||
| ), | ||
| ) | ||
| ) | ||
| eigenvalue_table = np.asarray(eigenvalue_table) | ||
| eigenstate_table = None | ||
| if subtract_ground: | ||
| for param_index, _ in enumerate(param_vals): | ||
| eigenvalue_table[param_index] -= eigenvalue_table[param_index, 0] | ||
| setattr(self, param_name, previous_paramval) | ||
| specdata = SpectrumData( | ||
| eigenvalue_table, | ||
| self.get_initdata(), | ||
| param_name, | ||
| param_vals, | ||
| state_table=eigenstate_table, | ||
| ) | ||
| if filename: | ||
| specdata.filewrite(filename) | ||
| return SpectrumData( | ||
| eigenvalue_table, | ||
| self.get_initdata(), | ||
| param_name, | ||
| param_vals, | ||
| state_table=eigenstate_table, | ||
| ) | ||
| def get_matelements_vs_paramvals( | ||
| self, | ||
| operator, | ||
| param_name, | ||
| param_vals, | ||
| evals_count=6, | ||
| num_cpus=settings.NUM_CPUS, | ||
| ): | ||
| """Calculates matrix elements for a varying system parameter, given an array of parameter values. Returns a | ||
| `SpectrumData` object containing matrix element data, eigenvalue data, and eigenstate data.. | ||
| Parameters | ||
| ---------- | ||
| operator: str | ||
| name of class method in string form, returning operator matrix | ||
| param_name: str | ||
| name of parameter to be varied | ||
| param_vals: ndarray | ||
| parameter values to be plugged in | ||
| evals_count: int, optional | ||
| number of desired eigenvalues (sorted from smallest to largest) (default value = 6) | ||
| num_cpus: int, optional | ||
| number of cores to be used for computation (default value: settings.NUM_CPUS) | ||
| Returns | ||
| ------- | ||
| SpectrumData object | ||
| """ | ||
| spectrumdata = self.get_spectrum_vs_paramvals( | ||
| param_name, | ||
| param_vals, | ||
| evals_count=evals_count, | ||
| get_eigenstates=True, | ||
| num_cpus=num_cpus, | ||
| ) | ||
| paramvals_count = len(param_vals) | ||
| matelem_table = np.empty( | ||
| shape=(paramvals_count, evals_count, evals_count), dtype=np.complex_ | ||
| ) | ||
| for index, paramval in tqdm( | ||
| enumerate(param_vals), | ||
| total=len(param_vals), | ||
| disable=settings.PROGRESSBAR_DISABLED, | ||
| leave=False, | ||
| ): | ||
| evecs = spectrumdata.state_table[index] | ||
| matelem_table[index] = self.matrixelement_table( | ||
| operator, evecs=evecs, evals_count=evals_count | ||
| ) | ||
| spectrumdata.matrixelem_table = matelem_table | ||
| return spectrumdata | ||
| def plot_evals_vs_paramvals( | ||
| self, | ||
| param_name, | ||
| param_vals, | ||
| evals_count=6, | ||
| subtract_ground=None, | ||
| num_cpus=settings.NUM_CPUS, | ||
| **kwargs, | ||
| ): | ||
| """Generates a simple plot of a set of eigenvalues as a function of one parameter. | ||
| The individual points correspond to the a provided array of parameter values. | ||
| Parameters | ||
| ---------- | ||
| param_name: str | ||
| name of parameter to be varied | ||
| param_vals: ndarray | ||
| parameter values to be plugged in | ||
| evals_count: int, optional | ||
| number of desired eigenvalues (sorted from smallest to largest) (default value = 6) | ||
| subtract_ground: bool, optional | ||
| whether to subtract ground state energy from all eigenvalues (default value = False) | ||
| num_cpus: int, optional | ||
| number of cores to be used for computation (default value: settings.NUM_CPUS) | ||
| **kwargs: dict | ||
| standard plotting option (see separate documentation) | ||
| Returns | ||
| ------- | ||
| Figure, Axes | ||
| """ | ||
| specdata = self.get_spectrum_vs_paramvals( | ||
| param_name, | ||
| param_vals, | ||
| evals_count=evals_count, | ||
| subtract_ground=subtract_ground, | ||
| num_cpus=num_cpus, | ||
| ) | ||
| return plot.evals_vs_paramvals(specdata, which=range(evals_count), **kwargs) | ||
| def plot_matrixelements( | ||
| self, | ||
| operator, | ||
| evecs=None, | ||
| evals_count=6, | ||
| mode="abs", | ||
| show_numbers=False, | ||
| show3d=True, | ||
| **kwargs, | ||
| ): | ||
| """Plots matrix elements for `operator`, given as a string referring to a class method | ||
| that returns an operator matrix. E.g., for instance `trm` of Transmon, the matrix element plot | ||
| for the charge operator `n` is obtained by `trm.plot_matrixelements('n')`. | ||
| When `esys` is set to None, the eigensystem with `which` eigenvectors is calculated. | ||
| Parameters | ||
| ---------- | ||
| operator: str | ||
| name of class method in string form, returning operator matrix | ||
| evecs: ndarray, optional | ||
| eigensystem data of evals, evecs; eigensystem will be calculated if set to None (default value = None) | ||
| evals_count: int, optional | ||
| number of desired matrix elements, starting with ground state (default value = 6) | ||
| mode: str, optional | ||
| entry from MODE_FUNC_DICTIONARY, e.g., `'abs'` for absolute value (default) | ||
| show_numbers: bool, optional | ||
| determines whether matrix element values are printed on top of the plot (default: False) | ||
| show3d: bool, optional | ||
| whether to show a 3d skyscraper plot of the matrix alongside the 2d plot (default: True) | ||
| **kwargs: dict | ||
| standard plotting option (see separate documentation) | ||
| Returns | ||
| ------- | ||
| Figure, Axes | ||
| """ | ||
| matrixelem_array = self.matrixelement_table(operator, evecs, evals_count) | ||
| if not show3d: | ||
| return plot.matrix2d( | ||
| matrixelem_array, mode=mode, show_numbers=show_numbers, **kwargs | ||
| ) | ||
| return plot.matrix( | ||
| matrixelem_array, mode=mode, show_numbers=show_numbers, **kwargs | ||
| ) | ||
| def plot_matelem_vs_paramvals( | ||
| self, | ||
| operator, | ||
| param_name, | ||
| param_vals, | ||
| select_elems=4, | ||
| mode="abs", | ||
| num_cpus=settings.NUM_CPUS, | ||
| **kwargs, | ||
| ): | ||
| """Generates a simple plot of a set of eigenvalues as a function of one parameter. | ||
| The individual points correspond to the a provided array of parameter values. | ||
| Parameters | ||
| ---------- | ||
| operator: str | ||
| name of class method in string form, returning operator matrix | ||
| param_name: str | ||
| name of parameter to be varied | ||
| param_vals: ndarray | ||
| parameter values to be plugged in | ||
| select_elems: int or list, optional | ||
| either maximum index of desired matrix elements, or list [(i1, i2), (i3, i4), ...] of index tuples | ||
| for specific desired matrix elements (default value = 4) | ||
| mode: str, optional | ||
| entry from MODE_FUNC_DICTIONARY, e.g., `'abs'` for absolute value (default value = 'abs') | ||
| num_cpus: int, optional | ||
| number of cores to be used for computation (default value = 1) | ||
| **kwargs: dict | ||
| standard plotting option (see separate documentation) | ||
| Returns | ||
| ------- | ||
| Figure, Axes | ||
| """ | ||
| if isinstance(select_elems, int): | ||
| evals_count = select_elems | ||
| else: | ||
| flattened_list = [index for tupl in select_elems for index in tupl] | ||
| evals_count = max(flattened_list) + 1 | ||
| specdata = self.get_matelements_vs_paramvals( | ||
| operator, param_name, param_vals, evals_count=evals_count, num_cpus=num_cpus | ||
| ) | ||
| return plot.matelem_vs_paramvals( | ||
| specdata, select_elems=select_elems, mode=mode, **kwargs | ||
| ) | ||
| def set_and_return(self, attr_name, value): | ||
| """ | ||
| Allows to set an attribute after which self is returned. This is useful for doing | ||
| something like example:: | ||
| qubit.set_and_return('flux', 0.23).some_method() | ||
| instead of example:: | ||
| qubit.flux=0.23 | ||
| qubit.some_method() | ||
| Parameters | ||
| ---------- | ||
| attr_name: str | ||
| name of class attribute in string form | ||
| value: any | ||
| value that the attribute is to be set to | ||
| Returns | ||
| ------- | ||
| self | ||
| """ | ||
| setattr(self, attr_name, value) | ||
| return self | ||
| # —QubitBaseClass1d————————————————————————————————————————————————————————————————————————————————————————————————————— | ||
| class QubitBaseClass1d(QubitBaseClass): | ||
| """Base class for superconducting qubit objects with one degree of freedom. Provide general mechanisms and routines | ||
| for plotting spectra, matrix elements, and writing data to files. | ||
| """ | ||
| # see PEP 526 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations | ||
| _evec_dtype = np.float_ | ||
| _default_grid: Grid1d | ||
| @abstractmethod | ||
| def potential(self, phi): | ||
| pass | ||
| @abstractmethod | ||
| def wavefunction(self, esys, which=0, phi_grid=None): | ||
| pass | ||
| @abstractmethod | ||
| def wavefunction1d_defaults(self, mode, evals, wavefunc_count): | ||
| pass | ||
| def plot_wavefunction( | ||
| self, which=0, mode="real", esys=None, phi_grid=None, scaling=None, **kwargs | ||
| ): | ||
| """Plot 1d phase-basis wave function(s). Must be overwritten by higher-dimensional qubits like FluxQubits and | ||
| ZeroPi. | ||
| Parameters | ||
| ---------- | ||
| esys: (ndarray, ndarray), optional | ||
| eigenvalues, eigenvectors | ||
| which: int or tuple or list, optional | ||
| single index or tuple/list of integers indexing the wave function(s) to be plotted. | ||
| If which is -1, all wavefunctions up to the truncation limit are plotted. | ||
| phi_grid: Grid1d, optional | ||
| used for setting a custom grid for phi; if None use self._default_grid | ||
| mode: str, optional | ||
| choices as specified in `constants.MODE_FUNC_DICT` (default value = 'abs_sqr') | ||
| scaling: float or None, optional | ||
| custom scaling of wave function amplitude/modulus | ||
| **kwargs: dict | ||
| standard plotting option (see separate documentation) | ||
| Returns | ||
| ------- | ||
| Figure, Axes | ||
| """ | ||
| fig_ax = kwargs.get("fig_ax") or plt.subplots() | ||
| kwargs["fig_ax"] = fig_ax | ||
| index_list = process_which(which, self.truncated_dim) | ||
| if esys is None: | ||
| evals_count = max(index_list) + 2 | ||
| esys = self.eigensys(evals_count) | ||
| evals, _ = esys | ||
| phi_grid = phi_grid or self._default_grid | ||
| potential_vals = self.potential(phi_grid.make_linspace()) | ||
| evals_count = len(index_list) | ||
| if evals_count == 1: | ||
| scale = set_scaling(self, scaling, potential_vals) | ||
| else: | ||
| scale = 0.75 * (evals[-1] - evals[0]) / evals_count | ||
| amplitude_modifier = constants.MODE_FUNC_DICT[mode] | ||
| kwargs = { | ||
| **self.wavefunction1d_defaults(mode, evals, wavefunc_count=len(index_list)), | ||
| **kwargs, | ||
| } | ||
| # in merging the dictionaries in the previous line: if any duplicates, later ones survive | ||
| for wavefunc_index in index_list: | ||
| phi_wavefunc = self.wavefunction( | ||
| esys, which=wavefunc_index, phi_grid=phi_grid | ||
| ) | ||
| phi_wavefunc.amplitudes = standardize_sign(phi_wavefunc.amplitudes) | ||
| phi_wavefunc.amplitudes = amplitude_modifier(phi_wavefunc.amplitudes) | ||
| plot.wavefunction1d( | ||
| phi_wavefunc, | ||
| potential_vals=potential_vals, | ||
| offset=phi_wavefunc.energy, | ||
| scaling=scale, | ||
| **kwargs, | ||
| ) | ||
| return fig_ax |
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
Sorry, the diff of this file is too big to display
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
8740580
0.87%32665
3.98%