From b97501073a2591596f4b5bb52e9d542437485086 Mon Sep 17 00:00:00 2001 From: Daniel Puzzuoli Date: Wed, 28 Feb 2024 14:47:43 -0800 Subject: [PATCH] Reformatting docs for perturbation module (#336) --- qiskit_dynamics/perturbation/__init__.py | 35 +++--- .../perturbation/array_polynomial.py | 67 ++++++------ .../perturbation/custom_binary_op.py | 70 ++++++------ qiskit_dynamics/perturbation/dyson_magnus.py | 103 ++++++++---------- .../perturbation/multiset_utils.py | 31 +++--- .../perturbation/perturbation_data.py | 8 +- .../perturbation/perturbation_utils.py | 25 ++--- .../perturbation/solve_lmde_perturbation.py | 4 +- 8 files changed, 155 insertions(+), 188 deletions(-) diff --git a/qiskit_dynamics/perturbation/__init__.py b/qiskit_dynamics/perturbation/__init__.py index 4177211ac..c4361a0ad 100644 --- a/qiskit_dynamics/perturbation/__init__.py +++ b/qiskit_dynamics/perturbation/__init__.py @@ -20,36 +20,33 @@ .. currentmodule:: qiskit_dynamics.perturbation This module contains tools for numerically computing and utilizing perturbation theory terms. -Perturbation theory is an advanced topic; a brief review of the concepts and notation required -to understand the contents in this module are given in the -:ref:`Time-dependent perturbation theory and multi-variable -series expansions review ` discussion. +Perturbation theory is an advanced topic; a brief review of the concepts and notation required to +understand the contents in this module are given in the :ref:`Time-dependent perturbation theory and +multi-variable series expansions review ` discussion. .. _td perturbation theory: Time-dependent perturbation theory ================================== -The function :func:`.solve_lmde_perturbation` computes -Dyson series :footcite:`dyson_radiation_1949` and -Magnus expansion :footcite:`magnus_exponential_1954,blanes_magnus_2009` terms -in a multi-variable setting via algorithms in :footcite:`puzzuoli_sensitivity_2022`. -It can also be used to compute Dyson-like integrals using the algorithm in -:footcite:`haas_engineering_2019`. Results are returned in either a :class:`PowerSeriesData` -or :class:`DysonLikeData` class, which are data classes with functionality for indexing -and accessing specific perturbation terms. See the function documentation for further details. +The function :func:`.solve_lmde_perturbation` computes Dyson series :footcite:`dyson_radiation_1949` +and Magnus expansion :footcite:`magnus_exponential_1954,blanes_magnus_2009` terms in a +multi-variable setting via algorithms in :footcite:`puzzuoli_sensitivity_2022`. It can also be used +to compute Dyson-like integrals using the algorithm in :footcite:`haas_engineering_2019`. Results +are returned in either a :class:`PowerSeriesData` or :class:`DysonLikeData` class, which are data +classes with functionality for indexing and accessing specific perturbation terms. See the function +documentation for further details. Truncated power-series representation and multisets =================================================== -The class :class:`.ArrayPolynomial` represents an array-valued multivariable polynomial -(i.e. a truncated power series), and provides functionality for -both evaluating and transforming array-valued polynomials. +The class :class:`.ArrayPolynomial` represents an array-valued multivariable polynomial (i.e. a +truncated power series), and provides functionality for both evaluating and transforming +array-valued polynomials. -This module makes use of the `multiset package `_ for -indexing multi-variable power series. See the -:ref:`multiset and power series notation section ` -of the perturbation review for an explanation of this convention. +This module makes use of the `multiset package `_ for indexing +multi-variable power series. See the :ref:`multiset and power series notation section ` of the perturbation review for an explanation of this convention. Perturbation module functions diff --git a/qiskit_dynamics/perturbation/array_polynomial.py b/qiskit_dynamics/perturbation/array_polynomial.py index 429316c27..a8f12ec96 100644 --- a/qiskit_dynamics/perturbation/array_polynomial.py +++ b/qiskit_dynamics/perturbation/array_polynomial.py @@ -59,23 +59,21 @@ class ArrayPolynomial: where in the above: - - :math:`S` is a finite set of multisets - indicating non-zero monomial terms, + - :math:`S` is a finite set of multisets indicating non-zero monomial terms, - For a given multiset of non-negative integers :math:`I=(i_1, \dots, i_k)`, :math:`c_I = c_{i_1} \times \dots \times c_{i_k}`, and - The :math:`A_I` are arrays of the same shape, indexed by the first dimension. - See the :ref:`multiset and power series notation section ` - of the perturbation review for an explanation of the multiset notation. + See the :ref:`multiset and power series notation section ` of the + perturbation review for an explanation of the multiset notation. An :class:`.ArrayPolynomial` is instantiated with the arguments: - ``constant_term`` specifying the array :math:`A_\emptyset`. - ``array_coefficients`` specifying a list of the arrays :math:`A_I`, or as a single array whose first index lists the :math:`A_I`, - - ``monomial_labels`` specifying the set :math:`S` as a list of - ``Multiset`` instances ordered in - correspondence with ``array_coefficients``. + - ``monomial_labels`` specifying the set :math:`S` as a list of ``Multiset`` instances + ordered in correspondence with ``array_coefficients``. For example, the :class:`.ArrayPolynomial` corresponding to the mathematical polynomial @@ -84,8 +82,8 @@ class ArrayPolynomial: f(c_0, c_1) = A_\emptyset + c_{(0)} A_{(0)} + c_{(0, 1)}A_{(0, 1)} + c_{(1, 1)}A_{(1, 1)} - for arrays :math:`A_\emptyset, A_{(0)}, A_{(0, 1)}, A_{(1, 1)}` stored in variables - ``A_c``, ``A0``, ``A01``, and ``A11`` can be instantiated with + for arrays :math:`A_\emptyset, A_{(0)}, A_{(0, 1)}, A_{(1, 1)}` stored in variables ``A_c``, + ``A0``, ``A01``, and ``A11`` can be instantiated with .. code-block:: python @@ -103,10 +101,10 @@ class ArrayPolynomial: ap(c) # polynomial evaluated on variables :class:`.ArrayPolynomial` supports some array properties, e.g. ``ap.shape`` and ``ap.ndim`` - return the shape and number of dimensions of the output of the polynomial. Some array - methods are also supported, such as ``transpose`` and ``trace``, and their output produces - a new :class:`.ArrayPolynomial` which evaluates to the array one would obtain by first - evaluating the original, then calling the array method. E.g. + return the shape and number of dimensions of the output of the polynomial. Some array methods + are also supported, such as ``transpose`` and ``trace``, and their output produces a new + :class:`.ArrayPolynomial` which evaluates to the array one would obtain by first evaluating the + original, then calling the array method. E.g. .. code-block:: python @@ -120,15 +118,15 @@ class ArrayPolynomial: ap3 = ap1 @ ap2 ap3(c) == ap1(c) @ ap2(c) - It also has specialized algebraic methods that perform algebraic operations while - "ignoring" terms. E.g., for two instances ``ap1`` and ``ap2``, the call + It also has specialized algebraic methods that perform algebraic operations while "ignoring" + terms. E.g., for two instances ``ap1`` and ``ap2``, the call .. code-block:: python ap1.matmul(ap2, monomial_filter=lambda x: len(x) <= 3) - is similar to ``ap1 @ ap2``, but will result in an :class:`.ArrayPolynomial` in which all - terms of degree larger than ``3`` will not be included in the results. + is similar to ``ap1 @ ap2``, but will result in an :class:`.ArrayPolynomial` in which all terms + of degree larger than ``3`` will not be included in the results. """ __array_priority__ = 20 @@ -357,11 +355,10 @@ def add( ) -> "ArrayPolynomial": """Add two polynomials with bounds on which terms to keep. - Optionally, a function ``monomial_filter`` can be provided to limit which monomials - appear in the output. It must accept as input a ``Multiset`` and return a ``bool``, - and a term with label given by ``multiset`` will be included only if - ``monomial_filter(multiset) == True``, and will not be computed if - ``monomial_filter(multiset) == False``. + Optionally, a function ``monomial_filter`` can be provided to limit which monomials appear + in the output. It must accept as input a ``Multiset`` and return a ``bool``, and a term with + label given by ``multiset`` will be included only if ``monomial_filter(multiset) == True``, + and will not be computed if ``monomial_filter(multiset) == False``. Args: other: Other to add to self. @@ -389,11 +386,10 @@ def matmul( ) -> "ArrayPolynomial": """Matmul self @ other with bounds on which terms to keep. - Optionally, a function ``monomial_filter`` can be provided to limit which monomials - appear in the output. It must accept as input a ``Multiset`` and return a ``bool``, - and a term with label given by ``multiset`` will be included only if - ``monomial_filter(multiset) == True``, and will not be computed if - ``monomial_filter(multiset) == False``. + Optionally, a function ``monomial_filter`` can be provided to limit which monomials appear + in the output. It must accept as input a ``Multiset`` and return a ``bool``, and a term with + label given by ``multiset`` will be included only if ``monomial_filter(multiset) == True``, + and will not be computed if ``monomial_filter(multiset) == False``. Args: other: Other to add to self. @@ -420,11 +416,10 @@ def mul( ) -> "ArrayPolynomial": """Entrywise multiplication of two ArrayPolynomials with bounds on which terms to keep. - Optionally, a function ``monomial_filter`` can be provided to limit which monomials - appear in the output. It must accept as input a ``Multiset`` and return a ``bool``, - and a term with label given by ``multiset`` will be included only if - ``monomial_filter(multiset) == True``, and will not be computed if - ``monomial_filter(multiset) == False``. + Optionally, a function ``monomial_filter`` can be provided to limit which monomials appear + in the output. It must accept as input a ``Multiset`` and return a ``bool``, and a term with + label given by ``multiset`` will be included only if ``monomial_filter(multiset) == True``, + and will not be computed if ``monomial_filter(multiset) == False``. Args: other: Other to add to self. @@ -553,14 +548,14 @@ def _get_monomial_compute_function(multisets: List[Multiset]) -> Callable: """Construct a vectorized function for computing multivariable monomial terms indicated by multisets. - The returned function takes in the individual variables as an array, and returns an array - of computed monomial terms in the order indicated by multisets. + The returned function takes in the individual variables as an array, and returns an array of + computed monomial terms in the order indicated by multisets. The returned function is vectorized in the sense that the supplied first order terms can be arrays. - The algorithm computes monomial terms of increasing order, recursively utilizing lower - order terms. + The algorithm computes monomial terms of increasing order, recursively utilizing lower order + terms. Args: multisets: list of multisets. diff --git a/qiskit_dynamics/perturbation/custom_binary_op.py b/qiskit_dynamics/perturbation/custom_binary_op.py index ee124d7b2..88a9d98a4 100644 --- a/qiskit_dynamics/perturbation/custom_binary_op.py +++ b/qiskit_dynamics/perturbation/custom_binary_op.py @@ -29,39 +29,35 @@ class _CustomBinaryOp: - r"""A binary operation between arrays of dimension >1d built from taking linear combinations - of a base binary operation acting on sub-arrays. + r"""A binary operation between arrays of dimension >1d built from taking linear combinations of + a base binary operation acting on sub-arrays. - This class constructs customized binary operations between - lists of arrays :math:`A = (A_i)` and :math:`B = (B_i)` of the form: + This class constructs customized binary operations between lists of arrays :math:`A = (A_i)` and + :math:`B = (B_i)` of the form: .. math:: (A \times B)_i = \sum_{jk} a_{ijk} f(A_j, B_k), - where :math:`a_{ijk}` is an array of complex scalars, and :math:`f` is a binary operation - (a common example being matrix multiplication). + where :math:`a_{ijk}` is an array of complex scalars, and :math:`f` is a binary operation (a + common example being matrix multiplication). - At instantiation the binary operation :math:`f`, as well - as the array :math:`a_{ijk}`, are specified. The array :math:`a_{ijk}` is given in a - sparse format, as a list where each - entry specifies the 2d sub-array :math:`a_{i}` as a 2-tuple - with entries: + At instantiation the binary operation :math:`f`, as well as the array :math:`a_{ijk}`, are + specified. The array :math:`a_{ijk}` is given in a sparse format, as a list where each entry + specifies the 2d sub-array :math:`a_{i}` as a 2-tuple with entries: - The non-zero entries of :math:`a_i` given as a list. - A 2d-array with each entry being the pair of indices ``[j,k]``. - Internally, this specification is translated into one more suited for efficient - evaluation: - - A specification of the unique pairs of required evaluations - :math:`f(A_j, B_k)` in terms of two arrays giving the left - indices and the right indices. - - A 2-tuple of 2d arrays specifying the linear combination of - unique evaluations of :math:`f` to compute the :math:`i^{th}` entry of the - binary operations. The :math:`i^{th}` entry of each array is: + Internally, this specification is translated into one more suited for efficient evaluation: + - A specification of the unique pairs of required evaluations :math:`f(A_j, B_k)` in terms + of two arrays giving the left indices and the right indices. + - A 2-tuple of 2d arrays specifying the linear combination of unique evaluations of + :math:`f` to compute the :math:`i^{th}` entry of the binary operations. The :math:`i^{th}` + entry of each array is: - The list of coefficients in the linear combination. - The index of the unique product for each coefficient. - These arrays are padded with the value ``-1`` to make each - linear combo the same length (relevant for JAX evaluation). + These arrays are padded with the value ``-1`` to make each linear combo the same length + (relevant for JAX evaluation). """ def __init__( @@ -149,27 +145,25 @@ def _compile_custom_operation_rule( unique_evaluation_len: Optional[int] = None, linear_combo_len: Optional[int] = None, ) -> Tuple[np.array, np.array]: - """Compile the list of unique evaluations and linear combinations required - to implement a given operation_rule. + """Compile the list of unique evaluations and linear combinations required to implement a given + operation_rule. See _CustomBinaryOp doc string for formatting details. Args: - operation_rule: Custom operation rule in the sparse format in the - _CustomBinaryOp doc string. - index_offset: Integer specifying a shift to apply in the 2nd and 3rd indices in the - sparse representation of :math:`a_{ijk}`. - unique_evaluation_len: Integer specifying a minimum length to represent the - unique multiplications list. The unique multiplication list - is padded with entries ``[-1, -1]`` to meet the minimum length. - linear_combo_len: Minimum length for linear combo specification. Coefficients are - padded with zeros, and the unique multiplication indices are - padded with ``-1``. + operation_rule: Custom operation rule in the sparse format in the _CustomBinaryOp doc + string. + index_offset: Integer specifying a shift to apply in the 2nd and 3rd indices in the sparse + representation of :math:`a_{ijk}`. + unique_evaluation_len: Integer specifying a minimum length to represent the unique + multiplications list. The unique multiplication list is padded with entries ``[-1, -1]`` + to meet the minimum length. + linear_combo_len: Minimum length for linear combo specification. Coefficients are padded + with zeros, and the unique multiplication indices are padded with ``-1``. Returns: - Tuple[np.array, np.array]: Multiplication rule compiled into a list of - unique products and a list of linear combinations of - unique products for implementing the custom dot rule. + Tuple[np.array, np.array]: Multiplication rule compiled into a list of unique products and a + list of linear combinations of unique products for implementing the custom dot rule. """ # force numpy usage for algebra specification @@ -251,8 +245,8 @@ def _compute_unique_evaluations( def _compute_linear_combos( unique_evaluations: np.array, linear_combo_rule: Tuple[np.array, np.array] ) -> np.array: - r"""Compute linear combinations of the entries in the array ``unique_mults`` - according to ``linear_combo_rule``. The :math:`j^{th}` entry of the output is given by + r"""Compute linear combinations of the entries in the array ``unique_mults`` according to + ``linear_combo_rule``. The :math:`j^{th}` entry of the output is given by :math:`sum_k c[j, k] unique_mults[idx[j, k]]`, where ``linear_combo_rule`` is ``(c, idx)``. """ M0 = np.zeros_like(unique_evaluations[0]) diff --git a/qiskit_dynamics/perturbation/dyson_magnus.py b/qiskit_dynamics/perturbation/dyson_magnus.py index cc14d4c5f..74c6b0b3a 100644 --- a/qiskit_dynamics/perturbation/dyson_magnus.py +++ b/qiskit_dynamics/perturbation/dyson_magnus.py @@ -12,10 +12,9 @@ # pylint: disable=invalid-name r""" -Core functionality for computing Dyson series [1] and Magnus expansion [2, 3] terms. -Specifically, Dyson-like terms are computed via the algorithm in [4], and -Dyson series and Magnus expansion terms are computed via the -method in [5]. +Core functionality for computing Dyson series [1] and Magnus expansion [2, 3] terms. Specifically, +Dyson-like terms are computed via the algorithm in [4], and Dyson series and Magnus expansion terms +are computed via the method in [5]. References: 1. F. Dyson, *The radiation theories of Tomonaga, Schwinger, and Feynman*, @@ -76,19 +75,18 @@ def _solve_lmde_dyson( t_eval: Optional[ArrayLike] = None, **kwargs, ) -> OdeResult: - """Helper function for computing Dyson terms using methods in References [4, 5]. - See documentation for :meth:`solve_lmde_perturbation`. + """Helper function for computing Dyson terms using methods in References [4, 5]. See + documentation for :meth:`solve_lmde_perturbation`. Args: perturbations: List of callable matrix functions to appear in Dyson terms. t_span: Integration limits. dyson_terms: Terms to compute. - perturbation_labels: Ordering/specification of the elements of perturbations. Only used - for dyson_like==False. + perturbation_labels: Ordering/specification of the elements of perturbations. Only used for + dyson_like==False. generator: Optional frame generator. y0: Optional initial state for frame generator LMDE. - dyson_in_frame: Whether to return the Dyson terms in the frame of the - the frame generator. + dyson_in_frame: Whether to return the Dyson terms in the frame of the the frame generator. dyson_like: Compute either Dyson or Dyson-like terms. integration_method: Integration method. t_eval: Optional additional time points at which to return the solution. @@ -171,8 +169,8 @@ def _solve_lmde_magnus( t_eval: Optional[ArrayLike] = None, **kwargs, ) -> OdeResult: - """Helper function for computing Magnus terms using method in Reference [5]. - See documentaiton for :meth:`solve_lmde_perturbation`. + """Helper function for computing Magnus terms using method in Reference [5]. See documentation + for :meth:`solve_lmde_perturbation`. Args: perturbations: List of callable matrix functions to appear in Dyson terms. @@ -227,19 +225,17 @@ def _solve_lmde_dyson_jax( t_eval: Optional[ArrayLike] = None, **kwargs, ) -> OdeResult: - """JAX version of ``_solve_lmde_dyson``. - See documentation for :meth:`solve_lmde_perturbation`. + """JAX version of ``_solve_lmde_dyson``. See documentation for :meth:`solve_lmde_perturbation`. Args: perturbations: List of callable matrix functions to appear in Dyson terms. t_span: Integration limits. dyson_terms: Terms to compute. perturbation_labels: Ordering/specification of the elements of perturbations. Only used if - dyson_like==False. + dyson_like==False. generator: Optional frame generator. y0: Optional initial state for frame generator LMDE. - dyson_in_frame: Whether to return the Dyson terms in the frame of the - the frame generator. + dyson_in_frame: Whether to return the Dyson terms in the frame of the the frame generator. dyson_like: Compute either Dyson or Dyson-like terms. integration_method: Integration method. t_eval: Optional additional time points at which to return the solution. @@ -321,8 +317,7 @@ def _solve_lmde_magnus_jax( t_eval: Optional[ArrayLike] = None, **kwargs, ) -> OdeResult: - """JAX version of ``_solve_lmde_magnus``. - See documentation for :meth:`solve_lmde_perturbation`. + """JAX version of ``_solve_lmde_magnus``. See documentation for :meth:`solve_lmde_perturbation`. Args: perturbations: List of callable matrix functions to appear in Dyson terms. @@ -380,8 +375,8 @@ def _setup_dyson_rhs( oc_dyson_indices: Ordered complete list of Dyson terms to compute. mat_dim: Dimension of outputs of generator and functions in perturbations. dyson_like: Whether the computation is for Dyson or Dyson-like terms. - perturbation_labels: List of lists specifying index information for perturbations. - Only used when dyson_like==False. + perturbation_labels: List of lists specifying index information for perturbations. Only used + when dyson_like==False. Returns: Callable @@ -435,8 +430,8 @@ def _setup_dyson_rhs_jax( perturbations: List of matrix functions appearing in Dyson terms. oc_dyson_indices: Ordered complete list of Dyson terms to compute. dyson_like: Whether the computation is for Dyson or Dyson-like terms. - perturbation_labels: List of lists specifying index information for perturbations. - Only used when dyson_like==False. + perturbation_labels: List of lists specifying index information for perturbations. Only used + when dyson_like==False. Returns: Callable @@ -474,9 +469,7 @@ def dyson_rhs(t, y): def _required_dyson_generator_indices(complete_dyson_indices: List) -> List: - """Given a complete list of dyson indices, determine which generator terms - are actually required. - """ + """Given a complete list of dyson indices, determine which generator terms are required.""" generator_indices = [] for term in complete_dyson_indices: if term[0] not in generator_indices: @@ -489,11 +482,11 @@ def _required_dyson_generator_indices(complete_dyson_indices: List) -> List: def _get_dyson_like_lmult_rule( complete_dyson_indices: List[List[int]], generator_indices: List[List[int]] ) -> List: - """Construct custom product rules, in the format required by ``custom_product``, - for a given set of Dyson terms. + """Construct custom product rules, in the format required by ``custom_product``, for a given set + of Dyson terms. - Assumption: the supplied list is complete, i.e. if a term depends on other - terms, then the terms it depends on are also in the list. + Assumption: the supplied list is complete, i.e. if a term depends on other terms, then the terms + it depends on are also in the list. Convention: G(t) is given the index -1 to preserve the indexing of perturbations. @@ -530,10 +523,9 @@ def _get_dyson_like_lmult_rule( def _get_complete_dyson_like_indices(dyson_terms: List[List[int]]) -> List[List[int]]: - """Given a list of Dyson terms to compute specified as lists of indices, - recursively construct all other Dyson terms that need to be computed, - returned as a list, ordered by increasing Dyson order, and - in lexicographic order within an order. + """Given a list of Dyson terms to compute specified as lists of indices, recursively construct + all other Dyson terms that need to be computed, returned as a list, ordered by increasing Dyson + order, and in lexicographic order within an order. Args: dyson_terms: Terms to compute. @@ -571,9 +563,8 @@ def _get_complete_dyson_like_indices(dyson_terms: List[List[int]]) -> List[List[ def _magnus_from_dyson(complete_index_multisets: List[Multiset], dyson_terms: np.array) -> np.array: - """Compute magnus terms from dyson terms using the recursion - relation presented in [5]. The term "Q Matrices" in helper functions refers to - the matrices used in the recursion relation in [5]. + """Compute magnus terms from dyson terms using the recursion relation presented in [5]. The term + "Q Matrices" in helper functions refers to the matrices used in the recursion relation in [5]. Args: complete_index_multisets: A complete and canonically ordered list of index Multisets. @@ -645,11 +636,10 @@ def scan_fun(B, x): def _q_recursive_compiled_rules(ordered_q_terms: List) -> Tuple[int, np.array, Tuple]: - """Construct compiled custom product rules for recursive computation - of Q matrices. + """Construct compiled custom product rules for recursive computation of Q matrices. - Note: this function "stacks" the rules into a single tuple whose formatting - is chosen to be usable with jax loop constructs. + Note: this function "stacks" the rules into a single tuple whose formatting is chosen to be + usable with jax loop constructs. Args: ordered_q_terms: Ordered list of Q matrix specifications. @@ -708,17 +698,15 @@ def _q_recursive_compiled_rules(ordered_q_terms: List) -> Tuple[int, np.array, T def _q_product_rule(q_term: Tuple, oc_q_term_list: List[Tuple]) -> List: - """Given a specification of a Q matrix and an ordered complete - list of Q matrix specifications, constructs the recursion relation required to - compute q_term, specified as a custom product rule for instantiating - a CustomProduct. + """Given a specification of a Q matrix and an ordered complete list of Q matrix specifications, + constructs the recursion relation required to compute q_term, specified as a custom product rule + for instantiating a CustomProduct. Note: - - This assumes len(sym_index) > 1, as the purpose of this - function is to apply the recursion rules, and no rule is required - when len(sym_index) == 1. - - This function also assumes that q_term, and oc_q_term_list are - correctly formatted in terms of internal sorting. + - This assumes len(sym_index) > 1, as the purpose of this function is to apply the recursion + rules, and no rule is required when len(sym_index) == 1. + - This function also assumes that q_term, and oc_q_term_list are correctly formatted in + terms of internal sorting. Args: q_term: Tuple with a Multiset and a product order (int) @@ -762,15 +750,12 @@ def _q_product_rule(q_term: Tuple, oc_q_term_list: List[Tuple]) -> List: def _get_q_term_list(complete_index_multisets: List[Multiset]) -> List: - """Construct a specification of the recursive Q matrices - required to compute all Magnus terms specified by - ``complete_index_multisets``. Each Q matrix is specified as - a 2-tuple with first entry a Multiset indexing the term, - and second entry the product order of the Q matrix. - - Note: This function assumes ``complete_index_multisets`` are - canonically ordered and correctly formatted. The output is then - a canonical ordering of the Q matrices. + """Construct a specification of the recursive Q matrices required to compute all Magnus terms + specified by ``complete_index_multisets``. Each Q matrix is specified as a 2-tuple with first + entry a Multiset indexing the term, and second entry the product order of the Q matrix. + + Note: This function assumes ``complete_index_multisets`` are canonically ordered and correctly + formatted. The output is then a canonical ordering of the Q matrices. Args: complete_index_multisets: Canonically ordered complete list of index Multisets. diff --git a/qiskit_dynamics/perturbation/multiset_utils.py b/qiskit_dynamics/perturbation/multiset_utils.py index 04fa1b235..d693f2983 100644 --- a/qiskit_dynamics/perturbation/multiset_utils.py +++ b/qiskit_dynamics/perturbation/multiset_utils.py @@ -47,8 +47,8 @@ def _multiset_to_sorted_list(multiset: Multiset) -> List: class _MultisetSortKey: - """Dummy class for usage as a key when sorting Multiset instances. This assumes the elements - of the multisets can themselves be sorted. + """Dummy class for usage as a key when sorting Multiset instances. This assumes the elements of + the multisets can themselves be sorted. """ __slots__ = ("multiset",) @@ -61,10 +61,10 @@ def __lt__(self, other: Multiset) -> bool: This orders first according to length (the number of elements in each multiset). If ``self`` and ``other`` are the same length, ``self < other`` if, when written as fully expanded and - sorted lists, ``self < other`` in lexicographic ordering. E.g. it holds that ``Multiset({0: - 2, 1: 1}) < Multiset({0: 1, 1: 2})``, as the list versions are ``x = [0, 0, 1]``, and ``y = - [0, 1, 1]``. Here ``x[0] == y[0]``, but ``x[1] < y[1]``, and hence ``x < y`` in this - ordering. + sorted lists, ``self < other`` in lexicographic ordering. E.g. it holds that + ``Multiset({0: 2, 1: 1}) < Multiset({0: 1, 1: 2})``, as the list versions are + ``x = [0, 0, 1]``, and ``y = [0, 1, 1]``. Here ``x[0] == y[0]``, but ``x[1] < y[1]``, and + hence ``x < y`` in this ordering. """ if len(self.multiset) < len(other.multiset): return True @@ -94,8 +94,8 @@ def _sorted_multisets(multisets: Iterable[Multiset]) -> List[Multiset]: def _clean_multisets(multisets: List[Multiset]) -> List[Multiset]: - """Given a list of multisets, remove duplicates, and sort in non-decreasing order - according to the _sorted_multisets function. + """Given a list of multisets, remove duplicates, and sort in non-decreasing order according to + the _sorted_multisets function. """ unique_multisets = [] @@ -111,8 +111,8 @@ def _clean_multisets(multisets: List[Multiset]) -> List[Multiset]: def _submultiset_filter( multiset_candidates: List[Multiset], multiset_list: List[Multiset] ) -> List[Multiset]: - """Filter the list of multiset_candidates based on whether they are a - submultiset of an element in multiset_list. + """Filter the list of multiset_candidates based on whether they are a submultiset of an element + in multiset_list. """ filtered_multisets = [] @@ -128,15 +128,14 @@ def _submultiset_filter( def _submultisets_and_complements( multiset: Multiset, submultiset_bound: Optional[int] = None ) -> Tuple[List[Multiset], List[Multiset]]: - """Return a pair of lists giving all submultisets of size smaller than - submultiset_bound, and corresponding complements. + """Return a pair of lists giving all submultisets of size smaller than submultiset_bound, and + corresponding complements. Note: Submultisets and compliments are always strict submultisets. Args: multiset: The multiset to construct submultisets from. - submultiset_bound: Strict upper bound on submultiset to include. - Defaults to len(multiset). + submultiset_bound: Strict upper bound on submultiset to include. Defaults to len(multiset). Returns: Submultisets and corresponding complements. @@ -174,8 +173,8 @@ def _submultisets_and_complements( def _get_all_submultisets(multisets: List[Multiset]) -> List[Multiset]: - """Given a list of multisets, return a list of all possible submultisets - of multisets in the list, including the original multisets. + """Given a list of multisets, return a list of all possible submultisets of multisets in the + list, including the original multisets. This returned list is sorted according to the ordering of the _sorted_multisets function. diff --git a/qiskit_dynamics/perturbation/perturbation_data.py b/qiskit_dynamics/perturbation/perturbation_data.py index 8423ac0e2..f56b87da0 100644 --- a/qiskit_dynamics/perturbation/perturbation_data.py +++ b/qiskit_dynamics/perturbation/perturbation_data.py @@ -46,8 +46,8 @@ def _preprocess_label(self, label: any) -> any: class PowerSeriesData(_LabeledData): - """Storage container for power series data. Labels are assumed to be ``Multiset`` instances, - and data is assumed to be a dense ``ArrayLike``. + """Storage container for power series data. Labels are assumed to be ``Multiset`` instances, and + data is assumed to be a dense ``ArrayLike``. """ def _preprocess_label(self, label: Multiset) -> Multiset: @@ -56,8 +56,8 @@ def _preprocess_label(self, label: Multiset) -> Multiset: class DysonLikeData(_LabeledData): - """Storage container for DysonLike series data. Labels are assumed to be lists of ints, - and data is assumed to be a dense ``ArrayLike``. + """Storage container for DysonLike series data. Labels are assumed to be lists of ints, and data + is assumed to be a dense ``ArrayLike``. """ def _preprocess_label(self, label: list) -> list: diff --git a/qiskit_dynamics/perturbation/perturbation_utils.py b/qiskit_dynamics/perturbation/perturbation_utils.py index 60868f4ea..6f058d372 100644 --- a/qiskit_dynamics/perturbation/perturbation_utils.py +++ b/qiskit_dynamics/perturbation/perturbation_utils.py @@ -33,16 +33,16 @@ def _merge_multiset_expansion_order_labels( expansion_order: Optional[int] = None, expansion_labels: Optional[List[Multiset]] = None, ) -> List[Multiset]: - """Helper function for merging expansion_order and expansion_labels arguments - in the multiset case for functions that require specifying expansion terms to compute. + """Helper function for merging expansion_order and expansion_labels arguments in the multiset + case for functions that require specifying expansion terms to compute. - Generates a list of all Multisets of a given size given by expansion_order, - and includes any additional multisets specified by expansion_labels. The elements - of the multisets are drawn from perturbation_labels, which is either a list of - ints, or a list of Multisets from which the ints are drawn. + Generates a list of all Multisets of a given size given by expansion_order, and includes any + additional multisets specified by expansion_labels. The elements of the multisets are drawn from + perturbation_labels, which is either a list of ints, or a list of Multisets from which the ints + are drawn. - At least one of expansion_order or expansion_labels must be specified. Accepts - only multisets and labels consisting of non-negative integers. + At least one of expansion_order or expansion_labels must be specified. Accepts only multisets + and labels consisting of non-negative integers. Args: perturbation_labels: Specification of elements of the multisets to generate. @@ -101,8 +101,8 @@ def _merge_list_expansion_order_labels( expansion_order: Optional[int] = None, expansion_labels: Optional[List[List[int]]] = None, ) -> List[int]: - """Helper function for merging expansion_order and expansion_labels arguments - in the list case for functions that require specifying expansion terms to compute. + """Helper function for merging expansion_order and expansion_labels arguments in the list case + for functions that require specifying expansion terms to compute. Generates a list of all lists of integers in [0, ..., perturbation_num - 1] of a given size given by expansion_order, and includes any additional lists specified by expansion_labels. @@ -121,10 +121,7 @@ def _merge_list_expansion_order_labels( # validate if expansion_order is None and expansion_labels is None: - raise QiskitError( - """At least one of expansion_order or - expansion_labels must be specified.""" - ) + raise QiskitError("At least one of expansion_order or expansion_labels must be specified.") if expansion_order is None: return expansion_labels diff --git a/qiskit_dynamics/perturbation/solve_lmde_perturbation.py b/qiskit_dynamics/perturbation/solve_lmde_perturbation.py index 4af68f0a4..96cf51f7e 100644 --- a/qiskit_dynamics/perturbation/solve_lmde_perturbation.py +++ b/qiskit_dynamics/perturbation/solve_lmde_perturbation.py @@ -198,8 +198,8 @@ def solve_lmde_perturbation( perturbation theory terms. Raises: - QiskitError: If problem with inputs, either ``expansion_method`` is unsupported, - or both of ``expansion_order`` and ``expansion_labels`` unspecified. + QiskitError: If problem with inputs, either ``expansion_method`` is unsupported, or both of + ``expansion_order`` and ``expansion_labels`` unspecified. .. footbibliography:: """