scqubits
Advanced tools
| --- | ||
| name: Bug report | ||
| about: Create a bug report to help us improve scqubits | ||
| title: "[Bug report]" | ||
| labels: '' | ||
| assignees: jkochNU | ||
| --- | ||
| ### Documentation check | ||
| If the bug manifests in unexpected behavior (as opposed to a crash), confirm that you have consulted the | ||
| [API documentation](https://scqubits.readthedocs.io/en/latest/api-doc/apidoc.html) | ||
| - [ ] I have checked the API documentation. | ||
| - [ ] I could not locate relevant information in the documentation or information is missing. | ||
| ### Describe the bug | ||
| Please provide a concise description of what the bug is. | ||
| ### Expected behavior | ||
| To minimize misunderstandings, please state briefly what you expected to happen. | ||
| ### To Reproduce | ||
| If not clear from your description above, please provide the steps to reproduce the behavior: | ||
| ### OS and version used (please complete the following information): | ||
| - OS: [e.g. Linux] | ||
| - scqubits version [e.g., 1.1] | ||
| - Python version [e.g. 3.7] | ||
| ### Additional context | ||
| Any additional information you would like to provide to aid us. |
| --- | ||
| name: Ideas, Suggestions, Feature Requests | ||
| about: Suggest an idea for enhancing scqubits | ||
| title: "[Suggestion/request]" | ||
| labels: enhancement | ||
| assignees: jkochNU | ||
| --- | ||
| **Idea for future enhancement of scqubits** | ||
| We welcome new ideas and suggestions for improving scqubits and enhancing its functionality in the future! Please describe what you have in mind. If your suggestion is related to a problem you have encountered, please describe the underlying problem. |
| --- | ||
| name: Other issues | ||
| about: All other issues go here. | ||
| title: "[Other issue]" | ||
| labels: '' | ||
| assignees: jkochNU | ||
| --- | ||
| name: black-check-latest | ||
| on: [pull_request, push] | ||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Set up Python 3.11 | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: 3.11 | ||
| - name: Install latest Black version | ||
| run: pip install black | ||
| - name: Run black --check --diff . | ||
| run: black --check --diff . |
| name: Locally install scqubits with mambabuild and run pytests | ||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
| - devel_peterg | ||
| - spc-main-devel | ||
| workflow_dispatch: | ||
| jobs: | ||
| build-all: | ||
| name: scqubits testing (${{ matrix.python-version }}, ${{ matrix.os }}) | ||
| runs-on: ${{ matrix.os }} | ||
| defaults: | ||
| run: | ||
| shell: bash -el {0} | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| os: ["ubuntu-latest", "macos-latest", "windows-latest"] | ||
| python-version: ['3.9', '3.10', '3.11', '3.12'] | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 2 | ||
| - name: Python setup for the rest of the jobs | ||
| uses: conda-incubator/setup-miniconda@v3 | ||
| with: | ||
| miniforge-variant: Miniforge3 | ||
| mamba-version: "*" | ||
| use-mamba: true | ||
| auto-update-conda: true | ||
| python-version: ${{ matrix.python-version }} | ||
| channels: conda-forge, defaults | ||
| channel-priority: true | ||
| auto-activate-base: true | ||
| - name: Add bin and Scripts to system path | ||
| run: | | ||
| echo $CONDA/bin >> $GITHUB_PATH | ||
| echo $CONDA/Scripts >> $GITHUB_PATH | ||
| - name: conda info | ||
| run: conda info | ||
| - name: Lint with flake8 | ||
| run: | | ||
| conda install flake8 | ||
| flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics | ||
| flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics | ||
| - name: Build with mambabuild | ||
| run: | | ||
| mamba install boa | ||
| conda mambabuild . --no-test | ||
| - name: Install scqubits locally and run tests | ||
| run: | | ||
| mamba install --use-local scqubits | ||
| mamba install pytest | ||
| mamba install pytest-cov | ||
| mamba install pathos | ||
| pytest -v --pyargs --cov=scqubits --cov-report=xml | ||
| pytest -v --pyargs scqubits --num_cpus=4 | ||
| - name: Upload results to Codecov | ||
| uses: codecov/codecov-action@v4 | ||
| with: | ||
| token: ${{ secrets.CODECOV_TOKEN }} | ||
| verbose: true |
| name: Upload Python Package to PyPi | ||
| on: | ||
| release: | ||
| types: [created] | ||
| jobs: | ||
| deploy: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v2 | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v2 | ||
| with: | ||
| python-version: '3.x' | ||
| - name: Install dependencies | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install setuptools wheel twine | ||
| - name: Build and publish | ||
| env: | ||
| TWINE_USERNAME: __token__ | ||
| TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} | ||
| run: | | ||
| python setup.py sdist bdist_wheel | ||
| twine upload dist/* |
+39
| .DS_Store | ||
| .idea/ | ||
| .asv/ | ||
| # Byte compiled | ||
| ./scqubits/io_utils/__pycache__ | ||
| ./scqubits/utils/__pycache__ | ||
| ./scqubits/core/__pycache__ | ||
| ./scqubits/ui/__pycache__ | ||
| ./scqubits/__pycache__ | ||
| *.py[cod] | ||
| # Packages | ||
| *.egg | ||
| *.egg-info | ||
| /scqubits/.ipynb_checkpoints/ | ||
| /scqubits/version.py | ||
| /scqubits.egg-info/ | ||
| /build/ | ||
| /dist/ | ||
| /sc_qubits.egg-info/ | ||
| /scqubits/tests/_tempdata/ | ||
| /scqubits/core/hspace.py | ||
| /scratch/ | ||
| /.vscode/ | ||
| /.pytest_cache/ | ||
| /.coverage | ||
| /.ipynb_checkpoints/ | ||
| /example/ | ||
| scqubits/ui/test.ipynb | ||
| scqubits/ui/.ipynb_checkpoints/test-checkpoint.ipynb | ||
| scqubits/.vscode/settings.json | ||
| *.ipynb | ||
| /.asv/ |
| # Python package | ||
| # Create and test a Python package on multiple Python versions. | ||
| # Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: | ||
| # https://docs.microsoft.com/azure/devops/pipelines/languages/python | ||
| trigger: | ||
| branches: | ||
| include: | ||
| - main | ||
| - spc-main-devel | ||
| - jk-devel | ||
| - Danny_vchos | ||
| - devel_peterg | ||
| - peterg_qutip5 | ||
| jobs: | ||
| - job: Linux | ||
| pool: | ||
| vmImage: 'ubuntu-latest' | ||
| strategy: | ||
| matrix: | ||
| Python39: | ||
| python.version: '3.9' | ||
| Python310: | ||
| python.version: '3.10' | ||
| Python311: | ||
| python.version: '3.11' | ||
| Python312: | ||
| python.version: '3.12' | ||
| steps: | ||
| - task: UsePythonVersion@0 | ||
| inputs: | ||
| versionSpec: '$(python.version)' | ||
| displayName: 'Use Python $(python.version)' | ||
| - script: | | ||
| pip install pytest pytest-azurepipelines | ||
| pip install ".[gui, develop]" | ||
| displayName: 'Install scqubits' | ||
| - script: | | ||
| pip install pipdeptree | ||
| pipdeptree | ||
| displayName: 'pipdeptree' | ||
| - script: | | ||
| pytest -v --pyargs scqubits | ||
| displayName: 'Pytest: single-core' | ||
| - script: | | ||
| export OMP_NUM_THREADS=1 | ||
| export OPENBLAS_NUM_THREADS=1 | ||
| export MKL_NUM_THREADS=1 | ||
| export VECLIB_MAXIMUM_THREADS=1 | ||
| export NUMEXPR_NUM_THREADS=1 | ||
| pytest -v --pyargs scqubits --num_cpus=4 | ||
| displayName: 'Pytest: multiprocessing' | ||
| # | ||
| # | ||
| - job: Windows | ||
| pool: | ||
| vmImage: 'windows-latest' | ||
| strategy: | ||
| matrix: | ||
| Python39: | ||
| python.version: '3.9' | ||
| Python310: | ||
| python.version: '3.10' | ||
| Python311: | ||
| python.version: '3.11' | ||
| Python312: | ||
| python.version: '3.12' | ||
| steps: | ||
| - task: UsePythonVersion@0 | ||
| inputs: | ||
| versionSpec: '$(python.version)' | ||
| displayName: 'Use Python $(python.version)' | ||
| - script: | | ||
| pip install pytest-azurepipelines | ||
| pip install ".[gui, develop]" | ||
| displayName: 'Install scqubits' | ||
| - script: | | ||
| pip install pipdeptree | ||
| pipdeptree | ||
| displayName: 'pipdeptree' | ||
| - script: | | ||
| pytest -v --pyargs scqubits | ||
| displayName: 'Pytest: single-core' | ||
| - script: | | ||
| set OMP_NUM_THREADS=1 | ||
| set OPENBLAS_NUM_THREADS=1 | ||
| set MKL_NUM_THREADS=1 | ||
| set VECLIB_MAXIMUM_THREADS=1 | ||
| set NUMEXPR_NUM_THREADS=1 | ||
| pytest -v --pyargs scqubits --num_cpus=4 | ||
| displayName: 'Pytest: multiprocessing' | ||
| # | ||
| # | ||
| - job: macOS | ||
| pool: | ||
| vmImage: "macos-latest" | ||
| strategy: | ||
| matrix: | ||
| Python39: | ||
| python.version: '3.9' | ||
| Python310: | ||
| python.version: '3.10' | ||
| Python311: | ||
| python.version: '3.11' | ||
| Python312: | ||
| python.version: '3.12' | ||
| steps: | ||
| - task: UsePythonVersion@0 | ||
| inputs: | ||
| versionSpec: '$(python.version)' | ||
| displayName: 'Use Python $(python.version)' | ||
| - script: | | ||
| pip install pytest-azurepipelines | ||
| pip install pytest-xdist | ||
| pip install ".[gui, develop]" | ||
| displayName: 'Install scqubits' | ||
| - script: | | ||
| pip install pipdeptree | ||
| pipdeptree | ||
| displayName: 'pipdeptree' | ||
| - script: | | ||
| export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES | ||
| pytest -v --pyargs scqubits | ||
| displayName: 'Pytest: single-core' | ||
| - script: | | ||
| export OMP_NUM_THREADS=1 | ||
| export OPENBLAS_NUM_THREADS=1 | ||
| export MKL_NUM_THREADS=1 | ||
| export VECLIB_MAXIMUM_THREADS=1 | ||
| export NUMEXPR_NUM_THREADS=1 | ||
| export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES | ||
| pytest -v --pyargs scqubits --num_cpus=4 | ||
| displayName: 'Pytest: multiprocessing' |
+20
| @article{scqubits2, | ||
| title = {Computer-aided quantization and numerical analysis of superconducting circuits}, | ||
| author = {S. P. Chitta et al.}, | ||
| year = 2022, | ||
| journal = {New J.\ Phys.}, | ||
| volume = 24, | ||
| pages = 103020, | ||
| doi = {10.1088/1367-2630/ac94f2}, | ||
| url = {https://doi.org/10.1088/1367-2630/ac94f2} | ||
| } | ||
| @article{scqubits1, | ||
| title = {scqubits: a Python package for superconducting circuits}, | ||
| author = {P. Groszkowski and Jens Koch}, | ||
| year = 2021, | ||
| journal = {Quantum}, | ||
| volume = 5, | ||
| pages = 583, | ||
| doi = {10.22331/q-2021-11-17-583}, | ||
| url = {https://doi.org/10.22331/q-2021-11-17-583} | ||
| } |
+85
| {% set name = "scqubits" %} | ||
| {% set version = "4.1.0" %} | ||
| package: | ||
| name: {{ name|lower }} | ||
| version: {{ version }} | ||
| source: | ||
| # url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz | ||
| # sha256: b721600b0d782c9ffea03ff7cd69f7b01ed7f72f3022ff37e943881d7b17af41 | ||
| path: . | ||
| build: | ||
| number: 0 | ||
| skip: true # [py<37] | ||
| script: "{{ PYTHON }} -m pip install . -vv" | ||
| channels: | ||
| - conda-forge | ||
| requirements: | ||
| host: | ||
| - python | ||
| - pip | ||
| - cython >=0.29.20 | ||
| - numpy >=1.14.2 | ||
| build: | ||
| - python | ||
| - cython >=0.29.20 | ||
| - numpy >=1.14.2 | ||
| - scipy | ||
| - {{ compiler('cxx') }} | ||
| run: | ||
| - python | ||
| - cycler | ||
| - matplotlib >=3.5.1 | ||
| - numpy >=1.14.2 | ||
| - qutip >=4.3.1 | ||
| - scipy >=1.5 | ||
| - dill | ||
| - sympy | ||
| - tqdm | ||
| - typing_extensions | ||
| - h5py >=2.10 | ||
| - pathos >=0.3.0 | ||
| - traitlets | ||
| # Optional dependencies | ||
| - ipywidgets | ||
| - ipyvuetify | ||
| test: | ||
| requires: | ||
| - pytest | ||
| - pytest-cov | ||
| - pathos | ||
| source_files: | ||
| - scqubits/ | ||
| imports: | ||
| - scqubits | ||
| commands: | ||
| - pytest -v --pyargs scqubits | ||
| - pytest -v --pyargs scqubits --num_cpus=4 | ||
| about: | ||
| home: https://github.com/scqubits/scqubits | ||
| license: BSD-3-Clause | ||
| license_family: BSD | ||
| license_file: LICENSE | ||
| summary: Superconducting qubits in Python | ||
| description: | | ||
| scqubits is an open-source Python library for simulating superconducting qubits. It is meant to give the user a | ||
| convenient way to obtain energy spectra of common superconducting qubits, plot energy levels as a function of | ||
| external parameters, calculate matrix elements etc. The library further provides an interface to QuTiP, making it | ||
| easy to work with composite Hilbert spaces consisting of coupled superconducting qubits and harmonic modes. | ||
| Internally, numerics within scqubits is carried out with the help of Numpy and Scipy; plotting capabilities rely | ||
| on Matplotlib. | ||
| doc_url: https://scqubits.readthedocs.io/ | ||
| dev_url: https://github.com/scqubits/scqubits | ||
| extra: | ||
| recipe-maintainers: | ||
| - jkochNU | ||
| - petergthatsme |
| [build-system] | ||
| requires = ["setuptools", "wheel", "setuptools_scm"] | ||
| build-backend = "setuptools.build_meta" | ||
| # Use setuptools_scm to generate version from Git tags | ||
| [tool.setuptools_scm] | ||
| write_to = "scqubits/version.py" | ||
| version_scheme = "only-version" | ||
| [project] | ||
| name = "scqubits" | ||
| description = "scqubits: superconducting qubits in Python" | ||
| dynamic = ["version"] | ||
| readme = "README.md" | ||
| license = { file = "LICENSE" } | ||
| requires-python = ">=3.9" | ||
| authors = [ | ||
| { name = "Jens Koch", email = "jens-koch@northwestern.edu" }, | ||
| { name = "Peter Groszkowski", email = "piotrekg@gmail.com" }, | ||
| ] | ||
| keywords = [ | ||
| "qubits", | ||
| "superconducting", | ||
| ] | ||
| classifiers = [ | ||
| "Development Status :: 5 - Production/Stable", | ||
| "Intended Audience :: Science/Research", | ||
| "License :: OSI Approved :: BSD License", | ||
| "Operating System :: MacOS", | ||
| "Operating System :: Microsoft :: Windows", | ||
| "Operating System :: POSIX", | ||
| "Operating System :: Unix", | ||
| "Programming Language :: Python", | ||
| "Programming Language :: Python :: 3", | ||
| "Topic :: Scientific/Engineering", | ||
| ] | ||
| dependencies = [ | ||
| "cycler", | ||
| "dill", | ||
| "pathos>=0.3.0", | ||
| "matplotlib>=3.5.1", | ||
| "numpy>=1.14.2", | ||
| "pathos>=0.3.0", | ||
| "qutip>=4.3.1", | ||
| "scipy>=1.5 ; sys_platform != 'darwin'", | ||
| "scipy>=1.5 ; sys_platform == 'darwin' and python_version < '3.10'", | ||
| "scipy>=1.5,<=1.13.1 ; sys_platform == 'darwin' and python_version >= '3.10'", | ||
| "sympy", | ||
| "tqdm", | ||
| "typing_extensions", | ||
| ] | ||
| [project.optional-dependencies] | ||
| gui = [ | ||
| "ipywidgets (>=7.5)", "ipyvuetify", "matplotlib-label-lines (>=0.3.6)", "h5py (>=2.10)", | ||
| ] | ||
| develop = [ | ||
| "pytest", "traitlets", "h5py (>=2.10)", | ||
| ] | ||
| [project.urls] | ||
| Homepage = "https://scqubits.readthedocs.io" | ||
| Repository = "https://github.com/scqubits/scqubits" |
| from typing import Any, Callable, Dict, List, Optional, Tuple, Union, TYPE_CHECKING | ||
| if TYPE_CHECKING: | ||
| from scqubits.core.circuit import Subsystem | ||
| import numpy as np | ||
| import sympy as sm | ||
| from matplotlib import pyplot as plt | ||
| from matplotlib.axes import Axes | ||
| from matplotlib.figure import Figure | ||
| from numpy import ndarray | ||
| import scqubits.core.discretization as discretization | ||
| import scqubits.core.oscillator as osc | ||
| import scqubits.core.storage as storage | ||
| import scqubits.utils.plot_defaults as defaults | ||
| import scqubits.utils.plotting as plot | ||
| from scqubits import get_units | ||
| from scqubits.io_utils.fileio_serializers import dict_serialize | ||
| from scqubits.core.circuit_utils import ( | ||
| sawtooth_potential, | ||
| get_trailing_number, | ||
| ) | ||
| from scqubits.utils.misc import ( | ||
| flatten_list_recursive, | ||
| list_intersection, | ||
| unique_elements_in_list, | ||
| ) | ||
| from scqubits.utils.plot_utils import _process_options | ||
| from abc import ABC | ||
| class CircuitPlot(ABC): | ||
| # **************************************************************** | ||
| # ************* Functions for plotting wave function ************* | ||
| # **************************************************************** | ||
| def _recursive_basis_change( | ||
| self, wf_reshaped, wf_dim, subsystem, relevant_indices=None | ||
| ): | ||
| """Method to change the basis recursively, to reverse hierarchical | ||
| diagonalization and get to the basis in which the variables were initially | ||
| defined. | ||
| Parameters | ||
| ---------- | ||
| wf_dim: | ||
| The dimension of the wave function which needs to be rewritten in terms of | ||
| the initial basis | ||
| """ | ||
| U_subsys = subsystem.eigensys(evals_count=subsystem.truncated_dim)[ | ||
| 1 | ||
| ] # eigensys(evals_count=subsystem.truncated_dim) | ||
| wf_sublist = list(range(len(wf_reshaped.shape))) | ||
| U_sublist = [wf_dim, len(wf_sublist)] | ||
| target_sublist = wf_sublist.copy() | ||
| target_sublist[wf_dim] = len(wf_sublist) | ||
| wf_new_basis = np.einsum( | ||
| wf_reshaped, wf_sublist, U_subsys.T, U_sublist, target_sublist | ||
| ) | ||
| if subsystem.hierarchical_diagonalization: | ||
| wf_shape = list(wf_new_basis.shape) | ||
| wf_shape[wf_dim] = [ | ||
| sub_subsys.truncated_dim for sub_subsys in subsystem.subsystems | ||
| ] | ||
| wf_new_basis = wf_new_basis.reshape(flatten_list_recursive(wf_shape)) | ||
| for sub_subsys_index, sub_subsys in enumerate(subsystem.subsystems): | ||
| if len(set(relevant_indices) & set(sub_subsys.dynamic_var_indices)) > 0: | ||
| wf_new_basis = self._recursive_basis_change( | ||
| wf_new_basis, | ||
| wf_dim + sub_subsys_index, | ||
| sub_subsys, | ||
| relevant_indices=relevant_indices, | ||
| ) | ||
| else: | ||
| if len(set(relevant_indices) & set(subsystem.dynamic_var_indices)) > 0: | ||
| wf_shape = list(wf_new_basis.shape) | ||
| wf_shape[wf_dim] = [ | ||
| ( | ||
| getattr(subsystem, cutoff_attrib) | ||
| if "ext" in cutoff_attrib | ||
| else (2 * getattr(subsystem, cutoff_attrib) + 1) | ||
| ) | ||
| for cutoff_attrib in subsystem.cutoff_names | ||
| ] | ||
| wf_new_basis = wf_new_basis.reshape(flatten_list_recursive(wf_shape)) | ||
| return wf_new_basis | ||
| def _basis_change_harm_osc_to_n( | ||
| self, wf_original_basis, wf_dim, var_index, grid_n: discretization.Grid1d | ||
| ): | ||
| """Method to change the basis from harmonic oscillator to n basis.""" | ||
| U_ho_n = np.array( | ||
| [ | ||
| osc.harm_osc_wavefunction( | ||
| n, | ||
| grid_n.make_linspace(), | ||
| abs(self.get_osc_param(var_index, which_param="length")), | ||
| ) | ||
| for n in range(getattr(self, "cutoff_ext_" + str(var_index))) | ||
| ] | ||
| ) | ||
| wf_sublist = [idx for idx, _ in enumerate(wf_original_basis.shape)] | ||
| U_sublist = [wf_dim, len(wf_sublist)] | ||
| target_sublist = wf_sublist.copy() | ||
| target_sublist[wf_dim] = len(wf_sublist) | ||
| wf_new_basis = np.einsum( | ||
| wf_original_basis, wf_sublist, U_ho_n.T, U_sublist, target_sublist | ||
| ) | ||
| return wf_new_basis | ||
| def _basis_change_harm_osc_to_phi( | ||
| self, wf_original_basis, wf_dim, var_index, grid_phi: discretization.Grid1d | ||
| ): | ||
| """Method to change the basis from harmonic oscillator to phi basis.""" | ||
| U_ho_phi = np.array( | ||
| [ | ||
| osc.harm_osc_wavefunction( | ||
| n, | ||
| grid_phi.make_linspace(), | ||
| abs(self.get_osc_param(var_index, which_param="length")), | ||
| ) | ||
| for n in range(getattr(self, "cutoff_ext_" + str(var_index))) | ||
| ] | ||
| ) | ||
| wf_sublist = [idx for idx, _ in enumerate(wf_original_basis.shape)] | ||
| U_sublist = [wf_dim, len(wf_sublist)] | ||
| target_sublist = wf_sublist.copy() | ||
| target_sublist[wf_dim] = len(wf_sublist) | ||
| wf_ext_basis = np.einsum( | ||
| wf_original_basis, wf_sublist, U_ho_phi, U_sublist, target_sublist | ||
| ) | ||
| return wf_ext_basis | ||
| def _basis_change_n_to_phi( | ||
| self, wf_original_basis, wf_dim, var_index, grid_phi: discretization.Grid1d | ||
| ): | ||
| """Method to change the basis from harmonic oscillator to phi basis.""" | ||
| U_n_phi = np.array( | ||
| [ | ||
| np.exp(n * grid_phi.make_linspace() * 1j) | ||
| for n in range( | ||
| -getattr(self, "cutoff_n_" + str(var_index)), | ||
| getattr(self, "cutoff_n_" + str(var_index)) + 1, | ||
| ) | ||
| ] | ||
| ) | ||
| wf_sublist = list(range(len(wf_original_basis.shape))) | ||
| U_sublist = [wf_dim, len(wf_sublist)] | ||
| target_sublist = wf_sublist.copy() | ||
| target_sublist[wf_dim] = len(wf_sublist) | ||
| wf_ext_basis = np.einsum( | ||
| wf_original_basis, wf_sublist, U_n_phi, U_sublist, target_sublist | ||
| ) | ||
| return wf_ext_basis | ||
| def _get_var_dim_for_reshaped_wf(self, wf_var_indices, var_index): | ||
| wf_dim = 0 | ||
| if not self.hierarchical_diagonalization: | ||
| return self.dynamic_var_indices.index(var_index) | ||
| for subsys in self.subsystems: | ||
| intersection = list_intersection(subsys.dynamic_var_indices, wf_var_indices) | ||
| if len(intersection) > 0 and var_index not in intersection: | ||
| if subsys.hierarchical_diagonalization: | ||
| wf_dim += subsys._get_var_dim_for_reshaped_wf( | ||
| wf_var_indices, var_index | ||
| ) | ||
| else: | ||
| wf_dim += len(subsys.dynamic_var_indices) | ||
| elif len(intersection) > 0 and var_index in intersection: | ||
| if subsys.hierarchical_diagonalization: | ||
| wf_dim += subsys._get_var_dim_for_reshaped_wf( | ||
| wf_var_indices, var_index | ||
| ) | ||
| else: | ||
| wf_dim += subsys.dynamic_var_indices.index(var_index) | ||
| break | ||
| else: | ||
| wf_dim += 1 | ||
| return wf_dim | ||
| def _dims_to_be_summed(self, var_indices: Tuple[int], num_wf_dims) -> List[int]: | ||
| all_var_indices = self.dynamic_var_indices | ||
| non_summed_dims = [] | ||
| for var_index in all_var_indices: | ||
| if var_index in var_indices: | ||
| non_summed_dims.append( | ||
| self._get_var_dim_for_reshaped_wf(var_indices, var_index) | ||
| ) | ||
| return [dim for dim in range(num_wf_dims) if dim not in non_summed_dims] | ||
| def _reshape_and_change_to_variable_basis( | ||
| self, wf: ndarray, var_indices: Tuple[int] | ||
| ) -> ndarray: | ||
| """This method changes the basis of the wavefunction when hierarchical | ||
| diagonalization is used. | ||
| Then reshapes the wavefunction to represent each of the variable indices as a | ||
| separate dimension. | ||
| """ | ||
| if self.hierarchical_diagonalization: | ||
| subsys_index_for_var_index = unique_elements_in_list( | ||
| [self.get_subsystem_index(index) for index in var_indices] | ||
| ) # getting the subsystem index for each of the variable indices | ||
| subsys_index_for_var_index.sort() | ||
| subsys_trunc_dims = [sys.truncated_dim for sys in self.subsystems] | ||
| # reshaping the wave functions to truncated dims of subsystems | ||
| wf_hd_reshaped = wf.reshape(*subsys_trunc_dims) | ||
| # **** Converting to the basis in which the variables are defined ***** | ||
| wf_original_basis = wf_hd_reshaped | ||
| for subsys_index in subsys_index_for_var_index: | ||
| wf_dim = 0 | ||
| for sys_index in range(subsys_index): | ||
| if sys_index in subsys_index_for_var_index: | ||
| wf_dim += len(self.subsystems[sys_index].dynamic_var_indices) | ||
| else: | ||
| wf_dim += 1 | ||
| wf_original_basis = self._recursive_basis_change( | ||
| wf_original_basis, | ||
| wf_dim, | ||
| self.subsystems[subsys_index], | ||
| relevant_indices=var_indices, | ||
| ) | ||
| else: | ||
| wf_original_basis = wf.reshape( | ||
| *[ | ||
| ( | ||
| getattr(self, cutoff_attrib) | ||
| if "ext" in cutoff_attrib | ||
| else (2 * getattr(self, cutoff_attrib) + 1) | ||
| ) | ||
| for cutoff_attrib in self.cutoff_names | ||
| ] | ||
| ) | ||
| return wf_original_basis | ||
| def _basis_for_var_index(self, var_index: int) -> str: | ||
| """Returns the ext_basis of the subsystem with no further subsystems to which | ||
| the var_index belongs.""" | ||
| if self.hierarchical_diagonalization: | ||
| subsys = self.subsystems[self.get_subsystem_index(var_index)] | ||
| return subsys._basis_for_var_index(var_index) | ||
| else: | ||
| if var_index in self.var_categories["extended"]: | ||
| return self.ext_basis | ||
| else: | ||
| return "periodic" | ||
| def _change_to_phi_basis( | ||
| self, | ||
| wf_original_basis: ndarray, | ||
| var_indices: Tuple[int], | ||
| grids_dict: Dict[int, Union[discretization.Grid1d, ndarray]], | ||
| change_discrete_charge_to_phi: bool, | ||
| ): | ||
| """Changes the basis of the varaible indices to discretized phi basis which is | ||
| amenable to plotting.""" | ||
| wf_ext_basis = wf_original_basis | ||
| for var_index in var_indices: | ||
| # finding the dimension corresponding to the var_index | ||
| if not self.hierarchical_diagonalization: | ||
| wf_dim = self.dynamic_var_indices.index(var_index) | ||
| else: | ||
| wf_dim = self._get_var_dim_for_reshaped_wf(var_indices, var_index) | ||
| var_basis = self._basis_for_var_index(var_index) | ||
| if var_basis == "harmonic": | ||
| wf_ext_basis = self._basis_change_harm_osc_to_phi( | ||
| wf_ext_basis, wf_dim, var_index, grids_dict[var_index] | ||
| ) | ||
| elif var_basis == "periodic" and change_discrete_charge_to_phi: | ||
| wf_ext_basis = self._basis_change_n_to_phi( | ||
| wf_ext_basis, wf_dim, var_index, grids_dict[var_index] | ||
| ) | ||
| return wf_ext_basis | ||
| def generate_wf_plot_data( | ||
| self, | ||
| which: int = 0, | ||
| mode: str = "abs-sqr", | ||
| var_indices: Tuple[int] = (1,), | ||
| eigensys: ndarray = None, | ||
| change_discrete_charge_to_phi: bool = True, | ||
| grids_dict: Dict[int, discretization.Grid1d] = None, | ||
| ): | ||
| """Returns treated wave function of the current Circuit instance for the | ||
| specified variables. | ||
| Parameters | ||
| ---------- | ||
| which: | ||
| integer to choose which wave function to plot | ||
| mode: | ||
| "abs", "real", "imag", "abs-sqr" - decides which part of the wave | ||
| function is plotted. | ||
| var_indices: | ||
| A tuple containing the indices of the variables chosen to plot the | ||
| wave function in. Should not have more than 2 entries. | ||
| eigensys: | ||
| eigenvalues and eigenstates of the Circuit instance; if not provided, | ||
| calling this method will perform a diagonalization to obtain these. | ||
| extended_variable_basis: str | ||
| The basis in which the extended variables are plotted. Can be either | ||
| "phi" or "charge". | ||
| periodic_variable_basis: str | ||
| The basis in which the periodic variables are plotted. Can be either | ||
| "phi" or "charge". | ||
| grids_dict: | ||
| A dictionary which pairs var indices with the requested grids used to create | ||
| the plot. | ||
| """ | ||
| # checking to see if eigensys needs to be generated | ||
| if eigensys is None: | ||
| _, wfs = self.eigensys(evals_count=which + 1) | ||
| else: | ||
| _, wfs = eigensys | ||
| wf = wfs[:, which] | ||
| # change the wf to the basis in which the variables were initially defined | ||
| wf_original_basis = self._reshape_and_change_to_variable_basis( | ||
| wf=wf, var_indices=var_indices | ||
| ) | ||
| # making a basis change to the desired basis for every var_index | ||
| wf_ext_basis = self._change_to_phi_basis( | ||
| wf_original_basis, | ||
| var_indices=var_indices, | ||
| grids_dict=grids_dict, | ||
| change_discrete_charge_to_phi=change_discrete_charge_to_phi, | ||
| ) | ||
| # sum over the dimensions not relevant to the ones in var_indices | ||
| # finding the dimensions which needs to be summed over | ||
| dims_to_be_summed = self._dims_to_be_summed( | ||
| var_indices, len(wf_ext_basis.shape) | ||
| ) | ||
| # summing over the dimensions | ||
| # summing over the dimensions | ||
| if mode == "abs-sqr": | ||
| wf_plot = np.sum( | ||
| np.abs(wf_ext_basis) ** 2, | ||
| axis=tuple(dims_to_be_summed), | ||
| ) | ||
| return wf_plot | ||
| if mode == "abs": | ||
| if len(dims_to_be_summed) == 0: | ||
| return np.abs(wf_ext_basis) | ||
| else: | ||
| raise AttributeError( | ||
| "Cannot plot the absolute value of the wave function in more than 2 dimensions." | ||
| ) | ||
| elif mode == "real": | ||
| if len(dims_to_be_summed) == 0: | ||
| return np.real(wf_ext_basis) | ||
| else: | ||
| raise AttributeError( | ||
| "Cannot plot the real part of the wave function in more than 2 dimensions." | ||
| ) | ||
| elif mode == "imag": | ||
| if len(dims_to_be_summed) == 0: | ||
| return np.imag(wf_ext_basis) | ||
| else: | ||
| raise AttributeError( | ||
| "Cannot plot the imaginary part of the wave function in more than 2 dimensions." | ||
| ) | ||
| def plot_wavefunction( | ||
| self, | ||
| which=0, | ||
| mode: str = "abs-sqr", | ||
| var_indices: Tuple[int] = (1,), | ||
| esys: Tuple[ndarray, ndarray] = None, | ||
| change_discrete_charge_to_phi: bool = True, | ||
| zero_calibrate: bool = True, | ||
| grids_dict: Dict[int, discretization.Grid1d] = {}, | ||
| **kwargs, | ||
| ) -> Tuple[Figure, Axes]: | ||
| """Returns the plot of the wavefunction in the requested variables. At most 2 | ||
| numbers of variables for wavefunction can be specified as plotting axis. If the | ||
| number of plotting variables for wave function is smaller than the number of | ||
| variables in the circuit, the marginal probability distribution of the state | ||
| with respect to the specified variables is plotted. This means the norm square | ||
| of the wave function is integrated over the rest of the variables and is then | ||
| plotted. | ||
| Parameters | ||
| ---------- | ||
| which: | ||
| integer to choose which wave function to plot | ||
| mode: | ||
| "abs", "real", "imag", "abs-sqr" - decides which part of the wave function is plotted, | ||
| by default "abs-sqr" | ||
| var_indices: | ||
| A tuple containing the indices of the variables chosen to plot the | ||
| wave function in. It should not have more than 2 entries. | ||
| esys: | ||
| The object returned by the method `.eigensys`, is used to avoid the | ||
| re-evaluation of the eigen systems if already evaluated. | ||
| change_discrete_charge_to_phi: | ||
| If True, the wave function is plotted in the phi basis for the periodic | ||
| variables. If False, the wave function is plotted in the charge basis | ||
| for the periodic variables. | ||
| zero_calibrate: bool, optional | ||
| if True, colors are adjusted to use zero wavefunction amplitude as the | ||
| neutral color in the palette | ||
| grids_dict: | ||
| A dictionary which pairs var indices with the grids used to create | ||
| the plot. The way to specify the grids is as follows: | ||
| 1. For extended variables, the grids should be of type `discretization.Grid1d`. | ||
| 2. When the discretized phi basis is used for the extended variable, the grids | ||
| used in the diagonalization is used to plot the wave function instead of | ||
| the grids specified here. | ||
| 3. For periodic variables, only if `change_discrete_charge_to_phi` is True, | ||
| the grid specified here will used for plotting. The grid is specified as an integer | ||
| which is the number of points in the grid. The grid has a minimum and maximum value | ||
| of -pi and pi respectively. | ||
| 4. If the grid is not specified for a variable that requires a grid for plotting (i.e. | ||
| extended variable with harmonic oscillator basis, or periodic variable with | ||
| `change_discrete_charge_to_phi` set to True), the default grid is used. | ||
| **kwargs: | ||
| plotting parameters | ||
| Returns | ||
| ------- | ||
| Returns a axes and figure for further editing. | ||
| """ | ||
| if len(var_indices) > 2: | ||
| raise AttributeError( | ||
| "Cannot plot wave function in more than 2 dimensions. The number of " | ||
| "dimensions should be less than 2." | ||
| ) | ||
| var_indices = np.sort(var_indices) | ||
| grids_per_varindex_dict = grids_dict or self.discretized_grids_dict_for_vars() | ||
| plot_data = self.generate_wf_plot_data( | ||
| which=which, | ||
| mode=mode, | ||
| var_indices=var_indices, | ||
| eigensys=esys, | ||
| change_discrete_charge_to_phi=change_discrete_charge_to_phi, | ||
| grids_dict=grids_per_varindex_dict, | ||
| ) | ||
| var_types = [] | ||
| for var_index in var_indices: | ||
| if var_index in self.var_categories["periodic"]: | ||
| if not change_discrete_charge_to_phi: | ||
| var_types.append("Charge in units of 2e, periodic variable:") | ||
| else: | ||
| var_types.append("Dimensionless flux, periodic variable:") | ||
| if var_index in self.var_categories["extended"]: | ||
| var_types.append("Dimensionless flux, extended variable:") | ||
| if len(var_indices) == 1: | ||
| return self._plot_wf_pdf_1D( | ||
| plot_data, | ||
| mode, | ||
| var_indices, | ||
| grids_per_varindex_dict, | ||
| change_discrete_charge_to_phi, | ||
| kwargs, | ||
| ) | ||
| elif len(var_indices) == 2: | ||
| return self._plot_wf_pdf_2D( | ||
| plot_data, | ||
| var_indices, | ||
| grids_per_varindex_dict, | ||
| change_discrete_charge_to_phi, | ||
| zero_calibrate=zero_calibrate, | ||
| kwargs=kwargs, | ||
| ) | ||
| def _plot_wf_pdf_2D( | ||
| self, | ||
| wf_plot: ndarray, | ||
| var_indices, | ||
| grids_per_varindex_dict, | ||
| change_discrete_charge_to_phi: bool, | ||
| zero_calibrate: bool, | ||
| kwargs, | ||
| ) -> Tuple[Figure, Axes]: | ||
| # check if each variable is periodic | ||
| grids = [] | ||
| labels = [] | ||
| for index_order in [1, 0]: | ||
| if not change_discrete_charge_to_phi and ( | ||
| var_indices[index_order] in self.var_categories["periodic"] | ||
| ): | ||
| grids.append( | ||
| [ | ||
| -getattr(self, "cutoff_n_" + str(var_indices[index_order])), | ||
| getattr(self, "cutoff_n_" + str(var_indices[index_order])), | ||
| 2 * getattr(self, "cutoff_n_" + str(var_indices[index_order])) | ||
| + 1, | ||
| ] | ||
| ) | ||
| labels.append(r"$n_{{{}}}$".format(str(var_indices[index_order]))) | ||
| else: | ||
| grids.append( | ||
| list( | ||
| grids_per_varindex_dict[var_indices[index_order]] | ||
| .get_initdata() | ||
| .values() | ||
| ), | ||
| ) | ||
| labels.append(r"$\theta_{{{}}}$".format(str(var_indices[index_order]))) | ||
| wavefunc_grid = discretization.GridSpec(np.asarray(grids)) | ||
| wavefunc = storage.WaveFunctionOnGrid(wavefunc_grid, wf_plot) | ||
| # obtain fig and axes from | ||
| fig, axes = plot.wavefunction2d( | ||
| wavefunc, | ||
| zero_calibrate=zero_calibrate, | ||
| ylabel=labels[1], | ||
| xlabel=labels[0], | ||
| **kwargs, | ||
| ) | ||
| # change frequency of tick mark for variables in charge basis | ||
| # also force the tick marks to be integers | ||
| if not change_discrete_charge_to_phi: | ||
| if var_indices[0] in self.var_categories["periodic"]: | ||
| if getattr(self, "cutoff_n_" + str(var_indices[0])) >= 6: | ||
| axes.yaxis.set_major_locator(plt.MaxNLocator(13, integer=True)) | ||
| else: | ||
| axes.yaxis.set_major_locator( | ||
| plt.MaxNLocator( | ||
| 1 + 2 * getattr(self, "cutoff_n_" + str(var_indices[0])), | ||
| integer=True, | ||
| ) | ||
| ) | ||
| if var_indices[1] in self.var_categories["periodic"]: | ||
| if getattr(self, "cutoff_n_" + str(var_indices[1])) >= 15: | ||
| axes.xaxis.set_major_locator(plt.MaxNLocator(31, integer=True)) | ||
| else: | ||
| axes.xaxis.set_major_locator( | ||
| plt.MaxNLocator( | ||
| 1 + 2 * getattr(self, "cutoff_n_" + str(var_indices[1])), | ||
| integer=True, | ||
| ) | ||
| ) | ||
| return fig, axes | ||
| def _plot_wf_pdf_1D( | ||
| self, | ||
| wf_plot: ndarray, | ||
| mode: str, | ||
| var_indices, | ||
| grids_per_varindex_dict, | ||
| change_discrete_charge_to_phi: bool, | ||
| kwargs, | ||
| ) -> Tuple[Figure, Axes]: | ||
| var_index = var_indices[0] | ||
| if not change_discrete_charge_to_phi and ( | ||
| var_indices[0] in self.var_categories["periodic"] | ||
| ): | ||
| ncut = self.cutoffs_dict()[var_indices[0]] | ||
| wavefunc = storage.WaveFunction( | ||
| basis_labels=np.linspace(-ncut, ncut, 2 * ncut + 1), | ||
| amplitudes=wf_plot, | ||
| ) | ||
| kwargs = { | ||
| **defaults.wavefunction1d_discrete("abs_sqr"), | ||
| **kwargs, | ||
| } | ||
| wavefunc.basis_labels = np.arange( | ||
| -getattr(self, "cutoff_n_" + str(var_index)), | ||
| getattr(self, "cutoff_n_" + str(var_index)) + 1, | ||
| ) | ||
| fig, axes = plot.wavefunction1d_discrete(wavefunc, **kwargs) | ||
| # changing the tick frequency for axes | ||
| if getattr(self, "cutoff_n_" + str(var_index)) >= 7: | ||
| axes.xaxis.set_major_locator(plt.MaxNLocator(15, integer=True)) | ||
| else: | ||
| axes.xaxis.set_major_locator( | ||
| plt.MaxNLocator(1 + 2 * getattr(self, "cutoff_n_" + str(var_index))) | ||
| ) | ||
| else: | ||
| wavefunc = storage.WaveFunction( | ||
| basis_labels=grids_per_varindex_dict[var_indices[0]].make_linspace(), | ||
| amplitudes=wf_plot, | ||
| ) | ||
| if mode == "abs": | ||
| ylabel = r"$|\psi(\theta_{{{}}})|$".format(str(var_indices[0])) | ||
| elif mode == "abs-sqr": | ||
| ylabel = r"$|\psi(\theta_{{{}}})|^2$".format(str(var_indices[0])) | ||
| elif mode == "real": | ||
| ylabel = r"$\mathrm{{Re}}(\psi(\theta_{{{}}}))$".format( | ||
| str(var_indices[0]) | ||
| ) | ||
| elif mode == "imag": | ||
| ylabel = r"$\mathrm{{Im}}(\psi(\theta_{{{}}}))$".format( | ||
| str(var_indices[0]) | ||
| ) | ||
| fig, axes = plot.wavefunction1d_nopotential( | ||
| wavefunc, | ||
| 0, | ||
| xlabel=r"$\theta_{{{}}}$".format(str(var_indices[0])), | ||
| ylabel=ylabel, | ||
| **kwargs, | ||
| ) | ||
| return fig, axes | ||
| # **************************************************************** | ||
| # ************* Functions for plotting potential ***************** | ||
| # **************************************************************** | ||
| def potential_energy(self, **kwargs) -> ndarray: | ||
| """Returns the full potential of the circuit evaluated in a grid of points as | ||
| chosen by the user or using default variable ranges. | ||
| Parameters | ||
| ---------- | ||
| θ<index>: | ||
| value(s) for variable :math:`\theta_i` in the potential. | ||
| """ | ||
| periodic_indices = self.var_categories["periodic"] | ||
| discretized_ext_indices = self.var_categories["extended"] | ||
| var_categories = discretized_ext_indices + periodic_indices | ||
| # substituting the parameters | ||
| potential_sym = self.potential_symbolic.subs("I", 1) | ||
| for ext_flux in self.external_fluxes: | ||
| potential_sym = potential_sym.subs(ext_flux, ext_flux * 2 * np.pi) | ||
| # constructing the grids | ||
| parameters = dict.fromkeys( | ||
| [f"θ{index}" for index in var_categories] | ||
| + [var.name for var in self.external_fluxes] | ||
| + [var.name for var in self.symbolic_params] | ||
| ) | ||
| for var_name in kwargs: | ||
| if isinstance(kwargs[var_name], np.ndarray): | ||
| parameters[var_name] = kwargs[var_name] | ||
| elif isinstance(kwargs[var_name], (int, float)): | ||
| parameters[var_name] = kwargs[var_name] | ||
| else: | ||
| raise AttributeError( | ||
| "Only float, int or Numpy ndarray assignments are allowed." | ||
| ) | ||
| for var_name in parameters.keys(): | ||
| if parameters[var_name] is None: | ||
| if var_name in [ | ||
| var.name | ||
| for var in list(self.symbolic_params.keys()) + self.external_fluxes | ||
| ]: | ||
| parameters[var_name] = getattr(self, var_name) | ||
| elif var_name in [f"θ{index}" for index in var_categories]: | ||
| raise AttributeError(var_name + " is not set.") | ||
| # creating a meshgrid for multiple dimensions | ||
| sweep_vars = {} | ||
| for var_name in kwargs: | ||
| if isinstance(kwargs[var_name], np.ndarray): | ||
| sweep_vars[var_name] = kwargs[var_name] | ||
| if len(sweep_vars) > 1: | ||
| sweep_vars.update( | ||
| zip( | ||
| sweep_vars, | ||
| np.meshgrid(*[grid for grid in sweep_vars.values()]), | ||
| ) | ||
| ) | ||
| for var_name in sweep_vars: | ||
| parameters[var_name] = sweep_vars[var_name] | ||
| potential_func = sm.lambdify( | ||
| parameters.keys(), potential_sym, [{"saw": sawtooth_potential}, "numpy"] | ||
| ) | ||
| return potential_func(*parameters.values()) | ||
| def plot_potential(self, **kwargs) -> Tuple[Figure, Axes]: | ||
| r"""Returns the plot of the potential for the circuit instance. Make sure to not | ||
| set more than two variables in the instance.potential to a Numpy array, as the | ||
| the code cannot plot with more than 3 dimensions. | ||
| Parameters | ||
| ---------- | ||
| θ<index>: | ||
| value(s) for the variable :math:`\theta_i` occurring in the potential. | ||
| Returns | ||
| ------- | ||
| Returns a axes and figure for further editing. | ||
| """ | ||
| periodic_indices = self.var_categories["periodic"] | ||
| discretized_ext_indices = self.var_categories["extended"] | ||
| var_categories = discretized_ext_indices + periodic_indices | ||
| # constructing the grids | ||
| parameters = dict.fromkeys( | ||
| [f"θ{index}" for index in var_categories] | ||
| + [var.name for var in self.external_fluxes] | ||
| + [var.name for var in self.symbolic_params] | ||
| ) | ||
| # filtering the plotting options | ||
| plot_kwargs = {} | ||
| list_of_keys = list(kwargs.keys()) | ||
| for key in list_of_keys: | ||
| if key not in parameters: | ||
| plot_kwargs[key] = kwargs[key] | ||
| del kwargs[key] | ||
| sweep_vars = {} | ||
| for var_name in kwargs: | ||
| if isinstance(kwargs[var_name], np.ndarray): | ||
| sweep_vars[var_name] = kwargs[var_name] | ||
| if len(sweep_vars) > 1: | ||
| sweep_vars.update(zip(sweep_vars, np.meshgrid(*list(sweep_vars.values())))) | ||
| for var_name in sweep_vars: | ||
| parameters[var_name] = sweep_vars[var_name] | ||
| if len(sweep_vars) > 2: | ||
| raise AttributeError( | ||
| "Cannot plot with a dimension greater than 3; Only give a maximum of " | ||
| "two grid inputs" | ||
| ) | ||
| potential_energies = self.potential_energy(**kwargs) | ||
| fig, axes = kwargs.get("fig_ax") or plt.subplots() | ||
| if len(sweep_vars) == 1: | ||
| axes.plot(*(list(sweep_vars.values()) + [potential_energies])) | ||
| axes.set_xlabel( | ||
| r"$\theta_{{{}}}$".format( | ||
| get_trailing_number(list(sweep_vars.keys())[0]) | ||
| ) | ||
| ) | ||
| axes.set_ylabel("Potential energy in " + get_units()) | ||
| if len(sweep_vars) == 2: | ||
| contourset = axes.contourf( | ||
| *(list(sweep_vars.values()) + [potential_energies]) | ||
| ) | ||
| var_indices = [ | ||
| get_trailing_number(var_name) for var_name in list(sweep_vars.keys()) | ||
| ] | ||
| axes.set_xlabel(r"$\theta_{{{}}}$".format(var_indices[0])) | ||
| axes.set_ylabel(r"$\theta_{{{}}}$".format(var_indices[1])) | ||
| cbar = plt.colorbar(contourset, ax=axes) | ||
| cbar.set_label("Potential energy in " + get_units()) | ||
| _process_options(fig, axes, **plot_kwargs) | ||
| return fig, axes |
| import functools | ||
| import itertools | ||
| import operator as builtin_op | ||
| import re | ||
| from typing import Any, Callable, Dict, List, Optional, Tuple, Union, TYPE_CHECKING | ||
| if TYPE_CHECKING: | ||
| from scqubits.core.circuit import Subsystem | ||
| import numpy as np | ||
| import qutip as qt | ||
| import sympy as sm | ||
| from sympy import latex | ||
| try: | ||
| from IPython.display import display, Latex | ||
| except ImportError: | ||
| _HAS_IPYTHON = False | ||
| else: | ||
| _HAS_IPYTHON = True | ||
| from scqubits.core.circuit_utils import ( | ||
| is_potential_term, | ||
| get_trailing_number, | ||
| round_symbolic_expr, | ||
| ) | ||
| from scqubits.utils.misc import ( | ||
| flatten_list_recursive, | ||
| list_intersection, | ||
| check_sync_status_circuit, | ||
| unique_elements_in_list, | ||
| ) | ||
| from abc import ABC | ||
| class CircuitSymMethods(ABC): | ||
| @staticmethod | ||
| def _contains_trigonometric_terms(hamiltonian): | ||
| """Check if the hamiltonian contains any trigonometric terms.""" | ||
| trigonometric_operators = [sm.cos, sm.sin, sm.Function("saw", real=True)] | ||
| return any(hamiltonian.atoms(operator) for operator in trigonometric_operators) | ||
| @staticmethod | ||
| def _is_symbol_periodic_charge(sym): | ||
| return sym.name[0] == "n" and sym.name[1:].isnumeric() | ||
| @staticmethod | ||
| def _is_symbol_continuous_charge(sym): | ||
| return sym.name[0] == "Q" and sym.name[1:].isnumeric() | ||
| @staticmethod | ||
| def _is_symbol_phase(sym): | ||
| return sym.name[0] == "θ" and sym.name[1:].isnumeric() | ||
| @staticmethod | ||
| def _find_and_categorize_variable_indices(hamiltonian): | ||
| periodic_var_indices = set( | ||
| get_trailing_number(symbol.name) | ||
| for symbol in hamiltonian.free_symbols | ||
| if CircuitSymMethods._is_symbol_periodic_charge(symbol) | ||
| ) | ||
| extended_var_indices = set( | ||
| get_trailing_number(symbol.name) | ||
| for symbol in hamiltonian.free_symbols | ||
| if CircuitSymMethods._is_symbol_continuous_charge(symbol) | ||
| ) | ||
| phase_var_indices = set( | ||
| get_trailing_number(symbol.name) | ||
| for symbol in hamiltonian.free_symbols | ||
| if CircuitSymMethods._is_symbol_phase(symbol) | ||
| ) | ||
| return periodic_var_indices, extended_var_indices, phase_var_indices | ||
| # @staticmethod | ||
| def _is_expression_purely_harmonic(self, hamiltonian): | ||
| """Method used to check if the hamiltonian is purely harmonic.""" | ||
| # if the hamiltonian contains any cos or sin term, return False | ||
| if self._contains_trigonometric_terms(hamiltonian): | ||
| return False | ||
| # if the hamiltonian contains any charge operator of periodic variables, return false | ||
| ( | ||
| periodic_charge_variable_index, | ||
| extended_charge_variable_index, | ||
| phase_variable_index, | ||
| ) = self._find_and_categorize_variable_indices(hamiltonian) | ||
| if len(periodic_charge_variable_index) > 0: | ||
| return False | ||
| # if the hamiltonian has any DoF where only its charge or flux operator is present, return False | ||
| if extended_charge_variable_index != phase_variable_index: | ||
| return False | ||
| return True | ||
| def _constants_in_subsys( | ||
| self, H_sys: sm.Expr, constants_list: List[sm.Expr] | ||
| ) -> List[sm.Expr]: | ||
| """Returns an expression of constants that belong to the subsystem with the | ||
| Hamiltonian H_sys. | ||
| Parameters | ||
| ---------- | ||
| H_sys: | ||
| subsystem hamiltonian | ||
| Returns | ||
| ------- | ||
| expression of constants belonging to the subsystem | ||
| """ | ||
| constants_subsys_list = [] | ||
| subsys_free_symbols = set(H_sys.free_symbols) | ||
| for term in constants_list: | ||
| if set(term.free_symbols) & subsys_free_symbols == set(term.free_symbols): | ||
| constants_subsys_list.append(term) | ||
| return constants_subsys_list | ||
| def _list_of_constants_from_expr(self, expr: sm.Expr) -> List[sm.Expr]: | ||
| ordered_terms = expr.as_ordered_terms() | ||
| constants = [ | ||
| term | ||
| for term in ordered_terms | ||
| if ( | ||
| set( | ||
| self.external_fluxes | ||
| + self.offset_charges | ||
| + self.free_charges | ||
| + list(self.symbolic_params.keys()) | ||
| + [sm.symbols("I")] | ||
| ) | ||
| & set(term.free_symbols) | ||
| ) | ||
| == set(term.free_symbols) | ||
| ] | ||
| return constants | ||
| def _sym_subsystem_hamiltonian_and_interactions( | ||
| self, | ||
| hamiltonian: sm.Expr, | ||
| subsys_indices: list, | ||
| non_operator_symbols: List[sm.Symbol], | ||
| ): | ||
| systems_sym = [] | ||
| interaction_sym = [] | ||
| constants = self._list_of_constants_from_expr(hamiltonian) | ||
| hamiltonian = self._remove_constants_from_hamiltonian(hamiltonian, constants) | ||
| for subsys_index_list in subsys_indices: | ||
| subsys_index_list = flatten_list_recursive(subsys_index_list) | ||
| H_sys, H_int = self._find_subsys_hamiltonian( | ||
| hamiltonian, subsys_index_list, non_operator_symbols | ||
| ) | ||
| # add the constants that belong to the subsystem | ||
| subsys_const_list = self._constants_in_subsys(H_sys, constants) | ||
| systems_sym.append(H_sys + sum(subsys_const_list)) | ||
| # remove the constants that are already added | ||
| constants = [const for const in constants if const not in subsys_const_list] | ||
| interaction_sym.append(H_int) | ||
| hamiltonian -= H_sys + H_int | ||
| if len(constants) > 0: | ||
| systems_sym[0] += sum(constants) | ||
| return systems_sym, interaction_sym | ||
| def _remove_constants_from_hamiltonian(self, hamiltonian, constants): | ||
| for const in constants: | ||
| hamiltonian -= const | ||
| return hamiltonian | ||
| def _find_subsys_hamiltonian( | ||
| self, hamiltonian, subsys_index_list, non_operator_symbols | ||
| ): | ||
| hamiltonian_terms = hamiltonian.as_ordered_terms() | ||
| H_sys = 0 * sm.symbols("x") | ||
| H_int = 0 * sm.symbols("x") | ||
| for term in hamiltonian_terms: | ||
| term_operator_indices = [ | ||
| get_trailing_number(var_sym.name) | ||
| for var_sym in term.free_symbols | ||
| if var_sym not in non_operator_symbols | ||
| ] | ||
| term_operator_indices_unique = unique_elements_in_list( | ||
| term_operator_indices | ||
| ) | ||
| if len(set(term_operator_indices_unique) - set(subsys_index_list)) == 0: | ||
| H_sys += term | ||
| if ( | ||
| len(set(term_operator_indices_unique) - set(subsys_index_list)) > 0 | ||
| and len(set(term_operator_indices_unique) & set(subsys_index_list)) > 0 | ||
| ): | ||
| H_int += term | ||
| return H_sys, H_int | ||
| @check_sync_status_circuit | ||
| def _evaluate_symbolic_expr(self, sym_expr, bare_esys=None) -> qt.Qobj: | ||
| sym_expr = self._substitute_parameters(sym_expr) | ||
| if sym_expr == 0: | ||
| return 0 | ||
| expr_dict = sym_expr.as_coefficients_dict() | ||
| terms = list(expr_dict.keys()) | ||
| eval_matrix_list = [ | ||
| self._evaluate_term(term, expr_dict[term], bare_esys) for term in terms | ||
| ] | ||
| return sum(eval_matrix_list) | ||
| def _substitute_parameters(self, sym_expr): | ||
| param_symbols = ( | ||
| self.external_fluxes | ||
| + self.offset_charges | ||
| + self.free_charges | ||
| + list(self.symbolic_params.keys()) | ||
| ) | ||
| for param in param_symbols: | ||
| sym_expr = sym_expr.subs(param, getattr(self, param.name)) | ||
| return sym_expr | ||
| def _evaluate_term(self, term, coefficient_sympy, bare_esys): | ||
| if term == 1: | ||
| return self._identity_qobj() * float(coefficient_sympy) | ||
| factors = term.as_ordered_factors() | ||
| factor_op_list = [ | ||
| self._evaluate_factor(factor, bare_esys) for factor in factors | ||
| ] | ||
| operator_list = self._combine_factors(factor_op_list, bare_esys) | ||
| return functools.reduce(builtin_op.mul, operator_list) * float( | ||
| coefficient_sympy | ||
| ) | ||
| def _evaluate_factor(self, factor, bare_esys): | ||
| if any([arg.has(sm.cos) or arg.has(sm.sin) for arg in (1.0 * factor).args]): | ||
| return self._evaluate_matrix_cosine_terms(factor, bare_esys=bare_esys) | ||
| elif any( | ||
| [arg.has(sm.Function("saw", real=True)) for arg in (1.0 * factor).args] | ||
| ): | ||
| return self._evaluate_sawtooth_factor(factor, bare_esys) | ||
| else: | ||
| return self._evaluate_operator_factor(factor) | ||
| def _evaluate_sawtooth_factor(self, factor, bare_esys): | ||
| if not self.hierarchical_diagonalization: | ||
| return self._evaluate_matrix_sawtooth_terms(factor, bare_esys=bare_esys) | ||
| index_subsystem = [ | ||
| self.return_root_child(get_trailing_number(sym.name)) | ||
| for sym in factor.free_symbols | ||
| ] | ||
| if len(np.unique(index_subsystem)) > 1: | ||
| raise Exception( | ||
| "Sawtooth function terms must belong to the same subsystem." | ||
| ) | ||
| operator = index_subsystem[0]._evaluate_matrix_sawtooth_terms(factor) | ||
| return self.identity_wrap_for_hd( | ||
| operator, index_subsystem[0], bare_esys=bare_esys | ||
| ) | ||
| def _evaluate_operator_factor(self, factor): | ||
| power_dict = dict(factor.as_powers_dict()) | ||
| free_sym = list(factor.free_symbols)[0] | ||
| if not self.hierarchical_diagonalization: | ||
| return self.get_operator_by_name(free_sym.name, power=power_dict[free_sym]) | ||
| subsys = self.return_root_child(get_trailing_number(free_sym.name)) | ||
| operator = subsys.get_operator_by_name( | ||
| free_sym.name, power=power_dict[free_sym] | ||
| ) | ||
| return (subsys, operator) | ||
| def _combine_factors(self, factor_op_list, bare_esys): | ||
| operators_per_subsys = {} | ||
| operator_list = [] | ||
| for factor_op in factor_op_list: | ||
| if not isinstance(factor_op, tuple): | ||
| operator_list.append(factor_op) | ||
| continue | ||
| subsys, operator = factor_op | ||
| if subsys not in operators_per_subsys: | ||
| operators_per_subsys[subsys] = [operator] | ||
| else: | ||
| operators_per_subsys[subsys].append(operator) | ||
| operator_list += [ | ||
| self.identity_wrap_for_hd( | ||
| functools.reduce(builtin_op.mul, operators_per_subsys[subsys]), | ||
| subsys, | ||
| bare_esys=bare_esys, | ||
| ) | ||
| for subsys in operators_per_subsys | ||
| ] | ||
| return operator_list | ||
| def _shift_harmonic_oscillator_potential(self, hamiltonian: sm.Expr) -> sm.Expr: | ||
| # shifting the harmonic oscillator potential to the point of external fluxes | ||
| flux_shift_vars = {} | ||
| for var_index in self.var_categories["extended"]: | ||
| flux_shift_vars[var_index] = sm.symbols("Δθ" + str(var_index)) | ||
| hamiltonian = hamiltonian.replace( | ||
| sm.symbols(f"θ{var_index}"), | ||
| sm.symbols(f"θ{var_index}") + flux_shift_vars[var_index], | ||
| ) # substituting the flux offset variable offsets to collect the | ||
| # coefficients later | ||
| hamiltonian = hamiltonian.expand() | ||
| flux_shift_equations = [ | ||
| hamiltonian.coeff(f"θ{var_index}").subs( | ||
| [(f"θ{i}", 0) for i in self.var_categories["extended"]] | ||
| ) | ||
| for var_index in flux_shift_vars.keys() | ||
| ] # finding the coefficients of the linear terms | ||
| A, b = sm.linear_eq_to_matrix( | ||
| flux_shift_equations, tuple(flux_shift_vars.values()) | ||
| ) | ||
| flux_shifts = sm.linsolve( | ||
| (A, b), tuple(flux_shift_vars.values()) | ||
| ) # solving for the flux offsets | ||
| if len(flux_shifts) != 0: | ||
| flux_shifts = list(list(flux_shifts)[0]) | ||
| else: | ||
| flux_shifts = [] | ||
| flux_shifts_dict = dict(zip(list(flux_shift_vars.values()), list(flux_shifts))) | ||
| hamiltonian = hamiltonian.subs( | ||
| list(flux_shifts_dict.items()) | ||
| ) # substituting the flux offsets to remove the linear terms | ||
| hamiltonian = hamiltonian.subs( | ||
| [(var, 0) for var in flux_shift_vars.values()] | ||
| ) # removing the shift vars from the Hamiltonian | ||
| # remove constants from Hamiltonian | ||
| hamiltonian -= hamiltonian.as_coefficients_dict()[1] | ||
| return round_symbolic_expr(hamiltonian.expand(), 16) | ||
| # * ########################################################################## | ||
| def _generate_sym_potential(self): | ||
| # and bringing the potential into the same form as for the class Circuit | ||
| potential_symbolic = 0 * sm.symbols("x") | ||
| for term in self.hamiltonian_symbolic.as_ordered_terms(): | ||
| if is_potential_term(term): | ||
| potential_symbolic += term | ||
| for i in self.dynamic_var_indices: | ||
| potential_symbolic = ( | ||
| potential_symbolic.replace( | ||
| sm.symbols(f"cosθ{i}"), sm.cos(1.0 * sm.symbols(f"θ{i}")) | ||
| ) | ||
| .replace(sm.symbols(f"sinθ{i}"), sm.sin(1.0 * sm.symbols(f"θ{i}"))) | ||
| .subs(sm.symbols("I"), 1 / (2 * np.pi)) | ||
| ) | ||
| return potential_symbolic | ||
| def _is_mat_mul_replacement_necessary(self, term): | ||
| return ( | ||
| set(self.var_categories["extended"]) | ||
| & set([get_trailing_number(str(i)) for i in term.free_symbols]) | ||
| ) and "*" in str(term) | ||
| def _replace_mat_mul_operator(self, term: sm.Expr): | ||
| if not self._is_mat_mul_replacement_necessary(term): | ||
| return str(term) | ||
| if self.ext_basis == "discretized": | ||
| term_string = str(term) | ||
| term_var_categories = [ | ||
| get_trailing_number(str(i)) for i in term.free_symbols | ||
| ] | ||
| if len(set(term_var_categories) & set(self.var_categories["extended"])) > 1: | ||
| if all(["Q" in var.name for var in term.free_symbols]): | ||
| term_string = str(term).replace( | ||
| "*", "@" | ||
| ) # replacing all the * with @ | ||
| elif self.ext_basis == "harmonic": | ||
| # replace ** with np.matrix_power | ||
| if "**" in str(term): | ||
| operators = [ | ||
| match.replace("**", "") | ||
| for match in re.findall(r"[^*]+\*{2}", str(term), re.MULTILINE) | ||
| ] | ||
| exponents = re.findall(r"(?<=\*{2})\d", str(term), re.MULTILINE) | ||
| new_string_list = [] | ||
| for idx, operator in enumerate(operators): | ||
| if get_trailing_number(operator) in self.var_categories["extended"]: | ||
| new_string_list.append( | ||
| f"matrix_power({operator},{exponents[idx]})" | ||
| ) | ||
| else: | ||
| new_string_list.append(operator + "**" + exponents[idx]) | ||
| term_string = "*".join(new_string_list) | ||
| else: | ||
| term_string = str(term) | ||
| # replace * with @ in the entire term | ||
| if len(term.free_symbols) > 1: | ||
| term_string = re.sub( | ||
| r"(?<=[^*])\*(?!\*)", "@", term_string, re.MULTILINE | ||
| ) | ||
| return term_string | ||
| def _generate_hamiltonian_sym_for_numerics( | ||
| self, | ||
| hamiltonian: Optional[sm.Expr] = None, | ||
| return_exprs=False, | ||
| ): | ||
| """Generates a symbolic expression which is ready for numerical evaluation | ||
| starting from the expression stored in the attribute :attr:`Circuit.hamiltonian_symbolic`. | ||
| Stores the result in the attribute :attr:`Circuit._hamiltonian_sym_for_numerics`. | ||
| """ | ||
| hamiltonian = hamiltonian or ( | ||
| self.hamiltonian_symbolic.expand() | ||
| ) # applying expand is critical; otherwise the replacement of p^2 with ps2 | ||
| # would not succeed | ||
| if self.ext_basis == "discretized": | ||
| # marking the squared momentum operators with a separate symbol | ||
| for i in self.var_categories["extended"]: | ||
| hamiltonian = hamiltonian.replace( | ||
| sm.symbols(f"Q{i}") ** 2, sm.symbols("Qs" + str(i)) | ||
| ) | ||
| # associate an identity matrix with the external flux vars | ||
| for ext_flux in self.external_fluxes: | ||
| hamiltonian = hamiltonian.subs( | ||
| ext_flux, ext_flux * sm.symbols("I") * 2 * np.pi | ||
| ) | ||
| # associate an identity matrix with offset and free charge vars | ||
| for charge_var in self.offset_charges + self.free_charges: | ||
| hamiltonian = hamiltonian.subs(charge_var, charge_var * sm.symbols("I")) | ||
| # finding the cosine terms | ||
| cos_terms = sum( | ||
| [term for term in hamiltonian.as_ordered_terms() if "cos" in str(term)] | ||
| ) | ||
| if return_exprs: | ||
| return hamiltonian, cos_terms | ||
| setattr(self, "_hamiltonian_sym_for_numerics", hamiltonian) | ||
| setattr(self, "junction_potential", cos_terms) | ||
| def _get_eval_hamiltonian_string(self, H: sm.Expr) -> str: | ||
| """Returns the string which defines the expression for Hamiltonian in harmonic | ||
| oscillator basis.""" | ||
| expr_dict = H.as_coefficients_dict() | ||
| # removing zero terms | ||
| expr_dict = {key: expr_dict[key] for key in expr_dict if expr_dict[key] != 0} | ||
| terms_list = list(expr_dict.keys()) | ||
| coeff_list = list(expr_dict.values()) | ||
| H_string = "" | ||
| for idx, term in enumerate(terms_list): | ||
| term_string = f"{coeff_list[idx]}*{self._replace_mat_mul_operator(term)}" | ||
| if float(coeff_list[idx]) > 0: | ||
| term_string = "+" + term_string | ||
| H_string += term_string | ||
| # replace all position, sin and cos operators with methods | ||
| H_string = re.sub(r"(?P<x>(θ\d)|(cosθ\d))", r"\g<x>_operator()", H_string) | ||
| # replace all other operators with methods | ||
| operator_symbols_list = flatten_list_recursive( | ||
| [ | ||
| ( | ||
| list(short_op_dict.values()) | ||
| if isinstance(short_op_dict, dict) | ||
| else short_op_dict | ||
| ) | ||
| for short_op_dict in list(self.vars.values()) | ||
| ] | ||
| ) | ||
| operator_name_list = [symbol.name for symbol in operator_symbols_list] | ||
| for operator_name in operator_name_list: | ||
| if "θ" not in operator_name: | ||
| H_string = H_string.replace( | ||
| operator_name, operator_name + "_operator()" | ||
| ) | ||
| return H_string | ||
| def _qutip_parameter_function_factory( | ||
| self, | ||
| parameter_expr: sm.Expr, | ||
| free_var_func_dict: Dict[str, Callable], | ||
| lambdify_func: Callable, | ||
| ) -> Callable: | ||
| def parameter_func(t, args): | ||
| return lambdify_func( | ||
| *[ | ||
| free_var_func_dict[sym.name](t, args) | ||
| for sym in parameter_expr.free_symbols | ||
| ] | ||
| ) | ||
| return parameter_func | ||
| @check_sync_status_circuit | ||
| def hamiltonian_for_qutip_dynamics( | ||
| self, | ||
| free_var_func_dict: Dict[str, Callable], | ||
| prefactor: float = 1.0, | ||
| extra_terms: Optional[str] = None, | ||
| ) -> Tuple[ | ||
| List[Union[qt.Qobj, Tuple[qt.Qobj, Callable]]], sm.Expr, Dict[qt.Qobj, sm.Expr] | ||
| ]: | ||
| """ | ||
| Returns the Hamiltonian in a format amenable to be forwarded to mesolve in Qutip. Also returns the symbolic expressions of time independent and time dependent terms of the Hamiltonian, which can be used for reference. `free_var_func_dict` is a dictionary with key-value pair `{"var": f}`, where `f` is a function returning the value of the variable `var` at time `t`. If one has extra terms to be added to the Hamiltonian (for instance, charge driving a fluxonium where there is no offset charge) they can be passed as a string in `extra_terms`. | ||
| For example, to get the Hamiltonian for a circuit where Φ1 is the time varying parameter, this method can be called in the following way:: | ||
| def flux_t(t, args): | ||
| return 0.5 + 0.02*np.sin(t*2) | ||
| def ng_t(t, args): | ||
| return 0.5 + 0.02*np.cos(t*2) | ||
| def EJ_t(t, args): | ||
| return (1-np.exp(-t/1))*0.2 | ||
| free_var_func_dict = {"Φ1": flux_t, "EJ": EJ_t, "ng": ng_t} | ||
| mesolve_input_H = self.hamiltonian_for_qutip_dynamics(free_var_func_dict, extra_terms="0.1*ng*Q1") | ||
| Parameters | ||
| ---------- | ||
| free_var_func_dict: | ||
| Dict, as defined in the description above | ||
| prefactor: | ||
| prefactor with which the Hamiltonian and corresponding operators are multiplied, useful to set it to `2*np.pi` for some qutip simulations | ||
| extra_terms: | ||
| a string which will be converted into sympy expression, containing terms which are not present in the Circuit Hamiltonian. It is useful to define custom drive operators. | ||
| """ | ||
| free_var_names = list(free_var_func_dict.keys()) | ||
| free_var_symbols = [sm.symbols(sym_name) for sym_name in free_var_names] | ||
| fixed_hamiltonian = 0 * sm.symbols("x") | ||
| time_varying_hamiltonian = [] | ||
| # adding extra terms to the Hamiltonian | ||
| if extra_terms: | ||
| extra_terms_sym = sm.parse_expr(extra_terms) | ||
| for extra_sym in extra_terms_sym.free_symbols: | ||
| if ( | ||
| extra_sym not in self.hamiltonian_symbolic.free_symbols | ||
| and extra_sym not in free_var_symbols | ||
| ): | ||
| raise Exception(f"{extra_sym.name} is unknown.") | ||
| else: | ||
| extra_terms_sym = 0 | ||
| sym_hamiltonian = self._hamiltonian_sym_for_numerics + extra_terms_sym | ||
| sym_hamiltonian = sym_hamiltonian.subs("I", 1).expand() | ||
| expr_dict = sym_hamiltonian.expand().as_coefficients_dict() | ||
| terms = list(expr_dict.keys()) | ||
| time_dep_terms = {} | ||
| for term in terms: | ||
| if len(list_intersection(list(term.free_symbols), free_var_symbols)) == 0: | ||
| fixed_hamiltonian = fixed_hamiltonian + term * expr_dict[term] | ||
| continue | ||
| # if the term does have a free variable | ||
| # expand trigonometrically | ||
| should_trig_expand = any( | ||
| [ | ||
| (free_sym in term.free_symbols and term.coeff(free_sym) == 0) | ||
| for free_sym in free_var_symbols | ||
| ] | ||
| ) | ||
| term_expanded = term.expand(trig=should_trig_expand) | ||
| term_expr_dict = term_expanded.as_coefficients_dict() | ||
| terms_in_term = list(term_expr_dict.keys()) | ||
| for inner_term in terms_in_term: | ||
| operator_expr, parameter_expr = inner_term.as_independent( | ||
| *free_var_symbols, as_Mul=True | ||
| ) | ||
| if parameter_expr in time_dep_terms: | ||
| time_dep_terms[parameter_expr] = ( | ||
| operator_expr * expr_dict[term] * term_expr_dict[inner_term] | ||
| + time_dep_terms[parameter_expr] | ||
| ) | ||
| else: | ||
| time_dep_terms[parameter_expr] = round_symbolic_expr( | ||
| operator_expr * expr_dict[term] * term_expr_dict[inner_term], 13 | ||
| ) | ||
| for parameter_expr in time_dep_terms: | ||
| # separating the time independent constants | ||
| for sym in parameter_expr.free_symbols: | ||
| if sym not in free_var_symbols: | ||
| parameter_expr = parameter_expr.subs(sym, getattr(self, sym.name)) | ||
| lambdify_func = sm.lambdify( | ||
| list(parameter_expr.free_symbols), parameter_expr, "numpy" | ||
| ) | ||
| parameter_func = self._qutip_parameter_function_factory( | ||
| parameter_expr, free_var_func_dict, lambdify_func | ||
| ) | ||
| operator_matrix = ( | ||
| self._evaluate_symbolic_expr(time_dep_terms[parameter_expr]) * prefactor | ||
| ) # also multiplying the constant to the operator | ||
| if operator_matrix == 0: | ||
| continue | ||
| time_varying_hamiltonian.append([operator_matrix, parameter_func]) | ||
| fixed_hamiltonian = fixed_hamiltonian.subs("I", 1) | ||
| return ( | ||
| [self._evaluate_symbolic_expr(fixed_hamiltonian) * prefactor] | ||
| + time_varying_hamiltonian, | ||
| fixed_hamiltonian, | ||
| dict((val, key) for key, val in time_dep_terms.items()), | ||
| ) | ||
| # **************************************************************** | ||
| # ***** Functions for pretty display of symbolic expressions ***** | ||
| # **************************************************************** | ||
| @staticmethod | ||
| def print_expr_in_latex(expr: Union[sm.Expr, List["sm.Equality"]]) -> None: | ||
| """Print a sympy expression or a list of equalities in LaTeX. | ||
| Parameters | ||
| ---------- | ||
| expr: | ||
| a sympy expressions or a list of equalities | ||
| """ | ||
| if isinstance(expr, sm.Expr): | ||
| display(Latex("$ " + sm.printing.latex(expr) + " $")) | ||
| elif isinstance(expr, list): | ||
| equalities_in_latex = "$ " | ||
| for eqn in expr: | ||
| equalities_in_latex += sm.printing.latex(eqn) + r" \\\ " | ||
| equalities_in_latex = equalities_in_latex[:-4] + " $" | ||
| display(Latex(equalities_in_latex)) | ||
| def __repr__(self) -> str: | ||
| # string to describe the Circuit | ||
| return self._id_str | ||
| def _repr_latex_(self): | ||
| """Describes the Circuit instance, its parameters, and the symbolic | ||
| Hamiltonian.""" | ||
| # string to describe the Circuit | ||
| if not _HAS_IPYTHON: | ||
| return self._id_str | ||
| # Hamiltonian string | ||
| H_latex_str = ( | ||
| "$H=" + sm.printing.latex(self.sym_hamiltonian(return_expr=True)) + "$" | ||
| ) | ||
| # describe the variables | ||
| cutoffs_dict = self.cutoffs_dict() | ||
| var_str = "Operators (flux, charge) - cutoff: " | ||
| if len(self.var_categories["periodic"]) > 0: | ||
| var_str += " \n Discrete Charge Basis: " | ||
| for var_index in self.var_categories["periodic"]: | ||
| var_str += ( | ||
| f"$(θ{var_index}, n{var_index}) - {cutoffs_dict[var_index]}$, " | ||
| ) | ||
| var_str_discretized = " \nDiscretized Phi basis: " | ||
| var_str_harmonic = " \nHarmonic oscillator basis: " | ||
| for var_index in self.var_categories["extended"]: | ||
| var_index_basis = self._basis_for_var_index(var_index) | ||
| if var_index_basis == "discretized": | ||
| var_str_discretized += ( | ||
| f"$(θ{var_index}, Q{var_index}) - {cutoffs_dict[var_index]}$, " | ||
| ) | ||
| if var_index_basis == "harmonic": | ||
| var_str_harmonic += ( | ||
| f"$(θ{var_index}, Q{var_index}) - {cutoffs_dict[var_index]}$, " | ||
| ) | ||
| if var_str_discretized == " \nDiscretized Phi basis: ": | ||
| var_str_discretized = "" | ||
| if var_str_harmonic == " \nHarmonic oscillator basis: ": | ||
| var_str_harmonic = "" | ||
| display(Latex(H_latex_str)) | ||
| display( | ||
| Latex( | ||
| var_str | ||
| + var_str_discretized | ||
| + (" \n" if var_str_discretized else "") | ||
| + var_str_harmonic | ||
| ) | ||
| ) | ||
| # symbolic parameters | ||
| if len(self.symbolic_params) > 0: | ||
| sym_params_str = "Symbolic parameters (symbol, default value): " | ||
| for sym, val in self.symbolic_params.items(): | ||
| sym_params_str += f"$({sym.name}, {val})$, " | ||
| display(Latex(sym_params_str)) | ||
| if len(self.external_fluxes) > 0: | ||
| sym_params_str = "External fluxes (symbol, default value): " | ||
| for sym in self.external_fluxes: | ||
| sym_params_str += f"$({sym.name}, {getattr(self, sym.name)})$, " | ||
| display(Latex(sym_params_str)) | ||
| if len(self.offset_charges) > 0: | ||
| sym_params_str = "Offset charges (symbol, default value): " | ||
| for sym in self.offset_charges: | ||
| sym_params_str += f"$({sym.name}, {getattr(self, sym.name)})$, " | ||
| display(Latex(sym_params_str)) | ||
| if len(self.free_charges) > 0: | ||
| sym_params_str = "Free charges (symbol, default value): " | ||
| for sym in self.free_charges: | ||
| sym_params_str += f"$({sym.name}, {getattr(self, sym.name)})$, " | ||
| display(Latex(sym_params_str)) | ||
| if self.hierarchical_diagonalization: | ||
| display(Latex(f"System hierarchy: {self.system_hierarchy}")) | ||
| display(Latex(f"Truncated Dimensions: {self.subsystem_trunc_dims}")) | ||
| def _make_expr_human_readable(self, expr: sm.Expr, float_round: int = 6) -> sm.Expr: | ||
| """Method returns a user readable symbolic expression for the current instance. | ||
| Parameters | ||
| ---------- | ||
| expr: | ||
| A symbolic sympy expression | ||
| float_round: | ||
| Number of digits after the decimal to which floats are rounded | ||
| Returns | ||
| ------- | ||
| Sympy expression which is simplified to make it human readable. | ||
| """ | ||
| expr_modified = expr | ||
| # rounding the decimals in the coefficients | ||
| # citation: | ||
| # https://stackoverflow.com/questions/43804701/round-floats-within-an-expression | ||
| # accepted answer | ||
| for term in sm.preorder_traversal(expr): | ||
| if isinstance(term, sm.Float): | ||
| expr_modified = expr_modified.subs(term, round(term, float_round)) | ||
| for var_index in self.dynamic_var_indices: | ||
| # replace sinθ with sin(..) and similarly with cos | ||
| expr_modified = ( | ||
| expr_modified.replace( | ||
| sm.symbols(f"cosθ{var_index}"), | ||
| sm.cos(1.0 * sm.symbols(f"θ{var_index}")), | ||
| ) | ||
| .replace( | ||
| sm.symbols(f"sinθ{var_index}"), | ||
| sm.sin(1.0 * sm.symbols(f"θ{var_index}")), | ||
| ) | ||
| .replace( | ||
| (1.0 * sm.symbols(f"θ{var_index}")), | ||
| (sm.symbols(f"θ{var_index}")), | ||
| ) | ||
| ) | ||
| # replace Qs with Q^2 etc | ||
| expr_modified = expr_modified.replace( | ||
| sm.symbols("Qs" + str(var_index)), sm.symbols(f"Q{var_index}") ** 2 | ||
| ) | ||
| expr_modified = expr_modified.replace( | ||
| sm.symbols("ng" + str(var_index)), sm.symbols("n_g" + str(var_index)) | ||
| ) | ||
| # replace I by 1 | ||
| expr_modified = expr_modified.replace(sm.symbols("I"), 1) | ||
| for ext_flux_var in self.external_fluxes: | ||
| # removing 1.0 decimals from flux vars | ||
| expr_modified = expr_modified.replace(1.0 * ext_flux_var, ext_flux_var) | ||
| return expr_modified | ||
| def sym_potential( | ||
| self, float_round: int = 6, print_latex: bool = False, return_expr: bool = False | ||
| ) -> Union[sm.Expr, None]: | ||
| """Method prints a user readable symbolic potential for the current instance. | ||
| Parameters | ||
| ---------- | ||
| float_round: | ||
| Number of digits after the decimal to which floats are rounded | ||
| print_latex: | ||
| if set to True, the expression is additionally printed as LaTeX code | ||
| return_expr: | ||
| if set to True, all printing is suppressed and the function will silently | ||
| return the sympy expression | ||
| """ | ||
| potential = self._make_expr_human_readable( | ||
| self.potential_symbolic, float_round=float_round | ||
| ) | ||
| for external_flux in self.external_fluxes: | ||
| potential = potential.replace( | ||
| external_flux, | ||
| sm.symbols( | ||
| "(2π" + "Φ_{" + str(get_trailing_number(str(external_flux))) + "})" | ||
| ), | ||
| ) | ||
| if print_latex: | ||
| print(latex(potential)) | ||
| if _HAS_IPYTHON: | ||
| self.print_expr_in_latex(potential) | ||
| else: | ||
| print(potential) | ||
| def sym_hamiltonian( | ||
| self, | ||
| subsystem_index: Optional[int] = None, | ||
| float_round: int = 6, | ||
| print_latex: bool = False, | ||
| return_expr: bool = False, | ||
| ) -> Union[sm.Expr, None]: | ||
| """Prints a user readable symbolic Hamiltonian for the current instance. | ||
| Parameters | ||
| ---------- | ||
| subsystem_index: | ||
| when set to an index, the Hamiltonian for the corresponding subsystem is | ||
| returned. | ||
| float_round: | ||
| Number of digits after the decimal to which floats are rounded | ||
| print_latex: | ||
| if set to True, the expression is additionally printed as LaTeX code | ||
| return_expr: | ||
| if set to True, all printing is suppressed and the function will silently | ||
| return the sympy expression | ||
| """ | ||
| if subsystem_index is not None: | ||
| if not self.hierarchical_diagonalization: | ||
| raise Exception( | ||
| "Hierarchical diagonalization was not enabled. Hence there " | ||
| "are no identified subsystems addressable by " | ||
| "subsystem_index." | ||
| ) | ||
| # start with the raw system hamiltonian | ||
| sym_hamiltonian = self._make_expr_human_readable( | ||
| self.subsystems[subsystem_index].hamiltonian_symbolic.expand(), | ||
| float_round=float_round, | ||
| ) | ||
| # create PE symbolic expressions | ||
| sym_hamiltonian_PE = self._make_expr_human_readable( | ||
| self.subsystems[subsystem_index].potential_symbolic.expand(), | ||
| float_round=float_round, | ||
| ) | ||
| # obtain the KE of hamiltonian | ||
| pot_symbols = ( | ||
| self.external_fluxes | ||
| + [ | ||
| sm.symbols("θ" + str(idx)) | ||
| for idx in self.var_categories["extended"] | ||
| ] | ||
| + [ | ||
| sm.symbols("θ" + str(idx)) | ||
| for idx in self.var_categories["periodic"] | ||
| ] | ||
| ) | ||
| sym_hamiltonian_KE = 0 * sm.Symbol("x") | ||
| for term in sym_hamiltonian.args: | ||
| if term.free_symbols.isdisjoint(pot_symbols): | ||
| sym_hamiltonian_KE = sm.Add(sym_hamiltonian_KE, term) | ||
| # add a symbolic 2pi | ||
| for external_flux in self.external_fluxes: | ||
| sym_hamiltonian_PE = self._make_expr_human_readable( | ||
| sym_hamiltonian_PE.replace( | ||
| external_flux, | ||
| sm.symbols( | ||
| "(2π" | ||
| + "Φ_{" | ||
| + str(get_trailing_number(str(external_flux))) | ||
| + "})" | ||
| ), | ||
| ), | ||
| float_round=float_round, | ||
| ) | ||
| # substitute free charges | ||
| for free_charge in self.free_charges: | ||
| sym_hamiltonian_PE = sym_hamiltonian_PE.subs( | ||
| free_charge, getattr(self, free_charge.name) | ||
| ) | ||
| # obtain system symbolic hamiltonian by glueing KE and PE | ||
| sym_hamiltonian = sm.Add( | ||
| sym_hamiltonian_KE, sym_hamiltonian_PE, evaluate=False | ||
| ) | ||
| else: | ||
| # create KE and PE symbolic expressions | ||
| sym_hamiltonian = self._make_expr_human_readable( | ||
| self.hamiltonian_symbolic.expand(), | ||
| float_round=float_round, | ||
| ) | ||
| # substitute free charges | ||
| for free_charge in self.free_charges: | ||
| sym_hamiltonian = sym_hamiltonian.subs( | ||
| free_charge, getattr(self, free_charge.name) | ||
| ) | ||
| pot_symbols = ( | ||
| self.external_fluxes | ||
| + [ | ||
| sm.symbols("θ" + str(idx)) | ||
| for idx in self.var_categories["extended"] | ||
| ] | ||
| + [ | ||
| sm.symbols("θ" + str(idx)) | ||
| for idx in self.var_categories["periodic"] | ||
| ] | ||
| ) | ||
| sym_hamiltonian_KE = 0 * sm.Symbol("x") | ||
| for term in sym_hamiltonian.args: | ||
| if term.free_symbols.isdisjoint(pot_symbols): | ||
| sym_hamiltonian_KE = sm.Add(sym_hamiltonian_KE, term) | ||
| sym_hamiltonian_PE = self._make_expr_human_readable( | ||
| self.potential_symbolic.expand(), float_round=float_round | ||
| ) | ||
| # add a 2pi coefficient in front of external fluxes, since the the external | ||
| # fluxes are measured in 2pi numerically | ||
| for external_flux in self.external_fluxes: | ||
| sym_hamiltonian_PE = sym_hamiltonian_PE.replace( | ||
| external_flux, | ||
| sm.symbols( | ||
| "(2π" | ||
| + "Φ_{" | ||
| + str(get_trailing_number(str(external_flux))) | ||
| + "})" | ||
| ), | ||
| ) | ||
| # add the KE and PE and suppress the evaluation | ||
| sym_hamiltonian = sm.Add( | ||
| sym_hamiltonian_KE, sym_hamiltonian_PE, evaluate=False | ||
| ) | ||
| if return_expr: | ||
| return sym_hamiltonian | ||
| if print_latex: | ||
| print(latex(sym_hamiltonian)) | ||
| if _HAS_IPYTHON: | ||
| self.print_expr_in_latex(sym_hamiltonian) | ||
| else: | ||
| print(sym_hamiltonian) | ||
| def sym_interaction( | ||
| self, | ||
| subsystem_indices: Tuple[int], | ||
| float_round: int = 6, | ||
| print_latex: bool = False, | ||
| return_expr: bool = False, | ||
| ) -> Union[sm.Expr, None]: | ||
| """Print the interaction between any set of subsystems for the current instance. | ||
| It would print the interaction terms having operators from all the subsystems | ||
| mentioned in the tuple. | ||
| Parameters | ||
| ---------- | ||
| subsystem_indices: | ||
| Tuple of subsystem indices | ||
| float_round: | ||
| Number of digits after the decimal to which floats are rounded | ||
| print_latex: | ||
| if set to True, the expression is additionally printed as LaTeX code | ||
| return_expr: | ||
| if set to True, all printing is suppressed and the function will silently | ||
| return the sympy expression | ||
| """ | ||
| interaction = sm.symbols("x") * 0 | ||
| for subsys_index_pair in itertools.combinations(subsystem_indices, 2): | ||
| for term in self.subsystem_interactions[ | ||
| min(subsys_index_pair) | ||
| ].as_ordered_terms(): | ||
| term_mod = term.subs( | ||
| [ | ||
| (symbol, 1) | ||
| for symbol in self.external_fluxes | ||
| + self.offset_charges | ||
| + self.free_charges | ||
| + list(self.symbolic_params.keys()) | ||
| + [sm.symbols("I")] | ||
| ] | ||
| ) | ||
| interaction_var_indices = [ | ||
| self.get_subsystem_index(get_trailing_number(symbol.name)) | ||
| for symbol in term_mod.free_symbols | ||
| ] | ||
| if np.array_equal( | ||
| np.sort(interaction_var_indices), np.sort(subsystem_indices) | ||
| ): | ||
| interaction += term | ||
| for external_flux in self.external_fluxes: | ||
| interaction = self._make_expr_human_readable( | ||
| interaction.replace(external_flux, external_flux / (2 * np.pi)), | ||
| float_round=float_round, | ||
| ) | ||
| interaction = interaction.replace( | ||
| external_flux, | ||
| sm.symbols( | ||
| "(2π" + "Φ_{" + str(get_trailing_number(str(external_flux))) + "})" | ||
| ), | ||
| ) | ||
| if return_expr: | ||
| return interaction | ||
| if print_latex: | ||
| print(latex(interaction)) | ||
| if _HAS_IPYTHON: | ||
| self.print_expr_in_latex(interaction) | ||
| else: | ||
| print(interaction) | ||
| def operator_names_in_hamiltonian_symbolic(self) -> List[str]: | ||
| """Returns a list of the names (strings) of all operators occurring in the | ||
| symbolic Hamiltonian.""" | ||
| return [ | ||
| symbol.name | ||
| for symbol in self.hamiltonian_symbolic.free_symbols | ||
| if ("ng" not in symbol.name and "Φ" not in symbol.name) | ||
| and symbol not in self.symbolic_params | ||
| ] | ||
| def offset_charge_transformation(self) -> None: | ||
| """Prints the variable transformation between offset charges of transformed | ||
| variables and the node charges.""" | ||
| if not hasattr(self, "symbolic_circuit"): | ||
| raise Exception( | ||
| f"{self._id_str} instance is not generated from a SymbolicCircuit instance, and hence does not have any associated branches." | ||
| ) | ||
| trans_mat = np.linalg.inv(self.transformation_matrix.T) | ||
| node_offset_charge_vars = [ | ||
| sm.symbols(f"q_n{index}") | ||
| for index in range( | ||
| 1, len(self.symbolic_circuit.nodes) - self.is_grounded + 1 | ||
| ) | ||
| ] | ||
| periodic_offset_charge_vars = [ | ||
| sm.symbols(f"ng{index}") | ||
| for index in self.symbolic_circuit.var_categories["periodic"] | ||
| ] | ||
| periodic_offset_charge_eqns = [] | ||
| for idx, node_var in enumerate(periodic_offset_charge_vars): | ||
| periodic_offset_charge_eqns.append( | ||
| self._make_expr_human_readable( | ||
| sm.Eq( | ||
| periodic_offset_charge_vars[idx], | ||
| np.sum(trans_mat[idx, :] * node_offset_charge_vars), | ||
| ) | ||
| ) | ||
| ) | ||
| if _HAS_IPYTHON: | ||
| self.print_expr_in_latex(periodic_offset_charge_eqns) | ||
| else: | ||
| print(periodic_offset_charge_eqns) |
| import copy | ||
| import itertools | ||
| import warnings | ||
| from typing import Any, Dict, List, Optional, Set, Tuple, Union | ||
| import numpy as np | ||
| from numpy import ndarray | ||
| import scipy as sp | ||
| import sympy | ||
| import sympy as sm | ||
| from sympy import symbols, Symbol | ||
| from scqubits.core.circuit_utils import ( | ||
| round_symbolic_expr, | ||
| _capacitance_variable_for_branch, | ||
| _junction_order, | ||
| get_trailing_number, | ||
| ) | ||
| import scqubits.io_utils.fileio_serializers as serializers | ||
| import scqubits.settings as settings | ||
| from itertools import chain | ||
| from scqubits.utils.misc import ( | ||
| flatten_list_recursive, | ||
| unique_elements_in_list, | ||
| ) | ||
| from scqubits.core.circuit_input import ( | ||
| remove_comments, | ||
| remove_branchline, | ||
| strip_empty_lines, | ||
| parse_code_line, | ||
| process_param, | ||
| ) | ||
| from abc import ABC | ||
| class Node: | ||
| """Class representing a circuit node, and handled by `Circuit`. The attribute | ||
| `branches` is a list of `Branch` objects containing all branches connected to the | ||
| node. | ||
| Parameters | ||
| ---------- | ||
| id: int | ||
| integer identifier of the node | ||
| marker: int | ||
| An internal attribute used to group nodes and identify sub-circuits in the | ||
| method independent_modes. | ||
| """ | ||
| def __init__(self, index: int, marker: int = 0): | ||
| self.index: int = index | ||
| self.marker: int = marker | ||
| self._init_params: Dict[str, int] = {"id": self.index, "marker": self.marker} | ||
| self.branches: List[Branch] = [] | ||
| def __str__(self) -> str: | ||
| return "Node {}".format(self.index) | ||
| def __repr__(self) -> str: | ||
| return "Node({})".format(self.index) | ||
| def connected_nodes(self, branch_type: str) -> List["Node"]: | ||
| """Returns a list of all nodes directly connected by branches to the current | ||
| node, either considering all branches or a specified `branch_type` - ("C", "L", "JJ", "all") for capacitive, inductive, Josephson junction, or all types of branches. | ||
| """ | ||
| result = [] | ||
| if branch_type == "all": | ||
| branch_list = self.branches | ||
| else: | ||
| branch_list = [ | ||
| branch for branch in self.branches if branch.type == branch_type | ||
| ] | ||
| for branch in branch_list: | ||
| if branch.nodes[0].index == self.index: | ||
| result.append(branch.nodes[1]) | ||
| else: | ||
| result.append(branch.nodes[0]) | ||
| return result | ||
| def is_ground(self) -> bool: | ||
| """Returns a bool if the node is a ground node (Node with index set to 0).""" | ||
| return True if self.index == 0 else False | ||
| def __deepcopy__(self, memo): | ||
| cls = self.__class__ | ||
| result = cls.__new__(cls) | ||
| memo[id(self)] = result | ||
| for k, v in self.__dict__.items(): | ||
| setattr(result, k, copy.deepcopy(v, memo)) | ||
| return result | ||
| class Branch: | ||
| """Class describing a circuit branch, used in the Circuit class. | ||
| Parameters | ||
| ---------- | ||
| n_i: | ||
| initial `Node` of the branch | ||
| n_f: | ||
| final `Node` of the branch | ||
| branch_type: | ||
| is the type of this Branch, example `"C"`, `"JJ"` or `"L"` | ||
| parameters: | ||
| list of parameters for the branch, namely for | ||
| capacitance: `[EC]`; | ||
| for inductance: `[10]`; | ||
| for Josephson Junction: `[EJ, 1]` | ||
| aux_params: | ||
| Dictionary of auxiliary parameters which map a symbol from the input file a numeric parameter. | ||
| Examples | ||
| -------- | ||
| `Branch("C", Node(1, 0), Node(2, 0))` is a capacitive branch connecting the nodes with indices 0 and 1. | ||
| """ | ||
| def __init__( | ||
| self, | ||
| n_i: Node, | ||
| n_f: Node, | ||
| branch_type: str, | ||
| parameters: List[Union[float, Symbol, int]], | ||
| index: Optional[int] = None, | ||
| aux_params: Dict[Symbol, float] = {}, | ||
| ): | ||
| self.nodes = (n_i, n_f) | ||
| self.type = branch_type | ||
| self.index = index | ||
| # store info of current branch inside the provided nodes | ||
| # setting the parameters if it is provided | ||
| self._set_parameters(parameters) | ||
| self.aux_params = aux_params | ||
| self.nodes[0].branches.append(self) | ||
| self.nodes[1].branches.append(self) | ||
| def __str__(self) -> str: | ||
| return ( | ||
| "Branch " | ||
| + self.type | ||
| + " connecting nodes: (" | ||
| + str(self.nodes[0].index) | ||
| + "," | ||
| + str(self.nodes[1].index) | ||
| + "); " | ||
| + str(self.parameters) | ||
| ) | ||
| def __repr__(self) -> str: | ||
| return f"Branch({self.type}, {self.nodes[0].index}, {self.nodes[1].index}, index: {self.index})" | ||
| def _set_parameters(self, parameters) -> None: | ||
| if self.type in ["C", "L"]: | ||
| self.parameters = {f"E{self.type}": parameters[0]} | ||
| elif "JJ" in self.type: | ||
| number_of_junc_params = _junction_order(self.type) | ||
| self.parameters = {} | ||
| for junc_order in range(1, number_of_junc_params + 1): | ||
| if junc_order == 1: | ||
| self.parameters["EJ"] = parameters[0] | ||
| else: | ||
| self.parameters[f"EJ{junc_order}"] = parameters[junc_order - 1] | ||
| self.parameters["ECJ"] = parameters[number_of_junc_params] | ||
| def node_ids(self) -> Tuple[int, int]: | ||
| """Returns the indices of the nodes connected by the branch.""" | ||
| return self.nodes[0].index, self.nodes[1].index | ||
| def is_connected(self, branch) -> bool: | ||
| """Returns a boolean indicating whether the current branch is connected to the | ||
| given `branch`""" | ||
| distinct_node_count = len(set(self.nodes + branch.nodes)) | ||
| if distinct_node_count < 4: | ||
| return True | ||
| return False | ||
| def common_node(self, branch) -> Set[Node]: | ||
| """Returns the common nodes between self and the `branch` given as input.""" | ||
| return set(self.nodes) & set(branch.nodes) | ||
| def __deepcopy__(self, memo): | ||
| cls = self.__class__ | ||
| result = cls.__new__(cls) | ||
| memo[id(self)] = result | ||
| for k, v in self.__dict__.items(): | ||
| setattr(result, k, copy.deepcopy(v, memo)) | ||
| return result | ||
| class Coupler: | ||
| """Coupler class is used to define elements which couple two existing branches in | ||
| the Circuit class. | ||
| Parameters | ||
| ---------- | ||
| branch1, branch2: | ||
| Branch objects which are being coupled. | ||
| coupling_type: | ||
| The type of coupling between the branches - allowed is mutual inductance - "ML" | ||
| parameters: | ||
| List of parameters for the coupling, namely for mutual inductance: {"EM": <value>} | ||
| aux_params: | ||
| Dictionary of auxiliary parameters which map a symbol from the input file a numeric parameter. | ||
| Examples | ||
| -------- | ||
| `Coupler("ML", branch1, branch2, "ML", 1e2)` | ||
| """ | ||
| def __init__( | ||
| self, | ||
| branch1: Branch, | ||
| branch2: Branch, | ||
| coupling_type: str, | ||
| parameters: List[Union[float, Symbol, int]], | ||
| index: Optional[int] = None, | ||
| aux_params: Dict[Symbol, float] = {}, | ||
| ): | ||
| self.branches = (branch1, branch2) | ||
| self.type = coupling_type | ||
| self._set_parameters(parameters) | ||
| self.aux_params = aux_params | ||
| self.index = index | ||
| def _set_parameters(self, parameters) -> None: | ||
| if self.type in ["ML"]: | ||
| self.parameters = {f"E{self.type}": parameters[0]} | ||
| def __repr__(self) -> str: | ||
| return f"Coupler({self.type}, ({self.branches[0].type}, {self.branches[0].node_ids()}), ({self.branches[1].type}, {self.branches[1].node_ids()}), index: {self.index})" | ||
| def __deepcopy__(self, memo): | ||
| cls = self.__class__ | ||
| result = cls.__new__(cls) | ||
| memo[id(self)] = result | ||
| for k, v in self.__dict__.items(): | ||
| setattr(result, k, copy.deepcopy(v, memo)) | ||
| return result | ||
| def make_coupler( | ||
| branches_list: List[Branch], | ||
| coupler_type: str, | ||
| idx1: int, | ||
| idx2: int, | ||
| params, | ||
| aux_params, | ||
| _branch_count: int, | ||
| ): | ||
| params_dict = {} | ||
| params = [process_param(param) for param in params] | ||
| if coupler_type == "ML": | ||
| params_dict[sm.symbols("EML")] = params[0][0] or params[0][1] | ||
| for idx in [idx1, idx2]: | ||
| if branches_list[idx].type != "L": | ||
| raise ValueError( | ||
| "Mutual inductance coupling is only allowed between inductive branches." | ||
| ) | ||
| branch1 = branches_list[idx1] | ||
| branch2 = branches_list[idx2] | ||
| sym_params_dict = { | ||
| param[0]: param[1] for param in params if param[0] | ||
| } # dictionary of symbolic params and the default values | ||
| return ( | ||
| Coupler( | ||
| branch1, | ||
| branch2, | ||
| coupler_type, | ||
| list(params_dict.values()), | ||
| _branch_count, | ||
| process_param(aux_params), | ||
| ), | ||
| sym_params_dict, | ||
| ) | ||
| def make_branch( | ||
| nodes_list: List[Node], | ||
| branch_type: str, | ||
| idx1: int, | ||
| idx2: int, | ||
| params, | ||
| aux_params, | ||
| _branch_count: int, | ||
| ): | ||
| params_dict = {} | ||
| params = [process_param(param) for param in params] | ||
| if "JJ" in branch_type: | ||
| for idx, param in enumerate( | ||
| params[:-1] | ||
| ): # getting EJi for all orders i specified | ||
| params_dict[sm.symbols(f"EJ{idx + 1}" if idx > 0 else "EJ")] = ( | ||
| param[0] or param[1] | ||
| ) | ||
| params_dict[sm.symbols("EC")] = params[-1][0] or params[-1][1] | ||
| if branch_type == "C": | ||
| params_dict[sm.symbols("EC")] = params[-1][0] or params[-1][1] | ||
| elif branch_type == "L": | ||
| params_dict[sm.symbols("EL")] = params[-1][0] or params[-1][1] | ||
| # return idx1, idx2, branch_type, list(params_dict.keys()), str(_branch_count), process_param(aux_params) | ||
| is_grounded = True if any([node.is_ground() for node in nodes_list]) else False | ||
| node_1 = nodes_list[idx1 if is_grounded else idx1 - 1] | ||
| node_2 = nodes_list[idx2 if is_grounded else idx2 - 1] | ||
| sym_params_dict = { | ||
| param[0]: param[1] for param in params if param[0] | ||
| } # dictionary of symbolic params and the default values | ||
| return ( | ||
| Branch( | ||
| node_1, | ||
| node_2, | ||
| branch_type, | ||
| list(params_dict.values()), | ||
| _branch_count, | ||
| process_param(aux_params), | ||
| ), | ||
| sym_params_dict, | ||
| ) | ||
| class SymbolicCircuitGraph(ABC): | ||
| def _spanning_tree( | ||
| self, consider_capacitive_loops: bool = False, use_closure_branches: bool = True | ||
| ): | ||
| r"""Returns a spanning tree (as a list of branches) for the given instance. | ||
| Notice that if the circuit contains multiple capacitive islands, the returned | ||
| spanning tree will not include the capacitive twig between two capacitive | ||
| islands. Option `use_closure_branches` can be set to `False` if one does not | ||
| want to use the internally set closure_branches. | ||
| This function also returns all the branches that form superconducting loops, and a | ||
| list of lists of nodes (node_sets), which keeps the generation info for nodes, e.g., | ||
| for the following spanning tree: | ||
| /---Node(2) | ||
| Node(1)---' | ||
| '---Node(3)---Node(4) | ||
| has the node_sets returned as [[Node(1)], [Node(2),Node(3)], [Node(4)]] | ||
| Returns | ||
| ------- | ||
| A list of spanning trees in the circuit, which does not include capacitor branches, | ||
| a list of branches that forms superconducting loops for each tree, and a list of lists of nodes | ||
| (node_sets) for each tree (which keeps the generation info for nodes of branches on the path) | ||
| and list of closure branches for each tree. | ||
| """ | ||
| # Make a copy of self; do not need symbolic expressions etc., so do a minimal | ||
| # initialization only | ||
| circ_copy = copy.deepcopy(self) | ||
| # adding an attribute for node list without ground | ||
| # circ_copy.nodes = circ_copy.nodes | ||
| if circ_copy.ground_node: | ||
| circ_copy.nodes.remove(circ_copy.ground_node) | ||
| # **************** removing all the capacitive branches and updating the nodes * | ||
| # identifying capacitive branches | ||
| branches_to_be_removed = [] | ||
| if not consider_capacitive_loops: | ||
| branches_to_be_removed = [ | ||
| branch for branch in list(circ_copy.branches) if branch.type == "C" | ||
| ] | ||
| for c_branch in branches_to_be_removed: | ||
| for ( | ||
| node | ||
| ) in ( | ||
| c_branch.nodes | ||
| ): # updating the branches attribute for each node that this branch | ||
| # connects | ||
| node.branches = [b for b in node.branches if b is not c_branch] | ||
| circ_copy.branches.remove(c_branch) # removing the branch | ||
| num_float_nodes = 1 | ||
| while num_float_nodes > 0: # breaks when no floating nodes are detected | ||
| num_float_nodes = 0 # setting | ||
| for node in circ_copy.nodes: | ||
| if len(node.branches) == 0: | ||
| circ_copy.nodes.remove(node) | ||
| num_float_nodes += 1 | ||
| continue | ||
| if len(node.branches) == 1: | ||
| branches_connected_to_node = node.branches[0] | ||
| circ_copy.branches.remove(branches_connected_to_node) | ||
| for new_node in branches_connected_to_node.nodes: | ||
| if new_node != node: | ||
| new_node.branches = [ | ||
| i | ||
| for i in new_node.branches | ||
| if i is not branches_connected_to_node | ||
| ] | ||
| num_float_nodes += 1 | ||
| continue | ||
| else: | ||
| circ_copy.nodes.remove(node) | ||
| if circ_copy.nodes == []: | ||
| return { | ||
| "list_of_trees": [], | ||
| "loop_branches_for_trees": [], | ||
| "node_sets_for_trees": [], | ||
| "closure_branches_for_trees": [], | ||
| } | ||
| # ***************************************************************************** | ||
| # **************** Constructing the node_sets *************** | ||
| node_sets_for_trees = [] # seperate node sets for separate trees | ||
| if circ_copy.is_grounded: | ||
| node_sets = [[circ_copy.ground_node]] | ||
| else: | ||
| node_sets = [ | ||
| [circ_copy.nodes[0]] | ||
| ] # starting with the first set that has the first node as the only element | ||
| node_sets_for_trees.append(node_sets) | ||
| num_nodes = len(circ_copy.nodes) | ||
| # this needs to be done as the ground node is not included in self.nodes | ||
| if circ_copy.is_grounded: | ||
| num_nodes += 1 | ||
| # finding all the sets of nodes and filling node_sets | ||
| node_set_index = 0 | ||
| tree_index = 0 | ||
| while ( | ||
| len(flatten_list_recursive(node_sets_for_trees)) | ||
| < num_nodes # checking to see if all the nodes are present in node_sets | ||
| ): | ||
| node_set = [] | ||
| for node in node_sets_for_trees[tree_index][node_set_index]: | ||
| node_set += node.connected_nodes("all") | ||
| node_set = [ | ||
| x | ||
| for x in unique_elements_in_list(node_set) | ||
| if x | ||
| not in flatten_list_recursive( | ||
| node_sets_for_trees[tree_index][: node_set_index + 1] | ||
| ) | ||
| ] | ||
| if node_set: | ||
| node_set.sort(key=lambda node: node.index) | ||
| # code to handle two different capacitive islands in the circuit. | ||
| if node_set == []: | ||
| node_sets_for_trees.append([]) | ||
| for node in circ_copy.nodes: | ||
| if node not in flatten_list_recursive( | ||
| node_sets_for_trees[tree_index] | ||
| ): | ||
| tree_index += 1 | ||
| node_sets_for_trees[tree_index].append([node]) | ||
| node_set_index = 0 | ||
| break | ||
| continue | ||
| node_sets_for_trees[tree_index].append(node_set) | ||
| node_set_index += 1 | ||
| # *************************** | ||
| # **************** constructing the spanning tree ########## | ||
| def connecting_branches(n1: Node, n2: Node): | ||
| return [branch for branch in n1.branches if branch in n2.branches] | ||
| def is_same_branch(branch_1: Branch, branch_2: Branch): | ||
| return branch_1.index == branch_2.index | ||
| def fetch_same_branch_from_circ(branch: Branch, circ): | ||
| for b in circ.branches: | ||
| if is_same_branch(b, branch): | ||
| return b | ||
| def fetch_same_node_from_circ(node: Node, circ): | ||
| for n in circ.nodes: | ||
| if n.index == node.index: | ||
| return n | ||
| list_of_trees = [] | ||
| for node_sets in node_sets_for_trees: | ||
| tree = [] # tree having branches of the instance that is copied | ||
| # find the branch connecting this node to another node in a previous node set. | ||
| for index, node_set in enumerate(node_sets): | ||
| if index == 0: | ||
| continue | ||
| for node in node_set: | ||
| for prev_node in node_sets[index - 1]: | ||
| if len(connecting_branches(node, prev_node)) != 0: | ||
| tree.append(connecting_branches(node, prev_node)[0]) | ||
| break | ||
| list_of_trees.append(tree) | ||
| # as the capacitors are removed to form the spanning tree, and as a result | ||
| # floating branches as well, the set of all branches which form the | ||
| # superconducting loops would be in circ_copy. | ||
| closure_branches_for_trees = [[] for tree in list_of_trees] | ||
| loop_branches_for_trees = [] | ||
| for tree_idx, tree in enumerate(list_of_trees): | ||
| loop_branches = tree.copy() | ||
| nodes_in_tree = flatten_list_recursive(node_sets_for_trees[tree_idx]) | ||
| for branch in [ | ||
| branch for branch in circ_copy.branches if branch not in tree | ||
| ]: | ||
| if len([node for node in branch.nodes if node in nodes_in_tree]) == 2: | ||
| loop_branches.append(branch) | ||
| closure_branches_for_trees[tree_idx].append(branch) | ||
| loop_branches_for_trees.append(loop_branches) | ||
| # get branches from the original circuit | ||
| for tree_idx, tree in enumerate(list_of_trees): | ||
| list_of_trees[tree_idx] = [ | ||
| fetch_same_branch_from_circ(branch, self) for branch in tree | ||
| ] | ||
| loop_branches_for_trees[tree_idx] = [ | ||
| fetch_same_branch_from_circ(branch, self) | ||
| for branch in loop_branches_for_trees[tree_idx] | ||
| ] | ||
| closure_branches_for_trees[tree_idx] = [ | ||
| fetch_same_branch_from_circ(branch, self) | ||
| for branch in closure_branches_for_trees[tree_idx] | ||
| ] | ||
| node_sets_for_trees[tree_idx] = [ | ||
| [fetch_same_node_from_circ(node, self) for node in node_set] | ||
| for node_set in node_sets_for_trees[tree_idx] | ||
| ] | ||
| # if the closure branches are manually set, then the spanning tree would be all | ||
| # the superconducting loop branches except the closure branches | ||
| if (self.closure_branches != [] and use_closure_branches) and np.all( | ||
| [isinstance(elem, Branch) for elem in self.closure_branches] | ||
| ): | ||
| closure_branches_for_trees = [ | ||
| [] for loop_branches in loop_branches_for_trees | ||
| ] | ||
| list_of_trees = [] | ||
| for tree_idx, loop_branches in enumerate(loop_branches_for_trees): | ||
| list_of_trees.append( | ||
| [ | ||
| branch | ||
| for branch in loop_branches | ||
| if branch not in self.closure_branches | ||
| ] | ||
| ) | ||
| closure_branches_for_trees[tree_idx] = [ | ||
| branch | ||
| for branch in loop_branches | ||
| if branch in self.closure_branches | ||
| ] | ||
| return { | ||
| "list_of_trees": list_of_trees, | ||
| "loop_branches_for_trees": loop_branches_for_trees, | ||
| "node_sets_for_trees": node_sets_for_trees, | ||
| "closure_branches_for_trees": closure_branches_for_trees, | ||
| } | ||
| def _closure_branches(self, spanning_tree_dict=None): | ||
| r"""Returns and stores the closure branches in the circuit.""" | ||
| return flatten_list_recursive( | ||
| (spanning_tree_dict or self.spanning_tree_dict)[ | ||
| "closure_branches_for_trees" | ||
| ] | ||
| ) | ||
| def _time_dependent_flux_distribution(self): | ||
| closure_branches = self._closure_branches() | ||
| # constructing the constraint matrix | ||
| R = np.zeros([len(self.branches), len(closure_branches)]) | ||
| # constructing branch capacitance matrix | ||
| C_diag = np.identity(len(self.branches)) * 0 | ||
| # constructing the matrix which transforms node to branch variables | ||
| W = np.zeros([len(self.branches), len(self.nodes) - self.is_grounded]) | ||
| for closure_brnch_idx, closure_branch in enumerate(closure_branches): | ||
| loop_branches = self._find_loop(closure_branch) | ||
| # setting the loop direction from the direction of the closure branch | ||
| R_prev_brnch = 1 | ||
| for b_idx, branch in enumerate(loop_branches): | ||
| R_elem = 1 | ||
| if b_idx == 0: | ||
| start_node = list(branch.common_node(loop_branches[1]))[0] | ||
| start_node_idx = branch.nodes.index(start_node) | ||
| if start_node_idx == 0: | ||
| R_elem *= -1 | ||
| if b_idx > 0: | ||
| start_node_idx = 1 if R_prev_brnch > 0 else 0 | ||
| start_node = loop_branches[b_idx - 1].nodes[start_node_idx] | ||
| R_elem = R_prev_brnch | ||
| if branch.node_ids()[start_node_idx] == start_node.index: | ||
| R_elem *= -1 | ||
| R_prev_brnch = R_elem | ||
| R[self.branches.index(branch), closure_brnch_idx] = R_elem | ||
| if R[self.branches.index(closure_branch), closure_brnch_idx] < 0: | ||
| R[:, closure_brnch_idx] = R[:, closure_brnch_idx] * -1 | ||
| for idx, branch in enumerate(self.branches): | ||
| if branch.type == "C" or "JJ" in branch.type: | ||
| EC = ( | ||
| branch.parameters["EC"] | ||
| if branch.type == "C" | ||
| else branch.parameters["ECJ"] | ||
| ) | ||
| if isinstance(EC, sympy.Symbol): | ||
| EC = self.symbolic_params[EC] | ||
| C_diag[idx, idx] = 1 / (EC * 8) | ||
| for node_idx, node in enumerate(branch.nodes): | ||
| if node.is_ground(): | ||
| continue | ||
| n_id = self.nodes.index(node) - self.is_grounded | ||
| W[idx, n_id] = (-1) ** node_idx | ||
| M = np.vstack([(W.T @ C_diag), R.T]) | ||
| I = np.vstack( | ||
| [ | ||
| np.zeros([len(self.nodes) - self.is_grounded, len(closure_branches)]), | ||
| np.identity(len(closure_branches)), | ||
| ] | ||
| ) | ||
| B = (np.linalg.pinv(M)) @ I | ||
| return B.round(10) @ self.external_fluxes | ||
| def _find_path_to_root( | ||
| self, node: Node, spanning_tree_dict=None | ||
| ) -> Tuple[int, List[Node], List[Branch], int]: | ||
| r"""Returns all the nodes and branches in the spanning tree path between the | ||
| input node and the root of the spanning tree. Also returns the distance | ||
| (generation) between the input node and the root node. The root of the spanning | ||
| tree is node 0 if there is a physical ground node, otherwise it is node 1. | ||
| Notice that the branches that sit on the boundaries of capacitive islands are | ||
| not included in the branch list. | ||
| Parameters | ||
| ---------- | ||
| node: Node | ||
| Node variable which is the input | ||
| Returns | ||
| ------- | ||
| An integer for the generation number, a list of ancestor nodes, and a list | ||
| of branches on the path | ||
| """ | ||
| # extract spanning trees node_sets (to determine the generation of the node) | ||
| tree_info_dict = spanning_tree_dict or self.spanning_tree_dict | ||
| # find out the generation number of the node in the spanning tree | ||
| for tree_idx, tree in enumerate(tree_info_dict["list_of_trees"]): | ||
| node_sets = tree_info_dict["node_sets_for_trees"][tree_idx] | ||
| tree = tree_info_dict["list_of_trees"][tree_idx] | ||
| # generation number begins from 0 | ||
| for igen, nodes in enumerate(node_sets): | ||
| nodes_id = [node.index for node in nodes] | ||
| if node.index in nodes_id: | ||
| generation = igen | ||
| break | ||
| # find out the path from the node to the root | ||
| current_node = node | ||
| ancestor_nodes_list = [] | ||
| branch_path_to_root = [] | ||
| root_node = node_sets[0][0] | ||
| if root_node == node: | ||
| return (0, [], [], tree_idx) | ||
| tree_perm_gen = (perm for perm in itertools.permutations(tree)) | ||
| while root_node not in ancestor_nodes_list: | ||
| ancestor_nodes_list = [] | ||
| branch_path_to_root = [] | ||
| current_node = node | ||
| try: | ||
| tree_perm = next(tree_perm_gen) | ||
| except StopIteration: | ||
| break | ||
| # finding the parent of the current_node, and the branch that links the | ||
| # parent and current_node | ||
| for branch in tree_perm: | ||
| common_node_list = [ | ||
| n for n in branch.nodes if n not in [current_node] | ||
| ] | ||
| if ( | ||
| len(common_node_list) == 1 | ||
| and common_node_list[0] not in ancestor_nodes_list | ||
| ): | ||
| second_node = common_node_list[0] | ||
| ancestor_nodes_list.append(second_node) | ||
| branch_path_to_root.append(branch) | ||
| current_node = second_node | ||
| if current_node.index == root_node.index: | ||
| break | ||
| if root_node in ancestor_nodes_list: | ||
| break | ||
| ancestor_nodes_list.reverse() | ||
| branch_path_to_root.reverse() | ||
| return generation, ancestor_nodes_list, branch_path_to_root, tree_idx | ||
| def _find_loop( | ||
| self, closure_branch: Branch, spanning_tree_dict=None | ||
| ) -> List["Branch"]: | ||
| r"""Find out the loop that is closed by the closure branch. | ||
| Parameters | ||
| ---------- | ||
| closure_branch: Branch | ||
| The input closure branch | ||
| Returns | ||
| ------- | ||
| A list of branches that corresponds to the loop closed by the closure branch | ||
| """ | ||
| # find out ancestor nodes, path to root and generation number for each node in the | ||
| # closure branch | ||
| tree_info_dict = spanning_tree_dict or self.spanning_tree_dict | ||
| _, _, path_1, tree_idx_0 = self._find_path_to_root( | ||
| closure_branch.nodes[0], tree_info_dict | ||
| ) | ||
| _, _, path_2, tree_idx_1 = self._find_path_to_root( | ||
| closure_branch.nodes[1], tree_info_dict | ||
| ) | ||
| # find branches that are not common in the paths, and then add the closure | ||
| # branch to form the loop | ||
| path_1 = unique_elements_in_list(path_1) | ||
| path_2 = unique_elements_in_list(path_2) | ||
| loop = ( | ||
| [branch for branch in path_1 if branch not in path_2] | ||
| + [branch for branch in path_2 if branch not in path_1] | ||
| + [closure_branch] | ||
| ) | ||
| return self._order_branches_in_loop(loop) | ||
| def _order_branches_in_loop(self, loop_branches): | ||
| branches_in_order = [loop_branches[0]] | ||
| branch_node_ids = [branch.node_ids() for branch in loop_branches] | ||
| prev_node_id = branch_node_ids[0][0] | ||
| while len(branches_in_order) < len(loop_branches): | ||
| for branch in [ | ||
| brnch for brnch in loop_branches if brnch not in branches_in_order | ||
| ]: | ||
| if prev_node_id in branch.node_ids(): | ||
| branches_in_order.append(branch) | ||
| break | ||
| prev_node_id = [idx for idx in branch.node_ids() if idx != prev_node_id][0] | ||
| return branches_in_order | ||
| def _set_external_fluxes( | ||
| self, | ||
| closure_branches: Optional[List[Union[Branch, Dict[Branch, float]]]] = None, | ||
| ): | ||
| # setting the class properties | ||
| if self.is_purely_harmonic and not self.use_dynamic_flux_grouping: | ||
| self.external_fluxes = [] | ||
| self.closure_branches = [] | ||
| return 0 | ||
| if closure_branches: | ||
| closure_branch_list = [ | ||
| branch if isinstance(branch, Branch) else list(branch.keys()) | ||
| for branch in closure_branches | ||
| ] | ||
| closure_branch_list = flatten_list_recursive(closure_branch_list) | ||
| for branch in closure_branch_list: | ||
| if branch.type == "C" and not self.use_dynamic_flux_grouping: | ||
| raise ValueError( | ||
| "The closure branch cannot be a capacitive branch, when dynamic flux grouping is not used." | ||
| ) | ||
| closure_branches = closure_branches or self._closure_branches() | ||
| if len(closure_branches) > 0: | ||
| self.closure_branches = closure_branches | ||
| self.external_fluxes = [ | ||
| symbols("Φ" + str(i + 1)) for i in range(len(closure_branches)) | ||
| ] | ||
| @staticmethod | ||
| def are_branchsets_disconnected( | ||
| branch_list1: List[Branch], branch_list2: List[Branch] | ||
| ) -> bool: | ||
| """Determines whether two sets of branches are disconnected. | ||
| Parameters | ||
| ---------- | ||
| branch_list1: | ||
| first list of branches | ||
| branch_list2: | ||
| second list of branches | ||
| Returns | ||
| ------- | ||
| bool | ||
| Returns True if the branches have a connection, else False | ||
| """ | ||
| node_array1 = np.array([branch.node_ids() for branch in branch_list1]).flatten() | ||
| node_array2 = np.array([branch.node_ids() for branch in branch_list2]).flatten() | ||
| return np.intersect1d(node_array1, node_array2).size == 0 | ||
| @staticmethod | ||
| def _parse_nodes(branches_list) -> List[Node]: | ||
| node_index_list = [] | ||
| for branch_list_input in [ | ||
| branch for branch in branches_list if branch[0] != "ML" | ||
| ]: | ||
| for idx in [1, 2]: | ||
| node_idx = branch_list_input[idx] | ||
| if node_idx not in node_index_list: | ||
| node_index_list.append(node_idx) | ||
| node_index_list.sort() | ||
| return [Node(idx) for idx in node_index_list] | ||
| @classmethod | ||
| def from_yaml( | ||
| cls, | ||
| input_string: str, | ||
| from_file: bool = True, | ||
| basis_completion: str = "heuristic", | ||
| use_dynamic_flux_grouping: bool = False, | ||
| initiate_sym_calc: bool = True, | ||
| ): | ||
| """ | ||
| Constructs the instance of Circuit from an input string. Here is an example of | ||
| an input string that is used to initiate an object of the | ||
| class `SymbolicCircuit`:: | ||
| #zero-pi.yaml | ||
| nodes : 4 | ||
| # zero-pi | ||
| branches: | ||
| - [JJ, 1,2, EJ = 10, 20] | ||
| - [JJ, 3,4, 10, 20] | ||
| - [L, 2,3, 0.008] | ||
| - [L, 4,1, 0.008] | ||
| - [C, 1,3, 0.02] | ||
| - [C, 2,4, 0.02] | ||
| Parameters | ||
| ---------- | ||
| input_string: | ||
| String describing the number of nodes and branches connecting then along | ||
| with their parameters | ||
| from_file: | ||
| Set to True by default, when a file name should be provided to | ||
| `input_string`, else the circuit graph description in YAML should be | ||
| provided as a string. | ||
| basis_completion: | ||
| choices: "heuristic" or "canonical"; used to choose a type of basis | ||
| for completing the transformation matrix. Set to "heuristic" by default. | ||
| use_dynamic_flux_grouping: bool | ||
| 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. | ||
| initiate_sym_calc: | ||
| set to True by default. Initiates the object attributes by calling | ||
| the function `initiate_symboliccircuit` method when set to True. | ||
| Set to False for debugging. | ||
| Returns | ||
| ------- | ||
| Instance of the class `SymbolicCircuit` | ||
| """ | ||
| if from_file: | ||
| file = open(input_string, "r") | ||
| circuit_desc = file.read() | ||
| file.close() | ||
| else: | ||
| circuit_desc = input_string | ||
| input_str = remove_comments(circuit_desc) | ||
| input_str = remove_branchline(input_str) | ||
| input_str = strip_empty_lines(input_str) | ||
| parsed_branches = [ | ||
| parse_code_line(code_line, branch_count) | ||
| for branch_count, code_line in enumerate(input_str.split("\n")) | ||
| ] | ||
| # find and create the nodes | ||
| nodes_list = cls._parse_nodes(parsed_branches) | ||
| # if the node indices do not start from 0, raise an error | ||
| node_ids = [node.index for node in nodes_list] | ||
| if min(node_ids) not in [0, 1]: | ||
| raise ValueError("The node indices should start from 0 or 1.") | ||
| # parse branches and couplers | ||
| branches_list = [] | ||
| couplers_list = [] | ||
| branch_var_dict = {} | ||
| # make individual branches | ||
| individual_branches = [ | ||
| branch for branch in parsed_branches if branch[0] != "ML" | ||
| ] | ||
| for parsed_branch in individual_branches: | ||
| branch, sym_params = make_branch(nodes_list, *parsed_branch) | ||
| for sym_param in sym_params: | ||
| if sym_param in branch_var_dict and sym_params[sym_param]: | ||
| raise Exception( | ||
| f"Symbol {sym_param} has already been assigned a value." | ||
| ) | ||
| if sym_params[sym_param]: | ||
| branch_var_dict[sym_param] = sym_params[sym_param] | ||
| branches_list.append(branch) | ||
| # make couplers | ||
| coupler_branches = [ | ||
| branch for branch in parsed_branches if branch not in individual_branches | ||
| ] | ||
| for parsed_branch in coupler_branches: | ||
| coupler, sym_params = make_coupler(branches_list, *parsed_branch) | ||
| for sym_param in sym_params: | ||
| if sym_param in branch_var_dict and sym_params[sym_param]: | ||
| raise Exception( | ||
| f"Symbol {sym_param} has already been assigned a value." | ||
| ) | ||
| if sym_params[sym_param]: | ||
| branch_var_dict[sym_param] = sym_params[sym_param] | ||
| couplers_list.append(coupler) | ||
| circuit = cls( | ||
| nodes_list, | ||
| branches_list, | ||
| couplers_list, | ||
| use_dynamic_flux_grouping=use_dynamic_flux_grouping, | ||
| branch_var_dict=branch_var_dict, | ||
| basis_completion=basis_completion, | ||
| initiate_sym_calc=initiate_sym_calc, | ||
| input_string=circuit_desc, | ||
| ) | ||
| return circuit | ||
| def _independent_modes( | ||
| self, | ||
| branch_subset: List[Branch], | ||
| single_nodes: bool = True, | ||
| basisvec_entries: Optional[List[int]] = None, | ||
| ): | ||
| """Returns the vectors which span a subspace where there is no generalized flux | ||
| difference across the branches present in the branch_subset. | ||
| Parameters | ||
| ---------- | ||
| single_nodes: | ||
| if the single nodes are taken into consideration for basis vectors. | ||
| """ | ||
| if basisvec_entries is None: | ||
| basisvec_entries = [1, 0] | ||
| nodes_copy = copy.copy(self.nodes) # copying self.nodes as it is being modified | ||
| # making sure that the ground node is placed at the end of the list | ||
| if self.ground_node: | ||
| nodes_copy.pop(0) # removing the ground node | ||
| nodes_copy = nodes_copy + [ | ||
| copy.copy(self.ground_node) | ||
| ] # reversing the order of the nodes | ||
| for node in nodes_copy: # reset the node markers | ||
| node.marker = 0 | ||
| # step 2: finding the maximum connected set of independent branches in | ||
| # branch_subset, then identifying the sets of nodes in each of those sets | ||
| branch_subset_copy = branch_subset.copy() | ||
| max_connected_subgraphs: List[List[Branch]] = ( | ||
| [] | ||
| ) # list containing the maximum connected subgraphs | ||
| while ( | ||
| len(branch_subset_copy) > 0 | ||
| ): # while loop ends when all the branches are sorted | ||
| b_0 = branch_subset_copy.pop(0) | ||
| max_connected_subgraph = [b_0] | ||
| while not self.are_branchsets_disconnected( | ||
| max_connected_subgraph, branch_subset_copy | ||
| ): | ||
| for b1 in branch_subset_copy: | ||
| for b2 in max_connected_subgraph: | ||
| if b1.is_connected(b2): | ||
| max_connected_subgraph.append(b1) | ||
| branch_subset_copy.remove(b1) | ||
| break | ||
| max_connected_subgraphs.append(max_connected_subgraph) | ||
| # finding the nodes in each of the maximum connected subgraph | ||
| nodes_in_max_connected_branchsets = [ | ||
| unique_elements_in_list( | ||
| list(chain(*[branch.nodes for branch in branch_set])) | ||
| ) | ||
| for branch_set in max_connected_subgraphs | ||
| ] | ||
| # using node.marker to mark the maximum connected subgraph to which a node | ||
| # belongs | ||
| for node_set_index, node_set in enumerate(nodes_in_max_connected_branchsets): | ||
| for node in node_set: | ||
| if any([n.is_ground() for n in node_set]): | ||
| node.marker = -1 | ||
| else: | ||
| node.marker = node_set_index + 1 | ||
| # marking ground nodes separately | ||
| for node in nodes_copy: | ||
| if node.is_ground(): | ||
| node.marker = -1 | ||
| node_branch_set_indices = [ | ||
| node.marker for node in nodes_copy | ||
| ] # identifies which node belongs to which maximum connected subgraphs; | ||
| # different numbers on two nodes indicates that they are not connected through | ||
| # any of the branches in branch_subset. 0 implies the node does not belong to | ||
| # any of the branches in max connected branch subsets and -1 implies the max | ||
| # connected branch set is connected to ground. | ||
| # step 3: Finding the linearly independent vectors spanning the vector space | ||
| # represented by branch_set_index | ||
| basis = [] | ||
| unique_branch_set_markers = unique_elements_in_list(node_branch_set_indices) | ||
| # removing the marker -1 as it is grounded. | ||
| branch_set_markers_ungrounded = [ | ||
| marker for marker in unique_branch_set_markers if marker != -1 | ||
| ] | ||
| for index in branch_set_markers_ungrounded: | ||
| basis.append( | ||
| [ | ||
| basisvec_entries[0] if i == index else basisvec_entries[1] | ||
| for i in node_branch_set_indices | ||
| ] | ||
| ) | ||
| if single_nodes: # taking the case where the node_branch_set_index is 0 | ||
| single_node_modes = [] | ||
| if node_branch_set_indices.count(0) > 0: | ||
| ref_vector = [ | ||
| basisvec_entries[0] if i == 0 else basisvec_entries[1] | ||
| for i in node_branch_set_indices | ||
| ] | ||
| positions = [ | ||
| index | ||
| for index, num in enumerate(ref_vector) | ||
| if num == basisvec_entries[0] | ||
| ] | ||
| for pos in positions: | ||
| single_node_modes.append( | ||
| [ | ||
| basisvec_entries[0] if x == pos else basisvec_entries[1] | ||
| for x, num in enumerate(node_branch_set_indices) | ||
| ] | ||
| ) | ||
| for mode in single_node_modes: | ||
| mat = np.array(basis + [mode]) | ||
| if np.linalg.matrix_rank(mat) == len(mat): | ||
| basis.append(mode) | ||
| if ( | ||
| self.is_grounded | ||
| ): # if grounded remove the last column and first row corresponding to the | ||
| basis = [i[:-1] for i in basis] | ||
| return basis | ||
| @staticmethod | ||
| def _mode_in_subspace(mode, subspace) -> bool: | ||
| """Method to check if the vector mode is a part of the subspace provided as a | ||
| set of vectors. | ||
| Parameters | ||
| ---------- | ||
| mode: | ||
| numpy ndarray of one dimension. | ||
| subspace: | ||
| numpy ndarray which represents a collection of basis vectors for a vector | ||
| subspace | ||
| """ | ||
| if len(subspace) == 0: | ||
| return False | ||
| matrix = np.vstack([subspace, np.array(mode)]) | ||
| return np.linalg.matrix_rank(matrix) == len(subspace) | ||
| def check_transformation_matrix( | ||
| self, transformation_matrix: ndarray, enable_warnings: bool = True | ||
| ) -> Dict[str, List[int]]: | ||
| """Method to identify the different modes in the transformation matrix provided | ||
| by the user. | ||
| Parameters | ||
| ---------- | ||
| transformation_matrix: | ||
| numpy ndarray which is a square matrix having the dimensions of the number | ||
| of nodes present in the circuit. | ||
| warnings: | ||
| If False, will not raise the warnings regarding any unidentified modes. It | ||
| is set to True by default. | ||
| Returns | ||
| ------- | ||
| A dictionary of lists which has the variable indices classified with | ||
| var indices corresponding to the rows of the transformation matrix | ||
| """ | ||
| # basic check to see if the matrix is invertible | ||
| if np.linalg.det(transformation_matrix) == 0: | ||
| raise Exception("The transformation matrix provided is not invertible.") | ||
| # find all the different types of modes present in the circuit. | ||
| # *************************** Finding the Periodic Modes ********************** | ||
| selected_branches = [branch for branch in self.branches if branch.type == "L"] | ||
| periodic_modes = self._independent_modes(selected_branches) | ||
| # *************************** Finding the frozen modes ********************** | ||
| selected_branches = [branch for branch in self.branches if branch.type != "L"] | ||
| frozen_modes = self._independent_modes(selected_branches, single_nodes=True) | ||
| # *************************** Finding the Cyclic Modes **************** | ||
| selected_branches = [branch for branch in self.branches if branch.type != "C"] | ||
| free_modes = self._independent_modes(selected_branches) | ||
| # ***************************# Finding the LC Modes **************** | ||
| selected_branches = [branch for branch in self.branches if "JJ" in branch.type] | ||
| LC_modes = self._independent_modes(selected_branches, single_nodes=False) | ||
| # ******************* including the Σ mode **************** | ||
| Σ = [1] * (len(self.nodes) - self.is_grounded) | ||
| if not self.is_grounded: # only append if the circuit is not grounded | ||
| mat = np.array(frozen_modes + [Σ]) | ||
| # check to see if the vectors are still independent | ||
| if np.linalg.matrix_rank(mat) < len(frozen_modes) + 1: | ||
| frozen_modes = frozen_modes[1:] + [Σ] | ||
| else: | ||
| frozen_modes.append(Σ) | ||
| # *********** Adding periodic, free and extended modes to frozen ************ | ||
| modes = [] # starting with the frozen modes | ||
| for m in ( | ||
| frozen_modes + free_modes + periodic_modes + LC_modes # + extended_modes | ||
| ): # This order is important | ||
| if not self._mode_in_subspace(m, modes): | ||
| modes.append(m) | ||
| for m in LC_modes: # adding the LC modes to the basis | ||
| if not self._mode_in_subspace(m, modes): | ||
| modes.append(m) | ||
| var_categories_circuit: Dict[str, list] = { | ||
| "periodic": [], | ||
| "extended": [], | ||
| "free": [], | ||
| "frozen": [], | ||
| } | ||
| for x, mode in enumerate(modes): | ||
| # calculate the number of periodic modes | ||
| if self._mode_in_subspace(Σ, [mode]) and not self.is_grounded: | ||
| continue | ||
| if self._mode_in_subspace(mode, frozen_modes): | ||
| var_categories_circuit["frozen"].append(x + 1) | ||
| continue | ||
| if self._mode_in_subspace(mode, free_modes): | ||
| var_categories_circuit["free"].append(x + 1) | ||
| continue | ||
| if self._mode_in_subspace(mode, periodic_modes): | ||
| var_categories_circuit["periodic"].append(x + 1) | ||
| continue | ||
| # Any mode which survived the above conditionals is an extended mode | ||
| var_categories_circuit["extended"].append(x + 1) | ||
| # Classifying the modes given in the transformation by the user | ||
| user_given_modes = transformation_matrix.transpose() | ||
| var_categories_user: Dict[str, list] = { | ||
| "periodic": [], | ||
| "extended": [], | ||
| "free": [], | ||
| "frozen": [], | ||
| "sigma": [], | ||
| } | ||
| sigma_mode_found = False | ||
| for x, mode in enumerate(user_given_modes): | ||
| # calculate the number of periodic modes | ||
| if self._mode_in_subspace(Σ, [mode]) and not self.is_grounded: | ||
| sigma_mode_found = True | ||
| var_categories_user["sigma"].append(x + 1) | ||
| continue | ||
| if self._mode_in_subspace(mode, frozen_modes): | ||
| var_categories_user["frozen"].append(x + 1) | ||
| continue | ||
| if self._mode_in_subspace(mode, free_modes): | ||
| var_categories_user["free"].append(x + 1) | ||
| continue | ||
| if self._mode_in_subspace(mode, periodic_modes): | ||
| var_categories_user["periodic"].append(x + 1) | ||
| continue | ||
| # Any mode which survived the above conditionals is an extended mode | ||
| var_categories_user["extended"].append(x + 1) | ||
| # comparing the modes in the user defined and the code generated transformation | ||
| mode_types = ["periodic", "extended", "free", "frozen"] | ||
| for mode_type in mode_types: | ||
| num_extra_modes = len(var_categories_circuit[mode_type]) - len( | ||
| var_categories_user[mode_type] | ||
| ) | ||
| if num_extra_modes > 0 and enable_warnings: | ||
| warnings.warn( | ||
| "Number of extra " | ||
| + mode_type | ||
| + " modes found: " | ||
| + str(num_extra_modes) | ||
| + "\n" | ||
| ) | ||
| if not self.is_grounded and not sigma_mode_found: | ||
| raise Exception( | ||
| "This circuit is not grounded, and so has a sigma mode. This transformation does not have a sigma mode." | ||
| ) | ||
| return var_categories_user | ||
| def variable_transformation_matrix(self) -> Tuple[ndarray, Dict[str, List[int]]]: | ||
| """Evaluates the boundary conditions and constructs the variable transformation | ||
| matrix, which is returned along with the dictionary `var_categories` which | ||
| classifies the types of variables present in the circuit. | ||
| Returns | ||
| ------- | ||
| tuple of transformation matrix for the node variables and `var_categories` | ||
| dict which classifies the variable types for each variable index | ||
| """ | ||
| # **************** Finding the Periodic Modes **************** | ||
| selected_branches = [branch for branch in self.branches if branch.type == "L"] | ||
| periodic_modes = self._independent_modes(selected_branches) | ||
| # **************** Finding the frozen modes **************** | ||
| selected_branches = [branch for branch in self.branches if branch.type != "L"] | ||
| frozen_modes = self._independent_modes(selected_branches, single_nodes=True) | ||
| # **************** Finding the Cyclic Modes **************** | ||
| selected_branches = [branch for branch in self.branches if branch.type != "C"] | ||
| free_modes = self._independent_modes(selected_branches) | ||
| # **************** including the Σ mode **************** | ||
| Σ = [1] * (len(self.nodes) - self.is_grounded) | ||
| if not self.is_grounded: # only append if the circuit is not grounded | ||
| mat = np.array(frozen_modes + [Σ]) | ||
| # check to see if the vectors are still independent | ||
| if np.linalg.matrix_rank(mat) < len(frozen_modes) + 1: | ||
| frozen_modes = frozen_modes[1:] + [Σ] | ||
| else: | ||
| frozen_modes.append(Σ) | ||
| # **************** Finding the LC Modes **************** | ||
| selected_branches = [branch for branch in self.branches if "JJ" in branch.type] | ||
| LC_modes = self._independent_modes( | ||
| selected_branches, single_nodes=False, basisvec_entries=[-1, 1] | ||
| ) | ||
| # **************** Adding frozen, free, periodic , LC and extended modes **** | ||
| modes = [] # starting with an empty list | ||
| for m in ( | ||
| frozen_modes + free_modes + periodic_modes + LC_modes # + extended_modes | ||
| ): # This order is important | ||
| mat = np.array(modes + [m]) | ||
| if np.linalg.matrix_rank(mat) == len(mat): | ||
| modes.append(m) | ||
| # ********** Completing the Basis **************** | ||
| # step 4: construct the new set of basis vectors | ||
| # constructing a standard basis | ||
| if self.basis_completion == "heuristic": | ||
| node_count = len(self.nodes) - self.is_grounded | ||
| standard_basis = [np.ones(node_count)] | ||
| vector_ref = np.zeros(node_count) | ||
| if node_count > 2: | ||
| vector_ref[: node_count - 2] = 1 | ||
| else: | ||
| vector_ref[: node_count - 1] = 1 | ||
| vector_set = ( | ||
| permutation | ||
| for permutation in itertools.permutations(vector_ref, node_count) | ||
| ) # making a generator | ||
| while np.linalg.matrix_rank(np.array(standard_basis)) < node_count: | ||
| a = next(vector_set) | ||
| mat = np.array(standard_basis + [a]) | ||
| if np.linalg.matrix_rank(mat) == len(mat): | ||
| standard_basis = standard_basis + [list(a)] | ||
| standard_basis = np.array(standard_basis) | ||
| elif self.basis_completion == "canonical": | ||
| standard_basis = np.identity(len(self.nodes) - self.is_grounded) | ||
| new_basis = modes.copy() | ||
| for m in standard_basis: # completing the basis | ||
| mat = np.array([i for i in new_basis] + [m]) | ||
| if np.linalg.matrix_rank(mat) == len(mat): | ||
| new_basis.append(m) | ||
| new_basis = np.array(new_basis) | ||
| # sorting the basis so that the free, periodic and frozen variables occur at | ||
| # the beginning. | ||
| if not self.is_grounded: | ||
| pos_Σ = [i for i in range(len(new_basis)) if new_basis[i].tolist() == Σ] | ||
| else: | ||
| pos_Σ = [] | ||
| pos_free = [ | ||
| i | ||
| for i in range(len(new_basis)) | ||
| if i not in pos_Σ | ||
| if new_basis[i].tolist() in free_modes | ||
| ] | ||
| pos_periodic = [ | ||
| i | ||
| for i in range(len(new_basis)) | ||
| if i not in pos_Σ | ||
| if i not in pos_free | ||
| if new_basis[i].tolist() in periodic_modes | ||
| ] | ||
| pos_frozen = [ | ||
| i | ||
| for i in range(len(new_basis)) | ||
| if i not in pos_Σ | ||
| if i not in pos_free | ||
| if i not in pos_periodic | ||
| if new_basis[i].tolist() in frozen_modes | ||
| ] | ||
| pos_rest = [ | ||
| i | ||
| for i in range(len(new_basis)) | ||
| if i not in pos_Σ | ||
| if i not in pos_free | ||
| if i not in pos_periodic | ||
| if i not in pos_frozen | ||
| ] | ||
| pos_list = pos_periodic + pos_rest + pos_free + pos_frozen + pos_Σ | ||
| # transforming the new_basis matrix | ||
| new_basis = new_basis[pos_list].T | ||
| # saving the variable identification to a dict | ||
| var_categories = { | ||
| "periodic": [ | ||
| i + 1 for i in range(len(pos_list)) if pos_list[i] in pos_periodic | ||
| ], | ||
| "extended": [ | ||
| i + 1 for i in range(len(pos_list)) if pos_list[i] in pos_rest | ||
| ], | ||
| "free": [i + 1 for i in range(len(pos_list)) if pos_list[i] in pos_free], | ||
| "frozen": [ | ||
| i + 1 for i in range(len(pos_list)) if pos_list[i] in pos_frozen | ||
| ], | ||
| "sigma": ( | ||
| [i + 1 for i in range(len(pos_list)) if pos_list[i] == pos_Σ[0]] | ||
| if not self.is_grounded | ||
| else [] | ||
| ), | ||
| } | ||
| return np.array(new_basis), var_categories |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
+0
-0
@@ -0,0 +0,0 @@ BSD 3-Clause License |
+0
-0
@@ -0,0 +0,0 @@ include README.md |
+145
-42
@@ -1,64 +0,167 @@ | ||
| Metadata-Version: 2.1 | ||
| Metadata-Version: 2.2 | ||
| Name: scqubits | ||
| Version: 4.2.0 | ||
| Version: 4.3 | ||
| Summary: scqubits: superconducting qubits in Python | ||
| Home-page: https://scqubits.readthedocs.io | ||
| Author: Jens Koch, Peter Groszkowski | ||
| Author-email: jens-koch@northwestern.edu, piotrekg@gmail.com | ||
| License: BSD | ||
| Keywords: superconducting qubits | ||
| Platform: Linux | ||
| Platform: Mac OSX | ||
| Platform: Unix | ||
| Platform: Windows | ||
| Author-email: Jens Koch <jens-koch@northwestern.edu>, Peter Groszkowski <piotrekg@gmail.com> | ||
| License: BSD 3-Clause License | ||
| Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski | ||
| All rights reserved. | ||
| Redistribution and use in source and binary forms, with or without | ||
| modification, are permitted provided that the following conditions are met: | ||
| 1. Redistributions of source code must retain the above copyright notice, this | ||
| list of conditions and the following disclaimer. | ||
| 2. Redistributions in binary form must reproduce the above copyright notice, | ||
| this list of conditions and the following disclaimer in the documentation | ||
| and/or other materials provided with the distribution. | ||
| 3. Neither the name of the copyright holder nor the names of its | ||
| contributors may be used to endorse or promote products derived from | ||
| this software without specific prior written permission. | ||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
| OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| Project-URL: Homepage, https://scqubits.readthedocs.io | ||
| Project-URL: Repository, https://github.com/scqubits/scqubits | ||
| Keywords: qubits,superconducting | ||
| Classifier: Development Status :: 5 - Production/Stable | ||
| Classifier: Intended Audience :: Science/Research | ||
| Classifier: License :: OSI Approved :: BSD License | ||
| Classifier: Operating System :: MacOS | ||
| Classifier: Operating System :: Microsoft :: Windows | ||
| Classifier: Operating System :: POSIX | ||
| Classifier: Operating System :: Unix | ||
| Classifier: Programming Language :: Python | ||
| Classifier: Programming Language :: Python :: 3 | ||
| Classifier: Topic :: Scientific/Engineering | ||
| Classifier: Operating System :: MacOS | ||
| Classifier: Operating System :: POSIX | ||
| Classifier: Operating System :: Unix | ||
| Classifier: Operating System :: Microsoft :: Windows | ||
| Requires-Python: >=3.9 | ||
| Description-Content-Type: text/markdown | ||
| 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: dill | ||
| Requires-Dist: pathos>=0.3.0 | ||
| Requires-Dist: matplotlib>=3.5.1 | ||
| Requires-Dist: numpy>=1.14.2 | ||
| Requires-Dist: pathos>=0.3.0 | ||
| Requires-Dist: qutip>=4.3.1 | ||
| Requires-Dist: scipy>=1.5; sys_platform != "darwin" | ||
| Requires-Dist: scipy>=1.5; sys_platform == "darwin" and python_version < "3.10" | ||
| Requires-Dist: scipy<=1.13.1,>=1.5; sys_platform == "darwin" and python_version >= "3.10" | ||
| Requires-Dist: sympy | ||
| Requires-Dist: tqdm | ||
| Requires-Dist: typing_extensions | ||
| Requires-Dist: pathos | ||
| Requires-Dist: dill | ||
| Provides-Extra: graphics | ||
| Requires-Dist: matplotlib-label-lines>=0.3.6; extra == "graphics" | ||
| Provides-Extra: explorer | ||
| Requires-Dist: ipywidgets>=7.5; extra == "explorer" | ||
| Provides-Extra: h5-support | ||
| Requires-Dist: h5py>=2.10; extra == "h5-support" | ||
| Provides-Extra: pathos | ||
| Requires-Dist: pathos; extra == "pathos" | ||
| Requires-Dist: dill; extra == "pathos" | ||
| Provides-Extra: gui | ||
| Requires-Dist: ipywidgets>=7.5; extra == "gui" | ||
| Requires-Dist: ipyvuetify; extra == "gui" | ||
| Requires-Dist: matplotlib-label-lines>=0.3.6; extra == "gui" | ||
| Requires-Dist: h5py>=2.10; extra == "gui" | ||
| Provides-Extra: develop | ||
| Requires-Dist: pytest; extra == "develop" | ||
| Requires-Dist: traitlets; extra == "develop" | ||
| Requires-Dist: h5py>=2.10; extra == "develop" | ||
| scqubits: superconducting qubits in Python | ||
| =========================================== | ||
| scqubits is an open-source Python library for simulating superconducting qubits. It is | ||
| meant to give the user a convenient way to obtain energy spectra of common | ||
| superconducting qubits, plot energy levels as a function of external parameters, | ||
| calculate matrix elements etc. The library further provides an interface to QuTiP, | ||
| making it easy to work with composite Hilbert spaces consisting of coupled | ||
| superconducting qubits and harmonic modes. Internally, numerics within scqubits is | ||
| carried out with the help of Numpy and Scipy; plotting capabilities rely on | ||
| [](https://anaconda.org/conda-forge/scqubits) | ||
| [](https://www.codefactor.io/repository/github/scqubits/scqubits) | ||
| [](https://codecov.io/gh/scqubits/scqubits) | ||
| [J. Koch](https://github.com/jkochNU), [P. Groszkowski](https://github.com/petergthatsme) | ||
| <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 | ||
| a convenient way to obtain energy spectra of common superconducting qubits, plot energy levels as a function of | ||
| external parameters, calculate matrix elements etc. The library further provides an interface to QuTiP, making it | ||
| easy to work with composite Hilbert spaces consisting of coupled superconducting qubits and harmonic modes. | ||
| Internally, numerics within scqubits is carried out with the help of Numpy and Scipy; plotting capabilities rely on | ||
| Matplotlib. | ||
| If scqubits is helpful to you in your research, please support its continued | ||
| development and maintenance. Use of scqubits in research publications is | ||
| If scqubits is helpful to you in your research, please support its continued | ||
| development and maintenance. Use of scqubits in research publications is | ||
| appropriately acknowledged by citing: | ||
| Peter Groszkowski and Jens Koch, 'scqubits: a Python package for superconducting qubits', | ||
| Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/ | ||
| 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 | ||
| ------------------------- | ||
| For Python 3.9 - 3.12: installation via conda is supported. | ||
| ``` | ||
| conda install -c conda-forge scqubits | ||
| ``` | ||
| Alternatively, scqubits can be installed via pip (although it should be noted that installing via pip under a conda environment is strongly discouraged, and is not guaranteed to work - see conda documentation). | ||
| ``` | ||
| pip install scqubits | ||
| ``` | ||
| Documentation | ||
| ------------- | ||
| The documentation for scqubits is available at: https://scqubits.readthedocs.io | ||
| Related Packages | ||
| ---------------- | ||
| There are two related packages on github: | ||
| documentation source code: https://github.com/scqubits/scqubits-doc | ||
| example notebooks: https://github.com/scqubits/scqubits-examples | ||
| Contribute | ||
| ---------- | ||
| You are welcome to contribute to scqubits development by forking this repository and sending pull requests, | ||
| or filing bug reports at the | ||
| [issues page](https://github.com/scqubits/scqubits/issues). | ||
| All contributions are acknowledged in the | ||
| [contributors](https://scqubits.readthedocs.io/en/latest/contributors.html) | ||
| section in the documentation. | ||
| All contributions are expected to be consistent with [PEP 8 -- Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/). | ||
| License | ||
| ------- | ||
| [](http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22Revised_BSD_License.22.2C_.22New_BSD_License.22.2C_or_.22Modified_BSD_License.22.29) | ||
| You are free to use this software, with or without modification, provided that the conditions listed in the LICENSE file are satisfied. |
+1
-1
@@ -6,3 +6,3 @@ scqubits: superconducting qubits in Python | ||
| [](https://www.codefactor.io/repository/github/scqubits/scqubits) | ||
| [](https://codecov.io/gh/scqubits/scqubits) | ||
| [](https://codecov.io/gh/scqubits/scqubits) | ||
@@ -9,0 +9,0 @@ |
+145
-42
@@ -1,64 +0,167 @@ | ||
| Metadata-Version: 2.1 | ||
| Metadata-Version: 2.2 | ||
| Name: scqubits | ||
| Version: 4.2.0 | ||
| Version: 4.3 | ||
| Summary: scqubits: superconducting qubits in Python | ||
| Home-page: https://scqubits.readthedocs.io | ||
| Author: Jens Koch, Peter Groszkowski | ||
| Author-email: jens-koch@northwestern.edu, piotrekg@gmail.com | ||
| License: BSD | ||
| Keywords: superconducting qubits | ||
| Platform: Linux | ||
| Platform: Mac OSX | ||
| Platform: Unix | ||
| Platform: Windows | ||
| Author-email: Jens Koch <jens-koch@northwestern.edu>, Peter Groszkowski <piotrekg@gmail.com> | ||
| License: BSD 3-Clause License | ||
| Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski | ||
| All rights reserved. | ||
| Redistribution and use in source and binary forms, with or without | ||
| modification, are permitted provided that the following conditions are met: | ||
| 1. Redistributions of source code must retain the above copyright notice, this | ||
| list of conditions and the following disclaimer. | ||
| 2. Redistributions in binary form must reproduce the above copyright notice, | ||
| this list of conditions and the following disclaimer in the documentation | ||
| and/or other materials provided with the distribution. | ||
| 3. Neither the name of the copyright holder nor the names of its | ||
| contributors may be used to endorse or promote products derived from | ||
| this software without specific prior written permission. | ||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
| OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| Project-URL: Homepage, https://scqubits.readthedocs.io | ||
| Project-URL: Repository, https://github.com/scqubits/scqubits | ||
| Keywords: qubits,superconducting | ||
| Classifier: Development Status :: 5 - Production/Stable | ||
| Classifier: Intended Audience :: Science/Research | ||
| Classifier: License :: OSI Approved :: BSD License | ||
| Classifier: Operating System :: MacOS | ||
| Classifier: Operating System :: Microsoft :: Windows | ||
| Classifier: Operating System :: POSIX | ||
| Classifier: Operating System :: Unix | ||
| Classifier: Programming Language :: Python | ||
| Classifier: Programming Language :: Python :: 3 | ||
| Classifier: Topic :: Scientific/Engineering | ||
| Classifier: Operating System :: MacOS | ||
| Classifier: Operating System :: POSIX | ||
| Classifier: Operating System :: Unix | ||
| Classifier: Operating System :: Microsoft :: Windows | ||
| Requires-Python: >=3.9 | ||
| Description-Content-Type: text/markdown | ||
| 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: dill | ||
| Requires-Dist: pathos>=0.3.0 | ||
| Requires-Dist: matplotlib>=3.5.1 | ||
| Requires-Dist: numpy>=1.14.2 | ||
| Requires-Dist: pathos>=0.3.0 | ||
| Requires-Dist: qutip>=4.3.1 | ||
| Requires-Dist: scipy>=1.5; sys_platform != "darwin" | ||
| Requires-Dist: scipy>=1.5; sys_platform == "darwin" and python_version < "3.10" | ||
| Requires-Dist: scipy<=1.13.1,>=1.5; sys_platform == "darwin" and python_version >= "3.10" | ||
| Requires-Dist: sympy | ||
| Requires-Dist: tqdm | ||
| Requires-Dist: typing_extensions | ||
| Requires-Dist: pathos | ||
| Requires-Dist: dill | ||
| Provides-Extra: graphics | ||
| Requires-Dist: matplotlib-label-lines>=0.3.6; extra == "graphics" | ||
| Provides-Extra: explorer | ||
| Requires-Dist: ipywidgets>=7.5; extra == "explorer" | ||
| Provides-Extra: h5-support | ||
| Requires-Dist: h5py>=2.10; extra == "h5-support" | ||
| Provides-Extra: pathos | ||
| Requires-Dist: pathos; extra == "pathos" | ||
| Requires-Dist: dill; extra == "pathos" | ||
| Provides-Extra: gui | ||
| Requires-Dist: ipywidgets>=7.5; extra == "gui" | ||
| Requires-Dist: ipyvuetify; extra == "gui" | ||
| Requires-Dist: matplotlib-label-lines>=0.3.6; extra == "gui" | ||
| Requires-Dist: h5py>=2.10; extra == "gui" | ||
| Provides-Extra: develop | ||
| Requires-Dist: pytest; extra == "develop" | ||
| Requires-Dist: traitlets; extra == "develop" | ||
| Requires-Dist: h5py>=2.10; extra == "develop" | ||
| scqubits: superconducting qubits in Python | ||
| =========================================== | ||
| scqubits is an open-source Python library for simulating superconducting qubits. It is | ||
| meant to give the user a convenient way to obtain energy spectra of common | ||
| superconducting qubits, plot energy levels as a function of external parameters, | ||
| calculate matrix elements etc. The library further provides an interface to QuTiP, | ||
| making it easy to work with composite Hilbert spaces consisting of coupled | ||
| superconducting qubits and harmonic modes. Internally, numerics within scqubits is | ||
| carried out with the help of Numpy and Scipy; plotting capabilities rely on | ||
| [](https://anaconda.org/conda-forge/scqubits) | ||
| [](https://www.codefactor.io/repository/github/scqubits/scqubits) | ||
| [](https://codecov.io/gh/scqubits/scqubits) | ||
| [J. Koch](https://github.com/jkochNU), [P. Groszkowski](https://github.com/petergthatsme) | ||
| <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 | ||
| a convenient way to obtain energy spectra of common superconducting qubits, plot energy levels as a function of | ||
| external parameters, calculate matrix elements etc. The library further provides an interface to QuTiP, making it | ||
| easy to work with composite Hilbert spaces consisting of coupled superconducting qubits and harmonic modes. | ||
| Internally, numerics within scqubits is carried out with the help of Numpy and Scipy; plotting capabilities rely on | ||
| Matplotlib. | ||
| If scqubits is helpful to you in your research, please support its continued | ||
| development and maintenance. Use of scqubits in research publications is | ||
| If scqubits is helpful to you in your research, please support its continued | ||
| development and maintenance. Use of scqubits in research publications is | ||
| appropriately acknowledged by citing: | ||
| Peter Groszkowski and Jens Koch, 'scqubits: a Python package for superconducting qubits', | ||
| Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/ | ||
| 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 | ||
| ------------------------- | ||
| For Python 3.9 - 3.12: installation via conda is supported. | ||
| ``` | ||
| conda install -c conda-forge scqubits | ||
| ``` | ||
| Alternatively, scqubits can be installed via pip (although it should be noted that installing via pip under a conda environment is strongly discouraged, and is not guaranteed to work - see conda documentation). | ||
| ``` | ||
| pip install scqubits | ||
| ``` | ||
| Documentation | ||
| ------------- | ||
| The documentation for scqubits is available at: https://scqubits.readthedocs.io | ||
| Related Packages | ||
| ---------------- | ||
| There are two related packages on github: | ||
| documentation source code: https://github.com/scqubits/scqubits-doc | ||
| example notebooks: https://github.com/scqubits/scqubits-examples | ||
| Contribute | ||
| ---------- | ||
| You are welcome to contribute to scqubits development by forking this repository and sending pull requests, | ||
| or filing bug reports at the | ||
| [issues page](https://github.com/scqubits/scqubits/issues). | ||
| All contributions are acknowledged in the | ||
| [contributors](https://scqubits.readthedocs.io/en/latest/contributors.html) | ||
| section in the documentation. | ||
| All contributions are expected to be consistent with [PEP 8 -- Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/). | ||
| License | ||
| ------- | ||
| [](http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22Revised_BSD_License.22.2C_.22New_BSD_License.22.2C_or_.22Modified_BSD_License.22.29) | ||
| You are free to use this software, with or without modification, provided that the conditions listed in the LICENSE file are satisfied. |
@@ -1,5 +0,7 @@ | ||
| numpy>=1.14.2 | ||
| cycler | ||
| cython>=0.29.20 | ||
| dill | ||
| pathos>=0.3.0 | ||
| matplotlib>=3.5.1 | ||
| numpy>=1.14.2 | ||
| pathos>=0.3.0 | ||
| qutip>=4.3.1 | ||
@@ -9,4 +11,2 @@ sympy | ||
| typing_extensions | ||
| pathos | ||
| dill | ||
@@ -22,13 +22,11 @@ [:sys_platform != "darwin"] | ||
| [explorer] | ||
| [develop] | ||
| pytest | ||
| traitlets | ||
| h5py>=2.10 | ||
| [gui] | ||
| ipywidgets>=7.5 | ||
| [graphics] | ||
| ipyvuetify | ||
| matplotlib-label-lines>=0.3.6 | ||
| [h5-support] | ||
| h5py>=2.10 | ||
| [pathos] | ||
| pathos | ||
| dill |
@@ -0,7 +1,15 @@ | ||
| .gitignore | ||
| CITATION.bib | ||
| LICENSE | ||
| MANIFEST.in | ||
| README.md | ||
| optional-requirements.txt | ||
| requirements.txt | ||
| setup.py | ||
| azure-pipelines.yml | ||
| meta.yaml | ||
| pyproject.toml | ||
| .github/ISSUE_TEMPLATE/bug_report.md | ||
| .github/ISSUE_TEMPLATE/ideas--suggestions--feature-requests.md | ||
| .github/ISSUE_TEMPLATE/other-issues.md | ||
| .github/workflows/black-check-latest.yml | ||
| .github/workflows/mamba-install-and-run-pytests.yml | ||
| .github/workflows/pythonpublish.yml | ||
| scqubits/__init__.py | ||
@@ -15,3 +23,2 @@ scqubits/py.typed | ||
| scqubits.egg-info/dependency_links.txt | ||
| scqubits.egg-info/not-zip-safe | ||
| scqubits.egg-info/requires.txt | ||
@@ -24,3 +31,5 @@ scqubits.egg-info/top_level.txt | ||
| scqubits/core/circuit_noise.py | ||
| scqubits/core/circuit_plotting.py | ||
| scqubits/core/circuit_routines.py | ||
| scqubits/core/circuit_sym_methods.py | ||
| scqubits/core/circuit_utils.py | ||
@@ -46,2 +55,3 @@ scqubits/core/constants.py | ||
| scqubits/core/symbolic_circuit.py | ||
| scqubits/core/symbolic_circuit_graph.py | ||
| scqubits/core/transmon.py | ||
@@ -71,2 +81,3 @@ scqubits/core/units.py | ||
| scqubits/io_utils/fileio_serializers.py | ||
| scqubits/tests/.coverage | ||
| scqubits/tests/__init__.py | ||
@@ -89,2 +100,3 @@ scqubits/tests/conftest.py | ||
| scqubits/tests/test_parametersweep.py | ||
| scqubits/tests/test_snailmon.future | ||
| scqubits/tests/test_spectrumlookup.py | ||
@@ -96,2 +108,3 @@ scqubits/tests/test_transmon.py | ||
| scqubits/tests/data/circuit_fluxonium.yaml | ||
| scqubits/tests/data/circuit_qutip_evolution_data.hdf5 | ||
| scqubits/tests/data/circuit_zeropi.yaml | ||
@@ -98,0 +111,0 @@ scqubits/tests/data/cos2phiqubit_1.hdf5 |
| scqubits | ||
| scqubits/core | ||
| scqubits/explorer | ||
| scqubits/io_utils | ||
| scqubits/tests | ||
| scqubits/ui | ||
| scqubits/utils |
@@ -11,3 +11,4 @@ # scqubits: superconducting qubits in Python | ||
| # LICENSE file in the root directory of this source tree. | ||
| """scqubits is an open-source Python library for simulating superconducting qubits. | ||
| """Scqubits is an open-source Python library for simulating superconducting qubits. | ||
| It is meant to give the user a convenient way to obtain energy spectra of common | ||
@@ -18,4 +19,4 @@ superconducting qubits, plot energy levels as a function of external parameters, | ||
| superconducting qubits and harmonic modes. Internally, numerics within scqubits is | ||
| carried out with the help of Numpy and Scipy; plotting capabilities rely on | ||
| Matplotlib.""" | ||
| carried out with the help of Numpy and Scipy; plotting capabilities rely on Matplotlib. | ||
| """ | ||
| ####################################################################################### | ||
@@ -22,0 +23,0 @@ |
@@ -0,0 +0,0 @@ # This file is part of scqubits: a Python package for superconducting qubits, |
@@ -43,5 +43,3 @@ # central_dispatch.py | ||
| class CentralDispatch: | ||
| """ | ||
| Primary class managing the central dispatch system. | ||
| """ | ||
| """Primary class managing the central dispatch system.""" | ||
@@ -60,3 +58,3 @@ def __init__(self): | ||
| """For given `event`, return the dict mapping each registered client to their | ||
| callback routine | ||
| callback routine. | ||
@@ -77,4 +75,3 @@ Parameters | ||
| ) -> None: | ||
| """ | ||
| Register object `who` for event `event`. (This modifies `clients_dict`.) | ||
| """Register object `who` for event `event`. (This modifies `clients_dict`.) | ||
@@ -81,0 +78,0 @@ Parameters |
@@ -0,5 +1,5 @@ | ||
| from typing import Tuple, Optional, Union, Dict | ||
| import pyparsing as pp | ||
| import os | ||
| from typing import List, Tuple | ||
| from scqubits.utils.misc import is_string_float | ||
@@ -209,7 +209,9 @@ from pyparsing import Group, Opt, Or, Literal, Suppress | ||
| Args: | ||
| Parameters | ||
| ---------- | ||
| code_line (str): string describing the branch from the input file | ||
| _branch_count (_type_): the count of the branch in a given circuit | ||
| Returns: | ||
| Returns | ||
| ------- | ||
| branch_type: str | ||
@@ -231,14 +233,17 @@ node_idx1: int | ||
| def convert_value_to_GHz(val, units): | ||
| """ | ||
| Converts a given value and units to energy in GHz. The units are given in a string in the format "pU" | ||
| where p is an optional multiplier prefix and U is units. For example: "pH", "nA", "fF", "eV" | ||
| """Converts a given value and units to energy in GHz. The units are given in a | ||
| string in the format "pU" where p is an optional multiplier prefix and U is units. | ||
| For example: "pH", "nA", "fF", "eV". | ||
| Args: | ||
| Parameters | ||
| ---------- | ||
| val (float): value in given units | ||
| units (str): units described in a string with an optional multiplier prefix | ||
| Raises: | ||
| Raises | ||
| ------ | ||
| ValueError: If the unit is unknown. | ||
| Returns: | ||
| Returns | ||
| ------- | ||
| float: Energy in GHz | ||
@@ -275,7 +280,11 @@ """ | ||
| def process_param(pattern): | ||
| def process_param( | ||
| pattern, | ||
| ) -> Union[Dict[sm.Symbol, float], Tuple[Optional[sm.Symbol], Optional[float]]]: | ||
| """Returns a tuple containing (symbol, value) given a pattern as detected by | ||
| pyparsing. | ||
| Either the symbol or the value can be returned to be none, when the symbol is | ||
| already assigned or no symbol is assigned to the given branch parameter. | ||
| """ | ||
| Returns a tuple containing (symbol, value) given a pattern as detected by pyparsing. | ||
| Either the symbol or the value can be returned to be none, when the symbol is already assigned or no symbol is assigned to the given branch parameter. | ||
| """ | ||
| name = pattern.getName() | ||
@@ -285,11 +294,11 @@ if name == "ASSIGN": | ||
| val = pattern[1] | ||
| units = None if pattern[-1] == None else pattern[-2:] | ||
| units = None if pattern[-1] is None else pattern[-2:] | ||
| val_converted = convert_value_to_GHz(val, units) | ||
| return sym, val_converted | ||
| return (sym, val_converted) | ||
| if name == "SYMBOL": | ||
| return sm.symbols(pattern[0]), None | ||
| return (sm.symbols(pattern[0]), None) | ||
| if name == "VALUE": | ||
| units = None if pattern[-1] == None else pattern[-2:] | ||
| units = None if pattern[-1] is None else pattern[-2:] | ||
| converted_val = convert_value_to_GHz(pattern[0], units) | ||
| return None, converted_val | ||
| return (None, converted_val) | ||
| if name == "AUX_PARAM": | ||
@@ -296,0 +305,0 @@ num_of_aux_params = int(len(pattern) / 2) |
@@ -14,3 +14,3 @@ # circuit-utils.py | ||
| import re | ||
| from typing import TYPE_CHECKING, Any, Callable, List, Union, Optional, Tuple, Dict | ||
| from typing import TYPE_CHECKING, Callable, List, Union, Optional, Tuple, Dict | ||
@@ -20,3 +20,2 @@ import numpy as np | ||
| import sympy as sm | ||
| import qutip as qt | ||
| from numpy import ndarray | ||
@@ -40,5 +39,4 @@ from scipy import sparse | ||
| def _junction_order(branch_type: str) -> int: | ||
| """ | ||
| Returns the order of the branch if it is a JJ branch, | ||
| if the order is n its energy is given by cos(phi) + cos(2*phi) + ... + cos(n*phi) | ||
| """Returns the order of the branch if it is a JJ branch, if the order is n its | ||
| energy is given by cos(phi) + cos(2*phi) + ... + cos(n*phi) | ||
@@ -51,3 +49,3 @@ Args: | ||
| Returns: | ||
| Returns | ||
| _type_: int, order of the josephson junction | ||
@@ -68,8 +66,9 @@ """ | ||
| def sawtooth_operator(x: Union[ndarray, csc_matrix]): | ||
| """ | ||
| Returns the operator evaluated using applying the sawtooth_potential function on the | ||
| diagonal elements of the operator x | ||
| """Returns the operator evaluated using applying the sawtooth_potential function on | ||
| the diagonal elements of the operator x. | ||
| Args: | ||
| x (Union[ndarray, csc_matrix]): argument of the sawtooth operator in the Hamiltonian | ||
| Parameters | ||
| ---------- | ||
| x: | ||
| argument of the sawtooth operator in the Hamiltonian | ||
| """ | ||
@@ -103,6 +102,4 @@ diagonal_elements = sawtooth_potential(x.diagonal()) | ||
| def _capactiance_variable_for_branch(branch_type: str): | ||
| """ | ||
| Returns the parameter name that stores the capacitance of the branch | ||
| """ | ||
| def _capacitance_variable_for_branch(branch_type: str): | ||
| """Returns the parameter name that stores the capacitance of the branch.""" | ||
| if "C" in branch_type: | ||
@@ -119,5 +116,4 @@ return "EC" | ||
| ) -> list: | ||
| """ | ||
| Function to generate a template for defining the truncated dimensions for subsystems | ||
| when hierarchical diagonalization is used. | ||
| """Function to generate a template for defining the truncated dimensions for | ||
| subsystems when hierarchical diagonalization is used. | ||
@@ -152,6 +148,4 @@ Parameters | ||
| def get_trailing_number(input_str: str) -> Union[int, None]: | ||
| """ | ||
| Returns the number trailing a string given as input. Example: | ||
| $ get_trailing_number("a23") | ||
| $ 23 | ||
| """Returns the number trailing a string given as input. Example: $ | ||
| get_trailing_number("a23") $ 23. | ||
@@ -171,27 +165,4 @@ Parameters | ||
| def get_operator_number(input_str: str) -> int: | ||
| """ | ||
| Returns the number inside an operator name. Example: | ||
| $ get_operator_number("annihilation9_operator") | ||
| $ 9 | ||
| Parameters | ||
| ---------- | ||
| input_str: | ||
| operator name (one of the methods ending with `_operator`) | ||
| Returns | ||
| ------- | ||
| returns the integer as int, else returns None | ||
| """ | ||
| match = re.search(r"(\d+)", input_str) | ||
| number = int(match.group()) | ||
| if not number: | ||
| raise Exception(f"{input_str} is not a valid operator name.") | ||
| return number | ||
| def _identity_phi(grid: discretization.Grid1d) -> csc_matrix: | ||
| """ | ||
| Returns identity operator in the discretized_phi basis. | ||
| """Returns identity operator in the discretized_phi basis. | ||
@@ -212,4 +183,3 @@ Parameters | ||
| def _phi_operator(grid: discretization.Grid1d) -> csc_matrix: | ||
| """ | ||
| Returns phi operator in the discretized_phi basis. | ||
| """Returns phi operator in the discretized_phi basis. | ||
@@ -234,4 +204,3 @@ Parameters | ||
| def _i_d_dphi_operator(grid: discretization.Grid1d) -> csc_matrix: | ||
| """ | ||
| Returns i*d/dphi operator in the discretized_phi basis. | ||
| """Returns i*d/dphi operator in the discretized_phi basis. | ||
@@ -251,4 +220,3 @@ Parameters | ||
| def _i_d2_dphi2_operator(grid: discretization.Grid1d) -> csc_matrix: | ||
| """ | ||
| Returns i*d2/dphi2 operator in the discretized_phi basis. | ||
| """Returns i*d2/dphi2 operator in the discretized_phi basis. | ||
@@ -268,4 +236,3 @@ Parameters | ||
| def _cos_phi(grid: discretization.Grid1d) -> csc_matrix: | ||
| """ | ||
| Returns cos operator in the discretized_phi basis. | ||
| """Returns cos operator in the discretized_phi basis. | ||
@@ -290,4 +257,3 @@ Parameters | ||
| def _sin_phi(grid: discretization.Grid1d) -> csc_matrix: | ||
| """ | ||
| Returns sin operator in the discretized_phi basis. | ||
| """Returns sin operator in the discretized_phi basis. | ||
@@ -312,5 +278,3 @@ Parameters | ||
| def _identity_theta(ncut: int) -> csc_matrix: | ||
| """ | ||
| Returns Operator identity in the charge basis. | ||
| """ | ||
| """Returns Operator identity in the charge basis.""" | ||
| dim_theta = 2 * ncut + 1 | ||
@@ -321,5 +285,3 @@ return sparse.identity(dim_theta, format="csc") | ||
| def _n_theta_operator(ncut: int) -> csc_matrix: | ||
| """ | ||
| Returns charge operator `n` in the charge basis. | ||
| """ | ||
| """Returns charge operator `n` in the charge basis.""" | ||
| dim_theta = 2 * ncut + 1 | ||
@@ -334,5 +296,3 @@ diag_elements = np.arange(-ncut, ncut + 1) | ||
| def _exp_i_theta_operator(ncut, prefactor=1) -> csc_matrix: | ||
| r""" | ||
| Operator :math:`\cos(\theta)`, acting only on the `\theta` Hilbert subspace. | ||
| """ | ||
| r"""Operator :math:`\cos(\theta)`, acting only on the `\theta` Hilbert subspace.""" | ||
| # if type(prefactor) != int: | ||
@@ -349,5 +309,3 @@ # raise ValueError("Prefactor must be an integer") | ||
| def _exp_i_theta_operator_conjugate(ncut) -> csc_matrix: | ||
| r""" | ||
| Operator :math:`\cos(\theta)`, acting only on the `\theta` Hilbert subspace. | ||
| """ | ||
| r"""Operator :math:`\cos(\theta)`, acting only on the `\theta` Hilbert subspace.""" | ||
| dim_theta = 2 * ncut + 1 | ||
@@ -362,3 +320,3 @@ matrix = sparse.dia_matrix( | ||
| def _cos_theta(ncut: int) -> csc_matrix: | ||
| """Returns operator :math:`\\cos \\varphi` in the charge basis""" | ||
| """Returns operator :math:`\\cos \\varphi` in the charge basis.""" | ||
| cos_op = 0.5 * (_exp_i_theta_operator(ncut) + _exp_i_theta_operator_conjugate(ncut)) | ||
@@ -369,3 +327,3 @@ return cos_op | ||
| def _sin_theta(ncut: int) -> csc_matrix: | ||
| """Returns operator :math:`\\sin \\varphi` in the charge basis""" | ||
| """Returns operator :math:`\\sin \\varphi` in the charge basis.""" | ||
| sin_op = ( | ||
@@ -382,5 +340,4 @@ -1j | ||
| ) -> List[sm.Symbol]: | ||
| """ | ||
| Returns the list of symbols generated using the var_str + iterable as the name | ||
| of the symbol. | ||
| """Returns the list of symbols generated using the var_str + iterable as the name of | ||
| the symbol. | ||
@@ -398,4 +355,3 @@ Parameters | ||
| def is_potential_term(term: sm.Expr) -> bool: | ||
| """ | ||
| Determines if a given sympy expression term is part of the potential | ||
| """Determines if a given sympy expression term is part of the potential. | ||
@@ -419,5 +375,4 @@ Parameters | ||
| def example_circuit(qubit: str) -> str: | ||
| """ | ||
| Returns example input strings for AnalyzeQCircuit and CustomQCircuit for some of the | ||
| popular qubits. | ||
| """Returns example input strings for AnalyzeQCircuit and CustomQCircuit for some of | ||
| the popular qubits. | ||
@@ -448,6 +403,9 @@ Parameters | ||
| def grid_operator_func_factory(inner_op: Callable, index: int) -> Callable: | ||
| def operator_func(self: "Subsystem"): | ||
| return self._kron_operator( | ||
| def operator_func( | ||
| self: "Subsystem", energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False | ||
| ): | ||
| native = self._kron_operator( | ||
| inner_op(self.discretized_grids_dict_for_vars()[index]), index | ||
| ) | ||
| return self.process_op(native_op=native, energy_esys=energy_esys) | ||
@@ -458,5 +416,27 @@ return operator_func | ||
| def hierarchical_diagonalization_func_factory(symbol_name: str) -> Callable: | ||
| def operator_func(self: "Subsystem"): | ||
| return Qobj_to_scipy_csc_matrix(self.get_operator_by_name(symbol_name)) | ||
| def operator_func( | ||
| self: "Subsystem", energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False | ||
| ): | ||
| """Returns the operator <op_name> (corresponds to the name of the method | ||
| "<op_name>_operator") for the Circuit/Subsystem instance. | ||
| Parameters | ||
| ---------- | ||
| energy_esys: | ||
| If `False` (default), returns charge operator n in the charge basis. | ||
| If `True`, energy eigenspectrum is computed, returns charge operator n in the energy eigenbasis. | ||
| If `energy_esys = esys`, where `esys` is a tuple containing two ndarrays (eigenvalues and energy | ||
| eigenvectors), returns charge operator n in the energy eigenbasis, and does not have to recalculate the | ||
| eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Returns the operator <op_name>(corresponds to the name of the method "<op_name>_operator"). | ||
| For `energy_esys=True`, n has dimensions of :attr:`truncated_dim` x :attr:`truncated_dim`. | ||
| If an actual eigensystem is handed to `energy_sys`, then `n` has dimensions of m x m, | ||
| where m is the number of given eigenvectors. | ||
| """ | ||
| native = Qobj_to_scipy_csc_matrix(self.get_operator_by_name(symbol_name)) | ||
| return self.process_op(native_op=native, energy_esys=energy_esys) | ||
| return operator_func | ||
@@ -483,3 +463,24 @@ | ||
| ) -> Callable: | ||
| def operator_func(self, op_type=op_type): | ||
| def operator_func( | ||
| self: "Subsystem", energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False | ||
| ): | ||
| """Returns the operator <op_name> (corresponds to the name of the method | ||
| "<op_name>_operator") for the Circuit/Subsystem instance. | ||
| Parameters | ||
| ---------- | ||
| energy_esys: | ||
| If `False` (default), returns charge operator n in the charge basis. | ||
| If `True`, energy eigenspectrum is computed, returns charge operator n in the energy eigenbasis. | ||
| If `energy_esys = esys`, where `esys` is a tuple containing two ndarrays (eigenvalues and energy | ||
| eigenvectors), returns charge operator n in the energy eigenbasis, and does not have to recalculate the | ||
| eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Returns the operator <op_name>(corresponds to the name of the method "<op_name>_operator"). | ||
| For `energy_esys=True`, n has dimensions of :attr:`truncated_dim` x :attr:`truncated_dim`. | ||
| If an actual eigensystem is handed to `energy_sys`, then `n` has dimensions of m x m, | ||
| where m is the number of given eigenvectors. | ||
| """ | ||
| prefactor = None | ||
@@ -492,7 +493,8 @@ if self.ext_basis == "harmonic": | ||
| if prefactor: | ||
| return self._kron_operator( | ||
| native = self._kron_operator( | ||
| inner_op(self.cutoffs_dict()[index], prefactor=prefactor), index | ||
| ) | ||
| else: | ||
| return self._kron_operator(inner_op(self.cutoffs_dict()[index]), index) | ||
| native = self._kron_operator(inner_op(self.cutoffs_dict()[index]), index) | ||
| return self.process_op(native_op=native, energy_esys=energy_esys) | ||
@@ -502,16 +504,5 @@ return operator_func | ||
| def compose(f: Callable, g: Callable) -> Callable: | ||
| """Returns the function f o g: x |-> f(g(x))""" | ||
| def g_after_f(x: Any) -> Any: | ||
| return f(g(x)) | ||
| return g_after_f | ||
| def _cos_dia(x: csc_matrix) -> csc_matrix: | ||
| """ | ||
| Take the diagonal of the array x, compute its cosine, and fill the result into | ||
| the diagonal of a sparse matrix | ||
| """ | ||
| """Take the diagonal of the array x, compute its cosine, and fill the result into | ||
| the diagonal of a sparse matrix.""" | ||
| return sparse.diags(np.cos(x.diagonal())).tocsc() | ||
@@ -521,6 +512,4 @@ | ||
| def _sin_dia(x: csc_matrix) -> csc_matrix: | ||
| """ | ||
| Take the diagonal of the array x, compute its sine, and fill the result into | ||
| the diagonal of a sparse matrix. | ||
| """ | ||
| """Take the diagonal of the array x, compute its sine, and fill the result into the | ||
| diagonal of a sparse matrix.""" | ||
| return sparse.diags(np.sin(x.diagonal())).tocsc() | ||
@@ -530,5 +519,3 @@ | ||
| def _sin_dia_dense(x: ndarray) -> ndarray: | ||
| """ | ||
| This is a special function to calculate the sin of dense diagonal matrices | ||
| """ | ||
| """This is a special function to calculate the sin of dense diagonal matrices.""" | ||
| return np.diag(np.sin(x.diagonal())) | ||
@@ -538,5 +525,3 @@ | ||
| def _cos_dia_dense(x: ndarray) -> ndarray: | ||
| """ | ||
| This is a special function to calculate the cos of dense diagonal matrices | ||
| """ | ||
| """This is a special function to calculate the cos of dense diagonal matrices.""" | ||
| return np.diag(np.cos(x.diagonal())) | ||
@@ -858,6 +843,5 @@ | ||
| ) -> ndarray: | ||
| """ | ||
| Assemble a transformation matrix for a large circuit that are made of smaller sub-circuits | ||
| and coupling elements. This method takes a list of sub-circuit transformation matrices as | ||
| the argument. | ||
| """Assemble a transformation matrix for a large circuit that are made of smaller | ||
| sub-circuits and coupling elements. This method takes a list of sub-circuit | ||
| transformation matrices as the argument. | ||
@@ -864,0 +848,0 @@ Parameters |
+173
-144
@@ -15,9 +15,6 @@ # circuit.py | ||
| import warnings | ||
| from typing import Any, Dict, List, Optional, Tuple, Union, Callable | ||
| from typing import Any, Dict, List, Optional, Tuple, Union, Callable, get_type_hints | ||
| import numpy as np | ||
| import sympy as sm | ||
| from matplotlib import pyplot as plt | ||
| from matplotlib.axes import Axes | ||
| from matplotlib.figure import Figure | ||
| from numpy import ndarray | ||
@@ -27,3 +24,3 @@ from sympy import latex | ||
| try: | ||
| from IPython.display import Latex, display | ||
| import IPython | ||
| except ImportError: | ||
@@ -39,6 +36,4 @@ _HAS_IPYTHON = False | ||
| from scqubits import settings | ||
| from scqubits.core.circuit_utils import ( | ||
| get_trailing_number, | ||
| is_potential_term, | ||
| ) | ||
@@ -54,7 +49,13 @@ from scqubits.core.symbolic_circuit import Branch, SymbolicCircuit | ||
| from scqubits.core.circuit_routines import CircuitRoutines | ||
| from scqubits.core.circuit_plotting import CircuitPlot | ||
| from scqubits.core.circuit_sym_methods import CircuitSymMethods | ||
| from scqubits.core.circuit_noise import NoisyCircuit | ||
| class CircuitABC(CircuitRoutines, CircuitSymMethods, CircuitPlot): | ||
| pass | ||
| class Subsystem( | ||
| CircuitRoutines, | ||
| CircuitABC, | ||
| base.QubitBaseClass, | ||
@@ -65,5 +66,4 @@ serializers.Serializable, | ||
| ): | ||
| """ | ||
| Defines a subsystem for a circuit, which can further be used recursively to define | ||
| subsystems within subsystem. | ||
| """Defines a subsystem for a circuit, which can further be used recursively to | ||
| define subsystems within subsystem. | ||
@@ -87,2 +87,35 @@ Parameters | ||
| sets the truncated dimension for the current subsystem, set to 10 by default. | ||
| Attributes | ||
| ---------- | ||
| hierarchical_diagonalization: bool | ||
| set to True when the circuit is defined hierarchically, by default `False` | ||
| hamiltonian_symbolic: Sympy.Expr | ||
| the symbolic Hamiltonian for the circuit | ||
| external_fluxes: List[Sympy.Symbol] | ||
| list of external flux variables | ||
| offset_charges: List[Sympy.Symbol] | ||
| list of offset charge variables | ||
| free_charges: List[Sympy.Symbol] | ||
| list of free charge variables | ||
| var_categories: Dict[str, List[int]] | ||
| dictionary with keys "periodic", "extended", "free", "frozen" and values as | ||
| the indices of the respective variable types | ||
| cutoff_names: List[str] | ||
| list of cutoff names for the variables | ||
| discretized_phi_range: Dict[int, Tuple[float, float]] | ||
| dictionary with keys as the indices of the extended variables and values as | ||
| the range of discretized phi variables | ||
| type_of_matrices: str | ||
| type of matrices used to construct the operators "dense" or "sparse", by default "sparse" | ||
| system_hierarchy: list | ||
| list of lists containing variable indices which is provided by the user to define subsystems | ||
| subsystem_trunc_dims: list | ||
| list of truncated dimensions for the subsystems inside the current subsystem | ||
| hilbert_space: HilbertSpace | ||
| HilbertSpace instance for the circuit, when hierarchical diagonalization is used | ||
| truncated_dim: int | ||
| truncated dimension for the current instance | ||
| is_purely_harmonic: bool | ||
| internally set to True when the instance is purely harmonic | ||
| """ | ||
@@ -95,5 +128,5 @@ | ||
| ext_basis: Union[str, List], | ||
| system_hierarchy: Optional[List] = None, | ||
| subsystem_trunc_dims: Optional[List] = None, | ||
| truncated_dim: Optional[int] = 10, | ||
| system_hierarchy: list = [], | ||
| subsystem_trunc_dims: list = [], | ||
| truncated_dim: int = 10, | ||
| evals_method: Union[Callable, str, None] = None, | ||
@@ -117,6 +150,6 @@ evals_method_options: Union[dict, None] = None, | ||
| self.system_hierarchy = system_hierarchy | ||
| self.truncated_dim = truncated_dim | ||
| self.truncated_dim: int = truncated_dim | ||
| self.subsystem_trunc_dims = subsystem_trunc_dims | ||
| self.is_child = True | ||
| self.is_child: bool = True | ||
| self.parent = parent | ||
@@ -128,12 +161,3 @@ self.hamiltonian_symbolic = hamiltonian_symbolic | ||
| self._H_LC_str_harmonic = None | ||
| # attribute to keep track if the symbolic Hamiltonian needs to be updated | ||
| self._make_property( | ||
| "_user_changed_parameter", | ||
| False, | ||
| "update_user_changed_parameter", | ||
| use_central_dispatch=False, | ||
| ) | ||
| self.ext_basis = ext_basis | ||
| self._find_and_set_sym_attrs() | ||
@@ -143,6 +167,2 @@ self.dynamic_var_indices: List[int] = flatten_list_recursive( | ||
| ) | ||
| parent_cutoffs_dict = self.parent.cutoffs_dict() | ||
| cutoffs: List[int] = [ | ||
| parent_cutoffs_dict[var_index] for var_index in self.dynamic_var_indices | ||
| ] | ||
@@ -174,3 +194,3 @@ self.var_categories: Dict[str, List[int]] = {} | ||
| self.potential_symbolic = self.generate_sym_potential() | ||
| self.potential_symbolic = self._generate_sym_potential() | ||
@@ -196,7 +216,7 @@ self.hierarchical_diagonalization: bool = ( | ||
| def _find_and_set_sym_attrs(self): | ||
| """Finds the symbolic and other circuit params from the symbolic Hamiltonian, | ||
| and sets the attribs external_fluxes, offset_charges and symbolic_params. | ||
| Only works when _frozen is set to False, or the above attribs are already set. | ||
| """ | ||
| Finds the symbolic and other circuit params from the symbolic Hamiltonian, and sets the attribs | ||
| external_fluxes, offset_charges and symbolic_params. Only works when _frozen is set to False, or | ||
| the above attribs are already set. | ||
| """ | ||
| self.external_fluxes = [ | ||
@@ -222,8 +242,2 @@ var | ||
| } | ||
| def _configure(self) -> None: | ||
| """ | ||
| Function which is used to initiate the subsystem instance. | ||
| """ | ||
| self._frozen = False | ||
| for idx, param in enumerate(self.symbolic_params): | ||
@@ -247,3 +261,2 @@ self._make_property( | ||
| ) | ||
| for cutoff_str in self.cutoff_names: | ||
@@ -253,2 +266,9 @@ self._make_property( | ||
| ) | ||
| def _configure(self) -> None: | ||
| """Function which is used to initiate the subsystem instance.""" | ||
| self._frozen = False | ||
| self._find_and_set_sym_attrs() | ||
| # if subsystem hamiltonian is purely harmonic | ||
@@ -264,23 +284,12 @@ if ( | ||
| # Creating the attributes for purely harmonic circuits | ||
| if ( | ||
| isinstance(self, Circuit) and self.parent.is_purely_harmonic | ||
| ): # assuming that the parent has only extended variables and are ordered | ||
| # starting from 1, 2, 3, ... | ||
| self.is_purely_harmonic = self.parent.is_purely_harmonic | ||
| self.normal_mode_freqs = self.parent.normal_mode_freqs[ | ||
| [var_idx - 1 for var_idx in self.var_categories["extended"]] | ||
| ] | ||
| if self.hierarchical_diagonalization: | ||
| # attribute to note updated subsystem indices | ||
| self.affected_subsystem_indices = [] | ||
| self._hamiltonian_sym_for_numerics = self.hamiltonian_symbolic.copy() | ||
| self.generate_subsystems() | ||
| self._generate_subsystems() | ||
| self.ext_basis = self.get_ext_basis() | ||
| self.update_interactions() | ||
| self._update_interactions() | ||
| self._check_truncation_indices() | ||
| self.affected_subsystem_indices = list(range(len(self.subsystems))) | ||
| else: | ||
| self.generate_hamiltonian_sym_for_numerics() | ||
| self._generate_hamiltonian_sym_for_numerics() | ||
| if self.is_purely_harmonic and self.ext_basis == "harmonic": | ||
@@ -290,4 +299,5 @@ self._diagonalize_purely_harmonic_hamiltonian() | ||
| self._set_vars() | ||
| self.operators_by_name = self.set_operators() | ||
| self.operators_by_name = self._set_operators() | ||
| self._out_of_sync_with_parent = False | ||
| if self.hierarchical_diagonalization: | ||
@@ -298,5 +308,12 @@ self._out_of_sync = False # for use with CentralDispatch | ||
| def _is_diagonalization_necessary(self) -> bool: | ||
| """Checks if the subsystem needs to be diagonalized.""" | ||
| parent_subsys_idx = self.parent.subsystems.index(self) | ||
| if parent_subsys_idx in self.parent.affected_subsystem_indices: | ||
| return True | ||
| return False | ||
| class Circuit( | ||
| CircuitRoutines, | ||
| CircuitABC, | ||
| base.QubitBaseClass, | ||
@@ -307,31 +324,65 @@ serializers.Serializable, | ||
| ): | ||
| """ | ||
| Class for analysis of custom superconducting circuits. | ||
| """Class for analysis of custom superconducting circuits. | ||
| Parameters | ||
| ---------- | ||
| input_string: str | ||
| input_string: | ||
| String describing the number of nodes and branches connecting then along | ||
| with their parameters | ||
| from_file: bool | ||
| from_file: | ||
| Set to True by default, when a file name should be provided to | ||
| `input_string`, else the circuit graph description in YAML should be | ||
| provided as a string. | ||
| basis_completion: str | ||
| basis_completion: | ||
| either "heuristic" or "canonical", defines the matrix used for completing the | ||
| transformation matrix. Sometimes used to change the variable transformation | ||
| to result in a simpler symbolic Hamiltonian, by default "heuristic" | ||
| ext_basis: str | ||
| ext_basis: | ||
| can be "discretized" or "harmonic" which chooses whether to use discretized | ||
| phi or harmonic oscillator basis for extended variables, | ||
| by default "discretized" | ||
| use_dynamic_flux_grouping: bool | ||
| use_dynamic_flux_grouping: | ||
| 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. | ||
| initiate_sym_calc: bool | ||
| attribute to initiate Circuit instance, by default `True` | ||
| truncated_dim: Optional[int] | ||
| initiate_sym_calc: | ||
| parameter to initiate Circuit instance, by default `True` | ||
| truncated_dim: | ||
| truncated dimension if the user wants to use this circuit instance in | ||
| HilbertSpace, by default `None` | ||
| Attributes | ||
| ---------- | ||
| hierarchical_diagonalization: bool | ||
| set to True when the circuit is defined hierarchically, by default `False` | ||
| hamiltonian_symbolic: sm.Expr | ||
| the symbolic Hamiltonian for the circuit | ||
| external_fluxes: List[sm.Symbol] | ||
| list of external flux variables | ||
| offset_charges: List[sm.Symbol] | ||
| list of offset charge variables | ||
| free_charges: List[sm.Symbol] | ||
| list of free charge variables | ||
| var_categories: Dict[str, List[int]] | ||
| dictionary with keys "periodic", "extended", "free", "frozen" and values as | ||
| the indices of the respective variable types | ||
| cutoff_names: List[str] | ||
| list of cutoff names for the variables | ||
| discretized_phi_range: Dict[int, Tuple[float, float]] | ||
| dictionary with keys as the indices of the extended variables and values as | ||
| the range of discretized phi variables | ||
| type_of_matrices: str | ||
| type of matrices used to construct the operators "dense" or "sparse", by default "sparse" | ||
| truncated_dim: int | ||
| truncated dimension for the current instance | ||
| is_purely_harmonic: bool | ||
| internally set to True when the instance is purely harmonic | ||
| dynamic_var_indices: List[int] | ||
| list of dynamic variable indices, showing the degrees of freedom of the circuit. | ||
| hilbert_space: HilbertSpace | ||
| HilbertSpace instance for the instance, when hierarchical diagonalization is used | ||
| system_hierarchy: list | ||
| list of lists containing variable indices which is provided by the user to define subsystems | ||
| subsystem_trunc_dims: list | ||
| list of truncated dimensions for the subsystems inside the current subsystem | ||
| """ | ||
@@ -343,3 +394,3 @@ | ||
| from_file: bool = True, | ||
| basis_completion="heuristic", | ||
| basis_completion: str = "heuristic", | ||
| ext_basis: str = "discretized", | ||
@@ -350,4 +401,4 @@ use_dynamic_flux_grouping: bool = False, | ||
| truncated_dim: int = 10, | ||
| symbolic_param_dict: Dict[str, float] = None, | ||
| symbolic_hamiltonian: sm.Expr = None, | ||
| symbolic_param_dict: Dict[str, float] = {}, | ||
| symbolic_hamiltonian: Optional[sm.Expr] = None, | ||
| evals_method: Union[Callable, str, None] = None, | ||
@@ -370,4 +421,8 @@ evals_method_options: Union[dict, None] = None, | ||
| raise Exception( | ||
| "Circuit instance cannot be initialized with both input_string and symbolic_hamiltonian." | ||
| "Circuit instance cannot be initialized with both input_string and a symbolic hamiltonian." | ||
| ) | ||
| if not symbolic_hamiltonian and not input_string: | ||
| raise Exception( | ||
| "Circuit instance must be initialized with either input_string or a symbolic hamiltonian." | ||
| ) | ||
| if input_string: | ||
@@ -385,3 +440,3 @@ self.from_yaml( | ||
| else: | ||
| elif symbolic_hamiltonian: | ||
| if use_dynamic_flux_grouping or generate_noise_methods: | ||
@@ -391,3 +446,3 @@ raise Exception( | ||
| ) | ||
| self.from_symbolic_hamiltonian( | ||
| self._from_symbolic_hamiltonian( | ||
| symbolic_hamiltonian=symbolic_hamiltonian, | ||
@@ -400,3 +455,3 @@ symbolic_param_dict=symbolic_param_dict, | ||
| def from_symbolic_hamiltonian( | ||
| def _from_symbolic_hamiltonian( | ||
| self, | ||
@@ -422,4 +477,4 @@ symbolic_hamiltonian: sm.Expr, | ||
| self.truncated_dim: int = truncated_dim | ||
| self.system_hierarchy: list = None | ||
| self.subsystem_trunc_dims: list = None | ||
| self.system_hierarchy: list = [] | ||
| self.subsystem_trunc_dims: list = [] | ||
| self.operators_by_name = None | ||
@@ -442,9 +497,2 @@ | ||
| self._out_of_sync = False # for use with CentralDispatch | ||
| self._make_property( | ||
| "_user_changed_parameter", | ||
| False, | ||
| "update_user_changed_parameter", | ||
| use_central_dispatch=False, | ||
| ) | ||
| if initiate_sym_calc: | ||
@@ -464,7 +512,6 @@ self.configure() | ||
| initiate_sym_calc: bool = True, | ||
| truncated_dim: int = None, | ||
| truncated_dim: int = 10, | ||
| ): | ||
| """ | ||
| Wrapper to Circuit __init__ to create a class instance. This is deprecated and | ||
| will not be supported in future releases. | ||
| """Wrapper to Circuit __init__ to create a class instance. This is deprecated | ||
| and will not be supported in future releases. | ||
@@ -518,4 +565,4 @@ Parameters | ||
| self.truncated_dim: int = truncated_dim | ||
| self.system_hierarchy: list = None | ||
| self.subsystem_trunc_dims: list = None | ||
| self.system_hierarchy: list = [] | ||
| self.subsystem_trunc_dims: list = [] | ||
| self.operators_by_name = None | ||
@@ -560,8 +607,2 @@ | ||
| self._out_of_sync = False # for use with CentralDispatch | ||
| self._make_property( | ||
| "_user_changed_parameter", | ||
| False, | ||
| "update_user_changed_parameter", | ||
| use_central_dispatch=False, | ||
| ) | ||
@@ -598,5 +639,3 @@ if initiate_sym_calc: | ||
| def _clear_unnecessary_attribs(self): | ||
| """ | ||
| Clear all the attributes which are not part of the circuit description | ||
| """ | ||
| """Clear all the attributes which are not part of the circuit description.""" | ||
| necessary_attrib_names = ( | ||
@@ -628,3 +667,3 @@ self.cutoff_names | ||
| closure_branches: Optional[List[Union[Branch, Dict[Branch, float]]]] = None, | ||
| ext_basis: Optional[str] = None, | ||
| ext_basis: Optional[Union[str, List[str]]] = None, | ||
| use_dynamic_flux_grouping: Optional[bool] = None, | ||
@@ -634,4 +673,3 @@ generate_noise_methods: bool = False, | ||
| ): | ||
| """ | ||
| Method which re-initializes a circuit instance to update, hierarchical | ||
| """Method which re-initializes a circuit instance to update, hierarchical | ||
| diagonalization parameters or closure branches or the variable transformation | ||
@@ -773,6 +811,5 @@ used to describe the circuit. | ||
| subsys_dict: Optional[Dict[str, Any]] = None, | ||
| ext_basis: Optional[str] = None, | ||
| ext_basis: Optional[Union[str, List[str]]] = None, | ||
| ): | ||
| """ | ||
| Method which re-initializes a circuit instance to update, hierarchical | ||
| """Method which re-initializes a circuit instance to update, hierarchical | ||
| diagonalization parameters or closure branches or the variable transformation | ||
@@ -866,3 +903,3 @@ used to describe the circuit. | ||
| self.potential_symbolic = self.generate_sym_potential() | ||
| self.potential_symbolic = self._generate_sym_potential() | ||
@@ -886,3 +923,3 @@ # changing the matrix type if necessary | ||
| self.ext_basis = ext_basis or self.ext_basis | ||
| self.generate_hamiltonian_sym_for_numerics() | ||
| self._generate_hamiltonian_sym_for_numerics() | ||
| if self.is_purely_harmonic and self.ext_basis == "harmonic": | ||
@@ -895,6 +932,5 @@ # using the default methods | ||
| self._set_vars() # setting the attribute vars to store operator symbols | ||
| self.operators_by_name = self.set_operators() | ||
| self.operators_by_name = self._set_operators() | ||
| else: | ||
| # list for updating necessary subsystems when calling build hilbertspace | ||
| self.affected_subsystem_indices = [] | ||
| self.operators_by_name = None | ||
@@ -911,4 +947,4 @@ self.system_hierarchy = system_hierarchy | ||
| self.ext_basis = ext_basis | ||
| self.generate_hamiltonian_sym_for_numerics() | ||
| self.generate_subsystems(subsys_dict=subsys_dict) | ||
| self._generate_hamiltonian_sym_for_numerics() | ||
| self._generate_subsystems(subsys_dict=subsys_dict) | ||
| self.ext_basis = ( | ||
@@ -919,5 +955,5 @@ self.get_ext_basis() | ||
| self._check_truncation_indices() | ||
| self.operators_by_name = self.set_operators() | ||
| self.operators_by_name = self._set_operators() | ||
| self.affected_subsystem_indices = list(range(len(self.subsystems))) | ||
| self.update_interactions() | ||
| self._update_interactions() | ||
@@ -935,3 +971,3 @@ # clear unnecessary attribs | ||
| closure_branches: Optional[List[Union[Branch, Dict[Branch, float]]]] = None, | ||
| ext_basis: Optional[str] = None, | ||
| ext_basis: Optional[Union[str, List[str]]] = None, | ||
| use_dynamic_flux_grouping: Optional[bool] = None, | ||
@@ -941,4 +977,3 @@ subsys_dict: Optional[Dict[str, Any]] = None, | ||
| ): | ||
| """ | ||
| Method which re-initializes a circuit instance to update, hierarchical | ||
| """Method which re-initializes a circuit instance to update, hierarchical | ||
| diagonalization parameters or closure branches or the variable transformation | ||
@@ -967,3 +1002,3 @@ used to describe the circuit. | ||
| 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. | ||
| 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: | ||
@@ -1104,3 +1139,3 @@ set to False by default. Indicates if the noise methods should be generated for the circuit instance. | ||
| self.ext_basis = "harmonic" | ||
| self.generate_hamiltonian_sym_for_numerics() | ||
| self._generate_hamiltonian_sym_for_numerics() | ||
| self.ext_basis = ext_basis or self.ext_basis | ||
@@ -1115,3 +1150,2 @@ if self.is_purely_harmonic and self.ext_basis == "harmonic": | ||
| # list for updating necessary subsystems when calling build hilbertspace | ||
| self.affected_subsystem_indices = [] | ||
| self.operators_by_name = None | ||
@@ -1126,7 +1160,7 @@ self.system_hierarchy = system_hierarchy | ||
| self.subsystem_trunc_dims = subsystem_trunc_dims | ||
| self.generate_hamiltonian_sym_for_numerics() | ||
| self._generate_hamiltonian_sym_for_numerics() | ||
| self.ext_basis = ext_basis or self.ext_basis | ||
| self.generate_subsystems(subsys_dict=subsys_dict) | ||
| self._generate_subsystems(subsys_dict=subsys_dict) | ||
| self.ext_basis = self.get_ext_basis() | ||
| self.update_interactions() | ||
| self._update_interactions() | ||
| self._check_truncation_indices() | ||
@@ -1136,3 +1170,3 @@ self.affected_subsystem_indices = list(range(len(self.subsystems))) | ||
| self._set_vars() # setting the attribute vars to store operator symbols | ||
| self.operators_by_name = self.set_operators() | ||
| self.operators_by_name = self._set_operators() | ||
| # clear unnecessary attribs | ||
@@ -1146,3 +1180,3 @@ self._clear_unnecessary_attribs() | ||
| def supported_noise_channels(self) -> List[str]: | ||
| """Return a list of supported noise channels""" | ||
| """Return a list of supported noise channels.""" | ||
| if not hasattr(self, "_noise_methods_generated"): | ||
@@ -1170,5 +1204,3 @@ raise Exception( | ||
| def variable_transformation(self, new_vars_to_node_vars=True) -> None: | ||
| """ | ||
| Prints the variable transformation used in this circuit | ||
| """ | ||
| """Prints the variable transformation used in this circuit.""" | ||
| trans_mat = self.transformation_matrix | ||
@@ -1210,4 +1242,4 @@ if new_vars_to_node_vars: | ||
| ) -> Union[sm.Expr, None]: | ||
| """ | ||
| Method that gives a user readable symbolic Lagrangian for the current instance | ||
| """Method that gives a user readable symbolic Lagrangian for the current | ||
| instance. | ||
@@ -1293,5 +1325,4 @@ Parameters | ||
| def sym_external_fluxes(self) -> Dict[sm.Expr, Tuple["Branch", List["Branch"]]]: | ||
| """ | ||
| Method returns a dictionary of Human readable external fluxes with associated | ||
| branches and loops (represented as lists of branches) for the current instance | ||
| """Method returns a dictionary of Human readable external fluxes with associated | ||
| branches and loops (represented as lists of branches) for the current instance. | ||
@@ -1321,8 +1352,7 @@ Returns | ||
| def oscillator_list(self, osc_index_list: List[int]): | ||
| """ | ||
| If hierarchical diagonalization is used, specify subsystems that corresponds to | ||
| single-mode oscillators, if there is any. The attributes `_osc_subsys_list` and | ||
| `osc_subsys_list` of the `hilbert_space` attribute of the Circuit instance will | ||
| be assigned accordingly, enabling the correct identification of harmonic modes | ||
| for the dispersive regime analysis in ParameterSweep. | ||
| """If hierarchical diagonalization is used, specify subsystems that corresponds | ||
| to single-mode oscillators, if there is any. The attributes `_osc_subsys_list` | ||
| and `osc_subsys_list` of the :attr:`hilbert_space` attribute of the Circuit instance | ||
| will be assigned accordingly, enabling the correct identification of harmonic | ||
| modes for the dispersive regime analysis in ParameterSweep. | ||
@@ -1344,3 +1374,3 @@ Parameters | ||
| raise Exception( | ||
| f"the subsystem has more than one harmonic oscillator mode" | ||
| "the subsystem has more than one harmonic oscillator mode" | ||
| ) | ||
@@ -1352,8 +1382,7 @@ else: | ||
| def qubit_list(self, qbt_index_list: List[int]): | ||
| """ | ||
| If hierarchical diagonalization is used, specify subsystems that corresponds to | ||
| single-mode oscillators, if there is any. The attributes `_osc_subsys_list` and | ||
| `osc_subsys_list` of the `hilbert_space` attribute of the Circuit instance will | ||
| be assigned accordingly, enabling the correct identification of harmonic modes | ||
| for the dispersive regime analysis in ParameterSweep. | ||
| """If hierarchical diagonalization is used, specify subsystems that corresponds | ||
| to single-mode oscillators, if there is any. The attributes `_osc_subsys_list` | ||
| and `osc_subsys_list` of the `hilbert_space` attribute of the Circuit instance | ||
| will be assigned accordingly, enabling the correct identification of harmonic | ||
| modes for the dispersive regime analysis in ParameterSweep. | ||
@@ -1360,0 +1389,0 @@ Parameters |
@@ -0,0 +0,0 @@ # constants.py |
@@ -23,5 +23,3 @@ # descriptors.py | ||
| class ReadOnlyProperty(Generic[TargetType]): | ||
| """ | ||
| Descriptor for read-only properties (stored in xxx._name) | ||
| """ | ||
| """Descriptor for read-only properties (stored in xxx._name)""" | ||
@@ -44,6 +42,5 @@ def __init__(self, target_type: Type[TargetType]): | ||
| class WatchedProperty(Generic[TargetType]): | ||
| """ | ||
| Descriptor class for properties that are to be monitored for changes. Upon change | ||
| """Descriptor class for properties that are to be monitored for changes. Upon change | ||
| of the value, the instance class invokes its `broadcast()` method to send the | ||
| appropriate event notification to CentralDispatch | ||
| appropriate event notification to CentralDispatch. | ||
@@ -50,0 +47,0 @@ Parameters |
+44
-71
@@ -34,8 +34,6 @@ # diag.py | ||
| ) -> 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 | ||
| """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. | ||
@@ -56,3 +54,2 @@ Parameters | ||
| merged dictionary | ||
| """ | ||
@@ -72,6 +69,5 @@ exclude = [] if exclude is None else exclude | ||
| ) -> Union[ndarray, csc_matrix, Qobj]: | ||
| """ | ||
| Casts a given operator (possibly given as a `Qobj`) into a | ||
| required form ('sparse' or 'dense' numpy array or scipy martrix) as | ||
| defined by `cast_to` parameter. | ||
| """Casts a given operator (possibly given as a `Qobj`) into a required form | ||
| ('sparse' or 'dense' numpy array or scipy martrix) as defined by `cast_to` | ||
| parameter. | ||
@@ -103,3 +99,2 @@ Operators of the type `Qobj` are first converted to a `ndarray` or a | ||
| matrix in the sparse or dense form | ||
| """ | ||
@@ -138,7 +133,6 @@ if cast_to not in ["sparse", "dense"]: | ||
| 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`. | ||
| """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`. | ||
@@ -157,3 +151,2 @@ Parameters | ||
| eigenvectors represented in terms of Qobjs | ||
| """ | ||
@@ -181,5 +174,4 @@ evecs_count = evecs.shape[1] | ||
| ) -> ndarray: | ||
| """ | ||
| Diagonalization based on scipy's (dense) `eigh` function. | ||
| Only evals are returned. | ||
| """Diagonalization based on scipy's (dense) `eigh` function. Only evals are | ||
| returned. | ||
@@ -198,3 +190,2 @@ Parameters | ||
| eigenvalues of matrix | ||
| """ | ||
@@ -212,5 +203,4 @@ m = _cast_matrix(matrix, "dense") | ||
| ) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]: | ||
| """ | ||
| Diagonalization based on scipy's (dense) eigh function. | ||
| Both evals and evecs are returned. | ||
| """Diagonalization based on scipy's (dense) eigh function. Both evals and evecs are | ||
| returned. | ||
@@ -229,3 +219,2 @@ Parameters | ||
| a tuple of eigenvalues and eigenvectors. Eigenvectors are Qobjs if matrix is a Qobj instance | ||
| """ | ||
@@ -246,5 +235,4 @@ m = _cast_matrix(matrix, "dense") | ||
| ) -> ndarray: | ||
| """ | ||
| Diagonalization based on scipy's (sparse) `eigsh` function. | ||
| Only evals are returned. | ||
| """Diagonalization based on scipy's (sparse) `eigsh` function. Only evals are | ||
| returned. | ||
@@ -288,5 +276,4 @@ Note the convoluted convention when it comes to ordering and how it is related | ||
| ) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]: | ||
| """ | ||
| Diagonalization based on scipy's (sparse) `eigsh` function. | ||
| Both evals and evecs are returned. | ||
| """Diagonalization based on scipy's (sparse) `eigsh` function. Both evals and evecs | ||
| are returned. | ||
@@ -319,3 +306,2 @@ Note the convoluted convention when it comes to ordering and how it is related | ||
| a tuple of eigenvalues and eigenvectors. Eigenvectors are Qobjs if matrix is a Qobj instance | ||
| """ | ||
@@ -351,5 +337,4 @@ m = _cast_matrix(matrix, "sparse") | ||
| ) -> ndarray: | ||
| """ | ||
| Diagonalization based on primme's (sparse) `eigsh` function. | ||
| Only evals are returned. | ||
| """Diagonalization based on primme's (sparse) `eigsh` function. Only evals are | ||
| returned. | ||
@@ -370,3 +355,2 @@ Requires that the primme library is installed. | ||
| eigenvalues of matrix | ||
| """ | ||
@@ -397,5 +381,4 @@ try: | ||
| ) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]: | ||
| """ | ||
| Diagonalization based on primme's (sparse) `eigsh` function. | ||
| Both evals and evecs are returned. | ||
| """Diagonalization based on primme's (sparse) `eigsh` function. Both evals and evecs | ||
| are returned. | ||
@@ -448,5 +431,4 @@ Requires that the primme library is installed. | ||
| ) -> ndarray: | ||
| """ | ||
| Diagonalization based on cupy's (dense) `eighvalsh` function | ||
| Only evals are returned. | ||
| """Diagonalization based on cupy's (dense) `eighvalsh` function Only evals are | ||
| returned. | ||
@@ -467,3 +449,2 @@ Requires that the cupy library is installed. | ||
| eigenvalues of matrix | ||
| """ | ||
@@ -486,5 +467,4 @@ try: | ||
| ) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]: | ||
| """ | ||
| Diagonalization based on cupy's (dense) `eigh` function. | ||
| Both evals and evecs are returned. | ||
| """Diagonalization based on cupy's (dense) `eigh` function. Both evals and evecs are | ||
| returned. | ||
@@ -505,3 +485,2 @@ Requires that the cupy library is installed. | ||
| a tuple of eigenvalues and eigenvectors. Eigenvectors are Qobjs if matrix is a Qobj instance | ||
| """ | ||
@@ -530,5 +509,4 @@ try: | ||
| ) -> ndarray: | ||
| """ | ||
| Diagonalization based on cupy's (sparse) `eigsh` function. | ||
| Only evals are returned. | ||
| """Diagonalization based on cupy's (sparse) `eigsh` function. Only evals are | ||
| returned. | ||
@@ -576,5 +554,4 @@ Requires that the cupy (and cupyx) library is installed. | ||
| ) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]: | ||
| """ | ||
| Diagonalization based on cupy's (sparse) eigsh function. | ||
| Both evals and evecs are returned. | ||
| """Diagonalization based on cupy's (sparse) eigsh function. Both evals and evecs are | ||
| returned. | ||
@@ -630,5 +607,4 @@ Requires that the cupy library is installed. | ||
| ) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]: | ||
| """ | ||
| Diagonalization based on jax's (dense) jax.scipy.linalg.eigh function. | ||
| Only eigenvalues are returned. | ||
| """Diagonalization based on jax's (dense) jax.scipy.linalg.eigh function. Only | ||
| eigenvalues are returned. | ||
@@ -654,3 +630,2 @@ If available, different backends/devics (e.g., particular GPUs) can be set | ||
| eigenvalues of matrix | ||
| """ | ||
@@ -679,5 +654,4 @@ try: | ||
| ) -> Union[Tuple[ndarray, ndarray], Tuple[ndarray, QutipEigenstates]]: | ||
| """ | ||
| Diagonalization based on jax's (dense) jax.scipy.linalg.eigh function. | ||
| Both evals and evecs are returned. | ||
| """Diagonalization based on jax's (dense) jax.scipy.linalg.eigh function. Both evals | ||
| and evecs are returned. | ||
@@ -703,3 +677,2 @@ If available, different backends/devics (e.g., particular GPUs) can be set | ||
| a tuple of eigenvalues and eigenvectors. Eigenvectors are Qobjs if matrix is a Qobj instance | ||
| """ | ||
@@ -746,3 +719,3 @@ try: | ||
| evals_count, | ||
| **_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True) | ||
| **_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True), | ||
| ), | ||
@@ -752,3 +725,3 @@ "esys_scipy_sparse_LA_shift-inverse": lambda matrix, evals_count, **kwargs: esys_scipy_sparse( | ||
| evals_count, | ||
| **_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True) | ||
| **_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True), | ||
| ), | ||
@@ -758,3 +731,3 @@ "evals_scipy_sparse_LM_shift-inverse": lambda matrix, evals_count, **kwargs: evals_scipy_sparse( | ||
| evals_count, | ||
| **_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True) | ||
| **_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True), | ||
| ), | ||
@@ -764,3 +737,3 @@ "esys_scipy_sparse_LM_shift-inverse": lambda matrix, evals_count, **kwargs: esys_scipy_sparse( | ||
| evals_count, | ||
| **_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True) | ||
| **_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True), | ||
| ), | ||
@@ -773,3 +746,3 @@ # primme sparse | ||
| evals_count=evals_count, | ||
| **_dict_merge(dict(which="SM"), kwargs, overwrite=True) | ||
| **_dict_merge(dict(which="SM"), kwargs, overwrite=True), | ||
| ), | ||
@@ -782,3 +755,3 @@ "esys_primme_sparse_SM": lambda matrix, evals_count, **kwargs: esys_primme_sparse( | ||
| evals_count=evals_count, | ||
| **_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True) | ||
| **_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True), | ||
| ), | ||
@@ -788,3 +761,3 @@ "esys_primme_sparse_LA_shift-inverse": lambda matrix, evals_count, **kwargs: esys_primme_sparse( | ||
| evals_count=evals_count, | ||
| **_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True) | ||
| **_dict_merge(dict(which="LA", sigma=0), kwargs, overwrite=True), | ||
| ), | ||
@@ -794,3 +767,3 @@ "evals_primme_sparse_LM_shift-inverse": lambda matrix, evals_count, **kwargs: evals_primme_sparse( | ||
| evals_count=evals_count, | ||
| **_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True) | ||
| **_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True), | ||
| ), | ||
@@ -800,3 +773,3 @@ "esys_primme_sparse_LM_shift-inverse": lambda matrix, evals_count, **kwargs: esys_primme_sparse( | ||
| evals_count=evals_count, | ||
| **_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True) | ||
| **_dict_merge(dict(which="LM", sigma=0), kwargs, overwrite=True), | ||
| ), | ||
@@ -803,0 +776,0 @@ # cupy dense |
@@ -50,9 +50,7 @@ # discretization.py | ||
| ) -> csc_matrix: | ||
| """ | ||
| Returns a dim x dim sparse matrix with constant diagonals of values `band_coeffs[ | ||
| """Returns a dim x dim sparse matrix with constant diagonals of values `band_coeffs[ | ||
| 0]`, `band_coeffs[1]`, ... along the (off-)diagonals specified by the offsets | ||
| `band_offsets[0]`, `band_offsets[1]`, ... The `has_corners` option allows | ||
| generation of band matrices with corner elements, in which lower off-diagonals | ||
| wrap into the top right corner and upper off-diagonals wrap into the bottom left | ||
| corner. | ||
| `band_offsets[0]`, `band_offsets[1]`, ... The `has_corners` option allows generation | ||
| of band matrices with corner elements, in which lower off-diagonals wrap into the | ||
| top right corner and upper off-diagonals wrap into the bottom left corner. | ||
@@ -141,2 +139,3 @@ Parameters | ||
| """Returns dict appropriate for creating/initializing a new Grid1d object. | ||
| Returns | ||
@@ -157,3 +156,4 @@ ------- | ||
| def make_linspace(self) -> ndarray: | ||
| """Returns a numpy array of the grid points | ||
| """Returns a numpy array of the grid points. | ||
| Returns | ||
@@ -168,4 +168,4 @@ ------- | ||
| ) -> csc_matrix: | ||
| """Generate sparse matrix for first derivative of the form | ||
| :math:`\\partial_{x_i}`. Uses STENCIL setting to construct the matrix with a | ||
| r"""Generate sparse matrix for first derivative of the form | ||
| :math:`\partial_{x_i}`. Uses STENCIL setting to construct the matrix with a | ||
| multi-point stencil. | ||
@@ -203,4 +203,4 @@ | ||
| ) -> csc_matrix: | ||
| """Generate sparse matrix for second derivative of the form | ||
| :math:`\\partial^2_{x_i}`. Uses STENCIL setting to construct the matrix with | ||
| r"""Generate sparse matrix for second derivative of the form | ||
| :math:`\partial^2_{x_i}`. Uses STENCIL setting to construct the matrix with | ||
| a multi-point stencil. | ||
@@ -237,4 +237,4 @@ | ||
| class GridSpec(dispatch.DispatchClient, serializers.Serializable): | ||
| """Class for specifying a general discretized coordinate grid | ||
| (arbitrary dimensions). | ||
| """Class for specifying a general discretized coordinate grid (arbitrary | ||
| dimensions). | ||
@@ -241,0 +241,0 @@ Parameters |
+100
-112
@@ -67,6 +67,5 @@ # flux_qubit.py | ||
| get_rate: bool = False, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> float: | ||
| r""" | ||
| Calculate the 1/f dephasing time (or rate) due to critical current noise of | ||
| r"""Calculate the 1/f dephasing time (or rate) due to critical current noise of | ||
| junction associated with Josephson energy :math:`EJ1`. | ||
@@ -89,5 +88,4 @@ | ||
| ------- | ||
| decoherence time in units of :math:`2\pi ({\rm system\,\,units})`, | ||
| decoherence time in units of :math:`2\pi` (system units), | ||
| or rate in inverse units. | ||
| """ | ||
@@ -107,3 +105,3 @@ if "tphi_1_over_f_cc1" not in self.supported_noise_channels(): | ||
| get_rate=get_rate, | ||
| **kwargs | ||
| **kwargs, | ||
| ) | ||
@@ -118,6 +116,5 @@ | ||
| get_rate: bool = False, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> float: | ||
| r""" | ||
| Calculate the 1/f dephasing time (or rate) due to critical current noise of | ||
| r"""Calculate the 1/f dephasing time (or rate) due to critical current noise of | ||
| junction associated with Josephson energy :math:`EJ2`. | ||
@@ -141,3 +138,3 @@ | ||
| :math:`T_{\phi}` time or rate: | ||
| decoherence time in units of :math:`2\pi ({\rm system\,\,units})`, or rate in inverse units. | ||
| decoherence time in units of :math:`2\pi` (system units), or rate in inverse units. | ||
| """ | ||
@@ -157,3 +154,3 @@ if "tphi_1_over_f_cc2" not in self.supported_noise_channels(): | ||
| get_rate=get_rate, | ||
| **kwargs | ||
| **kwargs, | ||
| ) | ||
@@ -168,7 +165,6 @@ | ||
| get_rate: bool = False, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> float: | ||
| r""" | ||
| Calculate the 1/f dephasing time (or rate) due to critical current noise of junction associated with | ||
| Josephson energy :math:`EJ3`. | ||
| r"""Calculate the 1/f dephasing time (or rate) due to critical current noise of | ||
| junction associated with Josephson energy :math:`EJ3`. | ||
@@ -190,3 +186,3 @@ Parameters | ||
| ------- | ||
| decoherence time in units of :math:`2\pi ({\rm system\,\,units})`, or rate in inverse units. | ||
| decoherence time in units of :math:`2\pi` (system units), or rate in inverse units. | ||
| """ | ||
@@ -206,3 +202,3 @@ if "tphi_1_over_f_cc3" not in self.supported_noise_channels(): | ||
| get_rate=get_rate, | ||
| **kwargs | ||
| **kwargs, | ||
| ) | ||
@@ -217,8 +213,7 @@ | ||
| get_rate: bool = False, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> float: | ||
| r""" | ||
| Calculate the 1/f dephasing time (or rate) due to critical-current noise | ||
| from all three Josephson junctions :math:`EJ1`, :math:`EJ2` and :math:`EJ3`. | ||
| The combined noise is calculated by summing the rates from the individual | ||
| r"""Calculate the 1/f dephasing time (or rate) due to critical-current noise from | ||
| all three Josephson junctions :math:`EJ1`, :math:`EJ2` and :math:`EJ3`. The | ||
| combined noise is calculated by summing the rates from the individual | ||
| contributions. | ||
@@ -269,3 +264,3 @@ | ||
| class FluxQubit(base.QubitBaseClass, serializers.Serializable, NoisyFluxQubit): | ||
| r"""Flux Qubit | ||
| r"""Flux Qubit. | ||
@@ -316,12 +311,12 @@ | [1] Orlando et al., Physical Review B, 60, 15398 (1999). | ||
| id_str: | ||
| optional string by which this instance can be referred to in `HilbertSpace` | ||
| optional string by which this instance can be referred to in :class:`HilbertSpace` | ||
| and `ParameterSweep`. If not provided, an id is auto-generated. | ||
| esys_method: | ||
| method for esys diagonalization, callable or string representation | ||
| esys_method_options: | ||
| dictionary with esys diagonalization options | ||
| evals_method: | ||
| method for evals diagonalization, callable or string representation | ||
| evals_method_options: | ||
| dictionary with evals diagonalization options | ||
| esys_method: | ||
| method for esys diagonalization, callable or string representation | ||
| esys_method_options: | ||
| dictionary with esys diagonalization options | ||
| evals_method: | ||
| method for evals diagonalization, callable or string representation | ||
| evals_method_options: | ||
| dictionary with evals diagonalization options | ||
| """ | ||
@@ -408,3 +403,3 @@ | ||
| def supported_noise_channels(cls) -> List[str]: | ||
| """Return a list of supported noise channels""" | ||
| """Return a list of supported noise channels.""" | ||
| return [ | ||
@@ -422,3 +417,3 @@ "tphi_1_over_f_cc1", | ||
| def EC_matrix(self) -> ndarray: | ||
| """Return the charging energy matrix""" | ||
| """Return the charging energy matrix.""" | ||
| Cmat = np.zeros((2, 2)) | ||
@@ -547,4 +542,3 @@ CJ1 = 1.0 / (2 * self.ECJ1) # capacitances in units where e is set to 1 | ||
| ) -> ndarray: | ||
| """ | ||
| Return Hamiltonian in the basis obtained by employing charge basis for both | ||
| """Return Hamiltonian in the basis obtained by employing charge basis for both | ||
| degrees of freedom or in the eigenenergy basis. | ||
@@ -563,4 +557,4 @@ | ||
| Hamiltonian in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, the Hamiltonian has dimensions of `truncated_dim` | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m, | ||
| unless `energy_esys` is specified, the Hamiltonian has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -576,4 +570,3 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator representing a derivative of the Hamiltonian with respect to | ||
| """Returns operator representing a derivative of the Hamiltonian with respect to | ||
| EJ1 in the native Hamiltonian basis or eigenenergy basis. | ||
@@ -592,4 +585,4 @@ | ||
| 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, | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -605,4 +598,3 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator representing a derivative of the Hamiltonian with respect to | ||
| """Returns operator representing a derivative of the Hamiltonian with respect to | ||
| EJ2 in the native Hamiltonian basis or eigenenergy basis. | ||
@@ -621,4 +613,4 @@ | ||
| 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, | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -634,4 +626,3 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator representing a derivative of the Hamiltonian with respect to | ||
| """Returns operator representing a derivative of the Hamiltonian with respect to | ||
| EJ3 in the native Hamiltonian basis or eigenenergy basis. | ||
@@ -650,4 +641,4 @@ | ||
| 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, | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -673,5 +664,4 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns the operator representing a derivative of the Hamiltonian with respect to flux | ||
| in the native Hamiltonian basis or eigenenergy basis. | ||
| """Returns the operator representing a derivative of the Hamiltonian with | ||
| respect to flux in the native Hamiltonian basis or eigenenergy basis. | ||
@@ -689,4 +679,4 @@ Parameters | ||
| 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, | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -731,4 +721,4 @@ """ | ||
| ) -> ndarray: | ||
| r""" | ||
| Returns the charge number operator conjugate to :math:`\phi_1` in the charge? or eigenenergy basis. | ||
| r"""Returns the charge number operator conjugate to :math:`\phi_1` in the charge? | ||
| or eigenenergy basis. | ||
@@ -746,4 +736,4 @@ Parameters | ||
| Charge number operator conjugate to :math:`\phi_1` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, for m given eigenvectors. | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, for m given eigenvectors. | ||
| """ | ||
@@ -756,4 +746,4 @@ native = np.kron(self._n_operator(), self._identity()) | ||
| ) -> ndarray: | ||
| r""" | ||
| Returns the charge number operator conjugate to :math:`\phi_2` in the charge? or eigenenergy basis. | ||
| r"""Returns the charge number operator conjugate to :math:`\phi_2` in the charge? | ||
| or eigenenergy basis. | ||
@@ -771,4 +761,4 @@ Parameters | ||
| Charge number operator conjugate to :math:`\phi_2` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, for m given eigenvectors. | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, for m given eigenvectors. | ||
| """ | ||
@@ -781,4 +771,3 @@ native = np.kron(self._identity(), self._n_operator()) | ||
| ) -> ndarray: | ||
| r""" | ||
| Returns operator :math:`e^{i\phi_1}` in the charge or eigenenergy basis. | ||
| r"""Returns operator :math:`e^{i\phi_1}` in the charge or eigenenergy basis. | ||
@@ -797,3 +786,3 @@ Parameters | ||
| unless energy_esys is specified, :math:`e^{i\phi_1}` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`e^{i\phi_1}` has dimensions of m x m, | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`e^{i\phi_1}` has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -807,4 +796,3 @@ """ | ||
| ) -> ndarray: | ||
| r""" | ||
| Returns operator :math:`e^{i\phi_2}` in the charge or eigenenergy basis. | ||
| r"""Returns operator :math:`e^{i\phi_2}` in the charge or eigenenergy basis. | ||
@@ -823,3 +811,3 @@ Parameters | ||
| unless energy_esys is specified, :math:`e^{i\phi_2}` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`e^{i\phi_2}` has dimensions of m x m, | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`e^{i\phi_2}` has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -833,4 +821,4 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator :math:`\\cos \\phi_1` in the charge or eigenenergy basis. | ||
| r""" | ||
| Returns operator :math:`\cos \phi_1` in the charge or eigenenergy basis. | ||
@@ -840,12 +828,12 @@ Parameters | ||
| energy_esys: | ||
| If `False` (default), returns operator :math:`\\cos \\phi_1` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\\cos \\phi_1` in the energy eigenbasis. | ||
| If `False` (default), returns operator :math:`\cos \phi_1` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\cos \phi_1` in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns operator :math:`\\cos \\phi_1` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| returns operator :math:`\cos \phi_1` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Operator :math:`\\cos \\phi_1` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\\cos \\phi_1` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\cos \\phi_1` has dimensions of m x m, | ||
| Operator :math:`\cos \phi_1` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\cos \phi_1` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\cos \phi_1` has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -861,4 +849,4 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator :math:`\\cos \\phi_2` in the charge or eigenenergy basis. | ||
| r""" | ||
| Returns operator :math:`\cos \phi_2` in the charge or eigenenergy basis. | ||
@@ -868,12 +856,12 @@ Parameters | ||
| energy_esys: | ||
| If `False` (default), returns operator :math:`\\cos \\phi_2` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\\cos \\phi_2` in the energy eigenbasis. | ||
| If `False` (default), returns operator :math:`\cos \phi_2` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\cos \phi_2` in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns operator :math:`\\cos \\phi_2` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| returns operator :math:`\cos \phi_2` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Operator :math:`\\cos \\phi_2` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\\cos \\phi_2` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\cos \\phi_2` has dimensions of m x m, | ||
| Operator :math:`\cos \phi_2` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\cos \phi_2` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\cos \phi_2` has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -889,4 +877,4 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator :math:`\\sin \\phi_1` in the charge or eigenenergy basis. | ||
| r""" | ||
| Returns operator :math:`\sin \phi_1` in the charge or eigenenergy basis. | ||
@@ -896,12 +884,12 @@ Parameters | ||
| energy_esys: | ||
| If `False` (default), returns operator :math:`\\sin \\phi_1` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\\sin \\phi_1` in the energy eigenbasis. | ||
| If `False` (default), returns operator :math:`\sin \phi_1` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\sin \phi_1` in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns operator :math:`\\sin \\phi_1` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| returns operator :math:`\sin \phi_1` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Operator :math:`\\sin \\phi_1` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\\sin \\phi_1` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\sin \\phi_1` has dimensions of m x m, | ||
| Operator :math:`\sin \phi_1` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\sin \phi_1` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\sin \phi_1` has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -917,4 +905,4 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator :math:`\\sin \\phi_2` in the charge or eigenenergy basis. | ||
| r""" | ||
| Returns operator :math:`\sin \phi_2` in the charge or eigenenergy basis. | ||
@@ -924,12 +912,12 @@ Parameters | ||
| energy_esys: | ||
| If `False` (default), returns operator :math:`\\sin \\phi_2` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\\sin \\phi_1` in the energy eigenbasis. | ||
| If `False` (default), returns operator :math:`\sin \phi_2` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\sin \phi_1` in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns operator :math:`\\sin \\phi_1` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| returns operator :math:`\sin \phi_1` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Operator :math:`\\sin \\phi_2` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\\sin \\phi_2` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\sin \\phi_2` has dimensions of m x m, | ||
| Operator :math:`\sin \phi_2` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\sin \phi_2` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\sin \phi_2` has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -946,5 +934,5 @@ """ | ||
| contour_vals: ndarray = None, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> Tuple[Figure, Axes]: | ||
| """ | ||
| r""" | ||
| Draw contour plot of the potential energy. | ||
@@ -955,3 +943,3 @@ | ||
| phi_grid: | ||
| used for setting a custom grid for phi; if None use self._default_grid | ||
| used for setting a custom grid for :math:`\phi`; if None use self._default_grid | ||
| contour_vals: | ||
@@ -976,4 +964,4 @@ specific contours to draw | ||
| ) -> storage.WaveFunctionOnGrid: | ||
| """ | ||
| Return a flux qubit wave function in phi1, phi2 basis | ||
| r""" | ||
| Return a flux qubit wave function in :math:`\phi_1`, :math:`\phi2` basis | ||
@@ -987,3 +975,3 @@ Parameters | ||
| phi_grid: | ||
| used for setting a custom grid for phi; if None use self._default_grid | ||
| used for setting a custom grid for :math:`\phi`; if None use self._default_grid | ||
| """ | ||
@@ -1025,5 +1013,5 @@ evals_count = max(which + 1, 3) | ||
| zero_calibrate: bool = True, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> Tuple[Figure, Axes]: | ||
| """Plots 2d phase-basis wave function. | ||
| r"""Plots 2d phase-basis wave function. | ||
@@ -1035,8 +1023,8 @@ Parameters | ||
| which: | ||
| index of wave function to be plotted (default value = (0) | ||
| index of wave function to be plotted (default value = 0) | ||
| phi_grid: | ||
| used for setting a custom grid for phi; if None use self._default_grid | ||
| used for setting a custom grid for :math:`\phi`; if None use self._default_grid | ||
| mode: | ||
| choices as specified in `constants.MODE_FUNC_DICT` | ||
| (default value = 'abs_sqr') | ||
| (default value = 'abs') | ||
| zero_calibrate: | ||
@@ -1043,0 +1031,0 @@ if True, colors are adjusted to use zero wavefunction amplitude as the |
@@ -61,3 +61,3 @@ # fluxonium.py | ||
| id_str: str | ||
| optional string by which this instance can be referred to in `HilbertSpace` | ||
| optional string by which this instance can be referred to in :class:`HilbertSpace` | ||
| and `ParameterSweep`. If not provided, an id is auto-generated. | ||
@@ -123,3 +123,3 @@ esys_method: | ||
| def supported_noise_channels(cls) -> List[str]: | ||
| """Return a list of supported noise channels""" | ||
| """Return a list of supported noise channels.""" | ||
| return [ | ||
@@ -163,4 +163,3 @@ "tphi_1_over_f_cc", | ||
| ) -> ndarray: | ||
| """ | ||
| Returns the phi operator in the LC harmonic oscillator or eigenenergy basis. | ||
| """Returns the phi operator in the LC harmonic oscillator or eigenenergy basis. | ||
@@ -179,3 +178,3 @@ Parameters | ||
| unless energy_esys is specified, phi operator has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, phi operator has dimensions of m x m, | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, phi operator has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -195,4 +194,4 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns the :math:`n = - i d/d\\phi` operator in the LC harmonic oscillator or eigenenergy basis. | ||
| r""" | ||
| Returns the :math:`n = - i d/d\phi` operator in the LC harmonic oscillator or eigenenergy basis. | ||
@@ -202,12 +201,12 @@ Parameters | ||
| energy_esys: | ||
| If `False` (default), returns the :math:`n = - i d/d\\phi` operator in the LC harmonic oscillator basis. | ||
| If `True`, the energy eigenspectrum is computed, returns the :math:`n = - i d/d\\phi` operator in the energy eigenbasis. | ||
| If `False` (default), returns the :math:`n = - i d/d\phi` operator in the LC harmonic oscillator basis. | ||
| If `True`, the energy eigenspectrum is computed, returns the :math:`n = - i d/d\phi` operator in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns the :math:`n = - i d/d\\phi` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| returns the :math:`n = - i d/d\phi` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Operator :math:`n = - i d/d\\phi` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`n = - i d/d\\phi` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`n = - i d/d\\phi` has dimensions of | ||
| Operator :math:`n = - i d/d\phi` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`n = - i d/d\phi` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`n = - i d/d\phi` has dimensions of | ||
| m x m, for m given eigenvectors. | ||
@@ -229,4 +228,4 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns the :math:`e^{i (\\alpha \\phi + \\beta) }` operator, with :math:`\\alpha` and :math:`\\beta` being | ||
| r""" | ||
| Returns the :math:`e^{i (\alpha \phi + \beta) }` operator, with :math:`\alpha` and :math:`\beta` being | ||
| numbers, in the LC harmonic oscillator or eigenenergy basis. | ||
@@ -237,7 +236,7 @@ | ||
| energy_esys: | ||
| If `False` (default), returns the :math:`e^{i (\\alpha \\phi + \\beta) }` operator in the LC harmonic | ||
| If `False` (default), returns the :math:`e^{i (\alpha \phi + \beta) }` operator in the LC harmonic | ||
| oscillator basis. If `True`, the energy eigenspectrum is computed, returns the | ||
| :math:`e^{i (\\alpha \\phi + \\beta) }` operator in the energy eigenbasis. | ||
| :math:`e^{i (\alpha \phi + \beta) }` operator in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns the :math:`e^{i (\\alpha \\phi + \\beta) }` operator in the energy eigenbasis, and does not have to | ||
| returns the :math:`e^{i (\alpha \phi + \beta) }` operator in the energy eigenbasis, and does not have to | ||
| recalculate eigenspectrum. | ||
@@ -247,6 +246,6 @@ | ||
| ------- | ||
| Operator :math:`e^{i (\\alpha \\phi + \\beta) }` in chosen basis as ndarray. If the eigenenergy basis is | ||
| chosen, unless energy_esys is specified, :math:`e^{i (\\alpha \\phi + \\beta) }` has dimensions of | ||
| Operator :math:`e^{i (\alpha \phi + \beta) }` in chosen basis as ndarray. If the eigenenergy basis is | ||
| chosen, unless energy_esys is specified, :math:`e^{i (\alpha \phi + \beta) }` has dimensions of | ||
| `truncated_dim`x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, | ||
| :math:`e^{i (\\alpha \\phi + \\beta) }` has dimensions of m x m, for m given eigenvectors. | ||
| :math:`e^{i (\alpha \phi + \beta) }` has dimensions of m x m, for m given eigenvectors. | ||
| """ | ||
@@ -263,4 +262,4 @@ exponent = 1j * (alpha * self.phi_operator()) | ||
| ) -> ndarray: | ||
| """ | ||
| Returns the :math:`\\cos (\\alpha \\phi + \\beta)` operator with :math:`\\alpha` and :math:`\\beta` being | ||
| r""" | ||
| Returns the :math:`\cos (\alpha \phi + \beta)` operator with :math:`\alpha` and :math:`\beta` being | ||
| numbers, in the LC harmonic oscillator or eigenenergy basis. | ||
@@ -271,12 +270,12 @@ | ||
| energy_esys: | ||
| If `False` (default), returns the :math:`\\cos (\\alpha \\phi + \\beta)` operator in the LC harmonic oscillator basis. | ||
| If `True`, the energy eigenspectrum is computed, returns the :math:`\\cos (\\alpha \\phi + \\beta)` operator in the energy eigenbasis. | ||
| If `False` (default), returns the :math:`\cos (\alpha \phi + \beta)` operator in the LC harmonic oscillator basis. | ||
| If `True`, the energy eigenspectrum is computed, returns the :math:`\cos (\alpha \phi + \beta)` operator in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns the :math:`\\cos (\\alpha \\phi + \\beta)` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| returns the :math:`\cos (\alpha \phi + \beta)` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Operator :math:`\\cos (\\alpha \\phi + \\beta)` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\\cos (\\alpha \\phi + \\beta)` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\cos (\\alpha \\phi + \\beta)` has dimensions of m x m, for m given eigenvectors. | ||
| Operator :math:`\cos (\alpha \phi + \beta)` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\cos (\alpha \phi + \beta)` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\cos (\alpha \phi + \beta)` has dimensions of m x m, for m given eigenvectors. | ||
| """ | ||
@@ -293,4 +292,4 @@ argument = alpha * self.phi_operator() + beta * np.eye(self.hilbertdim()) | ||
| ) -> ndarray: | ||
| """ | ||
| Returns the :math:`\\sin (\\alpha \\phi + \\beta)` operator with :math:`\\alpha` and :math:`\\beta` being | ||
| r""" | ||
| Returns the :math:`\sin (\alpha \phi + \beta)` operator with :math:`\alpha` and :math:`\beta` being | ||
| numbers, in the LC harmonic oscillator or eigenenergy basis. | ||
@@ -301,12 +300,12 @@ | ||
| energy_esys: | ||
| If `False` (default), returns the :math:`\\sin (\\alpha \\phi + \\beta)` operator in the LC harmonic oscillator basis. | ||
| If `True`, the energy eigenspectrum is computed, returns the :math:`\\sin (\\alpha \\phi + \\beta)` operator in the energy eigenbasis. | ||
| If `False` (default), returns the :math:`\sin (\alpha \phi + \beta)` operator in the LC harmonic oscillator basis. | ||
| If `True`, the energy eigenspectrum is computed, returns the :math:`\sin (\alpha \phi + \beta)` operator in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns the :math:`\\sin (\\alpha \\phi + \\beta)` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| returns the :math:`\sin (\alpha \phi + \beta)` operator in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Operator :math:`\\sin (\\alpha \\phi + \\beta)` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\\sin (\\alpha \\phi + \\beta)` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\sin (\\alpha \\phi + \\beta)` has dimensions of m x m, for m given eigenvectors. | ||
| Operator :math:`\sin (\alpha \phi + \beta)` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\sin (\alpha \phi + \beta)` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\sin (\alpha \phi + \beta)` has dimensions of m x m, for m given eigenvectors. | ||
| """ | ||
@@ -320,5 +319,4 @@ argument = alpha * self.phi_operator() + beta * np.eye(self.hilbertdim()) | ||
| ) -> ndarray: # follow Zhu et al., PRB 87, 024510 (2013) | ||
| """ | ||
| Constructs Hamiltonian matrix in harmonic-oscillator, following Zhu | ||
| et al., PRB 87, 024510 (2013), or eigenenergy basis. | ||
| """Constructs Hamiltonian matrix in harmonic-oscillator, following Zhu et al., | ||
| PRB 87, 024510 (2013), or eigenenergy basis. | ||
@@ -336,4 +334,4 @@ Parameters | ||
| Hamiltonian in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, the Hamiltonian has dimensions of `truncated_dim` | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m, | ||
| unless `energy_esys` is specified, the Hamiltonian has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -355,5 +353,5 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator representing a derivative of the Hamiltonian with respect to | ||
| EJ in the harmonic-oscillator or eigenenergy basis. The flux is grouped as in the Hamiltonian. | ||
| """Returns operator representing a derivative of the Hamiltonian with respect to | ||
| EJ in the harmonic-oscillator or eigenenergy basis. The flux is grouped as in | ||
| the Hamiltonian. | ||
@@ -371,4 +369,4 @@ Parameters | ||
| 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, | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -380,15 +378,8 @@ """ | ||
| # def d_hamiltonian_d_flux(self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False) -> ndarray: | ||
| # """Returns operator representing a derivative of the Hamiltonian with respect | ||
| # to flux. | ||
| # | ||
| # The flux is grouped as in the Hamiltonian.""" | ||
| # return -2 * np.pi * self.EJ * self.sin_phi_operator(1, 2 * np.pi * self.flux, energy_esys=energy_esys) | ||
| def d_hamiltonian_d_flux( | ||
| self, energy_esys: Union[bool, Tuple[ndarray, ndarray]] = False | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator representing a derivative of the Hamiltonian with respect to | ||
| flux in the harmonic-oscillator or eigenenergy basis. The flux is grouped as in the Hamiltonian. | ||
| """Returns operator representing a derivative of the Hamiltonian with respect to | ||
| flux in the harmonic-oscillator or eigenenergy basis. The flux is grouped as in | ||
| the Hamiltonian. | ||
@@ -406,4 +397,4 @@ Parameters | ||
| 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, | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -422,7 +413,7 @@ """ | ||
| def potential(self, phi: Union[float, ndarray]) -> ndarray: | ||
| """Fluxonium potential evaluated at `phi`. | ||
| r"""Fluxonium potential evaluated at :math:`\phi`. | ||
| Parameters | ||
| ---------- | ||
| float value of the phase variable `phi` | ||
| float value of the phase variable :math:`\phi` | ||
@@ -443,3 +434,3 @@ Returns | ||
| ) -> storage.WaveFunction: | ||
| """Returns a fluxonium wave function in `phi` basis | ||
| r"""Returns a fluxonium wave function in :math:`\phi` basis | ||
@@ -453,3 +444,3 @@ Parameters | ||
| phi_grid: | ||
| used for setting a custom grid for phi; if None use self._default_grid | ||
| used for setting a custom grid for :math:`\phi`; if None use self._default_grid | ||
| """ | ||
@@ -456,0 +447,0 @@ if esys is None: |
@@ -41,3 +41,3 @@ # generic_qubit.py | ||
| id_str: | ||
| optional string by which this instance can be referred to in `HilbertSpace` | ||
| optional string by which this instance can be referred to in :class:`HilbertSpace` | ||
| and `ParameterSweep`. If not provided, an id is auto-generated. | ||
@@ -64,3 +64,3 @@ """ | ||
| def hilbertdim(self) -> int: | ||
| """Returns Hilbert space dimension""" | ||
| """Returns Hilbert space dimension.""" | ||
| return 2 | ||
@@ -67,0 +67,0 @@ |
@@ -29,2 +29,3 @@ # hilbert_space.py | ||
| Union, | ||
| Literal, | ||
| cast, | ||
@@ -139,5 +140,4 @@ overload, | ||
| ) -> qt.Qobj: | ||
| """ | ||
| Returns the full Hamiltonian of the interacting quantum system described by the | ||
| HilbertSpace object | ||
| """Returns the full Hamiltonian of the interacting quantum system described by | ||
| the HilbertSpace object. | ||
@@ -173,4 +173,3 @@ Parameters | ||
| ) -> List[qt.Qobj]: | ||
| """ | ||
| Returns a list of identity-wrapped operators, one for each operator in | ||
| """Returns a list of identity-wrapped operators, one for each operator in | ||
| operator_list. Note: at this point, any callable operator is actually evaluated. | ||
@@ -192,3 +191,2 @@ | ||
| list of identity-wrapped operators | ||
| """ | ||
@@ -330,3 +328,7 @@ id_wrapped_operators = [] | ||
| idwrapped_ops_by_name[name] = spec_utils.identity_wrap( | ||
| op, subsys_list[subsys_index], subsys_list, evecs=evecs | ||
| op, | ||
| subsys_list[subsys_index], | ||
| subsys_list, | ||
| evecs=evecs, | ||
| op_in_eigenbasis=False, | ||
| ) | ||
@@ -371,6 +373,6 @@ return idwrapped_ops_by_name | ||
| multiple subsystems. The class provides methods to turn subsystem operators into | ||
| operators acting on the full Hilbert space, and establishes the interface to | ||
| qutip. Returned operators are of the `qutip.Qobj` type. The class also provides | ||
| methods for obtaining eigenvalues, absorption and emission spectra as a function | ||
| of an external parameter. | ||
| operators acting on the full Hilbert space, and establishes the interface to qutip. | ||
| Returned operators are of the :py:obj:`~qutip.Qobj` type. The class also provides methods for | ||
| obtaining eigenvalues, absorption and emission spectra as a function of an external | ||
| parameter. | ||
@@ -384,3 +386,3 @@ Parameters | ||
| `add_interaction` method. Alternatively, a list of interaction term objects | ||
| can be supplied here upon initialization of a `HilbertSpace` instance. | ||
| can be supplied here upon initialization of a :class:`HilbertSpace` instance. | ||
| esys_method: | ||
@@ -518,4 +520,3 @@ method for esys diagonalization, callable or string representation | ||
| """[Legacy] Auxiliary reference to self for compatibility with | ||
| SpectrumLookupMixin | ||
| class.""" | ||
| SpectrumLookupMixin class.""" | ||
| return self | ||
@@ -538,6 +539,4 @@ | ||
| def deserialize(cls, io_data: "IOData") -> HilbertSpace: | ||
| """ | ||
| Take the given IOData and return an instance of the described class, | ||
| initialized with the data stored in io_data. | ||
| """ | ||
| """Take the given IOData and return an instance of the described class, | ||
| initialized with the data stored in io_data.""" | ||
| alldata_dict = io_data.as_kwargs() | ||
@@ -551,5 +550,3 @@ alldata_dict["ignore_low_overlap"] = alldata_dict.pop("_ignore_low_overlap") | ||
| def serialize(self) -> "IOData": | ||
| """ | ||
| Convert the content of the current class instance into IOData format. | ||
| """ | ||
| """Convert the content of the current class instance into IOData format.""" | ||
| init_parameters = self._init_params.copy() | ||
@@ -603,5 +600,3 @@ init_parameters.remove("ignore_low_overlap") | ||
| def get_subsys_index(self, subsys: QuantumSys) -> int: | ||
| """ | ||
| Return the index of the given subsystem in the HilbertSpace. | ||
| """ | ||
| """Return the index of the given subsystem in the HilbertSpace.""" | ||
| return self._subsystems.index(subsys) | ||
@@ -615,3 +610,3 @@ | ||
| def subsystem_dims(self) -> List[int]: | ||
| """Returns list of the Hilbert space dimensions of each subsystem""" | ||
| """Returns list of the Hilbert space dimensions of each subsystem.""" | ||
| return [subsystem.truncated_dim for subsystem in self] | ||
@@ -621,3 +616,3 @@ | ||
| def dimension(self) -> int: | ||
| """Returns total dimension of joint Hilbert space""" | ||
| """Returns total dimension of joint Hilbert space.""" | ||
| return np.prod(np.asarray(self.subsystem_dims)).item() | ||
@@ -627,3 +622,3 @@ | ||
| def subsystem_count(self) -> int: | ||
| """Returns number of subsystems composing the joint Hilbert space""" | ||
| """Returns number of subsystems composing the joint Hilbert space.""" | ||
| return len(self._subsystems) | ||
@@ -634,3 +629,51 @@ | ||
| ################################################################################### | ||
| def generate_lookup(self, update_subsystem_indices: List[int] = None) -> None: | ||
| def generate_lookup( | ||
| self, | ||
| ordering: Literal["DE", "LX", "BE"] = "DE", | ||
| subsys_priority: Union[List[int], None] = None, | ||
| BEs_count: Union[int, None] = None, | ||
| update_subsystem_indices: Union[List[int], None] = None, | ||
| ) -> None: | ||
| """ | ||
| Label the dressed states by bare labels and generate the lookup table | ||
| with one of the following methods: | ||
| - Dressed Energy (ordering="DE"): traverse the eigenstates | ||
| in the order of their dressed energy, and find the corresponding bare | ||
| state label by overlaps (default) | ||
| - Lexical (ordering="LX"): traverse the bare states in `lexical order`_, | ||
| and perform the branch analysis generalized from Dumas et al. (2024). | ||
| - Bare Energy (ordering="BE"): traverse the bare states in the order of | ||
| their energy before coupling and perform label assignment. This is particularly | ||
| useful when the Hilbert space is too large and not all the eigenstates need | ||
| to be labeled. | ||
| Parameters | ||
| ---------- | ||
| ordering: | ||
| the ordering method for the labeling | ||
| - "DE": Dressed Energy (default) | ||
| - "LX": Lexical ordering | ||
| - "BE": Bare Energy | ||
| subsys_priority: | ||
| a permutation of the subsystem indices and bare labels. If it is provided, | ||
| lexical ordering is performed on the permuted labels. A "branch" is defined | ||
| as a series of eigenstates formed by putting excitations into the last | ||
| subsystem in the list. | ||
| BEs_count: | ||
| the number of eigenstates to be assigned, for "BE" scheme only. If None, | ||
| all eigenstates will be generated and labeled. | ||
| Returns | ||
| ------- | ||
| a NamedSlotsNdarray object containing the branch analysis results | ||
| organized by the parameter indices. | ||
| For each parameter point, a flattened multi-dimensional array | ||
| is stored, representing the dressed indices organized by the | ||
| bare indices. E.g. if the dimensions of the subsystems are D0, D1 and D2, | ||
| the returned array will be ravelled from the shape (D0, D1, D2). | ||
| .. _lexical order: https://en.wikipedia.org/wiki/Lexicographic_order#Cartesian_products/ | ||
| """ | ||
| self._lookup_exists = True | ||
@@ -642,5 +685,8 @@ bare_esys_dict = self.generate_bare_esys( | ||
| evals, evecs = self.eigensys( | ||
| evals_count=self.dimension, bare_esys=bare_esys_dict | ||
| ) | ||
| if ordering == "DE" or ordering == "LX" or BEs_count is None: | ||
| num_evals = self.dimension | ||
| else: | ||
| num_evals = BEs_count | ||
| evals, evecs = self.eigensys(evals_count=num_evals, bare_esys=bare_esys_dict) | ||
| # The following workaround ensures that eigenvectors maintain QutipEigenstates | ||
@@ -654,3 +700,6 @@ # view when getting placed inside an outer array | ||
| self._data["dressed_indices"] = spec_lookup.SpectrumLookupMixin.generate_lookup( | ||
| self | ||
| self, | ||
| ordering=ordering, | ||
| subsys_priority=subsys_priority, | ||
| BEs_count=BEs_count, | ||
| ) | ||
@@ -679,2 +728,3 @@ | ||
| ) | ||
| subsys.affected_subsystem_indices = [] | ||
| # diagonalizing only those subsystems present in update_subsystem_indices | ||
@@ -713,4 +763,5 @@ if subsys_index in update_subsystem_indices: | ||
| ) -> ndarray: | ||
| """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`. | ||
| """Calculates eigenvalues of the full Hamiltonian. Qutip's | ||
| :py:obj:`~qutip.Qobj.eigenenergies` is used by default, unless `self.evals_method` has | ||
| been set to something other than `None`. | ||
@@ -755,4 +806,4 @@ Parameters | ||
| """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`. | ||
| `qutip.Qobj.eigenenergies()` is used by default, unless `self.evals_method` has | ||
| been set to something other than `None`. | ||
@@ -869,5 +920,4 @@ Parameters | ||
| ) -> qt.Qobj: | ||
| """ | ||
| Returns the interaction Hamiltonian, based on the interaction terms specified | ||
| for the current HilbertSpace object | ||
| """Returns the interaction Hamiltonian, based on the interaction terms specified | ||
| for the current HilbertSpace object. | ||
@@ -922,3 +972,5 @@ Parameters | ||
| diag_qt_op = qt.Qobj(np.diagflat(evals[0:evals_count])) # type:ignore | ||
| return spec_utils.identity_wrap(diag_qt_op, subsystem, self.subsystem_list) | ||
| return spec_utils.identity_wrap( | ||
| diag_qt_op, subsystem, self.subsystem_list, op_in_eigenbasis=True | ||
| ) | ||
@@ -930,5 +982,5 @@ ################################################################################### | ||
| def diag_operator(self, diag_elements: ndarray, subsystem: QuantumSys) -> qt.Qobj: | ||
| """For given diagonal elements of a diagonal operator in `subsystem`, return | ||
| the `Qobj` operator for the full Hilbert space (perform wrapping in | ||
| identities for other subsystems). | ||
| """For given diagonal elements of a diagonal operator in `subsystem`, return the | ||
| `Qobj` operator for the full Hilbert space (perform wrapping in identities for | ||
| other subsystems). | ||
@@ -946,6 +998,8 @@ Parameters | ||
| diag_matrix[index, index] = diag_elements | ||
| return spec_utils.identity_wrap(diag_matrix, subsystem, self.subsystem_list) | ||
| return spec_utils.identity_wrap( | ||
| diag_matrix, subsystem, self.subsystem_list, op_in_eigenbasis=True | ||
| ) | ||
| def hubbard_operator(self, j: int, k: int, subsystem: QuantumSys) -> qt.Qobj: | ||
| """Hubbard operator :math:`|j\\rangle\\langle k|` for system `subsystem` | ||
| r"""Hubbard operator :math:`|j\rangle\langle k|` for system `subsystem` | ||
@@ -961,3 +1015,5 @@ Parameters | ||
| operator = qt.states.basis(dim, j) * qt.states.basis(dim, k).dag() | ||
| return spec_utils.identity_wrap(operator, subsystem, self.subsystem_list) | ||
| return spec_utils.identity_wrap( | ||
| operator, subsystem, self.subsystem_list, op_in_eigenbasis=True | ||
| ) | ||
@@ -974,3 +1030,5 @@ def annihilate(self, subsystem: QuantumSys) -> qt.Qobj: | ||
| operator = qt.destroy(dim) | ||
| return spec_utils.identity_wrap(operator, subsystem, self.subsystem_list) | ||
| return spec_utils.identity_wrap( | ||
| operator, subsystem, self.subsystem_list, op_in_eigenbasis=True | ||
| ) | ||
@@ -989,8 +1047,7 @@ ################################################################################### | ||
| ) -> SpectrumData: | ||
| """Return eigenvalues (and optionally eigenstates) of the full Hamiltonian as | ||
| a function of a parameter. Parameter values are specified as a list or array | ||
| in `param_vals`. The Hamiltonian `hamiltonian_func` must be a function of | ||
| that particular parameter, and is expected to internally set subsystem | ||
| parameters. If a `filename` string is provided, then eigenvalue data is | ||
| written to that file. | ||
| """Return eigenvalues (and optionally eigenstates) of the full Hamiltonian as a | ||
| function of a parameter. Parameter values are specified as a list or array in | ||
| `param_vals`. The Hamiltonian `hamiltonian_func` must be a function of that | ||
| particular parameter, and is expected to internally set subsystem parameters. If | ||
| a `filename` string is provided, then eigenvalue data is written to that file. | ||
@@ -1076,5 +1133,3 @@ Parameters | ||
| def standardize_eigenvector_phases(self) -> None: | ||
| """ | ||
| Standardize the phases of the (dressed) eigenvectors. | ||
| """ | ||
| """Standardize the phases of the (dressed) eigenvectors.""" | ||
| for idx, evec in enumerate(self._data["evecs"][0]): | ||
@@ -1085,2 +1140,3 @@ array = utils.Qobj_to_scipy_csc_matrix(evec) | ||
| @utils.check_lookup_exists | ||
| def op_in_dressed_eigenbasis( | ||
@@ -1109,3 +1165,3 @@ self, | ||
| .op_in_dressed_eigenbasis(op=<Callable>, truncated_dim=<int>) | ||
| .op_in_dressed_eigenbasis(op_callable_or_tuple=<Callable>, truncated_dim=<int>) | ||
@@ -1117,3 +1173,3 @@ 2. subsystem operators may be passed as arrays, along with the | ||
| .op_in_dressed_eigenbasis(op=(<ndarray>, <subsys>), | ||
| .op_in_dressed_eigenbasis(op_callable_or_tuple=(<ndarray>, <subsys>), | ||
| truncated_dim=<int>, | ||
@@ -1157,4 +1213,3 @@ op_in_bare_eigenbasis=<Bool>) | ||
| ) -> None: | ||
| """ | ||
| Specify the interaction between subsystems making up the `HilbertSpace` | ||
| """Specify the interaction between subsystems making up the :py:class:`HilbertSpace` | ||
| instance. `add_interaction(...)` offers three different interfaces: | ||
@@ -1167,3 +1222,3 @@ | ||
| 1. Simple interface for operator products | ||
| Specify `ndarray`, `csc_matrix`, or `dia_matrix` (subsystem operator in | ||
| Specify :py:class:`ndarray`, :py:class:`csc_matrix`, or :py:class:`dia_matrix` (subsystem operator in | ||
| subsystem-internal basis) along with the corresponding subsystem | ||
@@ -1207,4 +1262,4 @@ | ||
| id_str: | ||
| optional string by which this instance can be referred to in `HilbertSpace` | ||
| and `ParameterSweep`. If not provided, an id is auto-generated. | ||
| optional string by which this instance can be referred to in :py:class:`HilbertSpace` | ||
| and :py:class:`scqubits.ParameterSweep`. If not provided, an id is auto-generated. | ||
| """ | ||
@@ -1211,0 +1266,0 @@ if "expr" in kwargs: |
@@ -18,3 +18,3 @@ # namedslots_array.py | ||
| from collections import OrderedDict | ||
| from typing import Any, Dict, Iterable, List, Optional, Tuple, Union | ||
| from typing import Any, Dict, Iterable, List, Optional, Tuple, Union, Literal | ||
@@ -95,4 +95,3 @@ import numpy as np | ||
| ) -> NpIndexTuple: | ||
| """ | ||
| converts name-based and value-based indexing to standard numpy indexing | ||
| """Converts name-based and value-based indexing to standard numpy indexing. | ||
@@ -121,4 +120,3 @@ Parameters | ||
| ) -> NpIndexTupleNoEllipsis: | ||
| """ | ||
| Removes `...` from the multi-index by explicit slicing. | ||
| """Removes `...` from the multi-index by explicit slicing. | ||
@@ -155,5 +153,7 @@ Parameters | ||
| class ExtIndexObject: | ||
| """Object used for enabling enhanced indexing in NamedSlotsNdarray. Handles a | ||
| single idx_entry in multi-index""" | ||
| """Object used for enabling enhanced indexing in NamedSlotsNdarray. | ||
| Handles a single idx_entry in multi-index | ||
| """ | ||
| def __init__( | ||
@@ -169,4 +169,4 @@ self, idx_entry: ExtIndex, parameters: "Parameters", slot: Optional[int] = None | ||
| def convert_to_np_slice_entry(self, slice_entry: ExtSliceEntry) -> NpSliceEntry: | ||
| """Handles value-based slices, converting a float or complex value based | ||
| entry into the corresponding position-based entry""" | ||
| """Handles value-based slices, converting a float or complex value based entry | ||
| into the corresponding position-based entry.""" | ||
| if isinstance(slice_entry, (int, np.integer)): | ||
@@ -186,3 +186,3 @@ return slice_entry | ||
| """Convert a generalized multi-index entry into a valid numpy multi-index entry, | ||
| and returns that along with a str recording the idx_entry type""" | ||
| and returns that along with a str recording the idx_entry type.""" | ||
| if isinstance(idx_entry, (int, np.integer)): | ||
@@ -342,3 +342,3 @@ return "int", idx_entry | ||
| """Returns a dictionary specifying for each parameter name the number of | ||
| parameter values""" | ||
| parameter values.""" | ||
| return { | ||
@@ -352,3 +352,3 @@ name: len(self.paramvals_by_name[name]) | ||
| """Return a list of range objects suitable for looping over each parameter | ||
| set""" | ||
| set.""" | ||
| return [range(count) for count in self.counts] | ||
@@ -358,3 +358,3 @@ | ||
| def paramvals_list(self) -> List[ndarray]: | ||
| """Return list of all parameter values sets""" | ||
| """Return list of all parameter values sets.""" | ||
| return [self.paramvals_by_name[name] for name in self.paramnames_list] | ||
@@ -364,3 +364,3 @@ | ||
| def counts(self) -> Tuple[int, ...]: | ||
| """Returns list of the number of parameter values for each parameter set""" | ||
| """Returns list of the number of parameter values for each parameter set.""" | ||
| return tuple(len(paramvals) for paramvals in self) | ||
@@ -373,5 +373,4 @@ | ||
| ) -> "Parameters": | ||
| """ | ||
| Creates and returns a reduced Parameters object reflecting the fixing of a | ||
| subset of parameters | ||
| """Creates and returns a reduced Parameters object reflecting the fixing of a | ||
| subset of parameters. | ||
@@ -407,4 +406,3 @@ Parameters | ||
| ) -> "Parameters": | ||
| """ | ||
| Create and return a sliced Parameters object according to numpy slicing | ||
| """Create and return a sliced Parameters object according to numpy slicing | ||
| information. | ||
@@ -457,3 +455,30 @@ | ||
| def meshgrids_by_paramname( | ||
| self, | ||
| indexing: Literal["ij", "xy"] = "ij", | ||
| ) -> OrderedDict[str, "NamedSlotsNdarray"]: | ||
| """ | ||
| Creates and returns returns a dictionary containing the meshgrids of the | ||
| parameter lists. All meshgrids are instances of the NamedSlotNdarray | ||
| Parameters | ||
| ---------- | ||
| indexing: {'ij', 'xy'} | ||
| Matrix ('ij', default) or cartesian ('xy') or indexing of output. This | ||
| argument will be passed to the np.meshgrid() directly | ||
| Returns | ||
| ------- | ||
| An ordered dictionary or a list containing the meshgrids | ||
| """ | ||
| param_mesh = np.meshgrid(*self.paramvals_list, indexing=indexing) | ||
| param_mesh_nsarray = [ | ||
| NamedSlotsNdarray(mesh, self.paramvals_by_name) for mesh in param_mesh | ||
| ] | ||
| return OrderedDict(zip(self.paramnames_list, param_mesh_nsarray)) | ||
| class NamedSlotsNdarray(np.ndarray, Serializable): | ||
@@ -584,6 +609,4 @@ """ | ||
| def deserialize(cls, io_data: IOData) -> "NamedSlotsNdarray": | ||
| """ | ||
| Take the given IOData and return an instance of the described class, initialized | ||
| with the data stored in io_data. | ||
| """ | ||
| """Take the given IOData and return an instance of the described class, | ||
| initialized with the data stored in io_data.""" | ||
| if "input_array" in io_data.ndarrays: | ||
@@ -600,5 +623,3 @@ input_array = io_data.ndarrays["input_array"] | ||
| def serialize(self) -> IOData: | ||
| """ | ||
| Convert the content of the current class instance into IOData format. | ||
| """ | ||
| """Convert the content of the current class instance into IOData format.""" | ||
| import scqubits.io_utils.fileio as io | ||
@@ -636,3 +657,3 @@ | ||
| xlabel=self._parameters.names[0], | ||
| **kwargs | ||
| **kwargs, | ||
| ) | ||
@@ -639,0 +660,0 @@ |
@@ -23,6 +23,4 @@ # operators.py | ||
| def annihilation(dimension: int) -> ndarray: | ||
| """ | ||
| Returns a dense matrix of size dimension x dimension representing the annihilation | ||
| operator in number basis. | ||
| """ | ||
| """Returns a dense matrix of size dimension x dimension representing the | ||
| annihilation operator in number basis.""" | ||
| offdiag_elements = np.sqrt(range(1, dimension)) | ||
@@ -34,4 +32,3 @@ return np.diagflat(offdiag_elements, 1) | ||
| """Returns a matrix of size dimension x dimension representing the annihilation | ||
| operator in the format of a scipy sparse.csc_matrix. | ||
| """ | ||
| operator in the format of a scipy sparse.csc_matrix.""" | ||
| offdiag_elements = np.sqrt(range(dimension)) | ||
@@ -44,6 +41,4 @@ return sp.sparse.dia_matrix( | ||
| def creation(dimension: int) -> ndarray: | ||
| """ | ||
| Returns a dense matrix of size dimension x dimension representing the creation | ||
| operator in number basis. | ||
| """ | ||
| """Returns a dense matrix of size dimension x dimension representing the creation | ||
| operator in number basis.""" | ||
| return annihilation(dimension).T | ||
@@ -54,4 +49,3 @@ | ||
| """Returns a matrix of size dimension x dimension representing the creation operator | ||
| in the format of a scipy sparse.csc_matrix | ||
| """ | ||
| in the format of a scipy sparse.csc_matrix.""" | ||
| return annihilation_sparse(dimension).transpose().tocsc() | ||
@@ -61,3 +55,3 @@ | ||
| def hubbard_sparse(j1: int, j2: int, dimension: int) -> csc_matrix: | ||
| """The Hubbard operator :math:`|j1\\rangle>\\langle j2|` is returned as a matrix of | ||
| r"""The Hubbard operator :math:`|j_1\rangle>\langle j_2|` is returned as a matrix of | ||
| linear size dimension. | ||
@@ -84,4 +78,4 @@ | ||
| """Number operator matrix of size dimension x dimension in sparse matrix | ||
| representation. An additional prefactor can be directly included in the | ||
| generation of the matrix by supplying 'prefactor'. | ||
| representation. An additional prefactor can be directly included in the generation | ||
| of the matrix by supplying 'prefactor'. | ||
@@ -110,4 +104,4 @@ Parameters | ||
| """Number operator matrix of size dimension x dimension in sparse matrix | ||
| representation. An additional prefactor can be directly included in the | ||
| generation of the matrix by supplying 'prefactor'. | ||
| representation. An additional prefactor can be directly included in the generation | ||
| of the matrix by supplying 'prefactor'. | ||
@@ -136,3 +130,3 @@ Parameters | ||
| ) -> csc_matrix: | ||
| """Operator matrix for prefactor(a+a^dag) of size dimension x dimension in | ||
| r"""Operator matrix for prefactor(:math:`a+a^\dagger`) of size dimension x dimension in | ||
| sparse matrix representation. | ||
@@ -150,3 +144,3 @@ | ||
| ------- | ||
| prefactor * (a + a^dag) as sparse operator matrix, size dimension x dimension | ||
| prefactor * (:math:`a+a^\dagger`) as sparse operator matrix, size dimension x dimension | ||
| """ | ||
@@ -160,3 +154,3 @@ prefactor = prefactor if prefactor is not None else 1.0 | ||
| ) -> ndarray: | ||
| """Operator matrix for prefactor(a+a^dag) of size dimension x dimension in | ||
| r"""Operator matrix for prefactor(:math:`a+a^\dagger`) of size dimension x dimension in | ||
| sparse matrix representation. | ||
@@ -174,3 +168,3 @@ | ||
| ------- | ||
| prefactor (a + a^dag) as ndarray, size dimension x dimension | ||
| prefactor * (:math:`a+a^\dagger`) as ndarray, size dimension x dimension | ||
| """ | ||
@@ -183,3 +177,3 @@ return a_plus_adag_sparse(dimension, prefactor=prefactor).toarray() | ||
| ) -> ndarray: | ||
| """Operator matrix for cos(prefactor(a+a^dag)) of size dimension x dimension in | ||
| r"""Operator matrix for cos(prefactor(:math:`a+a^\dagger`)) of size dimension x dimension in | ||
| sparse matrix representation. | ||
@@ -197,3 +191,3 @@ | ||
| ------- | ||
| prefactor (a + a^dag) as ndarray, size dimension x dimension | ||
| prefactor * (:math:`a+a^\dagger`) as ndarray, size dimension x dimension | ||
| """ | ||
@@ -206,3 +200,3 @@ return sp.linalg.cosm(a_plus_adag_sparse(dimension, prefactor=prefactor).toarray()) | ||
| ) -> ndarray: | ||
| """Operator matrix for sin(prefactor(a+a^dag)) of size dimension x dimension in | ||
| r"""Operator matrix for sin(prefactor(:math:`a+a^\dagger`)) of size dimension x dimension in | ||
| sparse matrix representation. | ||
@@ -220,3 +214,3 @@ | ||
| ------- | ||
| prefactor (a + a^dag) as ndarray, size dimension x dimension | ||
| prefactor * (:math:`a+a^\dagger`) as ndarray, size dimension x dimension | ||
| """ | ||
@@ -229,3 +223,3 @@ return sp.linalg.sinm(a_plus_adag_sparse(dimension, prefactor=prefactor).toarray()) | ||
| ) -> csc_matrix: | ||
| """Operator matrix for prefactor(ia-ia^dag) of size dimension x dimension as | ||
| r"""Operator matrix for prefactor(:math:`ia-ia^\dagger`) of size dimension x dimension as | ||
| ndarray | ||
@@ -243,3 +237,3 @@ | ||
| ------- | ||
| prefactor (ia - ia^dag) as sparse operator matrix, size dimension x dimension | ||
| prefactor * (:math:`ia-ia^\dagger`) as sparse operator matrix, size dimension x dimension | ||
| """ | ||
@@ -255,3 +249,3 @@ prefactor = prefactor if prefactor is not None else 1.0 | ||
| ) -> ndarray: | ||
| """Operator matrix for prefactor(ia-ia^dag) of size dimension x dimension as | ||
| r"""Operator matrix for prefactor(:math:`ia-ia^\dagger`) of size dimension x dimension as | ||
| ndarray | ||
@@ -269,3 +263,3 @@ | ||
| ------- | ||
| prefactor (ia - ia^dag) as ndarray, size dimension x dimension | ||
| prefactor * (:math:`ia-ia^\dagger`) as ndarray, size dimension x dimension | ||
| """ | ||
@@ -272,0 +266,0 @@ return iadag_minus_ia_sparse(dimension, prefactor=prefactor).toarray() |
@@ -33,5 +33,5 @@ # oscillator.py | ||
| ) -> Union[float, ndarray]: | ||
| r"""For given quantum number n=0,1,2,... return the value of the harmonic | ||
| oscillator wave function :math:`\psi_n(x) = N H_n(x/l_{osc}) \exp(-x^2/2l_\text{ | ||
| osc})`, N being the proper normalization factor. | ||
| r"""For given quantum number :math:`n=0,1,2,\ldots` return the value of the harmonic | ||
| oscillator wave function :math:`\psi_n(x) = N H_n(x/l_{\rm osc}) \exp(-x^2/2 | ||
| l_{\rm osc})`, N being the proper normalization factor. | ||
@@ -63,3 +63,3 @@ Directly uses `scipy.special.pbdv` (implementation of the parabolic cylinder | ||
| r"""Returns the oscillator energy given a harmonic Hamiltonian of the form | ||
| :math:`H=\frac{1}{2}E_{\text{kin}}p^2 + \frac{1}{2}E_{\text{pot}}x^2`""" | ||
| :math:`H=\frac{1}{2}E_{\rm kin}p^2 + \frac{1}{2}E_{\rm pot}x^2`""" | ||
| return np.sqrt(E_kin * E_pot) | ||
@@ -70,3 +70,3 @@ | ||
| r"""Returns the oscillator length given a harmonic Hamiltonian of the form | ||
| :math:`H=\frac{1}{2}E_{\text{kin}}p^2 + \frac{1}{2}E_{\text{pot}}x^2`""" | ||
| :math:`H=\frac{1}{2}E_{\rm kin}p^2 + \frac{1}{2}E_{\rm pot}x^2`""" | ||
| return (E_kin / E_pot) ** (1 / 4) | ||
@@ -80,3 +80,3 @@ | ||
| r"""Class representing a harmonic oscillator/resonator governed by a Hamiltonian | ||
| :math:`H=E_\text{osc} a^{\dagger} a`, with :math:`a` being the annihilation | ||
| :math:`H=E_{\rm osc} a^\dagger a`, with :math:`a` being the annihilation | ||
| operator. | ||
@@ -93,3 +93,3 @@ | ||
| id_str: | ||
| optional string by which this instance can be referred to in `HilbertSpace` | ||
| optional string by which this instance can be referred to in :class:`HilbertSpace` | ||
| and `ParameterSweep`. If not provided, an id is auto-generated. | ||
@@ -134,3 +134,3 @@ """ | ||
| ) -> Tuple[ndarray, ndarray]: | ||
| """Returns array of eigenvalues and eigenvectors | ||
| """Returns array of eigenvalues and eigenvectors. | ||
@@ -149,11 +149,11 @@ Parameters | ||
| def hilbertdim(self) -> int: | ||
| """Returns Hilbert space dimension""" | ||
| """Returns Hilbert space dimension.""" | ||
| return self.truncated_dim | ||
| def creation_operator(self) -> ndarray: | ||
| """Returns the creation operator""" | ||
| """Returns the creation operator.""" | ||
| return op.creation(self.truncated_dim) | ||
| def annihilation_operator(self) -> ndarray: | ||
| """Returns the annihilation operator""" | ||
| """Returns the annihilation operator.""" | ||
| return op.annihilation(self.truncated_dim) | ||
@@ -168,4 +168,4 @@ | ||
| r"""Returns the phase operator defined as | ||
| :math:`l_\text{osc} (a + a^{\dagger})/\sqrt{2}`, with :math:`a` representing | ||
| an annihilation operator, and :math:`l_\text{osc}` the oscillator length. | ||
| :math:`l_{\rm osc} (a + a^\dagger)/\sqrt{2}`, with :math:`a` representing | ||
| an annihilation operator, and :math:`l_{\rm osc}` the oscillator length. | ||
| """ | ||
@@ -183,4 +183,4 @@ if self.l_osc is None: | ||
| r"""Returns the charge-number n operator defined as | ||
| :math:`i (a^{\dagger} - a)/ ( \sqrt{2} l_\text{osc})`, with :math:`a` representing | ||
| an annihilation operator, and :math:`l_\text{osc}` the oscillator length. | ||
| :math:`i (a^\dagger - a)/ ( \sqrt{2} l_{\rm osc})`, with :math:`a` representing | ||
| an annihilation operator, and :math:`l_{\rm osc}` the oscillator length. | ||
| """ | ||
@@ -203,3 +203,3 @@ | ||
| r"""Class representing a nonlinear Kerr oscillator/resonator governed by a Hamiltonian | ||
| :math:`H_\text{Kerr}=E_\text{osc} a^{\dagger} a - K a^{\dagger} a^{\dagger} a a`, with :math:`a` | ||
| :math:`H_{\rm Kerr}=E_{\rm osc} a^\dagger a - K a^\dagger a^\dagger a a`, with :math:`a` | ||
| being the annihilation operator. | ||
@@ -218,3 +218,3 @@ | ||
| id_str: | ||
| optional string by which this instance can be referred to in `HilbertSpace` | ||
| optional string by which this instance can be referred to in :class:`HilbertSpace` | ||
| and `ParameterSweep`. If not provided, an id is auto-generated. | ||
@@ -221,0 +221,0 @@ """ |
@@ -12,5 +12,3 @@ # qubit_base.py | ||
| ############################################################################ | ||
| """ | ||
| Provides the base classes for qubits | ||
| """ | ||
| """Provides the base classes for qubits.""" | ||
@@ -34,2 +32,3 @@ import functools | ||
| Type, | ||
| Callable, | ||
| ) | ||
@@ -91,4 +90,11 @@ | ||
| class QuantumSystem(DispatchClient, ABC): | ||
| """Generic quantum system class""" | ||
| """Generic quantum system class. | ||
| Attributes | ||
| ---------- | ||
| truncated_dim: int | ||
| Hilbert space dimension | ||
| """ | ||
| truncated_dim = descriptors.WatchedProperty(int, "QUANTUMSYSTEM_UPDATE") | ||
@@ -202,8 +208,8 @@ _init_params: List[str] | ||
| def hilbertdim(self) -> int: | ||
| """Returns dimension of Hilbert space""" | ||
| """Returns dimension of Hilbert space.""" | ||
| @classmethod | ||
| def get_operator_names(cls) -> List[str]: | ||
| """Returns a list of all operator names for the quantum system. | ||
| Note that this list omits any operators that start with "_". | ||
| """Returns a list of all operator names for the quantum system. Note that this | ||
| list omits any operators that start with "_". | ||
@@ -227,3 +233,3 @@ Parameters | ||
| def create(cls) -> "QuantumSystem": | ||
| """Use ipywidgets to create a new class instance""" | ||
| """Use ipywidgets to create a new class instance.""" | ||
| init_params = cls.default_params() | ||
@@ -235,3 +241,3 @@ instance = cls(**init_params) | ||
| def widget(self, params: Optional[Dict[str, Any]] = None): | ||
| """Use ipywidgets to modify parameters of class instance""" | ||
| """Use ipywidgets to modify parameters of class instance.""" | ||
| init_params = params or self.get_initdata() | ||
@@ -246,9 +252,7 @@ init_params.pop("id_str", None) | ||
| def default_params() -> Dict[str, Any]: | ||
| """Return dictionary with default parameter values for initialization of | ||
| class instance""" | ||
| """Return dictionary with default parameter values for initialization of class | ||
| instance.""" | ||
| def set_params_from_gui(self, change): | ||
| """ | ||
| Set new parameters through the provided dictionary. | ||
| """ | ||
| """Set new parameters through the provided dictionary.""" | ||
| param_name = change["owner"].name | ||
@@ -259,5 +263,3 @@ param_val = change["owner"].num_value | ||
| def set_params(self, **kwargs): | ||
| """ | ||
| Set new parameters through the provided dictionary. | ||
| """ | ||
| """Set new parameters through the provided dictionary.""" | ||
| for param_name, param_val in kwargs.items(): | ||
@@ -267,6 +269,6 @@ setattr(self, param_name, param_val) | ||
| def supported_noise_channels(self) -> List: | ||
| """Returns a list of noise channels this QuantumSystem supports. | ||
| If none, return an empty list. | ||
| """ | ||
| Returns a list of noise channels this QuantumSystem supports. If none, | ||
| return an empty list. | ||
| """ | ||
| return [] | ||
@@ -279,4 +281,25 @@ | ||
| 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 | ||
| """Base class for superconducting qubit objects. | ||
| Provide general mechanisms and routines for plotting spectra, matrix elements, and | ||
| writing data to files | ||
| Attributes | ||
| ---------- | ||
| truncated_dim: int | ||
| Hilbert space dimension | ||
| _default_grid: Grid1d | ||
| Discretization grid | ||
| _sys_type: str | ||
| Type of quantum system | ||
| _init_params: list | ||
| List of parameters used for initialization | ||
| evals_method: Union[Callable, str, None] | ||
| Method for calculating eigenvalues | ||
| evals_method_options: Union[Dict, None] | ||
| Options for eigenvalue calculation | ||
| esys_method: Union[Callable, str, None] | ||
| Method for calculating eigenvalues and eigenvectors | ||
| esys_method_options: Union[Dict, None] | ||
| Options for eigenvalue and eigenvector calculation | ||
| """ | ||
@@ -293,5 +316,5 @@ | ||
| id_str: Union[str, None], | ||
| evals_method: Union[str, None] = None, | ||
| evals_method: Union[Callable, str, None] = None, | ||
| evals_method_options: Union[Dict, None] = None, | ||
| esys_method: Union[str, None] = None, | ||
| esys_method: Union[Callable, str, None] = None, | ||
| esys_method_options: Union[Dict, None] = None, | ||
@@ -317,3 +340,3 @@ ): | ||
| def hamiltonian(self): | ||
| """Returns the Hamiltonian""" | ||
| """Returns the Hamiltonian.""" | ||
@@ -411,3 +434,3 @@ def _evals_calc(self, evals_count: int) -> ndarray: | ||
| filename: Optional[str] = None, | ||
| return_spectrumdata: "Literal[False]" = False, | ||
| return_spectrumdata: bool = False, | ||
| ) -> Tuple[ndarray, ndarray]: ... | ||
@@ -420,3 +443,3 @@ | ||
| filename: Optional[str], | ||
| return_spectrumdata: "Literal[True]", | ||
| return_spectrumdata: bool, | ||
| ) -> SpectrumData: ... | ||
@@ -478,5 +501,5 @@ | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| """Processes the operator `native_op`: either hand back `native_op` unchanged, or transform it into the | ||
| energy eigenbasis. (Native basis refers to the basis used internally by each qubit, e.g., charge basis in the | ||
| case of `Transmon`. | ||
| """Processes the operator `native_op`: either hand back `native_op` unchanged, | ||
| or transform it into the energy eigenbasis. (Native basis refers to the basis | ||
| used internally by each qubit, e.g., charge basis in the case of :class:`Transmon`. | ||
@@ -574,5 +597,5 @@ Parameters | ||
| operator: Union[str, ndarray, qt.Qobj, spmatrix], | ||
| evecs: ndarray = None, | ||
| evecs: Optional[ndarray] = None, | ||
| evals_count: int = 6, | ||
| filename: str = None, | ||
| filename: Optional[str] = None, | ||
| return_datastore: bool = False, | ||
@@ -584,4 +607,4 @@ ) -> Union[DataStore, ndarray]: | ||
| 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. | ||
| `trm.op_matrixelement_table('n_operator')`. When `esys` is set to `None`, the | ||
| eigensystem is calculated on-the-fly. | ||
@@ -623,4 +646,2 @@ Parameters | ||
| setattr(self, param_name, paramval) | ||
| if hasattr(self, "hierarchical_diagonalization"): | ||
| self.update() | ||
| return self.eigensys(evals_count=evals_count) | ||
@@ -632,4 +653,2 @@ | ||
| setattr(self, param_name, paramval) | ||
| if hasattr(self, "hierarchical_diagonalization"): | ||
| self.update() | ||
| return self.eigenvals(evals_count) | ||
@@ -647,5 +666,5 @@ | ||
| ) -> SpectrumData: | ||
| """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 | ||
| """Calculates eigenvalues/eigenstates for a varying system parameter, given an | ||
| array of parameter values. Returns a :class:`SpectrumData` object with | ||
| `energy_table[n]` containing eigenvalues calculated for parameter value | ||
| `param_vals[n]`. | ||
@@ -808,5 +827,5 @@ | ||
| ) -> SpectrumData: | ||
| """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 | ||
| """Calculates eigenvalues/eigenstates for a varying system parameter, given an | ||
| array of parameter values. Returns a :class:`SpectrumData` object with | ||
| `energy_table[n]` containing eigenvalues calculated for parameter value | ||
| `param_vals[n]`. | ||
@@ -903,5 +922,5 @@ | ||
| ) -> SpectrumData: | ||
| """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.. | ||
| """Calculates matrix elements for a varying system parameter, given an array of | ||
| parameter values. Returns a :class:`SpectrumData` object containing matrix element | ||
| data, eigenvalue data, and eigenstate data.. | ||
@@ -960,4 +979,4 @@ Parameters | ||
| """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. | ||
| parameter. The individual points correspond to the a provided array of parameter | ||
| values. | ||
@@ -1079,7 +1098,7 @@ Parameters | ||
| ) -> Union[Tuple[Figure, Tuple[Axes, Axes]], Tuple[Figure, Axes]]: | ||
| """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. | ||
| """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. | ||
@@ -1135,4 +1154,4 @@ Parameters | ||
| """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. | ||
| parameter. The individual points correspond to the a provided array of parameter | ||
| values. | ||
@@ -1206,4 +1225,5 @@ Parameters | ||
| """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. | ||
| Provide general mechanisms and routines for plotting spectra, matrix elements, and | ||
| writing data to files. | ||
| """ | ||
@@ -1257,4 +1277,4 @@ | ||
| ) -> Tuple[Figure, Axes]: | ||
| """Plot 1d phase-basis wave function(s). Must be overwritten by | ||
| higher-dimensional qubits like FluxQubits and ZeroPi. | ||
| """Plot 1d phase-basis wave function(s). Must be overwritten by higher- | ||
| dimensional qubits like FluxQubits and ZeroPi. | ||
@@ -1269,3 +1289,3 @@ Parameters | ||
| choices as specified in `constants.MODE_FUNC_DICT` | ||
| (default value = 'abs_sqr') | ||
| (default value = 'real') | ||
| esys: | ||
@@ -1272,0 +1292,0 @@ eigenvalues, eigenvectors |
+671
-51
@@ -15,4 +15,6 @@ # spec_lookup.py | ||
| import numbers | ||
| from copy import copy | ||
| from warnings import warn | ||
| from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union | ||
| from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union, Literal | ||
@@ -30,4 +32,5 @@ import numpy as np | ||
| from scqubits.core.namedslots_array import NamedSlotsNdarray | ||
| from scqubits.core.namedslots_array import NamedSlotsNdarray, convert_to_std_npindex | ||
| from scqubits.utils.typedefs import NpIndexTuple, NpIndices | ||
| from scqubits.utils.spectrum_utils import identity_wrap | ||
@@ -59,4 +62,5 @@ if TYPE_CHECKING: | ||
| class SpectrumLookupMixin(MixinCompatible): | ||
| """ | ||
| SpectrumLookupMixin is used as a mix-in class by `ParameterSweep`. It makes various | ||
| """SpectrumLookupMixin is used as a mix-in class by `ParameterSweep`. | ||
| It makes various | ||
| spectrum and spectrum lookup related methods directly available at the | ||
@@ -79,23 +83,94 @@ `ParameterSweep` level. | ||
| else: | ||
| self._current_param_indices = slice(None, None, None) | ||
| self._current_param_indices = ( | ||
| slice(None, None, None), | ||
| ) * self._parameters.ndim() | ||
| @property | ||
| def _bare_product_states_labels(self) -> List[Tuple[int, ...]]: | ||
| """Generates the list of bare-state labels in canonical order. | ||
| For example, | ||
| for a Hilbert space composed of two subsystems sys1 and sys2, each label is | ||
| of the type (3,0) meaning sys1 is in bare eigenstate 3, sys2 in bare | ||
| eigenstate 0. The full list then reads | ||
| [(0,0), (0,1), (0,2), ..., (0,max_2), | ||
| (1,0), (1,1), (1,2), ..., (1,max_2), | ||
| ... | ||
| (max_1,0), (max_1,1), (max_1,2), ..., (max_1,max_2)] | ||
| """ | ||
| Generates the list of bare-state labels in canonical order. For example, | ||
| for a Hilbert space composed of two subsystems sys1 and sys2, each label is | ||
| of the type (3,0) meaning sys1 is in bare eigenstate 3, sys2 in bare | ||
| eigenstate 0. The full list then reads | ||
| [(0,0), (0,1), (0,2), ..., (0,max_2), | ||
| (1,0), (1,1), (1,2), ..., (1,max_2), | ||
| ... | ||
| (max_1,0), (max_1,1), (max_1,2), ..., (max_1,max_2)] | ||
| """ | ||
| return list(np.ndindex(*self.hilbertspace.subsystem_dims)) | ||
| def generate_lookup(self) -> NamedSlotsNdarray: | ||
| def generate_lookup( | ||
| self, | ||
| ordering: Literal["DE", "LX", "BE"] = "DE", | ||
| subsys_priority: Union[List[int], None] = None, | ||
| BEs_count: Union[int, None] = None, | ||
| ) -> NamedSlotsNdarray: | ||
| """ | ||
| Label the dressed states by bare labels and generate the lookup table | ||
| with one of the following methods: | ||
| - Dressed Energy (ordering="DE"): traverse the eigenstates | ||
| in the order of their dressed energy, and find the corresponding bare | ||
| state label by overlaps (default) | ||
| - Lexical (ordering="LX"): traverse the bare states in `lexical order`_, | ||
| and perform the branch analysis generalized from Dumas et al. (2024). | ||
| - Bare Energy (ordering="BE"): traverse the bare states in the order of | ||
| their energy before coupling and perform label assignment. This is particularly | ||
| useful when the Hilbert space is too large and not all the eigenstates need | ||
| to be labeled. | ||
| Parameters | ||
| ---------- | ||
| ordering: | ||
| the ordering method for the dressed state labeling | ||
| - "DE": Dressed Energy (default) | ||
| - "LX": Lexical ordering | ||
| - "BE": Bare Energy | ||
| subsys_priority: | ||
| a permutation of the subsystem indices and bare labels. If it is provided, | ||
| lexical ordering is performed on the permuted labels. A "branch" is defined | ||
| as a series of eigenstates formed by putting excitations into the last | ||
| subsystem in the list. | ||
| BEs_count: | ||
| the number of eigenstates to be assigned, for "BE" scheme only. If None, | ||
| all available eigenstates will be labeled. | ||
| Returns | ||
| ------- | ||
| a NamedSlotsNdarray object containing the branch analysis results | ||
| organized by the parameter indices. | ||
| For each parameter point, a flattened multi-dimensional array | ||
| is stored, representing the dressed indices organized by the | ||
| bare indices. E.g. if the dimensions of the subsystems are D0, D1 and D2, | ||
| the returned array will be ravelled from the shape (D0, D1, D2). | ||
| .. _lexical order: https://en.wikipedia.org/wiki/Lexicographic_order#Cartesian_products/ | ||
| """ | ||
| if ordering == "LX" or ordering == "BE": | ||
| return self._branch_analysis( | ||
| ordering=ordering, | ||
| subsys_priority=subsys_priority, | ||
| transpose=False, | ||
| BEs_count=BEs_count, | ||
| ) | ||
| elif ordering == "DE": | ||
| if BEs_count is not None: | ||
| warn( | ||
| "BEs_count is not supported for DE ordering, " "it will be ignored." | ||
| ) | ||
| if subsys_priority is not None: | ||
| warn( | ||
| "subsys_priority is not supported for DE ordering, " | ||
| "it will be ignored." | ||
| ) | ||
| return self._generate_lookup_by_overlap() | ||
| else: | ||
| raise ValueError(f"Invalid ordering method: {ordering}") | ||
| def _generate_lookup_by_overlap(self) -> NamedSlotsNdarray: | ||
| """ | ||
| For each parameter value of the parameter sweep, generate the map between | ||
| bare states and | ||
| dressed states. | ||
| bare states and dressed states based on the overlap criterion. | ||
@@ -112,3 +187,3 @@ Returns | ||
| for index in param_indices: | ||
| dressed_indices[index] = self._generate_single_mapping(index) | ||
| dressed_indices[index] = self._generate_single_mapping_by_overlap(index) | ||
| dressed_indices = np.asarray(dressed_indices[:].tolist()) | ||
@@ -119,11 +194,10 @@ | ||
| def _generate_single_mapping( | ||
| def _generate_single_mapping_by_overlap( | ||
| self, | ||
| param_indices: Tuple[int, ...], | ||
| ) -> ndarray: | ||
| """ | ||
| For a single set of parameter values, specified by a tuple of indices | ||
| ``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 | ||
| (whenever possible). | ||
| """For a single set of parameter values, specified by a tuple of indices | ||
| ``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 (whenever | ||
| possible). | ||
@@ -173,5 +247,3 @@ Parameters | ||
| ) -> NpIndexTuple: | ||
| """ | ||
| Convert the NpIndices parameter indices to a tuple of NpIndices. | ||
| """ | ||
| """Convert the NpIndices parameter indices to a tuple of NpIndices.""" | ||
| param_indices = param_indices or self._current_param_indices | ||
@@ -189,4 +261,3 @@ if not isinstance(param_indices, tuple): | ||
| ) -> Union[ndarray, int, None]: | ||
| """ | ||
| For given bare product state return the corresponding dressed-state index. | ||
| """For given bare product state return the corresponding dressed-state index. | ||
@@ -223,4 +294,3 @@ Parameters | ||
| ) -> Union[Tuple[int, ...], None]: | ||
| """ | ||
| For given dressed index, look up the corresponding bare index. | ||
| """For given dressed index, look up the corresponding bare index. | ||
@@ -256,4 +326,3 @@ Returns | ||
| ) -> ndarray: | ||
| """ | ||
| Return the list of dressed eigenvectors | ||
| """Return the list of dressed eigenvectors. | ||
@@ -301,4 +370,4 @@ Parameters | ||
| ) -> Union[float, NamedSlotsNdarray]: # the return value may also be np.nan | ||
| """ | ||
| Look up dressed energy most closely corresponding to the given bare-state labels | ||
| """Look up dressed energy most closely corresponding to the given bare-state | ||
| labels. | ||
@@ -354,5 +423,4 @@ Parameters | ||
| ) -> Union[float, NamedSlotsNdarray]: | ||
| """ | ||
| Look up the dressed eigenenergy belonging to the given dressed index, | ||
| usually to be used with pre-slicing | ||
| """Look up the dressed eigenenergy belonging to the given dressed index, usually | ||
| to be used with pre-slicing. | ||
@@ -385,4 +453,4 @@ Parameters | ||
| ) -> NamedSlotsNdarray: | ||
| """ | ||
| Return ndarray of bare eigenstates for given subsystems and parameter index. | ||
| """Return ndarray of bare eigenstates for given subsystems and parameter index. | ||
| Eigenstates are expressed in the basis internal to the subsystems. Usually to be | ||
@@ -403,4 +471,3 @@ used with pre-slicing when part of `ParameterSweep`. | ||
| ) -> NamedSlotsNdarray: | ||
| """ | ||
| Return `NamedSlotsNdarray` of bare eigenenergies for given subsystem, usually | ||
| """Return :obj:`.NamedSlotsNdarray` of bare eigenenergies for given subsystem, usually | ||
| to be used with preslicing. | ||
@@ -429,6 +496,5 @@ | ||
| ) -> Qobj: | ||
| """ | ||
| Return the bare product state specified by `bare_index`. Note: no parameter | ||
| dependence here, since the Hamiltonian is always represented in the bare | ||
| product eigenbasis. | ||
| """Return the bare product state specified by `bare_index`. Note: no parameter | ||
| dependence here, since the Hamiltonian is always represented in the bare product | ||
| eigenbasis. | ||
@@ -451,4 +517,3 @@ Parameters | ||
| def all_params_fixed(self, param_indices: Union[slice, tuple]) -> bool: | ||
| """ | ||
| Checks whether the indices provided fix all the parameters. | ||
| """Checks whether the indices provided fix all the parameters. | ||
@@ -463,6 +528,561 @@ Parameters | ||
| True if all parameters are being fixed by `param_indices`. | ||
| """ | ||
| param_indices_std = convert_to_std_npindex( | ||
| np.index_exp[param_indices], self._parameters | ||
| ) | ||
| # Check if each dimension is being fixed to a single value or a length-1 array | ||
| fixed = [] | ||
| for params, idx in zip( | ||
| self._parameters.paramvals_by_name.values(), param_indices_std | ||
| ): | ||
| fixed.append(np.size(params[idx]) == 1) | ||
| return all(fixed) | ||
| @utils.check_lookup_exists | ||
| @utils.check_sync_status | ||
| def dressed_state_components( | ||
| self, | ||
| state_label: Union[Tuple[int, ...], List[int], int], | ||
| components_count: Union[int, None] = None, | ||
| return_probability: bool = True, | ||
| param_npindices: Optional[NpIndices] = None, | ||
| ) -> Dict[Tuple[int, ...], float]: | ||
| """ | ||
| if isinstance(param_indices, slice): | ||
| param_indices = (param_indices,) | ||
| return len(self._parameters) == len(param_indices) | ||
| A dressed state is a superposition of bare states. This function returns | ||
| a dressed state's bare conponents and the associated occupation | ||
| probabilities. They are sorted by probability in descending order. | ||
| Parameters | ||
| ---------- | ||
| state_label: | ||
| The bare label of the dressed state of interest. Could be | ||
| - a tuple/list of bare labels (int) | ||
| - a single dressed label (int) | ||
| components_count: | ||
| The number of components to be returned. If None, all components | ||
| will be returned. | ||
| return_probability: | ||
| Whether to return the occupation probabilities. If not, return | ||
| the probability amplitudes. | ||
| param_npindices: | ||
| This method only allows for a HilbertSpace object or a single | ||
| parameter ParameterSweep. If it's a multi-dimensional sweep, | ||
| param_npindices should be provided to specify a point in the | ||
| parameter space. If None, the current parameter preslicing will | ||
| be used. | ||
| Returns | ||
| ------- | ||
| A dictionary of the bare labels and their associated probability | ||
| (or probability amplitude if specified). | ||
| """ | ||
| param_npindices = self.set_npindextuple(param_npindices) | ||
| if not self.all_params_fixed(param_npindices): | ||
| raise ValueError( | ||
| "All parameters must be fixed to concrete values for " | ||
| "the use of `.dressed_state_component`." | ||
| ) | ||
| evecs = self["evecs"][param_npindices] | ||
| # find the desired state vector | ||
| if isinstance(state_label, tuple | list): | ||
| raveled_label = np.ravel_multi_index( | ||
| state_label, self.hilbertspace.subsystem_dims | ||
| ) | ||
| drs_idx = self["dressed_indices"][param_npindices][raveled_label] | ||
| if drs_idx is None: | ||
| raise IndexError(f"no dressed state found for bare label {state_label}") | ||
| elif isinstance(state_label, int | np.int_): | ||
| drs_idx = state_label | ||
| evec_1 = evecs[drs_idx] | ||
| ordered_label = np.argsort(np.abs(evec_1.full()[:, 0]))[::-1] | ||
| bare_label_list = [] | ||
| prob_list = [] | ||
| for idx in range(evec_1.shape[0]): | ||
| raveled_label = int(ordered_label[idx]) | ||
| bare_label = np.unravel_index( | ||
| raveled_label, self.hilbertspace.subsystem_dims | ||
| ) | ||
| prob_amp = evec_1.full()[raveled_label, 0] | ||
| bare_label_list.append(bare_label) | ||
| if return_probability: | ||
| prob = np.abs(prob_amp) ** 2 | ||
| prob_list.append(prob) | ||
| else: | ||
| prob_list.append(prob_amp) | ||
| if components_count is not None: | ||
| bare_label_list = bare_label_list[:components_count] | ||
| prob_list = prob_list[:components_count] | ||
| return dict(zip(bare_label_list, prob_list)) | ||
| def _branch_analysis_excite_op( | ||
| self, | ||
| mode: "Union[int, QuantumSys]", | ||
| ) -> Qobj: | ||
| """ | ||
| Branch analysis requires a step by step excitation of a chosen state, | ||
| which help to cover the entire Hilbert space and complete the | ||
| assignment of dressed indices. | ||
| This function returns the excitation operator for a given mode. | ||
| For the moment, it returns the creation operator for linear modes, | ||
| and Sum_i |i+1><i| operator for other modes. | ||
| Parameters | ||
| ---------- | ||
| mode: | ||
| The mode to be excited. | ||
| Returns | ||
| ------- | ||
| The excitation operator for the given mode, tensor producted with | ||
| the identity operators of the other subsystems. | ||
| """ | ||
| hilbertspace = self.hilbertspace | ||
| if isinstance(mode, int): | ||
| mode_idx = mode | ||
| mode = hilbertspace.subsystem_list[mode] | ||
| else: | ||
| mode_idx = hilbertspace.subsystem_list.index(mode) | ||
| if mode in hilbertspace.osc_subsys_list: | ||
| # annhilation operator | ||
| return hilbertspace.annihilate(mode).dag() | ||
| else: | ||
| # sum_j |j+1><j| | ||
| dims = hilbertspace.subsystem_dims | ||
| op = qt.qdiags( | ||
| np.ones(dims[mode_idx] - 1), | ||
| -1, | ||
| ) | ||
| return identity_wrap( | ||
| op, mode, hilbertspace.subsystem_list, op_in_eigenbasis=True | ||
| ) | ||
| def _branch_analysis_LX_step( | ||
| self, | ||
| subsys_priority: List[int], | ||
| recusion_depth: int, | ||
| init_drs_idx: int, | ||
| init_state: qt.Qobj, | ||
| remaining_drs_indices: List[int], | ||
| remaining_evecs: List[qt.Qobj], | ||
| ) -> Tuple[List, List]: | ||
| """ | ||
| Perform a single branch analysis according to Dumas et al. (2024). This | ||
| is a core function to be run recursively, which realized a depth-first | ||
| search in the tree - its leaves can be labeled by bare labels. | ||
| In a nutshell, the function will: | ||
| 1. Start from the "ground" state / starting point the branch, find | ||
| all of the branch states | ||
| 2. Remove the found states from the remaining candidates | ||
| 3. [If at the end of the depth-first search] Return the branch states | ||
| 4. [If not at the end] For each branch state, use it as an init state to | ||
| start such search again, which will return a (nested) list of branch | ||
| states. Combine the list of branch states and return a nested list of | ||
| those states | ||
| In such way, the function will recursively go through this multi-dimensional | ||
| Hilbert space and assign the eigenstates to their labels. | ||
| Parameters | ||
| ---------- | ||
| subsys_priority: | ||
| a permutation of the subsystem indices and bare labels. If it is | ||
| provided, lexical ordering is performed on the permuted labels. | ||
| It also represents the depth of the subsystem labels to be traversed. The later | ||
| the subsystem appears in the list, the deeper it is in the recursion. | ||
| A "branch" is defined as a series of eigenstates formed by | ||
| putting excitations into the last subsystem in the list. | ||
| recusion_depth: | ||
| the current depth of the recursion. It should be 0 at the beginning. | ||
| init_drs_idx: | ||
| the dressed index of the initial state of this branch. | ||
| init_state: | ||
| the initial state of this branch. | ||
| remaining_drs_indices: | ||
| the list of the remaining dressed indices to be assigned. | ||
| remaining_evecs: | ||
| The list of the remaining eigenstates to be assigned. | ||
| Returns | ||
| ------- | ||
| branch_drs_indices, branch_states | ||
| The (nested) list of the branch states and their dressed indices. | ||
| """ | ||
| hspace = self.hilbertspace | ||
| mode_index = subsys_priority[recusion_depth] | ||
| mode = hspace.subsystem_list[mode_index] | ||
| terminate_branch_length = hspace.subsystem_dims[mode_index] | ||
| # photon addition operator | ||
| excite_op = self._branch_analysis_excite_op(mode) | ||
| # loop over and find all states that matches the excited initial state | ||
| current_state = init_state | ||
| current_drs_idx = init_drs_idx | ||
| branch_drs_indices = [] | ||
| branch_states = [] | ||
| while True: | ||
| if recusion_depth == len(subsys_priority) - 1: | ||
| # we are at the end of the depth-first search: | ||
| # just add the state to the branch | ||
| branch_drs_indices.append(current_drs_idx) | ||
| branch_states.append(current_state) | ||
| else: | ||
| # continue the depth-first search: | ||
| # recursively call the function and append all the branch states | ||
| (_branch_drs_indices, _branch_states) = self._branch_analysis_LX_step( | ||
| subsys_priority, | ||
| recusion_depth + 1, | ||
| current_drs_idx, | ||
| current_state, | ||
| remaining_drs_indices, | ||
| remaining_evecs, | ||
| ) | ||
| branch_drs_indices.append(_branch_drs_indices) | ||
| branch_states.append(_branch_states) | ||
| # if the branch is long enough, terminate the loop | ||
| if len(branch_states) == terminate_branch_length: | ||
| break | ||
| # find the closest state to the excited current state | ||
| if len(remaining_evecs) == 0: | ||
| raise ValueError( | ||
| "No enough eigenstates to be assigned with a label. " | ||
| "It's likely that the eignestates are not complete. " | ||
| "Please try to obtain a complete set of eigenstates by " | ||
| "increasing `evals_count` before running the branch analysis." | ||
| ) | ||
| excited_state = (excite_op * current_state).unit() | ||
| overlaps = [np.abs(excited_state.overlap(evec)) for evec in remaining_evecs] | ||
| max_overlap_index = np.argmax(overlaps) | ||
| current_state = remaining_evecs[max_overlap_index] | ||
| current_drs_idx = remaining_drs_indices[max_overlap_index] | ||
| # remove the state from the remaining states | ||
| remaining_evecs.pop(max_overlap_index) | ||
| remaining_drs_indices.pop(max_overlap_index) | ||
| return branch_drs_indices, branch_states | ||
| def _branch_analysis_LX( | ||
| self, | ||
| param_indices: Tuple[int, ...], | ||
| subsys_priority: Optional[List[int]] = None, | ||
| transpose: bool = False, | ||
| ) -> np.ndarray: | ||
| """ | ||
| Perform a full branch analysis according to Dumas et al. (2024) for | ||
| a single parameter point using lexical ordering. Running through all | ||
| bare labels in the lexical order is equivalent to a depth-first traversal | ||
| in a tree structure. The method will start a recursive labeling using | ||
| method `_branch_analysis_LX_step`. | ||
| The eigenstates-bare-state-paring is based on the | ||
| "first-come-first-served" principle, the ordering of such traversal will | ||
| permute the bare labels and change the traversal order based on the | ||
| lexical order. For the last mode in the list, its states will be labelled | ||
| sequentially and organized in a single branch. | ||
| At the end, this function will organize the eigenstates into a | ||
| multi-dimensional array according to the mode_priority. | ||
| Parameters | ||
| ---------- | ||
| param_indices: | ||
| the indices of the parameter sweep to be analyzed. | ||
| subsys_priority: | ||
| a permutation of the subsystem indices and bare labels. If | ||
| it is provided, lexical ordering is performed on the permuted labels. | ||
| A "branch" is defined as a series of eigenstates formed by putting | ||
| excitations into the last subsystem in the list. | ||
| transpose: | ||
| if True, the returned array will be transposed, according to the | ||
| mode_priority. Otherwise, the array will be in the | ||
| shape of the subsystem dimensions in the original order. Now | ||
| it is a purely internal knob for testing. | ||
| Returns | ||
| ------- | ||
| branch_drs_indices | ||
| the multi-dimensional array of the dressed indices organized by | ||
| the mode_priority. If the dimensions of the subsystems are | ||
| D0, D1 and D2, the returned array will have the shape (D0, D1, D2). | ||
| If transposed is True, the array will be transposed according to | ||
| the mode_priority. | ||
| """ | ||
| if subsys_priority is None: | ||
| subsys_priority = list(range(self.hilbertspace.subsystem_count)) | ||
| else: | ||
| # check if the subsys_priority is a valid permutation of | ||
| # the subsystem indices: length and unique | ||
| if len(subsys_priority) != self.hilbertspace.subsystem_count: | ||
| raise ValueError( | ||
| "The length of subsys_priority does not match " | ||
| "the number of subsystems." | ||
| ) | ||
| if len(subsys_priority) != len(set(subsys_priority)): | ||
| raise ValueError( | ||
| "subsys_priority contains duplicate values, " | ||
| "which is supposed to be a permutation." | ||
| ) | ||
| # we assume that the ground state always has bare label (0, 0, ...) | ||
| evecs = self._data["evecs"][param_indices] | ||
| init_state = evecs[0] | ||
| remaining_evecs = list(evecs[1:]) | ||
| remaining_drs_indices = list(range(1, self.hilbertspace.dimension)) | ||
| branch_drs_indices, _ = self._branch_analysis_LX_step( | ||
| subsys_priority, 0, 0, init_state, remaining_drs_indices, remaining_evecs | ||
| ) | ||
| branch_drs_indices = np.array(branch_drs_indices) | ||
| if not transpose: | ||
| reversed_permutation = np.argsort(subsys_priority) | ||
| return np.transpose(branch_drs_indices, reversed_permutation) | ||
| return branch_drs_indices | ||
| def _branch_analysis_BE( | ||
| self, | ||
| param_indices: Tuple[int, ...], | ||
| subsys_priority: Optional[List[int]] = None, | ||
| BEs_count: Union[int, None] = None, | ||
| source_maj_vote: bool = False, | ||
| ) -> np.ndarray: | ||
| """ | ||
| Perform a full branch analysis according to Dumas et al. (2024) for | ||
| a single parameter point for a few eigenstates with the lowest bare | ||
| energies. It is particularly useful when the Hilbert space is too large | ||
| and not all the eigenstates need to be labeled. | ||
| In the bare energy ordering for branch analysis, the way to obtain the | ||
| excited dressed states | ||
| is ambiguous, e.g. |21> can be excited from |11> or |20>. So we need the | ||
| user to input `subsys_priority` to specify the path / branch to be taken. | ||
| It specifies the order of the subsystems to be excited, the last subsystem | ||
| in the list will be excited if possible. | ||
| Parameters | ||
| ---------- | ||
| param_indices: | ||
| the indices of the parameter sweep to be analyzed. | ||
| subsys_priority: | ||
| a permutation of the subsystem indices and bare labels. If | ||
| it is provided, lexical ordering is performed on the permuted labels. | ||
| A "branch" is defined as a series of eigenstates formed by putting | ||
| excitations into the last subsystem in the list. | ||
| BEs_count: | ||
| the number of states to be assigned. If None, all available eigenstates | ||
| will be assigned. | ||
| source_maj_vote: | ||
| if True, the branch will be determined by majority vote of the | ||
| potential candidates. It is purely an internal knob to test the | ||
| behavior of the branch analysis. It overrides mode_priority. | ||
| Returns | ||
| ------- | ||
| the multi-dimensional array of the dressed indices | ||
| """ | ||
| hspace = self.hilbertspace | ||
| dims = hspace.subsystem_dims | ||
| if subsys_priority is None: | ||
| subsys_priority = list(range(hspace.subsystem_count)) | ||
| if BEs_count is None: | ||
| BEs_count = len(self._data["evecs"][param_indices]) | ||
| elif len(self._data["evecs"][param_indices]) < BEs_count: | ||
| BEs_count = len(self._data["evecs"][param_indices]) | ||
| warn( | ||
| "evals_count is less than BEs_count, BEs_count is set to " | ||
| f"{len(self._data['evecs'][param_indices])}." | ||
| ) | ||
| # get the associated excitation operators | ||
| excite_op_list = [ | ||
| self._branch_analysis_excite_op(mode) for mode in hspace.subsystem_list | ||
| ] | ||
| # generate a list of their bare energies | ||
| bare_evals_by_sys = self._data["bare_evals"] | ||
| bare_evals = np.zeros(dims) | ||
| for idx in np.ndindex(tuple(dims)): | ||
| subsys_eval = [ | ||
| bare_evals_by_sys[subsys_idx][param_indices][level_idx] | ||
| for subsys_idx, level_idx in enumerate(idx) | ||
| ] | ||
| bare_evals[idx] = np.sum(subsys_eval) | ||
| bare_evals = bare_evals.ravel() | ||
| # sort the bare energies | ||
| # which will be the order of state assignment | ||
| sorted_indices = np.argsort(bare_evals)[:BEs_count] | ||
| # mode assignment | ||
| branch_drs_indices = np.ndarray(dims, dtype=object) | ||
| branch_drs_indices.fill(None) | ||
| evecs = self._data["evecs"][param_indices] | ||
| remaining_evecs = list(evecs) | ||
| remaining_drs_indices = list(range(0, self.hilbertspace.dimension)) | ||
| for raveled_bare_idx in sorted_indices: | ||
| # assign the dressed index for bare_idx | ||
| bare_idx = list(np.unravel_index(raveled_bare_idx, dims)) | ||
| if raveled_bare_idx == 0: | ||
| # the (0, 0, ...) is always assigned the dressed index 0 | ||
| branch_drs_indices[tuple(bare_idx)] = 0 | ||
| remaining_drs_indices.pop(0) | ||
| remaining_evecs.pop(0) | ||
| continue | ||
| # get previously assigned states (one less excitation) | ||
| # By comparing the excited states with the dressed states, | ||
| # we can find the dressed index of the current state | ||
| prev_bare_indices = [] | ||
| potential_drs_indices = [] | ||
| for subsys_idx in subsys_priority[::-1]: | ||
| # obtain the a bare index with one less excitation | ||
| prev_idx = copy(bare_idx) | ||
| if prev_idx[subsys_idx] == 0: | ||
| continue | ||
| prev_idx[subsys_idx] -= 1 | ||
| prev_drs_idx = branch_drs_indices[tuple(prev_idx)] | ||
| prev_bare_indices.append(prev_idx) | ||
| # state vector | ||
| prev_state = evecs[prev_drs_idx] | ||
| excited_state = excite_op_list[subsys_idx] * prev_state | ||
| excited_state = excited_state.unit() | ||
| # find the dressed index | ||
| overlaps = [ | ||
| np.abs(excited_state.overlap(evec)) for evec in remaining_evecs | ||
| ] | ||
| max_overlap_index = np.argmax(overlaps) | ||
| potential_drs_indices.append(remaining_drs_indices[max_overlap_index]) | ||
| if not source_maj_vote: | ||
| # we only need one path, which is the last one in the mode_priority | ||
| break | ||
| else: | ||
| # we need to check all the paths | ||
| continue | ||
| # do a majority vote, if equal, chose the first one | ||
| # this also works for source_maj_vote = False, when all lists are length 1 | ||
| unique_votes, counts = np.unique(potential_drs_indices, return_counts=True) | ||
| vote_result = np.argmax(counts) | ||
| drs_idx = unique_votes[vote_result] | ||
| idx_in_remaining_list = remaining_drs_indices.index(drs_idx) | ||
| # remove the state from the remaining states | ||
| remaining_evecs.pop(idx_in_remaining_list) | ||
| remaining_drs_indices.pop(idx_in_remaining_list) | ||
| branch_drs_indices[tuple(bare_idx)] = drs_idx | ||
| return branch_drs_indices | ||
| def _branch_analysis( | ||
| self, | ||
| ordering: Literal["LX", "BE"] = "BE", | ||
| subsys_priority: Optional[List[int]] = None, | ||
| transpose: bool = False, | ||
| BEs_count: Union[int, None] = None, | ||
| ) -> NamedSlotsNdarray: | ||
| """ | ||
| Perform a full branch analysis for all parameter points, according to | ||
| Dumas et al. (2024). We provide two orderings methods for the labeling: | ||
| - Lexical (ordering="LX"): traverse the bare states in `lexical order`_, | ||
| and perform the branch analysis generalized from Dumas et al. (2024). | ||
| - Bare Energy (ordering="BE"): traverse the bare states in the order of | ||
| their energy before coupling and perform label assignment. This is particularly | ||
| useful when the Hilbert space is too large and not all the eigenstates need | ||
| to be labeled. | ||
| Parameters | ||
| ---------- | ||
| ordering: | ||
| the ordering method for the labeling | ||
| - "LX": Lexical ordering | ||
| - "BE": Bare Energy | ||
| mode_priority: | ||
| a permutation of the subsystem indices and bare labels. If it | ||
| is provided, lexical ordering is performed on the permuted labels. | ||
| A "branch" is defined as a series of eigenstates formed by putting | ||
| excitations into the last subsystem in the list. | ||
| BEs_count: | ||
| the number of eigenstates to be labeled, for "BE" scheme only. If | ||
| None, all available eigenstates will be labeled. | ||
| Returns | ||
| ------- | ||
| a NamedSlotsNdarray object containing the branch analysis results | ||
| organized by the parameter indices. | ||
| For each parameter point, a flattened multi-dimensional array | ||
| is stored, representing the dressed indices organized by the | ||
| bare indices. E.g. if the dimensions of the subsystems are D0, D1 and D2, | ||
| the returned array will be ravelled from the shape (D0, D1, D2). | ||
| .. _lexical order: https://en.wikipedia.org/wiki/Lexicographic_order#Cartesian_products/ | ||
| """ | ||
| dressed_indices = np.empty(shape=self._parameters.counts, dtype=object) | ||
| param_indices = itertools.product(*map(range, self._parameters.counts)) | ||
| for index in param_indices: | ||
| if ordering == "LX": | ||
| if BEs_count is not None: | ||
| warn( | ||
| "BEs_count is not supported for lexical ordering, " | ||
| "it will be ignored." | ||
| ) | ||
| dressed_indices[index] = self._branch_analysis_LX( | ||
| index, | ||
| subsys_priority, | ||
| transpose, | ||
| ) | ||
| elif ordering == "BE": | ||
| dressed_indices[index] = self._branch_analysis_BE( | ||
| index, | ||
| subsys_priority, | ||
| BEs_count, | ||
| ) | ||
| else: | ||
| raise ValueError(f"Ordering {ordering} is not supported.") | ||
| dressed_indices = np.asarray(dressed_indices[:].tolist()) | ||
| parameter_dict = self._parameters.ordered_dict.copy() | ||
| shape = self._parameters.counts | ||
| return NamedSlotsNdarray( | ||
| dressed_indices.reshape(shape + (-1,)), | ||
| parameter_dict, | ||
| ) |
+18
-22
@@ -33,4 +33,4 @@ # storage.py | ||
| class WaveFunction: | ||
| """Container for wave function amplitudes defined for a specific basis. | ||
| Optionally, a corresponding energy is saved as well. | ||
| """Container for wave function amplitudes defined for a specific basis. Optionally, | ||
| a corresponding energy is saved as well. | ||
@@ -56,9 +56,8 @@ Parameters | ||
| def rescale(self, scale_factor: float) -> None: | ||
| """Rescale the wavefunction amplitudes by a given factor""" | ||
| """Rescale the wavefunction amplitudes by a given factor.""" | ||
| self.amplitudes *= scale_factor | ||
| def rescale_to_potential(self, potential_vals: np.ndarray): | ||
| """ | ||
| Rescale the dimensionless amplitude to a (pseudo-)energy that allows us to plot | ||
| wavefunctions and potential energies in the same plot. | ||
| """Rescale the dimensionless amplitude to a (pseudo-)energy that allows us to | ||
| plot wavefunctions and potential energies in the same plot. | ||
@@ -69,3 +68,2 @@ Parameters | ||
| array of potential energy values (that determine the energy range on the y axis | ||
| """ | ||
@@ -75,5 +73,5 @@ 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. | ||
| """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. | ||
@@ -88,3 +86,2 @@ Parameters | ||
| scale factor | ||
| """ | ||
@@ -149,3 +146,3 @@ FILL_FACTOR = 0.1 | ||
| param_vals: np.ndarray = None, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> None: | ||
@@ -169,4 +166,3 @@ self.system_params = system_params | ||
| def add_data(self, **kwargs) -> None: | ||
| """ | ||
| Adds one or several data sets to the DataStorage object. | ||
| """Adds one or several data sets to the DataStorage object. | ||
@@ -192,4 +188,4 @@ Parameters | ||
| """Container holding energy and state data as a function of a particular parameter | ||
| that is varied. Also stores all other system parameters used for generating the | ||
| set, and provides method for writing data to file. | ||
| that is varied. Also stores all other system parameters used for generating the set, | ||
| and provides method for writing data to file. | ||
@@ -223,3 +219,3 @@ Parameters | ||
| matrixelem_table: np.ndarray = None, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> None: | ||
@@ -239,7 +235,7 @@ self.system_params = system_params | ||
| matrixelem_table=matrixelem_table, | ||
| **kwargs | ||
| **kwargs, | ||
| ) | ||
| def subtract_ground(self) -> None: | ||
| """Subtract ground state energies from spectrum""" | ||
| """Subtract ground state energies from spectrum.""" | ||
| self.energy_table -= self.energy_table[:, 0] # type:ignore | ||
@@ -252,6 +248,6 @@ | ||
| label_list: List[str] = None, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> "Tuple[Figure, Axes]": | ||
| """Plots eigenvalues of as a function of one parameter, as stored in | ||
| `SpectrumData` object. | ||
| :class:`SpectrumData` object. | ||
@@ -281,3 +277,3 @@ Parameters | ||
| label_list=label_list, | ||
| **kwargs | ||
| **kwargs, | ||
| ) |
@@ -32,5 +32,4 @@ # sweeps.py | ||
| ) -> np.ndarray: | ||
| """ | ||
| Given parameter sweep data, compute and return a matrix element table using the bare | ||
| states of the specified subsystem. | ||
| """Given parameter sweep data, compute and return a matrix element table using the | ||
| bare states of the specified subsystem. | ||
@@ -72,4 +71,3 @@ Parameters | ||
| ) -> np.ndarray: | ||
| """ | ||
| Given parameter sweep data, compute and return a matrix element table using the | ||
| """Given parameter sweep data, compute and return a matrix element table using the | ||
| dressed states of the composite Hilbert space. | ||
@@ -76,0 +74,0 @@ |
@@ -66,3 +66,3 @@ # transmon.py | ||
| id_str: | ||
| optional string by which this instance can be referred to in `HilbertSpace` | ||
| optional string by which this instance can be referred to in :class:`HilbertSpace` | ||
| and `ParameterSweep`. If not provided, an id is auto-generated. | ||
@@ -119,3 +119,3 @@ esys_method: | ||
| def supported_noise_channels(cls) -> List[str]: | ||
| """Return a list of supported noise channels""" | ||
| """Return a list of supported noise channels.""" | ||
| return [ | ||
@@ -130,4 +130,4 @@ "tphi_1_over_f_cc", | ||
| def effective_noise_channels(cls) -> List[str]: | ||
| """Return a default list of channels used when calculating effective t1 and | ||
| t2 noise.""" | ||
| """Return a default list of channels used when calculating effective t1 and t2 | ||
| noise.""" | ||
| noise_channels = cls.supported_noise_channels() | ||
@@ -175,4 +175,3 @@ noise_channels.remove("t1_charge_impedance") | ||
| ) -> Tuple[float, float]: | ||
| """ | ||
| Finds the EJ and EC values given a qubit splitting `E01` and `anharmonicity`. | ||
| """Finds the EJ and EC values given a qubit splitting `E01` and `anharmonicity`. | ||
@@ -213,4 +212,3 @@ Parameters | ||
| ) -> ndarray: | ||
| """ | ||
| Returns charge operator n in the charge or eigenenergy basis. | ||
| """Returns charge operator n in the charge or eigenenergy basis. | ||
@@ -229,3 +227,3 @@ Parameters | ||
| Charge operator n in chosen basis as ndarray. | ||
| For `energy_esys=True`, n has dimensions of `truncated_dim` x `truncated_dim`. | ||
| For `energy_esys=True`, n has dimensions of :attr:`truncated_dim` x :attr:`truncated_dim`. | ||
| If an actual eigensystem is handed to `energy_sys`, then `n` has dimensions of m x m, | ||
@@ -241,4 +239,4 @@ where m is the number of given eigenvectors. | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator :math:`e^{i\\varphi}` in the charge or eigenenergy basis. | ||
| r""" | ||
| Returns operator :math:`e^{i\varphi}` in the charge or eigenenergy basis. | ||
@@ -248,12 +246,12 @@ Parameters | ||
| energy_esys: | ||
| If `False` (default), returns operator :math:`e^{i\\varphi}` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`e^{i\\varphi}` in the energy eigenbasis. | ||
| If `False` (default), returns operator :math:`e^{i\varphi}` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`e^{i\varphi}` in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns operator :math:`e^{i\\varphi}` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| returns operator :math:`e^{i\varphi}` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Operator :math:`e^{i\\varphi}` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`e^{i\\varphi}` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`e^{i\\varphi}` has dimensions of m x m, | ||
| Operator :math:`e^{i\varphi}` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`e^{i\varphi}` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`e^{i\varphi}` has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -269,4 +267,4 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator :math:`\\cos \\varphi` in the charge or eigenenergy basis. | ||
| r""" | ||
| Returns operator :math:`\cos \varphi` in the charge or eigenenergy basis. | ||
@@ -276,12 +274,12 @@ Parameters | ||
| energy_esys: | ||
| If `False` (default), returns operator :math:`\\cos \\varphi` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\\cos \\varphi` in the energy eigenbasis. | ||
| If `False` (default), returns operator :math:`\cos \varphi` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\cos \varphi` in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns operator :math:`\\cos \\varphi` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| returns operator :math:`\cos \varphi` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Operator :math:`\\cos \\varphi` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\\cos \\varphi` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\cos \\varphi` has dimensions of m x m, | ||
| Operator :math:`\cos \varphi` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\cos \varphi` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\cos \varphi` has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -296,4 +294,4 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator :math:`\\sin \\varphi` in the charge or eigenenergy basis. | ||
| r""" | ||
| Returns operator :math:`\sin \varphi` in the charge or eigenenergy basis. | ||
@@ -303,12 +301,12 @@ Parameters | ||
| energy_esys: | ||
| If `False` (default), returns operator :math:`\\sin \\varphi` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\\sin \\varphi` in the energy eigenbasis. | ||
| If `False` (default), returns operator :math:`\sin \varphi` in the charge basis. | ||
| If `True`, the energy eigenspectrum is computed, returns operator :math:`\sin \varphi` in the energy eigenbasis. | ||
| If `energy_esys = esys`, where esys is a tuple containing two ndarrays (eigenvalues and energy eigenvectors), | ||
| returns operator :math:`\\sin \\varphi` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| returns operator :math:`\sin \varphi` in the energy eigenbasis, and does not have to recalculate eigenspectrum. | ||
| Returns | ||
| ------- | ||
| Operator :math:`\\sin \\varphi` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\\sin \\varphi` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\\sin \\varphi` has dimensions of m x m, | ||
| Operator :math:`\sin \varphi` in chosen basis as ndarray. If the eigenenergy basis is chosen, | ||
| unless energy_esys is specified, :math:`\sin \varphi` has dimensions of truncated_dim | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, :math:`\sin \varphi` has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -323,4 +321,3 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns Hamiltonian in the charge or eigenenergy basis. | ||
| """Returns Hamiltonian in the charge or eigenenergy basis. | ||
@@ -338,3 +335,3 @@ Parameters | ||
| Hamiltonian in chosen basis as ndarray. For `energy_esys=False`, the Hamiltonian has dimensions of | ||
| `truncated_dim` x `truncated_dim`. For `energy_sys=esys`, the Hamiltonian has dimensions of m x m, | ||
| :attr:`truncated_dim` x :attr:`truncated_dim`. For `energy_sys=esys`, the Hamiltonian has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -359,5 +356,4 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator representing a derivative of the Hamiltonian with respect to | ||
| charge offset `ng` in the charge or eigenenergy basis. | ||
| """Returns operator representing a derivative of the Hamiltonian with respect to | ||
| charge offset :attr:`ng` in the charge or eigenenergy basis. | ||
@@ -375,4 +371,4 @@ Parameters | ||
| 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, | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -386,4 +382,3 @@ """ | ||
| ) -> ndarray: | ||
| """ | ||
| Returns operator representing a derivative of the Hamiltonian with respect to | ||
| """Returns operator representing a derivative of the Hamiltonian with respect to | ||
| EJ in the charge or eigenenergy basis. | ||
@@ -402,4 +397,4 @@ | ||
| 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, | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -411,7 +406,7 @@ """ | ||
| def hilbertdim(self) -> int: | ||
| """Returns Hilbert space dimension""" | ||
| """Returns Hilbert space dimension.""" | ||
| return 2 * self.ncut + 1 | ||
| def potential(self, phi: Union[float, ndarray]) -> ndarray: | ||
| """Transmon phase-basis potential evaluated at `phi`. | ||
| r"""Transmon phase-basis potential evaluated at :math:`\phi`. | ||
@@ -431,5 +426,5 @@ Parameters | ||
| nrange: Tuple[int, int] = None, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> Tuple[Figure, Axes]: | ||
| """Plots transmon wave function in charge basis | ||
| """Plots transmon wave function in charge basis. | ||
@@ -441,7 +436,7 @@ Parameters | ||
| mode: | ||
| `'abs_sqr', 'abs', 'real', 'imag'` | ||
| choices as specified in `constants.MODE_FUNC_DICT` (default value = 'real') | ||
| which: | ||
| index or indices of wave functions to plot (default value = 0) | ||
| index or indices of wave functions to plot (default value = 0) | ||
| nrange: | ||
| range of `n` to be included on the x-axis (default value = (-5,6)) | ||
| range of `n` to be included on the x-axis (default value = (-5,6)) | ||
| **kwargs: | ||
@@ -468,5 +463,5 @@ plotting parameters | ||
| scaling: float = None, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> Tuple[Figure, Axes]: | ||
| """Alias for plot_wavefunction""" | ||
| """Alias for plot_wavefunction.""" | ||
| return self.plot_wavefunction( | ||
@@ -478,3 +473,3 @@ esys=esys, | ||
| scaling=scaling, | ||
| **kwargs | ||
| **kwargs, | ||
| ) | ||
@@ -627,7 +622,6 @@ | ||
| dense form in the number basis, :math:`H_\text{CPB}=4E_\text{C}(\hat{ | ||
| n}-n_g)^2-\frac{\mathcal{E}_\text{J}(\Phi)}{2}(|n\rangle\langle n+1|+\text{ | ||
| h.c.})`, Here, the effective Josephson energy is flux-tunable: :math:`\mathcal{ | ||
| E}_J(\Phi) = E_{J,\text{max}} \sqrt{\cos^2(\pi\Phi/\Phi_0) + d^2 \sin^2( | ||
| \pi\Phi/\Phi_0)}` and :math:`d=(E_{J2}-E_{J1})(E_{J1}+E_{J2})` parametrizes the | ||
| junction asymmetry. | ||
| n}-n_g)^2-\frac{\mathcal{E}_\text{J}(\Phi)}{2}(|n\rangle\langle n+1|+\text{ h.c.})`, | ||
| Here, the effective Josephson energy is flux-tunable: :math:`\mathcal{ E}_J(\Phi) = | ||
| E_{J,\text{max}} \sqrt{\cos^2(\pi\Phi/\Phi_0) + d^2 \sin^2( \pi\Phi/\Phi_0)}` and | ||
| :math:`d=(E_{J2}-E_{J1})(E_{J1}+E_{J2})` parametrizes the junction asymmetry. | ||
@@ -656,3 +650,3 @@ Initialize with, for example:: | ||
| id_str: | ||
| optional string by which this instance can be referred to in `HilbertSpace` | ||
| optional string by which this instance can be referred to in :class:`HilbertSpace` | ||
| and `ParameterSweep`. If not provided, an id is auto-generated. | ||
@@ -708,4 +702,4 @@ esys_method: | ||
| def EJ(self) -> float: # type: ignore | ||
| """This is the effective, flux dependent Josephson energy, playing the role | ||
| of EJ in the parent class `Transmon`""" | ||
| """This is the effective, flux dependent Josephson energy, playing the role of | ||
| EJ in the parent class :class:`Transmon`""" | ||
| return self.EJmax * np.sqrt( | ||
@@ -729,3 +723,3 @@ np.cos(np.pi * self.flux) ** 2 + self.d**2 * np.sin(np.pi * self.flux) ** 2 | ||
| def supported_noise_channels(cls) -> List[str]: | ||
| """Return a list of supported noise channels""" | ||
| """Return a list of supported noise channels.""" | ||
| return [ | ||
@@ -744,3 +738,3 @@ "tphi_1_over_f_flux", | ||
| r"""Returns operator representing a derivative of the Hamiltonian with respect to | ||
| `flux` in the charge or eigenenergy basis. | ||
| :attr:`flux` in the charge or eigenenergy basis. | ||
@@ -764,4 +758,4 @@ Here, the derivative is taken with respect to flux before the qubit's :math:`\phi` degree of | ||
| 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, | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, operator has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -768,0 +762,0 @@ """ |
+12
-12
@@ -45,3 +45,4 @@ # units.py | ||
| """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` | ||
| The default unit system is GHz, but this can be changed by calling `set_units()` | ||
| with one of the `_supported_units` | ||
@@ -84,3 +85,3 @@ Parameters | ||
| def get_units_time_label(units: Optional[str] = None) -> str: | ||
| """Get a latex representation of 1/units""" | ||
| """Get a LaTeX representation of 1/units""" | ||
| units = units or _current_units | ||
@@ -104,4 +105,4 @@ if units not in _supported_units: | ||
| r""" | ||
| Converts `value` (a frequency or angular frequency) from currently set system units, | ||
| to standard units (Hz or 2pi/s). | ||
| Converts `value` (a frequency or angular frequency) from currently set system units | ||
| to standard units (Hz or 2pi/s). | ||
@@ -121,5 +122,4 @@ Parameters | ||
| def from_standard_units(value: float) -> float: | ||
| r""" | ||
| Converts `value` (a frequency or angular frequency) from standard units | ||
| (`[Hz]` or `2\pi / [s]`) to currently set system units. | ||
| r"""Converts `value` (a frequency or angular frequency) from standard units (`[Hz]` | ||
| or `2\pi / [s]`) to currently set system units. | ||
@@ -130,3 +130,3 @@ Parameters | ||
| a frequency or angular frequency assumed to be in standard units | ||
| (`[Hz]` or `2\pi / [s]`) | ||
| (`[Hz]` or `2\pi / [s]`) | ||
@@ -136,3 +136,2 @@ Returns | ||
| frequency or angular frequency converted to system units | ||
| """ | ||
@@ -143,5 +142,6 @@ return value / _units_factor[_current_units] | ||
| 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. | ||
| """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. | ||
@@ -148,0 +148,0 @@ Parameters |
@@ -43,3 +43,3 @@ # zeropi_full.py | ||
| class FullZeroPi(base.QubitBaseClass, serializers.Serializable, NoisyFullZeroPi): | ||
| r"""Zero-Pi qubit [Brooks2013]_ [Dempster2014]_ including coupling to the zeta mode. | ||
| r"""Zero-Pi qubit [Brooks2013]_ [Dempster2014]_ including coupling to the :math:`\zeta` mode. | ||
| The circuit is described by the Hamiltonian | ||
@@ -62,4 +62,4 @@ :math:`H = H_{0-\pi} + H_\text{int} + H_\zeta`, where | ||
| :math:`dC`, :math:`dE_\text{L}` follows [Groszkowski2018]_. Internally, | ||
| the ``FullZeroPi`` class formulates the Hamiltonian matrix via the | ||
| product basis of the decoupled Zero-Pi qubit (see ``ZeroPi``) on one hand, and the | ||
| the `FullZeroPi` class formulates the Hamiltonian matrix via the | ||
| product basis of the decoupled Zero-Pi qubit (see :class:`ZeroPi`) on one hand, and the | ||
| zeta LC oscillator on the other hand. | ||
@@ -104,12 +104,12 @@ | ||
| id_str: | ||
| optional string by which this instance can be referred to in `HilbertSpace` | ||
| optional string by which this instance can be referred to in :class:`HilbertSpace` | ||
| and `ParameterSweep`. If not provided, an id is auto-generated. | ||
| esys_method: | ||
| method for esys diagonalization, callable or string representation | ||
| esys_method_options: | ||
| dictionary with esys diagonalization options | ||
| evals_method: | ||
| method for evals diagonalization, callable or string representation | ||
| evals_method_options: | ||
| dictionary with evals diagonalization options | ||
| esys_method: | ||
| method for esys diagonalization, callable or string representation | ||
| esys_method_options: | ||
| dictionary with esys diagonalization options | ||
| evals_method: | ||
| method for evals diagonalization, callable or string representation | ||
| evals_method_options: | ||
| dictionary with evals diagonalization options | ||
| """ | ||
@@ -244,3 +244,3 @@ | ||
| def supported_noise_channels(cls) -> List[str]: | ||
| """Return a list of supported noise channels""" | ||
| """Return a list of supported noise channels.""" | ||
| return [ | ||
@@ -295,3 +295,3 @@ "tphi_1_over_f_cc", | ||
| def set_EC_via_ECS(self, ECS: float) -> None: | ||
| """Helper function to set `EC` by providing `ECS`, keeping `ECJ` constant.""" | ||
| """Helper function to set :attr:`EC` by providing `ECS`, keeping `ECJ` constant.""" | ||
| self._zeropi.set_EC_via_ECS(ECS) | ||
@@ -301,3 +301,3 @@ | ||
| def E_zeta(self) -> float: | ||
| """Returns energy quantum of the zeta mode""" | ||
| """Returns energy quantum of the zeta mode.""" | ||
| return (8.0 * self.EL * self.EC) ** 0.5 | ||
@@ -307,4 +307,4 @@ | ||
| raise ValueError( | ||
| "Cannot directly set `E_zeta`. Instead one can set its value through `EL`" | ||
| " or `EC`." | ||
| "Cannot directly set `E_zeta`. Instead one can set its value through :attr:`EL`" | ||
| " or :attr:`EC`." | ||
| ) | ||
@@ -350,3 +350,4 @@ | ||
| r"""Returns Hamiltonian in basis obtained by discretizing :math:`\phi`, employing | ||
| charge basis for :math:`\theta`, and Fock basis for :math:`\zeta`, or in the eigenenergy basis. | ||
| charge basis for :math:`\theta`, and Fock basis for :math:`\zeta`, or in the | ||
| eigenenergy basis. | ||
@@ -367,6 +368,5 @@ Parameters | ||
| Hamiltonian in chosen basis as csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, the Hamiltonian has dimensions of `truncated_dim` | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m, | ||
| unless `energy_esys` is specified, the Hamiltonian has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m, | ||
| for m given eigenvectors. | ||
| """ | ||
@@ -423,5 +423,5 @@ zeropi_dim = self.zeropi_cutoff | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Calculates a derivative of the Hamiltonian w.r.t flux, at the current value of flux, | ||
| as stored in the object. The returned operator is in the product basis or eigenenergy basis. | ||
| r"""Calculates a derivative of the Hamiltonian w.r.t flux, at the current value | ||
| of flux, as stored in the object. The returned operator is in the product basis | ||
| or eigenenergy basis. | ||
@@ -450,3 +450,3 @@ Helper method _zeropi_operator_in_product_basis is employed which converts | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -465,5 +465,4 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Calculates a derivative of the Hamiltonian w.r.t EJ. The returned operator is in the | ||
| product basis or eigenenergy basis. | ||
| r"""Calculates a derivative of the Hamiltonian w.r.t EJ. The returned operator is | ||
| in the product basis or eigenenergy basis. | ||
@@ -492,3 +491,3 @@ Helper method _zeropi_operator_in_product_basis is employed which converts | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -505,6 +504,5 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Calculates a derivative of the Hamiltonian w.r.t ng. | ||
| Returns matrix representing a derivative of the Hamiltonian in the native Hamiltonian basis | ||
| or eigenenergy basis. | ||
| r"""Calculates a derivative of the Hamiltonian w.r.t ng. Returns matrix | ||
| representing a derivative of the Hamiltonian in the native Hamiltonian basis or | ||
| eigenenergy basis. | ||
@@ -523,3 +521,3 @@ Parameters | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -566,4 +564,3 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Returns :math:`i d/d\phi` operator in the product or eigenenergy basis. | ||
| r"""Returns :math:`i d/d\phi` operator in the product or eigenenergy basis. | ||
@@ -592,3 +589,3 @@ Helper method _zeropi_operator_in_product_basis is employed which converts | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -607,4 +604,3 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Returns :math:`n_\theta` operator in the product or eigenenergy basis. | ||
| r"""Returns :math:`n_\theta` operator in the product or eigenenergy basis. | ||
@@ -633,3 +629,3 @@ Helper method _zeropi_operator_in_product_basis is employed which converts | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -648,4 +644,3 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Returns :math:`\phi` operator in the product or eigenenergy basis. | ||
| r"""Returns :math:`\phi` operator in the product or eigenenergy basis. | ||
@@ -674,3 +669,3 @@ Helper method _zeropi_operator_in_product_basis is employed which converts | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -685,3 +680,3 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| def hilbertdim(self) -> int: | ||
| """Returns Hilbert space dimension | ||
| """Returns Hilbert space dimension. | ||
@@ -724,3 +719,3 @@ Returns | ||
| def g_phi_coupling_matrix(self, zeropi_states: ndarray) -> ndarray: | ||
| """Returns a matrix of coupling strengths g^\\phi_{ll'} | ||
| r"""Returns a matrix of coupling strengths :math:`g^\phi_{ll'}` | ||
| [cmp. Dempster et al., Eq. (18)], using the states from the list | ||
@@ -736,3 +731,3 @@ `zeropi_states`. Most commonly, `zeropi_states` will contain eigenvectors of the | ||
| def g_theta_coupling_matrix(self, zeropi_states: ndarray) -> ndarray: | ||
| """Returns a matrix of coupling strengths i*g^\\theta_{ll'} | ||
| r"""Returns a matrix of coupling strengths :math:`ig^\theta_{ll'}` | ||
| [cmp. Dempster et al., Eq. (17)], using the states from the list | ||
@@ -749,3 +744,3 @@ 'zeropi_states'. | ||
| ) -> ndarray: | ||
| """Returns a matrix of coupling strengths g_{ll'} [cmp. Dempster et al., text | ||
| r"""Returns a matrix of coupling strengths :math:`g_{ll'}` [cmp. Dempster et al., text | ||
| above Eq. (17)], using the states from 'zeropi_states'. If | ||
@@ -752,0 +747,0 @@ `zeropi_states==None`, then a set of `self.zeropi` eigenstates is calculated. |
+69
-83
@@ -51,3 +51,3 @@ # zeropi.py | ||
| class ZeroPi(base.QubitBaseClass, serializers.Serializable, NoisyZeroPi): | ||
| r"""Zero-Pi Qubit | ||
| r"""Zero-Pi Qubit. | ||
@@ -58,3 +58,3 @@ | [1] Brooks et al., Physical Review A, 87(5), 052306 (2013). http://doi.org/10.1103/PhysRevA.87.052306 | ||
| Zero-Pi qubit without coupling to the `zeta` mode, i.e., no disorder in `EC` and | ||
| Zero-Pi qubit without coupling to the :math:`\zeta` mode, i.e., no disorder in `EC` and | ||
| `EL`, see Eq. (4) in Groszkowski et al., New J. Phys. 20, 043053 (2018), | ||
@@ -69,4 +69,4 @@ | ||
| Formulation of the Hamiltonian matrix proceeds by discretization of the `phi` | ||
| variable, and using charge basis for the `theta` variable. | ||
| Formulation of the Hamiltonian matrix proceeds by discretization of the :math:`\phi` | ||
| variable, and using charge basis for the :math:`\theta` variable. | ||
@@ -95,3 +95,3 @@ Parameters | ||
| ncut: | ||
| charge number cutoff for `n_theta`, `n_theta = -ncut, ..., ncut` | ||
| charge number cutoff for :math:`n_\theta`, :math:`n_\theta = -{\rm ncut}, \dots, {\rm ncut}` | ||
| ECS: | ||
@@ -103,13 +103,13 @@ total charging energy including large shunting capacitances and junction | ||
| id_str: | ||
| optional string by which this instance can be referred to in `HilbertSpace` | ||
| optional string by which this instance can be referred to in :class:`HilbertSpace` | ||
| and `ParameterSweep`. If not provided, an id is auto-generated. | ||
| esys_method: | ||
| method for esys diagonalization, callable or string representation | ||
| esys_method_options: | ||
| dictionary with esys diagonalization options | ||
| evals_method: | ||
| method for evals diagonalization, callable or string representation | ||
| evals_method_options: | ||
| dictionary with evals diagonalization options | ||
| """ | ||
| esys_method: | ||
| method for esys diagonalization, callable or string representation | ||
| esys_method_options: | ||
| dictionary with esys diagonalization options | ||
| evals_method: | ||
| method for evals diagonalization, callable or string representation | ||
| evals_method_options: | ||
| dictionary with evals diagonalization options | ||
| """ | ||
@@ -208,3 +208,3 @@ EJ = descriptors.WatchedProperty(float, "QUANTUMSYSTEM_UPDATE") | ||
| def supported_noise_channels(cls) -> List[str]: | ||
| """Return a list of supported noise channels""" | ||
| """Return a list of supported noise channels.""" | ||
| return [ | ||
@@ -279,7 +279,7 @@ "tphi_1_over_f_cc", | ||
| def set_EC_via_ECS(self, ECS: float) -> None: | ||
| """Helper function to set `EC` by providing `ECS`, keeping `ECJ` constant.""" | ||
| """Helper function to set :attr:`EC` by providing `ECS`, keeping `ECJ` constant.""" | ||
| self.EC = 1 / (1 / ECS - 1 / self.ECJ) | ||
| def hilbertdim(self) -> int: | ||
| """Returns Hilbert space dimension""" | ||
| """Returns Hilbert space dimension.""" | ||
| return self.grid.pt_count * (2 * self.ncut + 1) | ||
@@ -291,3 +291,3 @@ | ||
| ------- | ||
| value of the potential energy evaluated at phi, theta | ||
| value of the potential energy evaluated at :math:`\phi`, :math:`\theta` | ||
| """ | ||
@@ -305,4 +305,3 @@ return ( | ||
| def sparse_kinetic_mat(self) -> csc_matrix: | ||
| """ | ||
| Kinetic energy portion of the Hamiltonian. | ||
| """Kinetic energy portion of the Hamiltonian. | ||
@@ -343,4 +342,3 @@ Returns | ||
| def sparse_potential_mat(self) -> csc_matrix: | ||
| """ | ||
| Potential energy portion of the Hamiltonian. | ||
| """Potential energy portion of the Hamiltonian. | ||
@@ -398,6 +396,5 @@ Returns | ||
| ) -> csc_matrix: | ||
| r""" | ||
| Calculates Hamiltonian in basis obtained by discretizing :math:`\phi` and employing | ||
| charge basis for :math:`\theta` or in the eigenenergy basis. Returns matrix representing | ||
| the potential energy operator. | ||
| r"""Calculates Hamiltonian in basis obtained by discretizing :math:`\phi` and | ||
| employing charge basis for :math:`\theta` or in the eigenenergy basis. Returns | ||
| matrix representing the potential energy operator. | ||
@@ -416,4 +413,4 @@ Parameters | ||
| Hamiltonian in chosen basis as csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, the Hamiltonian has dimensions of `truncated_dim` | ||
| x `truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m, | ||
| unless `energy_esys` is specified, the Hamiltonian has dimensions of :attr:`truncated_dim` | ||
| x :attr:`truncated_dim`. Otherwise, if eigenenergy basis is chosen, Hamiltonian has dimensions of m x m, | ||
| for m given eigenvectors. | ||
@@ -427,9 +424,8 @@ """ | ||
| def sparse_d_potential_d_flux_mat(self) -> csc_matrix: | ||
| r""" | ||
| Calculates a derivative of the potential energy w.r.t flux, at the current value of | ||
| flux, as stored in the object. | ||
| r"""Calculates a derivative of the potential energy w.r.t flux, at the current | ||
| value of flux, as stored in the object. | ||
| The flux is assumed to be given in the units of the ratio \Phi_{ext}/\Phi_0. | ||
| So if \frac{\partial U}{ \partial \Phi_{\rm ext}}, is needed, the expr returned | ||
| by this function, needs to be multiplied by 1/\Phi_0. | ||
| The flux is assumed to be given in the units of the ratio :math:`\Phi_{\rm ext}/\Phi_0`. | ||
| So if :math:`\frac{\partial U}{\partial \Phi_{\rm ext}}`, is needed, the expr returned | ||
| by this function, needs to be multiplied by :math:`1/\Phi_0`. | ||
@@ -455,8 +451,6 @@ Returns | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Calculates a derivative of the Hamiltonian w.r.t flux, at the current value | ||
| r"""Calculates a derivative of the Hamiltonian w.r.t flux, at the current value | ||
| of flux, as stored in the object. The flux is assumed to be given in the units | ||
| of the ratio :math:`\Phi_{ext}/\Phi_0`. | ||
| Returns matrix representing a derivative of the Hamiltonian in the native Hamiltonian basis | ||
| or eigenenergy basis. | ||
| of the ratio :math:`\Phi_{ext}/\Phi_0`. Returns matrix representing a derivative | ||
| of the Hamiltonian in the native Hamiltonian basis or eigenenergy basis. | ||
@@ -475,3 +469,3 @@ Parameters | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -499,6 +493,5 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Calculates a derivative of the Hamiltonian w.r.t EJ. | ||
| Returns matrix representing a derivative of the Hamiltonian in the native Hamiltonian basis | ||
| or eigenenergy basis. | ||
| r"""Calculates a derivative of the Hamiltonian w.r.t EJ. Returns matrix | ||
| representing a derivative of the Hamiltonian in the native Hamiltonian basis or | ||
| eigenenergy basis. | ||
@@ -517,3 +510,3 @@ Parameters | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -528,6 +521,5 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Calculates a derivative of the Hamiltonian w.r.t ng as stored in the object. | ||
| Returns matrix representing a derivative of the Hamiltonian in the native Hamiltonian basis | ||
| or eigenenergy basis. | ||
| r"""Calculates a derivative of the Hamiltonian w.r.t ng as stored in the object. | ||
| Returns matrix representing a derivative of the Hamiltonian in the native | ||
| Hamiltonian basis or eigenenergy basis. | ||
@@ -546,3 +538,3 @@ Parameters | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -556,3 +548,3 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| r""" | ||
| Identity operator acting only on the `\phi` Hilbert subspace. | ||
| Identity operator acting only on the :math:`\phi` Hilbert subspace. | ||
| """ | ||
@@ -564,3 +556,3 @@ pt_count = self.grid.pt_count | ||
| r""" | ||
| Identity operator acting only on the `\theta` Hilbert subspace. | ||
| Identity operator acting only on the :math:`\theta` Hilbert subspace. | ||
| """ | ||
@@ -571,5 +563,3 @@ dim_theta = 2 * self.ncut + 1 | ||
| def i_d_dphi_operator(self) -> csc_matrix: | ||
| r""" | ||
| Operator :math:`i d/d\phi`. | ||
| """ | ||
| r"""Operator :math:`i d/d\phi`.""" | ||
| return sparse.kron( | ||
@@ -583,3 +573,3 @@ self.grid.first_derivative_matrix(prefactor=1j), | ||
| r""" | ||
| Operator :math:`\phi`, acting only on the `\phi` Hilbert subspace. | ||
| Operator :math:`\phi`, acting only on the :math:`\phi` Hilbert subspace. | ||
| """ | ||
@@ -596,4 +586,3 @@ pt_count = self.grid.pt_count | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Returns :math:`\phi` operator in the native or eigenenergy basis. | ||
| r"""Returns :math:`\phi` operator in the native or eigenenergy basis. | ||
@@ -612,3 +601,3 @@ Parameters | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -623,4 +612,3 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Returns :math:`n_\theta` operator in the native or eigenenergy basis. | ||
| r"""Returns :math:`n_\theta` operator in the native or eigenenergy basis. | ||
@@ -639,3 +627,3 @@ Parameters | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -654,3 +642,3 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| r""" | ||
| Operator :math:`\sin(\phi + x)`, acting only on the `\phi` Hilbert subspace.x | ||
| Operator :math:`\sin(\phi + x)`, acting only on the :math:`\phi` Hilbert subspace. | ||
| """ | ||
@@ -667,3 +655,3 @@ pt_count = self.grid.pt_count | ||
| r""" | ||
| Operator :math:`\cos(\phi + x)`, acting only on the `\phi` Hilbert subspace. | ||
| Operator :math:`\cos(\phi + x)`, acting only on the :math:`\phi` Hilbert subspace. | ||
| """ | ||
@@ -680,3 +668,3 @@ pt_count = self.grid.pt_count | ||
| r""" | ||
| Operator :math:`\cos(\theta)`, acting only on the `\theta` Hilbert subspace. | ||
| Operator :math:`\cos(\theta)`, acting only on the :math:`\theta` Hilbert subspace. | ||
| """ | ||
@@ -700,4 +688,3 @@ dim_theta = 2 * self.ncut + 1 | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Returns :math:`\cos(\theta)` operator in the native or eigenenergy basis. | ||
| r"""Returns :math:`\cos(\theta)` operator in the native or eigenenergy basis. | ||
@@ -716,3 +703,3 @@ Parameters | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -728,3 +715,3 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| r""" | ||
| Operator :math:`\sin(\theta)`, acting only on the `\theta` Hilbert space. | ||
| Operator :math:`\sin(\theta)`, acting only on the :math:`\theta` Hilbert space. | ||
| """ | ||
@@ -749,4 +736,3 @@ dim_theta = 2 * self.ncut + 1 | ||
| ) -> Union[ndarray, csc_matrix]: | ||
| r""" | ||
| Returns :math:`\sin(\theta)` operator in the native or eigenenergy basis. | ||
| r"""Returns :math:`\sin(\theta)` operator in the native or eigenenergy basis. | ||
@@ -765,3 +751,3 @@ Parameters | ||
| returned as a csc_matrix. If the eigenenergy basis is chosen, | ||
| unless `energy_esys` is specified, operator has dimensions of `truncated_dim` | ||
| unless `energy_esys` is specified, operator has dimensions of :attr:`truncated_dim` | ||
| x truncated_dim, and is returned as an ndarray. Otherwise, if eigenenergy basis is chosen, | ||
@@ -779,5 +765,5 @@ operator has dimensions of m x m, for m given eigenvectors, and is returned as an ndarray. | ||
| contour_vals: Union[List[float], ndarray] = None, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> Tuple[Figure, Axes]: | ||
| """Draw contour plot of the potential energy. | ||
| r"""Draw contour plot of the potential energy. | ||
@@ -787,3 +773,3 @@ Parameters | ||
| theta_grid: | ||
| used for setting a custom grid for theta; if None use self._default_grid | ||
| used for setting a custom grid for :math:`\theta`; if None use self._default_grid | ||
| contour_vals: | ||
@@ -804,3 +790,3 @@ **kwargs: | ||
| ylabel=r"$\theta$", | ||
| **kwargs | ||
| **kwargs, | ||
| ) | ||
@@ -814,3 +800,3 @@ | ||
| ) -> WaveFunctionOnGrid: | ||
| """Returns a zero-pi wave function in `phi`, `theta` basis | ||
| r"""Returns a zero-pi wave function in :math:`\phi,\theta` basis | ||
@@ -824,3 +810,3 @@ Parameters | ||
| theta_grid: | ||
| used for setting a custom grid for theta; if None use self._default_grid | ||
| used for setting a custom grid for :math:`\theta`; if None use self._default_grid | ||
| """ | ||
@@ -862,3 +848,3 @@ evals_count = max(which + 1, 3) | ||
| zero_calibrate: bool = True, | ||
| **kwargs | ||
| **kwargs, | ||
| ) -> Tuple[Figure, Axes]: | ||
@@ -872,3 +858,3 @@ """Plots 2d phase-basis wave function. | ||
| which: | ||
| index of wave function to be plotted (default value = (0) | ||
| index of wave function to be plotted (default value = 0) | ||
| theta_grid: | ||
@@ -878,3 +864,3 @@ used for setting a custom grid for theta; if None use self._default_grid | ||
| choices as specified in `constants.MODE_FUNC_DICT` | ||
| (default value = 'abs_sqr') | ||
| (default value = 'abs') | ||
| zero_calibrate: | ||
@@ -896,3 +882,3 @@ if True, colors are adjusted to use zero wavefunction amplitude as the | ||
| ylabel=r"$\theta$", | ||
| **kwargs | ||
| **kwargs, | ||
| ) |
@@ -158,3 +158,3 @@ # explorer_panels.py | ||
| shape=(paramvals_count, subsys.truncated_dim, subsys.truncated_dim), | ||
| dtype=np.complex_, | ||
| dtype=np.complex128, | ||
| ) | ||
@@ -161,0 +161,0 @@ for index, paramval in enumerate(param_vals): |
@@ -46,4 +46,3 @@ # explorer_settings.py | ||
| class ExplorerSettings: | ||
| """ | ||
| Generates the UI for Explorer settings. | ||
| """Generates the UI for Explorer settings. | ||
@@ -50,0 +49,0 @@ Parameters |
@@ -64,4 +64,7 @@ # explorer_widget.py | ||
| class PlotID: | ||
| """Class for storing plot identifiers. Used for plot panel selection.""" | ||
| """Class for storing plot identifiers. | ||
| Used for plot panel selection. | ||
| """ | ||
| SEP = " | " | ||
@@ -96,4 +99,3 @@ | ||
| class Explorer: | ||
| """ | ||
| Generates the UI for exploring `ParameterSweep` objects. | ||
| """Generates the UI for exploring `ParameterSweep` objects. | ||
@@ -503,3 +505,4 @@ Parameters | ||
| def active_switches_by_plot_id(self) -> Dict[PlotID, "ui.LinkedSwitch"]: | ||
| """Returns a dictionary labeling all selected switches by their plot_id names.""" | ||
| """Returns a dictionary labeling all selected switches by their plot_id | ||
| names.""" | ||
| return { | ||
@@ -513,9 +516,9 @@ plot_id: switch | ||
| def selected_plot_id_list(self) -> List[PlotID]: | ||
| """Returns a list of strings capturing the names of all panels selected via | ||
| the switches.""" | ||
| """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.""" | ||
| """Returns a list of selection sliders, one for each parameter that is part of | ||
| the underlying ParameterSweep object.""" | ||
| slider_by_name = { | ||
@@ -522,0 +525,0 @@ param_name: ui.DiscreteSetSlider( |
@@ -12,5 +12,3 @@ # fileio_backends.py | ||
| ############################################################################ | ||
| """ | ||
| Helper routines for writing data to h5 files. | ||
| """ | ||
| """Helper routines for writing data to h5 files.""" | ||
@@ -44,4 +42,3 @@ import ast | ||
| class IOWriter(ABC): | ||
| """ | ||
| ABC for writing class instance data to file. | ||
| """ABC for writing class instance data to file. | ||
@@ -78,11 +75,10 @@ Parameters | ||
| class H5Writer(IOWriter): | ||
| """Writes IOData to a custom-format h5 file""" | ||
| """Writes IOData to a custom-format h5 file.""" | ||
| def write_attributes(self, h5file_group: Union["Group", "File"]) -> None: # type: ignore | ||
| """ | ||
| Attribute data consists of | ||
| """Attribute data consists of. | ||
| 1. `__init__` parameters that are of type str or numerical. These are | ||
| directly written into `h5py.Group.attrs` 2. lists are stored under | ||
| `<h5py.Group>/__lists` 3. dicts are stored under `<h5py.Group>/__dicts` | ||
| 1. `__init__` parameters that are of type str or numerical. These are | ||
| directly written into `h5py.Group.attrs` 2. lists are stored under | ||
| `<h5py.Group>/__lists` 3. dicts are stored under `<h5py.Group>/__dicts` | ||
| """ | ||
@@ -112,6 +108,4 @@ h5file_group.attrs.create( | ||
| def write_ndarrays(self, h5file_group: Union["Group", "File"]) -> None: # type: ignore | ||
| """ | ||
| Writes ndarray (float or complex) data contained in `self.iodata` to the | ||
| provided `h5py.Group` as a `h5py.Dataset`, using gzip compression. | ||
| """ | ||
| """Writes ndarray (float or complex) data contained in `self.iodata` to the | ||
| provided `h5py.Group` as a `h5py.Dataset`, using gzip compression.""" | ||
| data_group = h5file_group.file.require_group("__data") | ||
@@ -127,7 +121,5 @@ for name, array in self.io_data.ndarrays.items(): | ||
| def write_objects(self, h5file_group: Union["Group", "File"]) -> None: # type: ignore | ||
| """ | ||
| Writes data representing a Python object other than ndarray, list and dict, | ||
| """Writes data representing a Python object other than ndarray, list and dict, | ||
| contained in `self.iodata` to the provided `h5py.Group` und | ||
| `<h5py.Group>/__objects`. | ||
| """ | ||
| `<h5py.Group>/__objects`.""" | ||
| h5file_group = h5file_group.create_group("__objects") | ||
@@ -141,6 +133,4 @@ for obj_name in self.io_data.objects.keys(): | ||
| def to_file(self, io_data: io.IOData, file_handle: "Group" = None) -> None: | ||
| """ | ||
| Takes the serialized IOData and writes it either to a new h5 file with file | ||
| name given by `self.filename` to to the given h5py.Group of an open h5 file. | ||
| """ | ||
| """Takes the serialized IOData and writes it either to a new h5 file with file | ||
| name given by `self.filename` to to the given h5py.Group of an open h5 file.""" | ||
| self.io_data = io_data | ||
@@ -164,5 +154,3 @@ if file_handle is None: | ||
| class H5Reader: | ||
| """ | ||
| Enables reading h5 files generated with scqubits. | ||
| """ | ||
| """Enables reading h5 files generated with scqubits.""" | ||
@@ -178,4 +166,3 @@ def __init__(self, filename: str, file_handle: "Group" = None) -> None: | ||
| ) -> Dict[str, Union[float, str, int]]: | ||
| """ | ||
| Converts h5 attribute data to a Python dictionary. | ||
| """Converts h5 attribute data to a Python dictionary. | ||
@@ -190,6 +177,5 @@ Parameters | ||
| def read_attributes(self, h5file_group: Union["Group", "File"]) -> Dict[str, Any]: | ||
| """ | ||
| Read data from h5 file group that is stored directly as `<h5py.Group>.attrs`, | ||
| or saved in subgroups titled `<h5py.Group>/__lists` and `<h5py.Group>/__dicts`. | ||
| """ | ||
| """Read data from h5 file group that is stored directly as `<h5py.Group>.attrs`, | ||
| or saved in subgroups titled `<h5py.Group>/__lists` and | ||
| `<h5py.Group>/__dicts`.""" | ||
| attributes = self.h5_attrs_to_dict(h5file_group.attrs) | ||
@@ -209,5 +195,3 @@ if "__dicts" in h5file_group: | ||
| def read_ndarrays(self, h5file_group: Union["Group", "File"]) -> Dict[str, ndarray]: | ||
| """ | ||
| Read numpy array data from h5 file group. | ||
| """ | ||
| """Read numpy array data from h5 file group.""" | ||
| ndarrays = {} | ||
@@ -235,6 +219,4 @@ | ||
| ) -> Dict[str, io.IOData]: | ||
| """ | ||
| Read data from the given h5 file group that represents a Python object other | ||
| than an ndarray, list, or dict. | ||
| """ | ||
| """Read data from the given h5 file group that represents a Python object other | ||
| than an ndarray, list, or dict.""" | ||
| inner_objects = {} | ||
@@ -247,7 +229,8 @@ h5file_group = h5file_group["__objects"] | ||
| def from_file(self, filename: str, file_handle: "Group" = None) -> io.IOData: | ||
| """Either opens a new h5 file for reading or accesses an already opened file via | ||
| the given h5.Group handle. | ||
| Reads all data from the three categories of attributes (incl. lists and dicts), | ||
| ndarrays, and objects. | ||
| """ | ||
| Either opens a new h5 file for reading or accesses an already opened file via | ||
| the given h5.Group handle. Reads all data from the three categories of | ||
| attributes (incl. lists and dicts), ndarrays, and objects. | ||
| """ | ||
| if file_handle is None: | ||
@@ -268,11 +251,9 @@ h5file_group = h5py.File(filename, "r", rdcc_nbytes=1024**2 * 200) | ||
| class CSVWriter(IOWriter): | ||
| """ | ||
| Given `filename="somename.csv"`, write initdata into somename.csv Then, additional | ||
| csv files are written for each dataset, with filenames: `"somename_" + dataname0 + | ||
| ".csv"` etc. | ||
| """ | ||
| """Given `filename="somename.csv"`, write initdata into somename.csv Then, | ||
| additional csv files are written for each dataset, with filenames: `"somename_" + | ||
| dataname0 + ".csv"` etc.""" | ||
| def append_ndarray_info(self, attributes): | ||
| """Add data set information to attributes, so that dataset names and | ||
| dimensions are available in attributes CSV file.""" | ||
| """Add data set information to attributes, so that dataset names and dimensions | ||
| are available in attributes CSV file.""" | ||
| for index, dataname in enumerate(self.io_data.ndarrays.keys()): | ||
@@ -356,3 +337,3 @@ data = self.io_data.ndarrays[dataname] | ||
| except ValueError: | ||
| data_array = np.loadtxt(filename, dtype=np.complex_) | ||
| data_array = np.loadtxt(filename, dtype=np.complex128) | ||
| if slices > 1: | ||
@@ -386,4 +367,3 @@ nrows, ncols = data_array.shape | ||
| def np_savetxt_3d(array3d: ndarray, filename: str): | ||
| """ | ||
| Helper function that splits a 3d numpy array into 2d slices for writing as csv | ||
| """Helper function that splits a 3d numpy array into 2d slices for writing as csv | ||
| data to a new file. Slices are separated by a comment row `# New slice`. | ||
@@ -390,0 +370,0 @@ |
@@ -22,11 +22,9 @@ # fileio_qutip.py | ||
| class QutipEigenstates(np.ndarray, Serializable): | ||
| """Wrapper class that adds serialization functionality to the numpy | ||
| ndarray class.""" | ||
| """Wrapper class that adds serialization functionality to the numpy ndarray | ||
| class.""" | ||
| @classmethod | ||
| def deserialize(cls, io_data: IOData) -> np.ndarray: # type:ignore | ||
| """ | ||
| Take the given IOData and return an instance of the described class, initialized | ||
| with the data stored in io_data. | ||
| """ | ||
| """Take the given IOData and return an instance of the described class, | ||
| initialized with the data stored in io_data.""" | ||
| # Qobj in Qutip>=5 wants this to be a nested list | ||
@@ -44,5 +42,3 @@ qobj_dims = io_data.ndarrays["qobj_dims"].tolist() | ||
| def serialize(self) -> IOData: | ||
| """ | ||
| Convert the content of the current class instance into IOData format. | ||
| """ | ||
| """Convert the content of the current class instance into IOData format.""" | ||
| import scqubits.io_utils.fileio as io | ||
@@ -65,6 +61,9 @@ | ||
| def filewrite(self, filename: str): | ||
| """Convenience method bound to the class. Simply accesses the | ||
| `write` function.""" | ||
| """Convenience method bound to the class. | ||
| Simply accesses the | ||
| `write` function. | ||
| """ | ||
| import scqubits.io_utils.fileio as io | ||
| io.write(self, filename) |
@@ -12,5 +12,3 @@ # fileio_serializers.py | ||
| ############################################################################ | ||
| """ | ||
| Helper classes for writing data to files. | ||
| """ | ||
| """Helper classes for writing data to files.""" | ||
@@ -50,4 +48,7 @@ import inspect | ||
| def __new__(cls: Type[SerializableType], *args, **kwargs) -> SerializableType: | ||
| """Modified `__new__` to set up `cls._init_params`. The latter is used to | ||
| record which of the `__init__` parameters are to be stored/read in file IO.""" | ||
| """Modified `__new__` to set up `cls._init_params`. | ||
| The latter is used to | ||
| record which of the `__init__` parameters are to be stored/read in file IO. | ||
| """ | ||
| cls._init_params = get_init_params(cls) | ||
@@ -66,12 +67,8 @@ return super().__new__(cls) | ||
| def deserialize(cls: Type[SerializableType], io_data: "IOData") -> SerializableType: | ||
| """ | ||
| Take the given IOData and return an instance of the described class, | ||
| initialized with the data stored in io_data. | ||
| """ | ||
| """Take the given IOData and return an instance of the described class, | ||
| initialized with the data stored in io_data.""" | ||
| return cls(**io_data.as_kwargs()) | ||
| def serialize(self) -> "IOData": | ||
| """ | ||
| Convert the content of the current class instance into IOData format. | ||
| """ | ||
| """Convert the content of the current class instance into IOData format.""" | ||
| initdata = {name: getattr(self, name) for name in self._init_params} | ||
@@ -85,4 +82,7 @@ if hasattr(self, "_id_str"): | ||
| def filewrite(self, filename: str) -> None: | ||
| """Convenience method bound to the class. Simply accesses the `write` | ||
| function.""" | ||
| """Convenience method bound to the class. | ||
| Simply accesses the `write` | ||
| function. | ||
| """ | ||
| import scqubits.io_utils.fileio as io | ||
@@ -157,6 +157,4 @@ | ||
| def type_dispatch(entity: Serializable) -> Callable: | ||
| """ | ||
| Based on the type of the object ``entity``, return the appropriate function that | ||
| converts the entity into the appropriate category of IOData | ||
| """ | ||
| """Based on the type of the object ``entity``, return the appropriate function that | ||
| converts the entity into the appropriate category of IOData.""" | ||
| if isinstance(entity, TO_ATTRIBUTE): | ||
@@ -177,5 +175,3 @@ return _add_attribute | ||
| def Expr_serialize(expr_instance: Expr) -> "IOData": | ||
| """ | ||
| Create an IODate instance for a sympy expression via string conversion | ||
| """ | ||
| """Create an IODate instance for a sympy expression via string conversion.""" | ||
| import scqubits.io_utils.fileio as io | ||
@@ -196,5 +192,3 @@ | ||
| def dict_serialize(dict_instance: Dict[str, Any]) -> "IOData": | ||
| """ | ||
| Create an IOData instance from dictionary data. | ||
| """ | ||
| """Create an IOData instance from dictionary data.""" | ||
| import scqubits.io_utils.fileio as io | ||
@@ -217,5 +211,3 @@ | ||
| def OrderedDict_serialize(dict_instance: Dict[str, Any]) -> "IOData": | ||
| """ | ||
| Create an IOData instance from dictionary data. | ||
| """ | ||
| """Create an IOData instance from dictionary data.""" | ||
| import scqubits.io_utils.fileio as io | ||
@@ -240,5 +232,3 @@ | ||
| def csc_matrix_serialize(csc_matrix_instance: csc_matrix) -> "IOData": | ||
| """ | ||
| Create an IOData instance from dictionary data. | ||
| """ | ||
| """Create an IOData instance from dictionary data.""" | ||
| import scqubits.io_utils.fileio as io | ||
@@ -267,5 +257,3 @@ | ||
| def NoneType_serialize(none_instance: None) -> "IOData": | ||
| """ | ||
| Create an IOData instance to write `None` to file. | ||
| """ | ||
| """Create an IOData instance to write `None` to file.""" | ||
| import scqubits.io_utils.fileio as io | ||
@@ -282,5 +270,3 @@ | ||
| def listlike_serialize(listlike_instance: Union[List, Tuple]) -> "IOData": | ||
| """ | ||
| Create an IOData instance from list data. | ||
| """ | ||
| """Create an IOData instance from list data.""" | ||
| import scqubits.io_utils.fileio as io | ||
@@ -310,5 +296,3 @@ | ||
| def range_serialize(range_instance: range) -> "IOData": | ||
| """ | ||
| Create an IOData instance from range data. | ||
| """ | ||
| """Create an IOData instance from range data.""" | ||
| import scqubits.io_utils.fileio as io | ||
@@ -328,3 +312,3 @@ | ||
| def Expr_deserialize(iodata: "IOData") -> Expr: | ||
| """Turn IOData instance back into a dict""" | ||
| """Turn IOData instance back into a dict.""" | ||
| from sympy import sympify | ||
@@ -336,3 +320,3 @@ | ||
| def dict_deserialize(iodata: "IOData") -> Dict[str, Any]: | ||
| """Turn IOData instance back into a dict""" | ||
| """Turn IOData instance back into a dict.""" | ||
| return dict(**iodata.as_kwargs()) | ||
@@ -342,3 +326,3 @@ | ||
| def OrderedDict_deserialize(iodata: "IOData") -> Dict[str, Any]: | ||
| """Turn IOData instance back into a dict""" | ||
| """Turn IOData instance back into a dict.""" | ||
| dict_data = iodata.as_kwargs() | ||
@@ -349,3 +333,3 @@ return OrderedDict([dict_data[key] for key in sorted(dict_data, key=int)]) | ||
| def csc_matrix_deserialize(iodata: "IOData") -> csc_matrix: | ||
| """Turn IOData instance back into a csc_matrix""" | ||
| """Turn IOData instance back into a csc_matrix.""" | ||
| csc_dict = dict(**iodata.as_kwargs()) | ||
@@ -359,3 +343,3 @@ return csc_matrix( | ||
| def NoneType_deserialize(iodata: "IOData") -> None: | ||
| """Turn IOData instance back into a csc_matrix""" | ||
| """Turn IOData instance back into a csc_matrix.""" | ||
| return None | ||
@@ -365,3 +349,3 @@ | ||
| def list_deserialize(iodata: "IOData") -> List[Any]: | ||
| """Turn IOData instance back into a list""" | ||
| """Turn IOData instance back into a list.""" | ||
| dict_data = iodata.as_kwargs() | ||
@@ -372,3 +356,3 @@ return [dict_data[key] for key in sorted(dict_data, key=int)] | ||
| def tuple_deserialize(iodata: "IOData") -> Tuple: | ||
| """Turn IOData instance back into a tuple""" | ||
| """Turn IOData instance back into a tuple.""" | ||
| return tuple(list_deserialize(iodata)) | ||
@@ -393,6 +377,4 @@ | ||
| def get_init_params(obj: Serializable) -> List[str]: | ||
| """ | ||
| Returns a list of the parameters entering the `__init__` method of the given | ||
| object `obj`. | ||
| """ | ||
| """Returns a list of the parameters entering the `__init__` method of the given | ||
| object `obj`.""" | ||
| init_params = list(inspect.signature(obj.__init__).parameters.keys()) # type: ignore | ||
@@ -399,0 +381,0 @@ if "self" in init_params: |
@@ -12,5 +12,3 @@ # fileio.py | ||
| ############################################################################ | ||
| """ | ||
| Helper routines for writing data to files. | ||
| """ | ||
| """Helper routines for writing data to files.""" | ||
@@ -34,5 +32,3 @@ import os | ||
| class IOData: | ||
| """ | ||
| Class for processing input/output data | ||
| """ | ||
| """Class for processing input/output data.""" | ||
@@ -53,3 +49,3 @@ def __init__( | ||
| """Return a joint dictionary of attributes, ndarrays, and objects, as used in | ||
| __init__ calls""" | ||
| __init__ calls.""" | ||
| return {**self.attributes, **self.ndarrays, **self.objects} | ||
@@ -59,5 +55,4 @@ | ||
| def serialize(the_object: "Serializable") -> IOData: | ||
| """ | ||
| Turn the given Python object into an IOData object, needed for writing data to file. | ||
| """ | ||
| """Turn the given Python object into an IOData object, needed for writing data to | ||
| file.""" | ||
| if hasattr(the_object, "serialize"): | ||
@@ -77,4 +72,4 @@ return the_object.serialize() | ||
| def deserialize(iodata: IOData) -> Any: | ||
| """ | ||
| Turn IOData back into a Python object of the appropriate kind. | ||
| """Turn IOData back into a Python object of the appropriate kind. | ||
| An object is deemed deserializable if | ||
@@ -99,4 +94,3 @@ 1) it is recorded in SERIALIZABLE_REGISTRY and has a `.deserialize` method | ||
| def write(the_object: Any, filename: str, file_handle: "h5py.Group" = None) -> None: | ||
| """ | ||
| Write `the_object` to a file with name `filename`. The optional `file_handle` | ||
| """Write `the_object` to a file with name `filename`. The optional `file_handle` | ||
| parameter is used as a group name in case of h5 files. | ||
@@ -119,4 +113,3 @@ | ||
| def read(filename: str, file_handle: "h5py.Group" = None) -> Any: | ||
| """ | ||
| Read a Serializable object from file. | ||
| """Read a Serializable object from file. | ||
@@ -140,3 +133,3 @@ Parameters | ||
| class FileIOFactory: | ||
| """Factory method for choosing reader/writer according to given format""" | ||
| """Factory method for choosing reader/writer according to given format.""" | ||
@@ -146,6 +139,4 @@ def get_writer( | ||
| ) -> "IOWriter": | ||
| """ | ||
| Based on the extension of the provided file name, return the appropriate | ||
| writer engine. | ||
| """ | ||
| """Based on the extension of the provided file name, return the appropriate | ||
| writer engine.""" | ||
| import scqubits.io_utils.fileio_backends as io_backends | ||
@@ -169,6 +160,4 @@ | ||
| ) -> Union["CSVReader", "H5Reader"]: | ||
| """ | ||
| Based on the extension of the provided file name, return the appropriate | ||
| reader engine. | ||
| """ | ||
| """Based on the extension of the provided file name, return the appropriate | ||
| reader engine.""" | ||
| if get_external_reader: | ||
@@ -175,0 +164,0 @@ return get_external_reader(file_name, file_handle=file_handle) |
| # Marker file for PEP 561. |
@@ -0,0 +0,0 @@ """ |
@@ -19,6 +19,4 @@ # testing.py | ||
| def run(): | ||
| """ | ||
| Run the pytest scripts for scqubits. | ||
| """ | ||
| """Run the pytest scripts for scqubits.""" | ||
| # runs tests in scqubits.tests directory | ||
| pytest.main(["-v", TESTDIR]) |
@@ -72,3 +72,3 @@ # conftest.py --- for use with pytest | ||
| class BaseTest: | ||
| """Used as base class for the pytests of qubit classes""" | ||
| """Used as base class for the pytests of qubit classes.""" | ||
@@ -79,3 +79,3 @@ qbt = None # class instance of qubit to be tested | ||
| def set_tmpdir(self, request): | ||
| """Pytest fixture that provides a temporary directory for writing test files""" | ||
| """Pytest fixture that provides a temporary directory for writing test files.""" | ||
| setattr(self, "tmpdir", request.getfixturevalue("tmpdir")) | ||
@@ -82,0 +82,0 @@ |
@@ -0,0 +0,0 @@ branches: |
@@ -0,0 +0,0 @@ branches: |
@@ -0,0 +0,0 @@ # zero-pi |
@@ -0,0 +0,0 @@ # test_centraldispatch.py |
@@ -24,5 +24,5 @@ import os | ||
| system_hierarchy=system_hierarchy, | ||
| subsystem_trunc_dims=[100, 30], | ||
| subsystem_trunc_dims=[10, 10], | ||
| ) | ||
| circ.update() | ||
| esys = circ.eigensys() | ||
@@ -33,3 +33,6 @@ | ||
| def test_plot_wf(): | ||
| circ.plot_wavefunction(which=0, var_indices=(2, 3)) | ||
| circ.plot_wavefunction(which=0, var_indices=(2, 3), esys=esys) | ||
| circ.subsystems[0].plot_wavefunction(which=0, var_indices=(1, 3), mode="abs") | ||
| circ.subsystems[0].plot_wavefunction(which=0, var_indices=(1, 3), mode="real") | ||
| circ.subsystems[0].plot_wavefunction(which=0, var_indices=(1, 3), mode="imag") | ||
@@ -36,0 +39,0 @@ @staticmethod |
@@ -16,3 +16,5 @@ # test_circuit.py | ||
| import numpy as np | ||
| import qutip as qt | ||
| import pytest | ||
| from scqubits.io_utils.fileio import read | ||
@@ -50,5 +52,3 @@ import scqubits as scq | ||
| def test_zero_pi_discretized(): | ||
| """ | ||
| Test for symmetric zero-pi in discretized phi basis. | ||
| """ | ||
| """Test for symmetric zero-pi in discretized phi basis.""" | ||
| zp_yaml = """# zero-pi circuit | ||
@@ -67,6 +67,5 @@ branches: | ||
| circ_d.cutoff_ext_2 = 30 | ||
| circ_d.cutoff_ext_3 = 80 | ||
| circ_d.cutoff_ext_3 = 200 | ||
| circ_d.configure(system_hierarchy=[[1, 3], [2]], subsystem_trunc_dims=[30, 20]) | ||
| circ_d.cutoff_ext_3 = 200 | ||
| sym_zp = circ_d.subsystems[0] | ||
@@ -90,5 +89,3 @@ eigensys = sym_zp.eigensys() | ||
| def test_circuit_with_symbolic_hamiltonian(): | ||
| """ | ||
| Test for initiating Circuit module with symbolic Hamiltonian. | ||
| """ | ||
| """Test for initiating Circuit module with symbolic Hamiltonian.""" | ||
| import sympy as sm | ||
@@ -184,3 +181,2 @@ | ||
| DFC.cutoff_ext_4 = 110 | ||
| DFC.update() | ||
@@ -207,3 +203,2 @@ eigs = DFC.eigenvals() | ||
| circ.EJ = 0.01 | ||
| circ.update() | ||
| eigs_ref = np.array( | ||
@@ -252,3 +247,3 @@ [ | ||
| system_hierarchy = [[[1], [3]], [2], [4]] | ||
| subsystem_trunc_dims = [[34, [6, 6]], 6, 6] | ||
| subsystem_trunc_dims = [[6, [6, 6]], 6, 6] | ||
@@ -265,8 +260,7 @@ DFC.configure( | ||
| DFC.cutoff_ext_1 = 110 | ||
| DFC.cutoff_ext_2 = 110 | ||
| DFC.cutoff_ext_3 = 110 | ||
| DFC.cutoff_ext_4 = 110 | ||
| DFC.update() | ||
| DFC.get_spectrum_vs_paramvals("Φ1", np.linspace(0, 1, 11), num_cpus=num_cpus) | ||
| DFC.cutoff_ext_1 = 40 | ||
| DFC.cutoff_ext_2 = 40 | ||
| DFC.cutoff_ext_3 = 40 | ||
| DFC.cutoff_ext_4 = 40 | ||
| DFC.get_spectrum_vs_paramvals("Φ1", np.linspace(0, 1, 3), num_cpus=num_cpus) | ||
@@ -282,3 +276,2 @@ paramvals_by_name = { | ||
| DFC.Φ2 = Φ2 | ||
| DFC.update() | ||
@@ -292,1 +285,67 @@ ps = scq.ParameterSweep( | ||
| ) | ||
| @staticmethod | ||
| def test_qutip_dynamics(num_cpus): | ||
| # Let's start by defining a fluxonium | ||
| inp_yaml = """ | ||
| branches: | ||
| - [JJ, 1, 2, 4, 0.5] | ||
| - [L, 1, 2, 1.3] | ||
| - [C, 1, 2, 2] | ||
| """ | ||
| circ = scq.Circuit( | ||
| inp_yaml, | ||
| from_file=False, | ||
| use_dynamic_flux_grouping=True, | ||
| ext_basis="discretized", | ||
| ) | ||
| circ.cutoff_ext_1 = 100 | ||
| circ.Φ1 = 0.5 | ||
| # defining Hierarchical diagonalization to limit to the lowest two states | ||
| circ.configure(system_hierarchy=[[1]], subsystem_trunc_dims=[10]) | ||
| # Define time dependent functions for the parameters | ||
| def flux(t, args): | ||
| freq = args["freq"] | ||
| return 0.001 * np.sin(2 * np.pi * freq * t) + 0.5 | ||
| # to charge drive the fluxonium, we need an extra parameter ng1. This can be added using extra_terms | ||
| def charge(t, args): | ||
| freq = args["freq"] | ||
| return 0.02 * np.sin(2 * np.pi * freq * t + np.pi / 2) | ||
| # Generating necessary operators and time dependent coefficients | ||
| H_mesolve, *H_sym_ref = circ.hamiltonian_for_qutip_dynamics( | ||
| free_var_func_dict={"Φ1": flux, "ng1": charge}, | ||
| extra_terms="Q1*ng1", | ||
| prefactor=np.pi * 2, | ||
| ) | ||
| # H_mesolve can be used to evolve the system using qutip functions like mesolve | ||
| # ground state as initial state | ||
| eigs, evecs = circ.eigensys(evals_count=5) | ||
| wf0 = qt.Qobj(evecs[:, 0]) | ||
| initial_state_proj = wf0 * wf0.dag() # to see the overlap | ||
| tf = 100 # final time in nanoseconds | ||
| freq = eigs[1] - eigs[0] # transition frequency between the first two states | ||
| # time evolve the system | ||
| result = qt.mesolve( | ||
| H_mesolve, | ||
| wf0, | ||
| np.linspace(0, tf, 500), | ||
| args={"freq": freq}, | ||
| e_ops=[initial_state_proj], | ||
| options=dict(atol=1e-12), | ||
| ) | ||
| expectation_vals = result.expect[0] | ||
| ref_expectation_vals = np.empty_like(expectation_vals) | ||
| ref_expectation_vals[:] = read(DATADIR + "/circuit_qutip_evolution_data.hdf5")[ | ||
| : | ||
| ] | ||
| assert np.allclose( | ||
| expectation_vals, | ||
| ref_expectation_vals, | ||
| ) |
@@ -0,0 +0,0 @@ # test_cos2phi_qubit.py |
@@ -137,3 +137,4 @@ # test_diag.py | ||
| def test_custom_diagonalization_evals_method_matches_default(library): | ||
| """Test custom diagonalization gives the same eigenvalues as using the default method.""" | ||
| """Test custom diagonalization gives the same eigenvalues as using the default | ||
| method.""" | ||
@@ -180,3 +181,4 @@ if library == "jax": | ||
| def test_custom_diagonalization_matches_default_with_composite_systems(library): | ||
| """Test custom diagonalization gives same eigenvalues as using the default method for composite systems.""" | ||
| """Test custom diagonalization gives same eigenvalues as using the default method | ||
| for composite systems.""" | ||
@@ -223,3 +225,4 @@ if library == "jax": | ||
| 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.""" | ||
| """Test custom diagonalization gives same result as using the default method when | ||
| using a custom procedure.""" | ||
@@ -226,0 +229,0 @@ def custom_esys(matrix, evals_count, **kwargs): |
@@ -0,0 +0,0 @@ # test_explorer.py |
@@ -0,0 +0,0 @@ # test_fluxonium.py |
@@ -0,0 +0,0 @@ # test_fluxqubit.py |
@@ -0,0 +0,0 @@ # test_fullzeropi.py |
@@ -0,0 +0,0 @@ # test_gui.py |
@@ -479,1 +479,119 @@ # test_hilbertspace.py | ||
| assert op1 == op2 | ||
| def test_HilbertSpace_labeling(self): | ||
| hspace = self.hilbertspace_initialize2() | ||
| reference_indices = np.array( | ||
| [ | ||
| 0, | ||
| 3, | ||
| 7, | ||
| 15, | ||
| 1, | ||
| 5, | ||
| 12, | ||
| 22, | ||
| 2, | ||
| 6, | ||
| 14, | ||
| 24, | ||
| 13, | ||
| 23, | ||
| 32, | ||
| 40, | ||
| 4, | ||
| 10, | ||
| 18, | ||
| 28, | ||
| 8, | ||
| 16, | ||
| 25, | ||
| 33, | ||
| 9, | ||
| 17, | ||
| 27, | ||
| 35, | ||
| 26, | ||
| 34, | ||
| 41, | ||
| 45, | ||
| 11, | ||
| 21, | ||
| 31, | ||
| 39, | ||
| 19, | ||
| 29, | ||
| 36, | ||
| 42, | ||
| 20, | ||
| 30, | ||
| 38, | ||
| 44, | ||
| 37, | ||
| 43, | ||
| 46, | ||
| 47, | ||
| ], | ||
| dtype=int, | ||
| ) | ||
| reference_indices_BE10 = np.array( | ||
| [ | ||
| 0, | ||
| 3, | ||
| 7, | ||
| None, | ||
| 1, | ||
| 5, | ||
| None, | ||
| None, | ||
| 2, | ||
| 6, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| 4, | ||
| None, | ||
| None, | ||
| None, | ||
| 8, | ||
| None, | ||
| None, | ||
| None, | ||
| 9, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| None, | ||
| ], | ||
| dtype=object, | ||
| ) | ||
| hspace.generate_lookup(ordering="LX", subsys_priority=[2, 1, 0]) | ||
| assert np.all(hspace["dressed_indices"][0].astype(int) == reference_indices) | ||
| hspace.generate_lookup(ordering="BE", subsys_priority=[2, 1, 0], BEs_count=10) | ||
| assert np.all(hspace["dressed_indices"][0] == reference_indices_BE10) | ||
| hspace.generate_lookup(ordering="DE") | ||
| assert np.all(hspace["dressed_indices"][0].astype(int) == reference_indices) |
@@ -0,0 +0,0 @@ # test_namedslotsndarray.py |
@@ -0,0 +0,0 @@ # test_noise.py |
@@ -54,4 +54,4 @@ # test_parameters.py | ||
| def test_paravals_list(): | ||
| def test_paramvals_list(): | ||
| tst = Parameters(paramvals_by_name) | ||
| assert tst.paramvals_list == [paramvals1, paramvals2] |
@@ -29,3 +29,3 @@ # test_parametersweep.py | ||
| def initialize(self, num_cpus): | ||
| def initialize(self, num_cpus, evals_count=20): | ||
| # Set up the components / subspaces of our Hilbert space | ||
@@ -97,3 +97,3 @@ scq.settings.MULTIPROC = "pathos" | ||
| update_hilbertspace=update_hilbertspace, | ||
| evals_count=20, | ||
| evals_count=evals_count, | ||
| subsys_update_info=subsys_update_info, | ||
@@ -139,1 +139,66 @@ num_cpus=num_cpus, | ||
| sweep_copy = scq.read(self.tmpdir + "test.h5") | ||
| def test_ParameterSweep_labeling(self, num_cpus): | ||
| sweep = self.initialize(num_cpus, evals_count=36) | ||
| # DE ordering is already generated by default | ||
| # we compare 9th state only | ||
| ref_indices_DE = np.array( | ||
| [ | ||
| [8, 8, 8], | ||
| [8, 8, 8], | ||
| [8, 8, 8], | ||
| [11, 11, 11], | ||
| [9, 9, 9], | ||
| [8, 8, 8], | ||
| [6, 6, 6], | ||
| [11, 11, 11], | ||
| [None, None, None], | ||
| [9, 9, 9], | ||
| [7, 7, 7], | ||
| ], | ||
| dtype=object, | ||
| ) | ||
| assert np.all(sweep["dressed_indices"][..., 9] == ref_indices_DE) | ||
| # BE ordering | ||
| indices_BE10 = sweep.generate_lookup( | ||
| ordering="BE", subsys_priority=[2, 1, 0], BEs_count=10 | ||
| ) | ||
| ref_indices_BE10 = np.array( | ||
| [ | ||
| [8, 8, 8], | ||
| [8, 8, 8], | ||
| [8, 8, 8], | ||
| [None, None, None], | ||
| [9, 9, 9], | ||
| [8, 8, 8], | ||
| [6, 6, 6], | ||
| [None, None, None], | ||
| [None, None, None], | ||
| [9, 9, 9], | ||
| [7, 7, 7], | ||
| ], | ||
| dtype=object, | ||
| ) | ||
| assert np.all(indices_BE10[..., 9] == ref_indices_BE10) | ||
| # LX ordering | ||
| indices_LX = sweep.generate_lookup(ordering="LX", subsys_priority=[2, 1, 0]) | ||
| ref_indices_LX = np.array( | ||
| [ | ||
| [8, 8, 8], | ||
| [8, 8, 8], | ||
| [8, 8, 8], | ||
| [11, 11, 11], | ||
| [9, 9, 9], | ||
| [8, 8, 8], | ||
| [6, 6, 6], | ||
| [11, 11, 11], | ||
| [11, 11, 11], | ||
| [9, 9, 9], | ||
| [7, 7, 7], | ||
| ], | ||
| dtype=object, | ||
| ) | ||
| assert np.all(indices_LX[..., 9] == ref_indices_LX) |
@@ -0,0 +0,0 @@ # test_spectrumlookup.py |
@@ -0,0 +0,0 @@ # test_transmon.py |
@@ -0,0 +0,0 @@ # test_units.py |
@@ -0,0 +0,0 @@ # test_zeropi.py |
@@ -0,0 +0,0 @@ # This file is part of scqubits: a Python package for superconducting qubits, |
@@ -120,4 +120,6 @@ # gui_custom_widgets.py | ||
| 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. | ||
| """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. | ||
| """ | ||
@@ -414,3 +416,4 @@ | ||
| def setup_card(self, new_panel: ClosablePanel): | ||
| """Sets up a new card, connecting its close button to the card collection's `close_card` method.""" | ||
| """Sets up a new card, connecting its close button to the card collection's | ||
| `close_card` method.""" | ||
| card = new_panel.card | ||
@@ -469,4 +472,4 @@ card.id = new_panel.panel_id | ||
| 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. | ||
| """Changes the number of columns in the grid by setting `explorer.cols` and | ||
| resizing all widgets in the grid. | ||
@@ -477,3 +480,2 @@ Parameters | ||
| number of columns in the grid | ||
| """ | ||
@@ -484,7 +486,5 @@ self.ncols = ncols | ||
| 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. | ||
| """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. | ||
@@ -491,0 +491,0 @@ Returns |
@@ -0,0 +0,0 @@ # gui_defaults.py |
@@ -0,0 +0,0 @@ # gui_navbar.py |
@@ -65,6 +65,6 @@ # gui_setup.py | ||
| 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. | ||
| """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. | ||
@@ -207,3 +207,3 @@ Parameters | ||
| def init_dict_v_noise_params(active_qubit) -> Dict[str, v.VuetifyWidget]: | ||
| """Creates all the widgets associated with coherence times plots""" | ||
| """Creates all the widgets associated with coherence times plots.""" | ||
| dict_v_noise_params = {} | ||
@@ -239,5 +239,3 @@ noise_params = ["T", "omega_low", "omega_high", "t_exp"] | ||
| ) -> Dict[str, v.VuetifyWidget]: | ||
| """Creates all the widgets associated with the parameters of the | ||
| chosen qubit. | ||
| """ | ||
| """Creates all the widgets associated with the parameters of the chosen qubit.""" | ||
| dict_v_qubit_params = {} | ||
@@ -301,5 +299,4 @@ | ||
| ) -> 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. | ||
| """ | ||
| """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 = {} | ||
@@ -306,0 +303,0 @@ total_dict = { |
@@ -53,4 +53,4 @@ # hspace_widget.py | ||
| class HilbertSpaceUi: | ||
| """Class for setup and display of the widget used for creation of a | ||
| HilbertSpace object.""" | ||
| """Class for setup and display of the widget used for creation of a HilbertSpace | ||
| object.""" | ||
@@ -585,5 +585,4 @@ @utils.Required(ipyvuetify=_HAS_IPYVUETIFY) | ||
| def create_hilbertspace_widget(callback_func): | ||
| """ | ||
| Display ipywidgets interface for creating a HilbertSpace object. Typically, | ||
| this function will be called by `HilbertSpace.create()``. | ||
| """Display ipywidgets interface for creating a HilbertSpace object. Typically, this | ||
| function will be called by `HilbertSpace.create()``. | ||
@@ -590,0 +589,0 @@ Parameters |
@@ -42,4 +42,3 @@ # qubit_widget.py | ||
| ) -> None: | ||
| """ | ||
| Displays ipyvuetify for initialization of a QuantumSystem object. | ||
| """Displays ipyvuetify for initialization of a QuantumSystem object. | ||
@@ -46,0 +45,0 @@ Parameters |
@@ -0,0 +0,0 @@ # This file is part of scqubits: a Python package for superconducting qubits, |
@@ -19,4 +19,3 @@ # cpu_switch.py | ||
| def get_map_method(num_cpus: int) -> Callable: | ||
| """ | ||
| Selects the correct `.map` method depending on the specified number of desired | ||
| """Selects the correct `.map` method depending on the specified number of desired | ||
| cores. If num_cpus>1, the multiprocessing/pathos pool is started here. | ||
@@ -23,0 +22,0 @@ |
+24
-28
@@ -62,4 +62,3 @@ # misc.py | ||
| def make_bare_labels(subsystem_count: int, *args) -> Tuple[int, ...]: | ||
| """ | ||
| For two given subsystem states, return the full-system bare state label obtained | ||
| """For two given subsystem states, return the full-system bare state label obtained | ||
| by placing all remaining subsys_list in their ground states. | ||
@@ -87,3 +86,3 @@ | ||
| def drop_private_keys(full_dict: Dict[str, Any]) -> Dict[str, Any]: | ||
| """Filter for entries in the full dictionary that have numerical values""" | ||
| """Filter for entries in the full dictionary that have numerical values.""" | ||
| return {key: value for key, value in full_dict.items() if key[0] != "_"} | ||
@@ -191,7 +190,14 @@ | ||
| def wrapper(self, *args, **kwargs): | ||
| if self.is_child and self._out_of_sync_with_parent: | ||
| with warnings.catch_warnings(): | ||
| warnings.simplefilter("ignore") | ||
| warnings.warn( | ||
| "[scqubits] Some quantum system parameters have been changed and" | ||
| " generated circuit data could be outdated, potentially leading to" | ||
| " incorrect results. Circuit data can be refreshed via" | ||
| " <Circuit>.generate_circuit()", | ||
| Warning, | ||
| ) | ||
| # update the circuit if necessary | ||
| if (self._user_changed_parameter) or ( | ||
| self.hierarchical_diagonalization | ||
| and (self._out_of_sync or len(self.affected_subsystem_indices) > 0) | ||
| ): | ||
| if self.hierarchical_diagonalization: | ||
| self.update() | ||
@@ -269,9 +275,7 @@ return func(self, *args, **kwargs) | ||
| def get_shape(lst, shape=()): | ||
| """ | ||
| returns the shape of nested lists similarly to numpy's shape. | ||
| """Returns the shape of nested lists similarly to numpy's shape. | ||
| :param lst: the nested list | ||
| :param shape: the shape up to the current recursion depth | ||
| :return: the shape including the current depth | ||
| (finally this will be the full depth) | ||
| :return: the shape including the current depth (finally this will be the full depth) | ||
| """ | ||
@@ -328,5 +332,4 @@ if not isinstance(lst, Sequence): | ||
| def about(print_info=True) -> Optional[str]: | ||
| """Prints or returns a string with basic information about | ||
| scqubits as well as installed version of various packages | ||
| that scqubits depends on. | ||
| """Prints or returns a string with basic information about scqubits as well as | ||
| installed version of various packages that scqubits depends on. | ||
@@ -370,4 +373,3 @@ Parameters | ||
| def cite(print_info=True): | ||
| """Prints or returns a string with scqubits citation | ||
| information. | ||
| """Prints or returns a string with scqubits citation information. | ||
@@ -383,3 +385,2 @@ Parameters | ||
| None or str | ||
| """ | ||
@@ -420,4 +421,3 @@ fs = StringIO() | ||
| def flatten_list(nested_list): | ||
| """ | ||
| Flattens a list of lists once, not recursive. | ||
| """Flattens a list of lists once, not recursive. | ||
@@ -438,4 +438,3 @@ Parameters | ||
| def flatten_list_recursive(some_list: list) -> list: | ||
| """ | ||
| Flattens a list of lists recursively. | ||
| """Flattens a list of lists recursively. | ||
@@ -461,4 +460,3 @@ Parameters | ||
| def unique_elements_in_list(list_object: list) -> list: | ||
| """ | ||
| Returns a list of all the unique elements in the list | ||
| """Returns a list of all the unique elements in the list. | ||
@@ -480,5 +478,4 @@ Parameters | ||
| 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.) | ||
| """Takes a list as an argument and returns the number of lists in that list. (Counts | ||
| lists at root level only, no recursion.) | ||
@@ -502,4 +499,3 @@ Parameters | ||
| ) -> List[str]: | ||
| """ | ||
| Find all public names in a module. | ||
| """Find all public names in a module. | ||
@@ -506,0 +502,0 @@ Parameters |
@@ -49,4 +49,3 @@ # plot_defaults.py | ||
| ) -> float: | ||
| """ | ||
| Sets the scaling parameter for 1d wavefunctions | ||
| """Sets the scaling parameter for 1d wavefunctions. | ||
@@ -132,3 +131,4 @@ Returns | ||
| mode: | ||
| amplitude modifier, needed to give the correct default y label""" | ||
| amplitude modifier, needed to give the correct default y label | ||
| """ | ||
| ylabel = r"$\psi_j(n)$" | ||
@@ -141,3 +141,3 @@ if mode: | ||
| def wavefunction2d() -> Dict[str, Any]: | ||
| """Plot defaults for plotting.wavefunction2d""" | ||
| """Plot defaults for plotting.wavefunction2d.""" | ||
| return {"figsize": (8, 3)} | ||
@@ -149,3 +149,3 @@ | ||
| ) -> Dict[str, Any]: | ||
| """Plot defaults for plotting.contours""" | ||
| """Plot defaults for plotting.contours.""" | ||
| aspect_ratio = (y_vals[-1] - y_vals[0]) / (x_vals[-1] - x_vals[0]) | ||
@@ -157,3 +157,3 @@ figsize = (8, 8 * aspect_ratio) | ||
| def matrix() -> Dict[str, Any]: | ||
| """Plot defaults for plotting.matrix""" | ||
| """Plot defaults for plotting.matrix.""" | ||
| return {"figsize": (10, 5)} | ||
@@ -163,3 +163,3 @@ | ||
| def evals_vs_paramvals(specdata: "SpectrumData", **kwargs) -> Dict[str, Any]: | ||
| """Plot defaults for plotting.evals_vs_paramvals""" | ||
| """Plot defaults for plotting.evals_vs_paramvals.""" | ||
| kwargs["xlabel"] = kwargs.get("xlabel") or recast_name(specdata.param_name) | ||
@@ -173,3 +173,3 @@ kwargs["ylabel"] = kwargs.get("ylabel") or "energy [{}]".format(units.get_units()) | ||
| ) -> Dict[str, Any]: | ||
| """Plot defaults for plotting.matelem_vs_paramvals""" | ||
| """Plot defaults for plotting.matelem_vs_paramvals.""" | ||
| return {"xlabel": recast_name(specdata.param_name), "ylabel": "matrix element"} | ||
@@ -179,3 +179,3 @@ | ||
| def chi(param_name: Union[str, None], **kwargs) -> Dict[str, Any]: | ||
| """Plot defaults for sweep_plotting.chi""" | ||
| """Plot defaults for sweep_plotting.chi.""" | ||
| kwargs["xlabel"] = kwargs.get("xlabel") or recast_name(param_name) | ||
@@ -189,3 +189,3 @@ kwargs["ylabel"] = kwargs.get("ylabel") or r"$\chi_j$ [{}]".format( | ||
| def chi01(param_name: Union[str, None], yval: float, **kwargs) -> Dict[str, Any]: | ||
| """Plot defaults for sweep_plotting.chi01""" | ||
| """Plot defaults for sweep_plotting.chi01.""" | ||
| kwargs["xlabel"] = kwargs.get("xlabel") or recast_name(param_name) | ||
@@ -202,3 +202,3 @@ kwargs["ylabel"] = kwargs.get("ylabel") or r"$\chi_{{01}}$ [{}]".format( | ||
| def charge_matrixelem(param_name: str, **kwargs) -> Dict[str, Any]: | ||
| """Plot defaults for sweep_plotting.charge_matrixelem""" | ||
| """Plot defaults for sweep_plotting.charge_matrixelem.""" | ||
| kwargs["xlabel"] = kwargs.get("xlabel") or recast_name(param_name) | ||
@@ -205,0 +205,0 @@ kwargs["ylabel"] = kwargs.get("ylabel") or r"$|\langle i |n| j \rangle|$" |
@@ -56,4 +56,3 @@ # plot_utils.py | ||
| ) -> Dict[str, Any]: | ||
| """ | ||
| Select options from kwargs for a given plot_type and return them in a dictionary. | ||
| """Select options from kwargs for a given plot_type and return them in a dictionary. | ||
@@ -72,3 +71,2 @@ Parameters | ||
| dictionary with key/value pairs corresponding to selected options from kwargs | ||
| """ | ||
@@ -91,4 +89,3 @@ direct_plot_options = direct_plot_options or _direct_plot_options | ||
| ) -> None: | ||
| """ | ||
| Processes plotting options. | ||
| """Processes plotting options. | ||
@@ -137,4 +134,3 @@ Parameters | ||
| """Processes a single 'special' option, i.e., one internal to scqubits and not to be | ||
| handed further down to matplotlib. | ||
| """ | ||
| handed further down to matplotlib.""" | ||
| if key == "ymax": | ||
@@ -141,0 +137,0 @@ ymax = value |
@@ -57,4 +57,3 @@ # plotting.py | ||
| ) -> Tuple[Figure, Axes]: | ||
| """ | ||
| Plots the amplitude of a single real-valued 1d wave function, along with the | ||
| """Plots the amplitude of a single real-valued 1d wave function, along with the | ||
| potential energy if provided. | ||
@@ -101,4 +100,3 @@ | ||
| ) -> Tuple[Figure, Axes]: | ||
| """ | ||
| Plots the amplitude of a single real-valued 1d wave function, along with the | ||
| """Plots the amplitude of a single real-valued 1d wave function, along with the | ||
| potential energy if provided. | ||
@@ -137,4 +135,3 @@ | ||
| def wavefunction1d_discrete(wavefunc: "WaveFunction", **kwargs) -> Tuple[Figure, Axes]: | ||
| """ | ||
| Plots the amplitude of a real-valued 1d wave function in a discrete basis. | ||
| """Plots the amplitude of a real-valued 1d wave function in a discrete basis. | ||
| (Example: transmon in the charge basis.) | ||
@@ -170,4 +167,3 @@ | ||
| ) -> Tuple[Figure, Axes]: | ||
| """ | ||
| Creates a density plot of the amplitude of a real-valued wave function in 2 | ||
| """Creates a density plot of the amplitude of a real-valued wave function in 2 | ||
| "spatial" dimensions. | ||
@@ -278,4 +274,3 @@ | ||
| ) -> Tuple[Figure, Tuple[Axes, Axes]]: | ||
| """ | ||
| Create a "skyscraper" plot and a 2d color-coded plot of a matrix. | ||
| """Create a "skyscraper" plot and a 2d color-coded plot of a matrix. | ||
@@ -318,3 +313,3 @@ Parameters | ||
| ) -> Tuple[Figure, Axes]: | ||
| """Display a 3d skyscraper plot of the matrix | ||
| """Display a 3d skyscraper plot of the matrix. | ||
@@ -465,4 +460,4 @@ Parameters | ||
| ) -> Tuple[Figure, Axes]: | ||
| """Plot of a set of ydata vs xdata. | ||
| The individual points correspond to the a provided array of parameter values. | ||
| """Plot of a set of ydata vs xdata. The individual points correspond to the a | ||
| provided array of parameter values. | ||
@@ -572,4 +567,4 @@ Parameters | ||
| ) -> Tuple[Figure, Axes]: | ||
| """Generates a simple plot of matrix elements as a function of one parameter. | ||
| The individual points correspond to the a provided array of parameter values. | ||
| """Generates a simple plot of matrix elements as a function of one parameter. The | ||
| individual points correspond to the a provided array of parameter values. | ||
@@ -576,0 +571,0 @@ Parameters |
@@ -42,3 +42,4 @@ # spectrum_utils.py | ||
| 2. Test for degenerate eigenvalues. If there are any, need to orthogonalize the | ||
| eigenvectors properly.""" | ||
| eigenvectors properly. | ||
| """ | ||
| mat_size = args[0].shape[0] | ||
@@ -92,4 +93,4 @@ kwargs["v0"] = settings.RANDOM_ARRAY[:mat_size] | ||
| not specified, the `position` is set as follows. Find the maximum between the | ||
| leftmost point and the halfway point of the wavefunction. The position of that | ||
| point is used to determine the phase factor to be eliminated. | ||
| leftmost point and the halfway point of the wavefunction. The position of that point | ||
| is used to determine the phase factor to be eliminated. | ||
@@ -113,4 +114,4 @@ Parameters | ||
| def standardize_phases(complex_array: np.ndarray) -> np.ndarray: | ||
| """Uses `extract_phase` to obtain global phase from `array` and returns | ||
| standardized array with global phase factor standardized. | ||
| """Uses `extract_phase` to obtain global phase from `array` and returns standardized | ||
| array with global phase factor standardized. | ||
@@ -132,5 +133,5 @@ Parameters | ||
| Summing up to the midpoint only is to address the danger that the sum is | ||
| actually zero, which may be the case for odd wavefunctions taken over an interval | ||
| centered at zero. | ||
| Summing up to the midpoint only is to address the danger that the sum is actually | ||
| zero, which may be the case for odd wavefunctions taken over an interval centered at | ||
| zero. | ||
| """ | ||
@@ -268,5 +269,7 @@ halfway_position = len(real_array) // 2 | ||
| """Takes spectral data of energy eigenvalues and returns the absorption spectrum | ||
| relative to a state of given index. Calculated by subtracting from eigenenergies | ||
| the energy of the select state. Resulting negative frequencies, if the reference | ||
| state is not the ground state, are omitted. | ||
| relative to a state of given index. | ||
| Calculated by subtracting from eigenenergies the energy of the select state. | ||
| Resulting negative frequencies, if the reference state is not the ground state, are | ||
| omitted. | ||
| """ | ||
@@ -280,6 +283,7 @@ assert isinstance(spectrum_data.energy_table, ndarray) | ||
| """Takes spectral data of energy eigenvalues and returns the emission spectrum | ||
| relative to a state of given index. The resulting "upwards" transition | ||
| frequencies are calculated by subtracting from eigenenergies the energy of the | ||
| select state, and multiplying the result by -1. Resulting negative frequencies, | ||
| corresponding to absorption instead, are omitted. | ||
| relative to a state of given index. | ||
| The resulting "upwards" transition frequencies are calculated by subtracting from | ||
| eigenenergies the energy of the select state, and multiplying the result by -1. | ||
| Resulting negative frequencies, corresponding to absorption instead, are omitted. | ||
| """ | ||
@@ -293,4 +297,4 @@ assert isinstance(spectrum_data.energy_table, ndarray) | ||
| def convert_evecs_to_ndarray(evecs_qutip: ndarray) -> np.ndarray: | ||
| """Takes a qutip eigenstates array, as obtained with .eigenstates(), and converts | ||
| it into a pure numpy array. | ||
| """Takes a qutip eigenstates array, as obtained with .eigenstates(), and converts it | ||
| into a pure numpy array. | ||
@@ -350,8 +354,9 @@ Parameters | ||
| if isinstance(operator, qt.Qobj): | ||
| return operator | ||
| if isinstance(operator, (np.ndarray, csc_matrix, csr_matrix, dia_matrix)): | ||
| return convert_matrix_to_qobj(operator, subsystem, op_in_eigenbasis, evecs) | ||
| operator = Qobj_to_scipy_csc_matrix(operator) | ||
| if isinstance(operator, str): | ||
| return convert_opstring_to_qobj(operator, subsystem, evecs) | ||
| raise TypeError("Unsupported operator type: ", type(operator)) | ||
| elif isinstance(operator, (np.ndarray, csc_matrix, csr_matrix, dia_matrix)): | ||
| return convert_matrix_to_qobj(operator, subsystem, op_in_eigenbasis, evecs) | ||
| else: | ||
| raise TypeError("Unsupported operator type: ", type(operator)) | ||
@@ -362,8 +367,8 @@ | ||
| ) -> List[Tuple[int, ...]]: | ||
| """Based on a bare state label (i1, i2, ...) with i1 being the excitation level | ||
| of subsystem 1, i2 the excitation level of subsystem 2 etc., generate a list of | ||
| new bare state labels. These bare state labels correspond to target states | ||
| reached from the given initial one by single-photon qubit transitions. These are | ||
| transitions where one of the qubit excitation levels increases at a time. There | ||
| are no changes in oscillator photon numbers. | ||
| """Based on a bare state label (i1, i2, ...) with i1 being the excitation level of | ||
| subsystem 1, i2 the excitation level of subsystem 2 etc., generate a list of new | ||
| bare state labels. These bare state labels correspond to target states reached from | ||
| the given initial one by single-photon qubit transitions. These are transitions | ||
| where one of the qubit excitation levels increases at a time. There are no changes | ||
| in oscillator photon numbers. | ||
@@ -394,5 +399,4 @@ Parameters | ||
| ) -> Tuple[np.ndarray, List[np.ndarray]]: | ||
| """ | ||
| Takes data generated by a map of eigensystem calls and returns the eigenvalue and | ||
| eigenstate tables | ||
| """Takes data generated by a map of eigensystem calls and returns the eigenvalue and | ||
| eigenstate tables. | ||
@@ -418,8 +422,7 @@ Returns | ||
| ) -> Qobj: | ||
| """Takes the `operator` belonging to `subsystem` and "wraps" it in identities. | ||
| The full Hilbert space is taken to consist of all subsystems given as | ||
| `subsys_list`. `subsystem` must be one element in that list. For each of the | ||
| other subsystems in the list, an identity operator of the correct dimension is | ||
| generated and inserted into the appropriate Kronecker product "sandwiching" the | ||
| operator. | ||
| """Takes the `operator` belonging to `subsystem` and "wraps" it in identities. The | ||
| full Hilbert space is taken to consist of all subsystems given as `subsys_list`. | ||
| `subsystem` must be one element in that list. For each of the other subsystems in | ||
| the list, an identity operator of the correct dimension is generated and inserted | ||
| into the appropriate Kronecker product "sandwiching" the operator. | ||
@@ -437,4 +440,3 @@ Parameters | ||
| whether `operator` is given in the `subsystem` eigenbasis; otherwise, | ||
| `operator` is assumed to be in the internal QuantumSystem basis. This | ||
| argument is ignored if `operator` is given as a Qobj. | ||
| `operator` is assumed to be in the internal QuantumSystem basis. | ||
| evecs: | ||
@@ -447,11 +449,7 @@ internal `QuantumSystem` eigenstates, used to convert `operator` into eigenbasis | ||
| operator is expressed in the bare product basis consisting of the energy | ||
| eigenstates of each subsystem (unless `operator` is provided as a `Qobj`, | ||
| in which case no conversion takes place). | ||
| eigenstates of each subsystem. | ||
| """ | ||
| # checking if operator is not qt.Qobj, as callable(qt.Qobj) returns True. | ||
| if not isinstance(operator, qt.Qobj) and callable(operator): | ||
| try: | ||
| operator = operator(energy_esys=(None, evecs)) | ||
| except TypeError: | ||
| operator = operator() | ||
| op_in_eigenbasis = True | ||
| operator = operator() | ||
@@ -458,0 +456,0 @@ subsys_operator = convert_operator_to_qobj( |
@@ -0,0 +0,0 @@ # typedefs.py |
+16
-4
@@ -1,4 +0,16 @@ | ||
| # THIS FILE IS GENERATED FROM scqubits SETUP.PY | ||
| short_version = '4.2.0' | ||
| version = '4.2.0' | ||
| release = True | ||
| # file generated by setuptools_scm | ||
| # don't change, don't track in version control | ||
| TYPE_CHECKING = False | ||
| if TYPE_CHECKING: | ||
| from typing import Tuple, Union | ||
| VERSION_TUPLE = Tuple[Union[int, str], ...] | ||
| else: | ||
| VERSION_TUPLE = object | ||
| version: str | ||
| __version__: str | ||
| __version_tuple__: VERSION_TUPLE | ||
| version_tuple: VERSION_TUPLE | ||
| __version__ = version = '4.3' | ||
| __version_tuple__ = version_tuple = (4, 3) |
+0
-0
@@ -0,0 +0,0 @@ [egg_info] |
| h5py>=2.10 | ||
| pytest | ||
| ipywidgets | ||
| ipyvuetify | ||
| traitlets | ||
| typing_extensions |
| 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 | ||
| qutip>=4.3.1 | ||
| sympy | ||
| tqdm | ||
| typing_extensions | ||
| pathos | ||
| dill | ||
Sorry, the diff of this file is not supported yet
-168
| """scqubits: superconducting qubits in Python | ||
| =========================================== | ||
| scqubits is an open-source Python library for simulating superconducting qubits. It is | ||
| meant to give the user a convenient way to obtain energy spectra of common | ||
| superconducting qubits, plot energy levels as a function of external parameters, | ||
| calculate matrix elements etc. The library further provides an interface to QuTiP, | ||
| making it easy to work with composite Hilbert spaces consisting of coupled | ||
| superconducting qubits and harmonic modes. Internally, numerics within scqubits is | ||
| carried out with the help of Numpy and Scipy; plotting capabilities rely on | ||
| Matplotlib. | ||
| If scqubits is helpful to you in your research, please support its continued | ||
| development and maintenance. Use of scqubits in research publications is | ||
| appropriately acknowledged by citing: | ||
| Peter Groszkowski and Jens Koch, 'scqubits: a Python package for superconducting qubits', | ||
| Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/ | ||
| """ | ||
| # | ||
| # 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. | ||
| ############################################################################ | ||
| import os | ||
| import sys | ||
| import setuptools | ||
| DOCLINES = __doc__.split("\n") | ||
| CLASSIFIERS = """\ | ||
| Development Status :: 5 - Production/Stable | ||
| Intended Audience :: Science/Research | ||
| License :: OSI Approved :: BSD License | ||
| Programming Language :: Python | ||
| Programming Language :: Python :: 3 | ||
| Topic :: Scientific/Engineering | ||
| Operating System :: MacOS | ||
| Operating System :: POSIX | ||
| Operating System :: Unix | ||
| Operating System :: Microsoft :: Windows | ||
| """ | ||
| EXTRA_KWARGS = {} | ||
| # version information about scqubits goes here | ||
| MAJOR = 4 | ||
| MINOR = 2 | ||
| MICRO = 0 | ||
| ISRELEASED = True | ||
| VERSION = "%d.%d.%d" % (MAJOR, MINOR, MICRO) | ||
| CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) | ||
| with open(os.path.join(CURRENT_DIR, "requirements.txt")) as requirements: | ||
| INSTALL_REQUIRES = requirements.read().splitlines() | ||
| EXTRAS_REQUIRE = { | ||
| "graphics": ["matplotlib-label-lines (>=0.3.6)"], | ||
| "explorer": ["ipywidgets (>=7.5)"], | ||
| "h5-support": ["h5py (>=2.10)"], | ||
| "pathos": ["pathos", "dill"], | ||
| } | ||
| TESTS_REQUIRE = ["h5py (>=2.7.1)", "pathos", "dill", "ipywidgets", "pytest"] | ||
| PACKAGES = [ | ||
| "scqubits", | ||
| "scqubits/core", | ||
| "scqubits/tests", | ||
| "scqubits/utils", | ||
| "scqubits/ui", | ||
| "scqubits/io_utils", | ||
| "scqubits/explorer", | ||
| ] | ||
| PYTHON_VERSION = ">=3.9" | ||
| NAME = "scqubits" | ||
| AUTHOR = "Jens Koch, Peter Groszkowski" | ||
| AUTHOR_EMAIL = "jens-koch@northwestern.edu, piotrekg@gmail.com" | ||
| LICENSE = "BSD" | ||
| DESCRIPTION = DOCLINES[0] | ||
| LONG_DESCRIPTION = "\n".join(DOCLINES[2:]) | ||
| KEYWORDS = "superconducting qubits" | ||
| URL = "https://scqubits.readthedocs.io" | ||
| CLASSIFIERS = [_f for _f in CLASSIFIERS.split("\n") if _f] | ||
| PLATFORMS = ["Linux", "Mac OSX", "Unix", "Windows"] | ||
| def git_short_hash(): | ||
| try: | ||
| git_str = "+" + os.popen('git log -1 --format="%h"').read().strip() | ||
| except OSError: | ||
| git_str = "" | ||
| else: | ||
| if git_str == "+": # fixes setuptools PEP issues with versioning | ||
| git_str = "" | ||
| return git_str | ||
| FULLVERSION = VERSION | ||
| if not ISRELEASED: | ||
| FULLVERSION += ".dev" + str(MICRO) + git_short_hash() | ||
| def write_version_py(filename="scqubits/version.py"): | ||
| cnt = """\ | ||
| # THIS FILE IS GENERATED FROM scqubits SETUP.PY | ||
| short_version = '%(version)s' | ||
| version = '%(fullversion)s' | ||
| release = %(isrelease)s | ||
| """ | ||
| versionfile = open(filename, "w") | ||
| try: | ||
| versionfile.write( | ||
| cnt | ||
| % { | ||
| "version": VERSION, | ||
| "fullversion": FULLVERSION, | ||
| "isrelease": str(ISRELEASED), | ||
| } | ||
| ) | ||
| finally: | ||
| versionfile.close() | ||
| local_path = os.path.dirname(os.path.abspath(sys.argv[0])) | ||
| os.chdir(local_path) | ||
| sys.path.insert(0, local_path) | ||
| sys.path.insert(0, os.path.join(local_path, "scqubits")) # to retrieve _version | ||
| # always rewrite _version | ||
| if os.path.exists("scqubits/version.py"): | ||
| os.remove("scqubits/version.py") | ||
| write_version_py() | ||
| setuptools.setup( | ||
| name=NAME, | ||
| version=FULLVERSION, | ||
| packages=PACKAGES, | ||
| author=AUTHOR, | ||
| author_email=AUTHOR_EMAIL, | ||
| license=LICENSE, | ||
| description=DESCRIPTION, | ||
| long_description=LONG_DESCRIPTION, | ||
| keywords=KEYWORDS, | ||
| url=URL, | ||
| classifiers=CLASSIFIERS, | ||
| platforms=PLATFORMS, | ||
| install_requires=INSTALL_REQUIRES, | ||
| extras_require=EXTRAS_REQUIRE, | ||
| tests_require=TESTS_REQUIRE, | ||
| zip_safe=False, | ||
| include_package_data=True, | ||
| python_requires=PYTHON_VERSION, | ||
| **EXTRA_KWARGS | ||
| ) |
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
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.
8951263
2.41%156
9.09%33196
1.63%