Source code for scqubits.utils.misc

# misc.py
#
# This file is part of scqubits: a Python package for superconducting qubits,
# Quantum 5, 583 (2021). https://quantum-journal.org/papers/q-2021-11-17-583/
#
#    Copyright (c) 2019 and later, Jens Koch and Peter Groszkowski
#    All rights reserved.
#
#    This source code is licensed under the BSD-style license found in the
#    LICENSE file in the root directory of this source tree.
############################################################################

import ast
import functools
import platform
import warnings

from collections.abc import Sequence
from io import StringIO
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union

import numpy as np
import qutip as qt
import scipy as sp

from scqubits.settings import IN_IPYTHON

if IN_IPYTHON:
    from tqdm.notebook import tqdm
else:
    from tqdm import tqdm


[docs]def process_which(which: Union[int, Iterable[int]], max_index: int) -> List[int]: """Processes different ways of specifying the selection of wanted eigenvalues/eigenstates. Parameters ---------- which: single index or tuple/list of integers indexing the eigenobjects. If 'which' is -1, all indices up to the max_index limit are included. max_index: maximum index value Returns ------- indices """ if isinstance(which, int): if which == -1: return list(range(max_index)) return [which] return list(which)
[docs]def make_bare_labels(subsystem_count: int, *args) -> Tuple[int, ...]: """ For two given subsystem states, return the full-system bare state label obtained by placing all remaining subsys_list in their ground states. Parameters ---------- subsystem_count: number of subsys_list inside Hilbert space *args: each argument is a tuple of the form (subsys_index, label) Returns ------- Suppose there are 5 subsys_list in total. Let (subsys_index1=0, label1=3), (subsys_index2=2, label2=1). Then the returned bare-state tuple is: (3,0,1,0,0) """ bare_labels = [0] * subsystem_count for subsys_index, label in args: bare_labels[subsys_index] = label return tuple(bare_labels)
[docs]def drop_private_keys(full_dict: Dict[str, Any]) -> Dict[str, Any]: """Filter for entries in the full dictionary that have numerical values""" return {key: value for key, value in full_dict.items() if key[0] != "_"}
[docs]class InfoBar: """Static "progress" bar used whenever multiprocessing is involved. Parameters ---------- desc: Description text to be displayed on the static information bar. num_cpus: Number of CPUS/cores employed in underlying calculation. """ def __init__(self, desc: str, num_cpus: int) -> None: self.desc = desc self.num_cpus = num_cpus self.tqdm_bar: Optional[tqdm] = None def __enter__(self) -> None: self.tqdm_bar = tqdm( total=0, disable=(self.num_cpus == 1), leave=False, desc=self.desc, bar_format="{desc}", ) def __exit__(self, *args) -> None: self.tqdm_bar.close()
[docs]class Required: """Decorator class, ensuring that a given requirement or set of requirements is fulfilled. Parameters ---------- dict {str: bool} All bool conditions have to be True to pass. The provided str keys are used to display information on what condition is failing. """ def __init__(self, **requirements) -> None: self.requirements_bools = list(requirements.values()) self.requirements_names = list(requirements.keys()) def __call__(self, func: Callable, *args, **kwargs) -> Callable: @functools.wraps(func) def decorated_func(*args, **kwargs): if all(self.requirements_bools): return func(*args, **kwargs) else: raise Exception( "ImportError: use of this method requires the optional package(s):" " {}. If you wish to use this functionality, the corresponding" " package(s) must be installed manually. (Installation via `conda" " install -c conda-forge <packagename>` or `pip install" " <packagename>` is recommended.)".format(self.requirements_names) ) return decorated_func
[docs]def check_sync_status(func: Callable) -> Callable: @functools.wraps(func) def wrapper(self, *args, **kwargs): if self._out_of_sync: warnings.warn( "[scqubits] Some system parameters have been changed and" " generated spectrum data could be outdated, potentially leading to" " incorrect results. Spectral data can be refreshed via" " <HilbertSpace>.generate_lookup() or <ParameterSweep>.run()", Warning, ) return func(self, *args, **kwargs) return wrapper
[docs]class DeprecationMessage: """Decorator class, producing an adjustable warning and info upon usage of the decorated function. Parameters ---------- warning_message: Warnings message to be sent upon decorated (deprecated) routing """ def __init__(self, warning_msg: str) -> None: self.warning_msg = warning_msg def __call__(self, func: Callable, *args, **kwargs) -> Callable: @functools.wraps(func) def decorated_func(*args, **kwargs): warnings.warn(self.warning_msg, FutureWarning) return func(*args, **kwargs) return decorated_func
[docs]def to_expression_or_string(string_expr: str) -> Any: try: return ast.literal_eval(string_expr) except ValueError: return string_expr
[docs]def remove_nones(dict_data: Dict[str, Any]) -> Dict[str, Any]: return {key: value for key, value in dict_data.items() if value is not None}
[docs]def qt_ket_to_ndarray(qobj_ket: qt.Qobj) -> np.ndarray: # Qutip's `.eigenstates()` returns an object-valued ndarray, each idx_entry of which # is a Qobj ket. return np.asarray(qobj_ket.data.todense())
[docs]def get_shape(lst, 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) """ if not isinstance(lst, Sequence): # base case return shape # peek ahead and assure all lists in the next depth # have the same length if isinstance(lst[0], Sequence): l = len(lst[0]) if not all(len(item) == l for item in lst): msg = "not all lists have the same length" raise ValueError(msg) shape += (len(lst),) # recurse shape = get_shape(lst[0], shape) return shape
[docs]def tuple_to_short_str(the_tuple: tuple) -> str: short_str = "" for entry in the_tuple: short_str += str(entry) + "," return short_str[:-1]
[docs]def to_list(obj: Any) -> List[Any]: if isinstance(obj, list): return obj if isinstance(obj, np.ndarray): return obj.tolist() return [obj]
[docs]def about(print_info=True): """Prints or returns a string with basic information about scqubits as well as installed version of various packages that scqubits depends on. Parameters ---------- print_info: bool Flag that determines if string with information should be printed (if True) or returned (if False). Returns ------- None or str """ from scqubits import __version__ fs = StringIO() fs.write("scqubits: a Python library for simulating superconducting qubits\n") fs.write("****************************************************************\n") fs.write("Developed by J. Koch, P. Groszkowski\n") fs.write("Main Github page: https://github.com/scqubits/scqubits\n") fs.write( "Online documentation page: https://scqubits.readthedocs.io/en/latest/\n\n" ) fs.write("scqubits version: {}\n".format(__version__)) fs.write("numpy version: {}\n".format(np.__version__)) fs.write("scipy version: {}\n".format(sp.__version__)) fs.write("QuTiP version: {}\n".format(qt.__version__)) fs.write( "Platform: {} ({})\n".format(platform.system(), platform.machine()) ) if print_info: print(fs.getvalue()) return None else: return fs.getvalue()
[docs]def cite(print_info=True): """Prints or returns a string with scqubits citation information. Parameters ---------- print_info: bool Flag that determines if string with information should be printed (if True) or returned (if False). Returns ------- None or str """ fs = StringIO() fs.write("Peter Groszkowski and Jens Koch,\n") fs.write("'scqubits: a Python package for superconducting qubits'\n") fs.write("Quantum 5, 583 (2021).\n") fs.write("https://quantum-journal.org/papers/q-2021-11-17-583/\n") if print_info: print(fs.getvalue()) return None else: return fs.getvalue()
[docs]def is_float_string(the_string: str) -> bool: try: float(the_string) return True except ValueError: return False
[docs]def list_intersection(list1: list, list2: list) -> list: return list(set(list1) & set(list2))
[docs]def flatten_list(nested_list): """ Flattens a list of lists once, not recursive. Parameters ---------- nested_list: A list of lists, which can hold any class instance. Returns ------- Flattened list of objects """ return functools.reduce(lambda a, b: a + b, nested_list)
[docs]def flatten_list_recursive(S): """ Flattens a list of lists recursively. Parameters ---------- nested_list: A list of lists, which can hold any class instance. Returns ------- Flattened list of objects """ if S == []: return S if isinstance(S[0], list): return flatten_list_recursive(S[0]) + flatten_list_recursive(S[1:]) return S[:1] + flatten_list_recursive(S[1:])