From 1dcb3516ee3806e793a8aa2e992447532d9c712e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Rodr=C3=ADguez=20Aldavero?= Date: Fri, 5 Jul 2024 10:04:54 +0200 Subject: [PATCH 1/7] Optimize cross algorithms and add greedy variant --- src/seemps/analysis/cross/__init__.py | 7 +- src/seemps/analysis/cross/black_box.py | 223 +++++++++++--- src/seemps/analysis/cross/cross.py | 134 ++++++--- src/seemps/analysis/cross/cross_dmrg.py | 140 +++++---- src/seemps/analysis/cross/cross_greedy.py | 346 ++++++++++++++++++++++ src/seemps/analysis/cross/cross_maxvol.py | 218 +++++++------- tests/test_analysis/test_cross.py | 169 +++++++++-- 7 files changed, 974 insertions(+), 263 deletions(-) create mode 100644 src/seemps/analysis/cross/cross_greedy.py diff --git a/src/seemps/analysis/cross/__init__.py b/src/seemps/analysis/cross/__init__.py index b86d1af..53e1cde 100644 --- a/src/seemps/analysis/cross/__init__.py +++ b/src/seemps/analysis/cross/__init__.py @@ -1,19 +1,22 @@ from .black_box import ( BlackBoxLoadMPS, + BlackBoxLoadTT, BlackBoxLoadMPO, BlackBoxComposeMPS, - BlackBoxComposeMPO, ) from .cross_maxvol import cross_maxvol, CrossStrategyMaxvol from .cross_dmrg import cross_dmrg, CrossStrategyDMRG +from .cross_greedy import cross_greedy, CrossStrategyGreedy __all__ = [ "BlackBoxLoadMPS", + "BlackBoxLoadTT", "BlackBoxLoadMPO", "BlackBoxComposeMPS", - "BlackBoxComposeMPO", "cross_maxvol", "cross_dmrg", + "cross_greedy", "CrossStrategyMaxvol", "CrossStrategyDMRG", + "CrossStrategyGreedy", ] diff --git a/src/seemps/analysis/cross/black_box.py b/src/seemps/analysis/cross/black_box.py index da2c4c9..f7aa557 100644 --- a/src/seemps/analysis/cross/black_box.py +++ b/src/seemps/analysis/cross/black_box.py @@ -5,15 +5,14 @@ from ..mesh import Interval, Mesh, mps_to_mesh_matrix from ..sampling import evaluate_mps from ...state import MPS -from ...mpo import MPO class BlackBox(ABC): """ Abstract base class representing generic black-box functions. - These are generic objects that can be evaluated by indexing them with indices - similarly as a multidimensional array or a Mesh object. They serve as arguments for the - tensor cross-interpolation algorithms. + A black-box function represents an implicit representation of a function + that can be indexed with indices similarly as a multidimensional array. + These objects are fundamental for the efficient implementation of TCI algorithms. """ base: int @@ -32,8 +31,43 @@ def __getitem__(self, mps_indices: np.ndarray) -> np.ndarray: ... class BlackBoxLoadMPS(BlackBox): """ - Black-box representing the quantization of a multivariate function discretized on a Mesh - with a given base and mps_order. Used to load the black-box function in a MPS. + Black-box representing a multivariate scalar function discretized on an `Interval` or + `Mesh` object. Each function degree of freedom is quantized in a given `base` and assigned + a collection of MPS tensors. If the function is multivariate, the tensors are arranged + according to the `mps_order`. + + Parameters + ---------- + func : Callable + The multivariate scalar function to be represented as MPS. + domain : Union[Interval, Mesh] + The domain where the function is discretized. + base : int, default=2 + The required base or physical dimension of the MPS. + mps_order : str, default='A' + The order of the qubits of the MPS, either 'serial' ('A') or 'interleaved ('B'). + + Example + ------- + .. code-block:: python + + # Load a bivariate Gaussian function using some TCI variant. + + # Define the tensorized function following the convention of having the dimension index first. + func = lambda tensor: np.exp(-(tensor[0]**2 + tensor[1]**2)) + + # Define the bivariate domain implictly using `Interval` and `Mesh` + start, stop = -1, 1 + n_qubits = 10 + interval = RegularInterval(start, stop, 2**n_qubits) + mesh = Mesh([interval, interval]) + + # Define the black box. + black_box = BlackBoxLoadMPS(func, mesh) + + # Load the function in the given domain using some TCI variant (e.g. DMRG, Maxvol or Greedy). + cross_results = cross_X(black_box) + mps = cross_results.mps """ def __init__( @@ -44,7 +78,7 @@ def __init__( mps_order: str = "A", ): super().__init__(func) - self.mesh = Mesh([domain]) if isinstance(domain, Interval) else domain + self.mesh = Mesh([domain]) if not isinstance(domain, Mesh) else domain self.base = base self.mps_order = mps_order @@ -64,20 +98,107 @@ def __init__( def __getitem__(self, mps_indices: np.ndarray) -> np.ndarray: self.evals += len(mps_indices) - # TODO: The transpose is necessary here because the mesh convention (dimension index last) - # and the cross convention (dimension index first) are opposite. This should be fixed. + # Transpose because of opposite conventions for mesh (dimension index last) + # and cross (dimension index first). return self.func(self.mesh[mps_indices @ self.map_matrix].T) # type: ignore +class BlackBoxLoadTT(BlackBox): + """ + Black-box representing a multivariate scalar function discretized on a `Mesh` object + following the tensor-train structure. Each function degree of freedom is assigned to + one of each TT tensors. + + Parameters + ---------- + func : Callable + The multivariate scalar function to be represented as MPS. + mesh : Mesh + The domain where the function is discretized. + + Example + ------- + .. code-block:: python + + # Load a bivariate Gaussian function using some TCI variant. + + # Define the tensorized function following the convention of having the dimension index first. + func = lambda tensor: np.exp(-(tensor[0]**2 + tensor[1]**2)) + + # Define the bivariate domain implictly using `Interval` and `Mesh` + start, stop = -1, 1 + nodes = 1000 + interval = RegularInterval(start, stop, nodes) + mesh = Mesh([interval, interval]) + + # Define the black box. + black_box = BlackBoxLoadTT(func, mesh) + + # Load the function in the given domain using some TCI variant (e.g. DMRG, Maxvol or Greedy). + cross_results = cross_X(black_box) + tensor_train = cross_results.mps + """ + + def __init__( + self, + func: Callable, + mesh: Mesh, + ): + super().__init__(func) + self.mesh = mesh + self.base = np.inf # type: ignore + self.sites_per_dimension = [1 for _ in self.mesh.dimensions] + self.sites = sum(self.sites_per_dimension) + self.dimension = len(self.sites_per_dimension) + self.physical_dimensions = [interval.size for interval in self.mesh.intervals] + + def __getitem__(self, mps_indices: np.ndarray) -> np.ndarray: + self.evals += len(mps_indices) + return self.func(self.mesh[mps_indices].T) # type: ignore + + class BlackBoxLoadMPO(BlackBox): """ - Black-box representing the quantization of a multivariate function discretized on a Mesh - with a given base and mps_order. Used to load the black-box function in a MPO. + Black-box representing a 2-dimensional function discretized on a 2D `Mesh` + and quantized in a MPO with physical dimensions given by `base_mpo`. Can be + used to load operators in MPO using tensor cross-interpolation. In practice, + this object is equivalently represented as a MPS with physical dimensions + of size `base_mpo**2`, whose indices can be subsequently split to form + the required MPO. + + Parameters + ---------- + func : Callable + The bivariate scalar function to be represented as MPO. + mesh : Mesh + The two-dimensional discretization where the function is discretized. + base_mpo : int, default=2 + The required physical dimension of each index of the MPO. + is_diagonal : bool, default=True + Flag that helps in the convergence of TCI for diagonal operators by restricting + the convergence evaluation to the main diagonal. + + Example + ------- + .. code-block:: python + + # Load a 2D Gaussian function in a non-diagonal MPO using some TCI variant. + + # Define the tensorized function following the convention of having the dimension index first. + func = lambda tensor: np.exp(-(tensor[0]**2 + tensor[1]**2)) + + # Define the bivariate domain implictly using `Interval` and `Mesh`. + start, stop = -1, 1 + num_qubits = 10 + interval = RegularInterval(start, stop, 2**n) + mesh = Mesh([interval, interval]) - As opposed to BlackBoxMesh2MPS, this class represents an operator by assigning - pairs of variables to the operator rows and columns. At the moment it only works - for univariate MPOs, that is, for bivariate functions f(x, y) and bivariate meshes - Mesh([interval_x, interval_y]) representing the individual elements of the operator. + # Define the black box. + black_box = BlackBoxLoadMPO(func, mesh) + + # Load the function in the given domain using some tci variant (e.g. DMRG, Maxvol or Greedy). + cross_results = cross_X(black_box) + mpo = mps_as_mpo(cross_results.mps) # Unfold into a MPO. """ # TODO: Generalize for multivariate MPOs. @@ -86,41 +207,32 @@ def __init__( func: Callable, mesh: Mesh, base_mpo: int = 2, - mpo_order: str = "A", is_diagonal: bool = False, ): super().__init__(func) self.mesh = mesh self.base_mpo = base_mpo - self.mpo_order = mpo_order self.is_diagonal = is_diagonal - # Check if the mesh is bivariate (representing a 1d MPO) if not (mesh.dimension == 2 and mesh.dimensions[0] == mesh.dimensions[1]): raise ValueError("The mesh must be bivariate for a 1d MPO") - - # Check if the mesh can be quantized with the given base self.sites = int(np.emath.logn(self.base_mpo, mesh.dimensions[0])) if not self.base_mpo**self.sites == mesh.dimensions[0]: raise ValueError(f"The mesh cannot be quantized with base {self.base_mpo}") - # Define the structure of the equivalent MPS self.base = base_mpo**2 self.dimension = 1 self.physical_dimensions = [self.base] * self.sites self.sites_per_dimension = [self.sites] - - # If the MPO is diagonal, restrict the randomly sampled indices for evaluating - # the error to the diagonal (s_i = s_j) => s = i*s + i, i = 0, 1, ..., s-1 - self.allowed_sampling_indices = ( - [s * base_mpo + s for s in range(base_mpo)] if self.is_diagonal else None - ) - - # Compute the transformation matrix (for the MPO indices with base_mpo) self.map_matrix = mps_to_mesh_matrix( self.sites_per_dimension, base=self.base_mpo ) + # If the MPO is diagonal, restrict the allowed indices for random sampling to the main diagonal. + self.allowed_indices = ( + [s * base_mpo + s for s in range(base_mpo)] if self.is_diagonal else None + ) + def __getitem__(self, mps_indices: np.ndarray) -> np.ndarray: self.evals += len(mps_indices) row_indices = (mps_indices // self.base_mpo) @ self.map_matrix @@ -131,14 +243,44 @@ def __getitem__(self, mps_indices: np.ndarray) -> np.ndarray: class BlackBoxComposeMPS(BlackBox): """ - Black-box representing the composition of a scalar function on a collection of MPS. - The function must act on the list of MPS and these must be of same physical dimensions. + Black-box representing the composition of a multivariate scalar function with + a collection of MPS objects. + + Parameters + ---------- + func : Callable + The function to compose with the collection of MPS objects. Must be + scalar, and each of its degrees of freedom must refer to each of the MPS + in the `mps_list` collection. For example, `f(x, y) = sin(x + y**2)` + acts on two MPS representing respectively `x` and `y`. + mps_list : list[MPS] + A list of MPS of the same physical dimension, to be composed with `func`. + Their physical dimensions are assumed similar and constant. The number of MPS + must match the dimension of `func`. + + Example + ------- + .. code-block:: python + + # Use TCI to compose a three-dimensional function with three MPS. + + # Assume the three initial MPS are given and are of the same structure. + mps_0, mps_1, mps_2 = ... + + # Define the three dimensional function by its action on the MPS. + func = lambda v: v[0]**2 + np.sin(v[0]*v[1]) + np.cos(v[0]*v[2]) + + # Define the black-box. + black_box = BlackBoxComposeMPS(func, [mps_0, mps_1, mps_2]) + + # Compose the three MPS with the function `func`. + cross_results = cross_X(black_box) + mps = cross_results.mps """ def __init__(self, func: Callable, mps_list: list[MPS]): super().__init__(func) - # Assert that the physical dimensions are the same for all MPS self.physical_dimensions = mps_list[0].physical_dimensions() for mps in mps_list: if mps.physical_dimensions() != self.physical_dimensions: @@ -156,20 +298,3 @@ def __getitem__(self, mps_indices: np.ndarray) -> np.ndarray: for mps in self.mps_list: mps_values.append(evaluate_mps(mps, mps_indices)) return self.func(mps_values) - - -class BlackBoxComposeMPO(BlackBox): - """ - Black-box representing the composition of a scalar function on a collection of MPO. - This is actually a good application of MPO Chebyshev approximation. - - Note: The function of a matrix is not equivalent to the function of its elements, so this cannot be - performed in a straightforward manner similarly as BlackBoxMPS. - Possible alternatives are methods such as: - - Lagrange-Sylvester interpolation (requires eigenvalues). - - Cauchy contour integral formula. - etc. - """ - - def __init__(self, func: Callable, mpo_list: MPO): - raise NotImplementedError diff --git a/src/seemps/analysis/cross/cross.py b/src/seemps/analysis/cross/cross.py index 2db1bba..deb394e 100644 --- a/src/seemps/analysis/cross/cross.py +++ b/src/seemps/analysis/cross/cross.py @@ -1,10 +1,10 @@ import numpy as np +import scipy.linalg import dataclasses import functools from typing import Optional from copy import deepcopy -from scipy.linalg import lu, solve_triangular # type: ignore from .black_box import BlackBox from ..sampling import evaluate_mps, random_mps_indices @@ -20,8 +20,7 @@ class CrossStrategy: tol_sampling: float = 1e-10 norm_sampling: float = np.inf num_samples: int = 1000 - check_norm_2: bool = False - tol_norm_2: float = 1e-10 + tol_norm_2: Optional[float] = None rng: np.random.Generator = dataclasses.field( default_factory=lambda: np.random.default_rng() ) @@ -34,16 +33,15 @@ class CrossStrategy: Maximum number of sweeps allowed. maxbond : int, default=1000 Maximum MPS bond dimension allowed. - tol_sampling : float, default=1e-10 + tol_sampling : float, default=1e-12 Tolerance for the sampled error. norm_sampling : float, default=np.inf Norm used to measure the sampled error. num_samples : int, default=1000 Number of function samples to evaluate the error. - check_norm_2 : bool, default=True - Whether to check the change in norm-2 of the MPS after each sweep. - tol_norm_2 : float, default=1e-10 - Tolerance for the change in norm-2 of the MPS. + tol_norm_2 : float, optional + Tolerance for the increment in norm-2 of the MPS after each sweep. + If None, this increment is not measured. rng : np.random.Generator, default=np.random.default_rng() Random number generator used to initialize the algorithm and sample the error. """ @@ -51,28 +49,56 @@ class CrossStrategy: @dataclasses.dataclass class CrossResults: + """ + Dataclass containing the results from TCI. + + Parameters + ---------- + mps : MPS + The resulting MPS interpolation of the black-box function. + evals : int + The number of function evaluations required for the interpolation. + points : np.ndarray + The indices of the discretization points whose multivariate crosses yield + the interpolation. + callback_output : VectorLike, optional + An array collecting the results of the callback function, called at each iteration. + trajectories : VectorLike, optional + A collection of arrays containing information of the interpolation for each iteration. + """ + mps: MPS evals: int + points: np.ndarray + callback_output: Optional[VectorLike] = None trajectories: Optional[VectorLike] = None class CrossInterpolation: + """Auxiliar base class for TCI used to keep track of the required + interpolation information.""" + def __init__( self, black_box: BlackBox, - initial_point: np.ndarray, + initial_points: np.ndarray, ): self.black_box = black_box self.sites = black_box.sites - self.I_l, self.I_g = self.points_to_indices(initial_point) + self.I_l, self.I_g = self.points_to_indices(initial_points) self.I_s = [np.arange(s).reshape(-1, 1) for s in black_box.physical_dimensions] # Placeholders self.mps = random_mps(black_box.physical_dimensions) self.previous_mps: MPS = deepcopy(self.mps) self.previous_error: float = np.inf - self.sample_indices: Optional[np.ndarray] = None + self.mps_indices: Optional[np.ndarray] = None self.func_samples: Optional[np.ndarray] = None + def sample_fiber(self, k: int) -> np.ndarray: + i_l, i_s, i_g = self.I_l[k], self.I_s[k], self.I_g[k] + mps_indices = self.combine_indices(i_l, i_s, i_g) + return self.black_box[mps_indices].reshape((len(i_l), len(i_s), len(i_g))) + def sample_error( self, num_samples: int, @@ -80,24 +106,60 @@ def sample_error( allowed_indices: Optional[list[int]] = None, rng: np.random.Generator = np.random.default_rng(), ) -> float: - if self.sample_indices is None: - self.sample_indices = random_mps_indices( - self.mps, + if self.mps_indices is None: + self.mps_indices = random_mps_indices( + self.mps.physical_dimensions(), num_indices=num_samples, allowed_indices=allowed_indices, rng=rng, ) if self.func_samples is None: - self.func_samples = self.black_box[self.sample_indices].reshape(-1) - mps_samples = evaluate_mps(self.mps, self.sample_indices) + self.func_samples = self.black_box[self.mps_indices].reshape(-1) + mps_samples = evaluate_mps(self.mps, self.mps_indices) error = np.linalg.norm(self.func_samples - mps_samples, ord=norm_error) # type: ignore prefactor = np.prod(self.func_samples.shape) ** (1 / norm_error) return error / prefactor # type: ignore - def norm_2_change(self) -> float: - change_norm = (self.mps - self.previous_mps).norm() / self.previous_mps.norm() + def norm_2_increment(self) -> float: + norm_increment = ( + (self.mps - self.previous_mps).norm() / self.previous_mps.norm() + ) ** 2 self.previous_mps = deepcopy(self.mps) - return change_norm + return norm_increment + + def indices_to_points(self, forward: bool) -> np.ndarray: + """ + Computes the MPS 'points' that result in the best TCI approximation. + This is done performing a sweep with the square maxvol decomposition. + """ + if forward: + for k in range(self.sites): + fiber = self.sample_fiber(k) + r_l, s, r_g = fiber.shape + C = fiber.reshape(r_l * s, r_g) + Q, _ = np.linalg.qr(C) + I, _ = maxvol_square(Q) + if k < self.sites - 1: + self.I_l[k + 1] = self.combine_indices(self.I_l[k], self.I_s[k])[I] + else: + indices = self.I_l[1:] + [ + self.combine_indices(self.I_l[k], self.I_s[k])[I] + ] + else: + for k in reversed(range(self.sites)): + fiber = self.sample_fiber(k) + r_l, s, r_g = fiber.shape + R = fiber.reshape(r_l, s * r_g) + Q, _ = np.linalg.qr(R.T) + I, _ = maxvol_square(Q) + if k > 0: + self.I_g[k - 1] = self.combine_indices(self.I_s[k], self.I_g[k])[I] + else: + indices = [ + self.combine_indices(self.I_s[0], self.I_g[0])[I] + ] + self.I_g[:-1] + # TODO: Get points from indices + return np.array([]) @staticmethod def points_to_indices(points: np.ndarray) -> tuple[list, list]: @@ -112,7 +174,7 @@ def points_to_indices(points: np.ndarray) -> tuple[list, list]: def combine_indices(*indices: np.ndarray) -> np.ndarray: """ Computes the Cartesian product of a set of multi-indices arrays and arranges the - result as concatenated indices in C order. + result as concatenated indices in C order (column-major). Parameters ---------- @@ -128,8 +190,7 @@ def combine_indices(*indices: np.ndarray) -> np.ndarray: [4, 5, 6, 1]]) """ - # TODO: Compute a collection of rows of the cartesian product directly without first - # computing the whole cartesian product. + # TODO: Avoid computing the whole cartesian product. def cartesian(A: np.ndarray, B: np.ndarray) -> np.ndarray: A_repeated = np.repeat(A, repeats=B.shape[0], axis=0) B_tiled = np.tile(B, (A.shape[0], 1)) @@ -150,9 +211,9 @@ def maxvol_square( ---------- A : np.ndarray A tall (n x r) matrix with more rows than columns (n > r). - maxiter : int, default = 100 + maxiter : int, default=100 Maximum number of iterations allowed. - tol : float, default = 1.05 + tol : float, default=1.1 Sensibility of the algorithm. Returns @@ -166,10 +227,10 @@ def maxvol_square( if n <= r: I, B = np.arange(n, dtype=int), np.eye(n) return I, B - P, L, U = lu(A, check_finite=False) # type: ignore + P, L, U = scipy.linalg.lu(A, check_finite=False) # type: ignore I = P[:, :r].argmax(axis=0) - Q = solve_triangular(U, A.T, trans=1, check_finite=False) - B = solve_triangular( + Q = scipy.linalg.solve_triangular(U, A.T, trans=1, check_finite=False) + B = scipy.linalg.solve_triangular( L[:r, :], Q, trans=1, check_finite=False, unit_diagonal=True, lower=True ).T for _ in range(maxiter): @@ -190,27 +251,24 @@ def _check_convergence( cross_strategy: CrossStrategy, logger: Logger, ) -> bool: - allowed_sampling_indices = getattr( - cross.black_box, "allowed_sampling_indices", None - ) error = cross.sample_error( cross_strategy.num_samples, cross_strategy.norm_sampling, - allowed_indices=allowed_sampling_indices, + allowed_indices=getattr(cross.black_box, "allowed_indices", None), rng=cross_strategy.rng, ) - # TODO: Use max_bond_dimension() from MPS maxbond = cross.mps.max_bond_dimension() + evals = cross.black_box.evals - cross_strategy.num_samples # subtract error evals if logger: logger( f"Cross sweep {1+sweep:3d} with error({cross_strategy.num_samples} samples " - f"in norm-{cross_strategy.norm_sampling})={error}, maxbond={maxbond}, evals(cumulative)={cross.black_box.evals}" + f"in norm-{cross_strategy.norm_sampling})={error}, maxbond={maxbond}, evals(cumulative)={evals}" ) - if cross_strategy.check_norm_2: - change_norm = cross.norm_2_change() - logger(f"Norm-2 change {change_norm}") - if change_norm <= cross_strategy.tol_norm_2: - logger(f"Stationary state reached with norm-2 change {change_norm}") + if cross_strategy.tol_norm_2 is not None: + norm_increment = cross.norm_2_increment() + logger(f"Norm-2 increment {norm_increment}") + if norm_increment <= cross_strategy.tol_norm_2: + logger(f"Stationary state reached with norm-2 increment {norm_increment}") return True if error < cross_strategy.tol_sampling: logger(f"State converged within tolerance {cross_strategy.tol_sampling}") diff --git a/src/seemps/analysis/cross/cross_dmrg.py b/src/seemps/analysis/cross/cross_dmrg.py index 06c7c4c..805e543 100644 --- a/src/seemps/analysis/cross/cross_dmrg.py +++ b/src/seemps/analysis/cross/cross_dmrg.py @@ -1,5 +1,7 @@ import numpy as np +import scipy.linalg from dataclasses import dataclass +from typing import Optional, Callable from .cross import ( BlackBox, @@ -9,9 +11,9 @@ maxvol_square, _check_convergence, ) +from ..sampling import random_mps_indices from ...state import Strategy, DEFAULT_TOLERANCE -from ...state._contractions import _contract_last_and_first -from ...state.schmidt import _destructive_svd +from ...state.schmidt import svd from ...state.core import destructively_truncate_vector from ...truncate import SIMPLIFICATION_STRATEGY from ...tools import make_logger @@ -22,48 +24,40 @@ simplification_tolerance=DEFAULT_TOLERANCE**2, ) +# TODO: Implement local error evaluation + @dataclass class CrossStrategyDMRG(CrossStrategy): - maxvol_tol: float = 1.1 - maxvol_maxiter: int = 100 strategy: Strategy = DEFAULT_CROSS_STRATEGY + tol_maxvol_square: float = 1.05 + maxiter_maxvol_square: int = 10 """ - Dataclass containing the parameters for the rectangular maxvol-based TCI. + Dataclass containing the parameters for the DMRG-based TCI. The common parameters are documented in the base `CrossStrategy` class. Parameters ---------- - maxvol_tol : float, default = 1.1 - Sensibility for the square maxvol decomposition. - maxvol_maxiter : int, default = 100 - Maximum number of iterations for the square maxvol decomposition. - strategy : Strategy, default = DEFAULT_CROSS_STRATEGY + strategy : Strategy, default=DEFAULT_CROSS_STRATEGY Simplification strategy used at the truncation of Schmidt values at each SVD split of the DMRG superblocks. + tol_maxvol_square : float, default=1.05 + Sensibility for the square maxvol decomposition. + maxiter_maxvol_square : int, default=10 + Maximum number of iterations for the square maxvol decomposition. """ -class CrossInterpolationDMRG(CrossInterpolation): - def __init__(self, black_box: BlackBox, initial_point: np.ndarray): - super().__init__(black_box, initial_point) - - def sample_superblock(self, k: int) -> np.ndarray: - i_l, i_g = self.I_l[k], self.I_g[k + 1] - i_s1, i_s2 = self.I_s[k], self.I_s[k + 1] - mps_indices = self.combine_indices(i_l, i_s1, i_s2, i_g) - return self.black_box[mps_indices].reshape( - (len(i_l), len(i_s1), len(i_s2), len(i_g)) - ) - - def cross_dmrg( black_box: BlackBox, cross_strategy: CrossStrategyDMRG = CrossStrategyDMRG(), + initial_points: Optional[np.ndarray] = None, + callback: Optional[Callable] = None, ) -> CrossResults: """ Computes the MPS representation of a black-box function using the tensor cross-approximation (TCI) algorithm based on two-site optimizations in a DMRG-like manner. + The black-box function can represent several different structures. See `black_box` for usage examples. Parameters ---------- @@ -71,32 +65,70 @@ def cross_dmrg( The black box to approximate as a MPS. cross_strategy : CrossStrategy, default=CrossStrategy() A dataclass containing the parameters of the algorithm. + initial_points : np.ndarray, optional + A collection of initial points used to initialize the algorithm. + If None, an initial random point is used. + callback : Callable, optional + A callable called on the MPS after each iteration. + The output of the callback is included in a list 'callback_output' in CrossResults. Returns ------- - mps : MPS - The MPS representation of the black-box function. + CrossResults + A dataclass containing the MPS representation of the black-box function, + among other useful information. """ - initial_point = cross_strategy.rng.integers( - low=0, high=black_box.base, size=black_box.sites - ) - cross = CrossInterpolationDMRG(black_box, initial_point) + if initial_points is None: + initial_points = random_mps_indices( + black_box.physical_dimensions, + num_indices=1, + allowed_indices=getattr(black_box, "allowed_indices", None), + rng=cross_strategy.rng, + ) + + cross = CrossInterpolationDMRG(black_box, initial_points) converged = False + callback_output = [] with make_logger(2) as logger: for i in range(cross_strategy.maxiter): # Forward sweep + direction = True for k in range(cross.sites - 1): - _update_dmrg(cross, k, True, cross_strategy) + _update_dmrg(cross, k, direction, cross_strategy) + if callback: + callback_output.append(callback(cross.mps, logger=logger)) if converged := _check_convergence(cross, i, cross_strategy, logger): break # Backward sweep + direction = False for k in reversed(range(cross.sites - 1)): - _update_dmrg(cross, k, False, cross_strategy) + _update_dmrg(cross, k, direction, cross_strategy) + if callback: + callback_output.append(callback(cross.mps, logger=logger)) if converged := _check_convergence(cross, i, cross_strategy, logger): break if not converged: logger("Maximum number of TT-Cross iterations reached") - return CrossResults(mps=cross.mps, evals=black_box.evals) + points = cross.indices_to_points(direction) + return CrossResults( + mps=cross.mps, + points=points, + evals=black_box.evals, + callback_output=callback_output, + ) + + +class CrossInterpolationDMRG(CrossInterpolation): + def __init__(self, black_box: BlackBox, initial_point: np.ndarray): + super().__init__(black_box, initial_point) + + def sample_superblock(self, k: int) -> np.ndarray: + i_l, i_g = self.I_l[k], self.I_g[k + 1] + i_s1, i_s2 = self.I_s[k], self.I_s[k + 1] + mps_indices = self.combine_indices(i_l, i_s1, i_s2, i_g) + return self.black_box[mps_indices].reshape( + (len(i_l), len(i_s1), len(i_s2), len(i_g)) + ) def _update_dmrg( @@ -108,31 +140,35 @@ def _update_dmrg( superblock = cross.sample_superblock(k) r_l, s1, s2, r_g = superblock.shape A = superblock.reshape(r_l * s1, s2 * r_g) - ## SVD - U, S, V = _destructive_svd(A) + ## Non-destructive SVD + U, S, V = svd(A, check_finite=False) destructively_truncate_vector(S, cross_strategy.strategy) r = S.size U, S, V = U[:, :r], np.diag(S), V[:r, :] ## if forward: - C = U.reshape(r_l * s1, r) - Q, T = np.linalg.qr(C) - I, G = maxvol_square( - Q, cross_strategy.maxvol_maxiter, cross_strategy.maxvol_tol - ) - cross.I_l[k + 1] = cross.combine_indices(cross.I_l[k], cross.I_s[k])[I] - cross.mps[k] = G.reshape(r_l, s1, r) - if k == cross.sites - 2: - cross.mps[k] = _contract_last_and_first(cross.mps[k], Q[I] @ T) + if k < cross.sites - 2: + C = U.reshape(r_l * s1, r) + Q, _ = scipy.linalg.qr(C, mode="economic", overwrite_a=True, check_finite=False) # type: ignore + I, G = maxvol_square( + Q, cross_strategy.maxiter_maxvol_square, cross_strategy.tol_maxvol_square # type: ignore + ) + cross.I_l[k + 1] = cross.combine_indices(cross.I_l[k], cross.I_s[k])[I] + cross.mps[k] = G.reshape(r_l, s1, r) + else: + cross.mps[k] = U.reshape(r_l, s1, r) cross.mps[k + 1] = (S @ V).reshape(r, s2, r_g) else: - R = V.reshape(r, s2 * r_g) - Q, T = np.linalg.qr(R.T) - I, G = maxvol_square( - Q, cross_strategy.maxvol_maxiter, cross_strategy.maxvol_tol - ) - cross.mps[k + 1] = (G.T).reshape(r, s2, r_g) - cross.I_g[k] = cross.combine_indices(cross.I_s[k + 1], cross.I_g[k + 1])[I] - if k == 0: + if k > 0: + R = V.reshape(r, s2 * r_g) + Q, _ = scipy.linalg.qr( # type: ignore + R.T, mode="economic", overwrite_a=True, check_finite=False + ) + I, G = maxvol_square( + Q, cross_strategy.maxiter_maxvol_square, cross_strategy.tol_maxvol_square # type: ignore + ) + cross.I_g[k] = cross.combine_indices(cross.I_s[k + 1], cross.I_g[k + 1])[I] + cross.mps[k + 1] = (G.T).reshape(r, s2, r_g) + else: cross.mps[k] = (U @ S).reshape(r_l, s1, r) - cross.mps[k + 1] = _contract_last_and_first((Q[I] @ T).T, cross.mps[k + 1]) + cross.mps[k + 1] = V.reshape(r, s2, r_g) diff --git a/src/seemps/analysis/cross/cross_greedy.py b/src/seemps/analysis/cross/cross_greedy.py new file mode 100644 index 0000000..a5d5173 --- /dev/null +++ b/src/seemps/analysis/cross/cross_greedy.py @@ -0,0 +1,346 @@ +import numpy as np +import scipy.linalg +from typing import TypeVar, Union, Optional, Callable +from dataclasses import dataclass + +from .cross import ( + CrossInterpolation, + CrossResults, + CrossStrategy, + BlackBox, + _check_convergence, +) +from ..sampling import random_mps_indices +from ...state import MPS +from ...state._contractions import _contract_last_and_first +from ...tools import make_logger, Logger + + +@dataclass +class CrossStrategyGreedy(CrossStrategy): + tol_pivot: float = 1e-10 + partial: bool = True + maxiter_partial: int = 5 + points_partial: int = 10 + """ + Dataclass containing parameters for TCI with greedy pivot updates. + Supplements the base `CrossStrategy` class. + + Parameters + ---------- + tol_pivot : float, default=1e-12 + Minimum allowable error for a pivot, excluding those below this threshold. + The algorithm halts when the maximum pivot error across all sites falls below this limit. + partial : bool, default=True + Whether to use a row-column alternating partial search strategy to find pivots in the superblock. + If False, performs a 'full search' that uses more function evaluations (O(chi) vs. O(chi^2)) but + can introduce potentially smaller errors. + maxiter_partial : int, default=5 + Number of row-column iterations in each partial search. + points_partial : int, default=10 + Number of initial random points used to initialize each partial search. + """ + + +def cross_greedy( + black_box: BlackBox, + cross_strategy: CrossStrategyGreedy = CrossStrategyGreedy(), + initial_points: Optional[np.ndarray] = None, + callback: Optional[Callable] = None, +) -> CrossResults: + """ + Computes the MPS representation of a black-box function using the tensor cross-approximation (TCI) + algorithm based on two-site optimizations following greedy updates of the pivot matrices. + The black-box function can represent several different structures. See `black_box` for usage examples. + + Parameters + ---------- + black_box : BlackBox + The black box to approximate as a MPS. + cross_strategy : CrossStrategy, default=CrossStrategy() + A dataclass containing the parameters of the algorithm. + initial_points : np.ndarray, optional + A collection of initial points used to initialize the algorithm. + If None, an initial random point is used. + callback : Callable, optional + A callable called on the MPS after each iteration. + The output of the callback is included in a list 'callback_output' in CrossResults. + + Returns + ------- + CrossResults + A dataclass containing the MPS representation of the black-box function, + among other useful information. + """ + if initial_points is None: + initial_points = random_mps_indices( + black_box.physical_dimensions, + num_indices=1, + allowed_indices=getattr(black_box, "allowed_indices", None), + rng=cross_strategy.rng, + ) + cross = CrossInterpolationGreedy(black_box, initial_points) + + if cross_strategy.partial == True: + update_method = _update_partial_search + else: + update_method = _update_full_search + + pivot_errors = np.zeros((black_box.sites - 1,)) + converged = False + callback_output = [] + with make_logger(2) as logger: + for i in range(cross_strategy.maxiter): + # Forward sweep + direction = True + for k in range(cross.sites - 1): + pivot_errors[k] = update_method(cross, k, cross_strategy) + if callback: + callback_output.append(callback(cross.mps, logger=logger)) + if converged := ( + _check_convergence(cross, i, cross_strategy, logger) + or _check_local_convergence(pivot_errors, cross_strategy, logger) + ): + break + # Backward sweep + direction = False + for k in reversed(range(cross.sites - 1)): + pivot_errors[k] = update_method(cross, k, cross_strategy) + if callback: + callback_output.append(callback(cross.mps, logger=logger)) + if converged := ( + _check_convergence(cross, i, cross_strategy, logger) + or _check_local_convergence(pivot_errors, cross_strategy, logger) + ): + break + if not converged: + logger("Maximum number of TT-Cross iterations reached") + points = cross.indices_to_points(direction) + return CrossResults( + mps=cross.mps, + points=points, + evals=black_box.evals, + callback_output=callback_output, + ) + + +class CrossInterpolationGreedy(CrossInterpolation): + def __init__(self, black_box: BlackBox, initial_point: np.ndarray): + super().__init__(black_box, initial_point) + self.fibers = [self.sample_fiber(k) for k in range(self.sites)] + self.Q_factors = [] + self.R_matrices = [] + for fiber in self.fibers[:-1]: + Q, R = self.fiber_to_QR(fiber) + self.Q_factors.append(Q) + self.R_matrices.append(R) + + ## Translate the initial multiindices I_l and I_g to integer indices J_l and J_g + ## TODO: Refactor + def get_row_indices(rows, all_rows): + large_set = {tuple(row): idx for idx, row in enumerate(all_rows)} + return np.array([large_set[tuple(row)] for row in rows]) + + J_l = [] + J_g = [] + for k in range(self.sites - 1): + i_l = self.combine_indices(self.I_l[k], self.I_s[k]) + J_l.append(get_row_indices(self.I_l[k + 1], i_l)) + i_g = self.combine_indices(self.I_l[k], self.I_s[k]) + J_g.append(get_row_indices(self.I_l[k + 1], i_g)) + self.J_l = [np.array([])] + J_l # add empty indices to respect convention + self.J_g = J_g[::-1] + [np.array([])] + ## + + G_cores = [self.Q_to_G(Q, j_l) for Q, j_l in zip(self.Q_factors, self.J_l[1:])] + self.mps = MPS(G_cores + [self.fibers[-1]]) + + _Index = TypeVar("_Index", bound=Union[np.intp, np.ndarray, slice]) + + def sample_superblock( + self, k: int, j_l: _Index = slice(None), j_g: _Index = slice(None) + ) -> np.ndarray: + i_ls = self.combine_indices(self.I_l[k], self.I_s[k])[j_l] + i_ls = i_ls.reshape(1, -1) if i_ls.ndim == 1 else i_ls # Prevent collapse to 1D + i_sg = self.combine_indices(self.I_s[k + 1], self.I_g[k + 1])[j_g] + i_sg = i_sg.reshape(1, -1) if i_sg.ndim == 1 else i_sg + mps_indices = self.combine_indices(i_ls, i_sg) + return self.black_box[mps_indices].reshape((len(i_ls), len(i_sg))) + + def sample_skeleton( + self, k: int, j_l: _Index = slice(None), j_g: _Index = slice(None) + ) -> np.ndarray: + r_l, r_s1, chi = self.mps[k].shape + chi, r_s2, r_g = self.fibers[k + 1].shape + G = self.mps[k].reshape(r_l * r_s1, chi)[j_l] + R = self.fibers[k + 1].reshape(chi, r_s2 * r_g)[:, j_g] + return _contract_last_and_first(G, R) + + def update_indices(self, k: int, j_l: _Index, j_g: _Index) -> None: + i_l = self.combine_indices(self.I_l[k], self.I_s[k])[j_l] + i_g = self.combine_indices(self.I_s[k + 1], self.I_g[k + 1])[j_g] + self.I_l[k + 1] = np.vstack((self.I_l[k + 1], i_l)) + self.J_l[k + 1] = np.append(self.J_l[k + 1], j_l) # type: ignore + self.I_g[k] = np.vstack((self.I_g[k], i_g)) + self.J_g[k] = np.append(self.J_g[k], j_g) # type: ignore + + def update_tensors( + self, + k: int, + r: np.ndarray, + c: np.ndarray, + ) -> None: + # Update left fiber, Q-factor and MPS site + r_l, r_s1, chi = self.fibers[k].shape + C = self.fibers[k].reshape(r_l * r_s1, chi) + self.fibers[k] = np.hstack((C, c.reshape(-1, 1))).reshape(r_l, r_s1, chi + 1) + + QR_INSERT = False # TODO: Check why it is unstable + if QR_INSERT: + Q = self.Q_factors[k].reshape(r_l * r_s1, chi) + Q, self.R_matrices[k] = scipy.linalg.qr_insert( + Q, + self.R_matrices[k], + u=c, + k=Q.shape[1], + which="col", + rcond=None, + check_finite=False, + ) + self.Q_factors[k] = Q.reshape(r_l, r_s1, chi + 1) + else: + self.Q_factors[k], self.R_matrices[k] = self.fiber_to_QR(self.fibers[k]) + self.mps[k] = self.Q_to_G(self.Q_factors[k], self.J_l[k + 1]) + + # Update right fiber, Q-factor and MPS site + chi, r_s2, r_g = self.fibers[k + 1].shape + R = self.fibers[k + 1].reshape(chi, r_s2 * r_g) + self.fibers[k + 1] = np.vstack((R, r)).reshape(chi + 1, r_s2, r_g) + if k < self.sites - 2: + if QR_INSERT: + Q = self.Q_factors[k + 1].reshape(chi * r_s2, r_g) + Q, self.R_matrices[k + 1] = scipy.linalg.qr_insert( + Q, + self.R_matrices[k + 1], + u=r.reshape(-1, Q.shape[1]), + k=Q.shape[0], + which="row", + check_finite=False, + ) + self.Q_factors[k + 1] = Q.reshape(chi + 1, r_s2, r_g) + else: + self.Q_factors[k + 1], self.R_matrices[k + 1] = self.fiber_to_QR( + self.fibers[k + 1] + ) + self.mps[k + 1] = self.Q_to_G(self.Q_factors[k + 1], self.J_l[k + 2]) + else: + self.mps[k + 1] = self.fibers[k + 1] + + @staticmethod + def fiber_to_QR(fiber: np.ndarray) -> tuple[np.ndarray, np.ndarray]: + """Performs the QR decomposition of a fiber.""" + r_l, r_s, r_g = fiber.shape + Q, R = scipy.linalg.qr( # type: ignore + fiber.reshape(r_l * r_s, r_g), mode="economic", check_finite=False + ) + Q_factor = Q.reshape(r_l, r_s, r_g) # type: ignore + return Q_factor, R + + @staticmethod + def Q_to_G(Q_factor: np.ndarray, j_l: np.ndarray) -> np.ndarray: + """Transforms a Q-factor into a MPS tensor core G.""" + r_l, r_s, r_g = Q_factor.shape + Q = Q_factor.reshape(r_l * r_s, r_g) + P = scipy.linalg.inv(Q[j_l], check_finite=False) + G = _contract_last_and_first(Q, P) + return G.reshape(r_l, r_s, r_g) + + +def _update_full_search( + cross: CrossInterpolationGreedy, + k: int, + cross_strategy: CrossStrategyGreedy, +) -> float: + max_pivots = cross.black_box.base ** (1 + min(k, cross.sites - (k + 2))) + if len(cross.I_g[k]) >= max_pivots or len(cross.I_l[k + 1]) >= max_pivots: + return 0 + + A = cross.sample_superblock(k) + B = cross.sample_skeleton(k) + + error_function = lambda A, B: np.abs(A - B) + diff = error_function(A, B) + j_l, j_g = np.unravel_index(np.argmax(diff), A.shape) + pivot_error = diff[j_l, j_g] + + if pivot_error >= cross_strategy.tol_pivot: + cross.update_indices(k, j_l=j_l, j_g=j_g) + cross.update_tensors(k, r=A[j_l, :], c=A[:, j_g]) + + return pivot_error + + +def _update_partial_search( + cross: CrossInterpolationGreedy, + k: int, + cross_strategy: CrossStrategyGreedy, +) -> float: + max_pivots = cross.black_box.base ** (1 + min(k, cross.sites - (k + 2))) + if len(cross.I_g[k]) >= max_pivots or len(cross.I_l[k + 1]) >= max_pivots: + return 0 + + j_l_random = cross_strategy.rng.integers( + low=0, + high=len(cross.I_l[k]) * len(cross.I_s[k]), + size=cross_strategy.points_partial, + ) + j_g_random = cross_strategy.rng.integers( + low=0, + high=len(cross.I_s[k + 1]) * len(cross.I_g[k + 1]), + size=cross_strategy.points_partial, + ) + A_random = cross.sample_superblock(k, j_l=j_l_random, j_g=j_g_random) + B_random = cross.sample_skeleton(k, j_l=j_l_random, j_g=j_g_random) + + error_function = lambda A, B: np.abs(A - B) + diff = error_function(A_random, B_random) + i, j = np.unravel_index(np.argmax(diff), A_random.shape) + j_l, j_g = j_l_random[i], j_g_random[j] + + for iter in range(cross_strategy.maxiter_partial): + # Traverse column residual + c_A = cross.sample_superblock(k, j_g=j_g).reshape(-1) + c_B = cross.sample_skeleton(k, j_g=j_g) + new_j_l = np.argmax(error_function(c_A, c_B)) + if new_j_l == j_l and iter > 0: + break + j_l = new_j_l + + # Traverse row residual + r_A = cross.sample_superblock(k, j_l=j_l).reshape(-1) + r_B = cross.sample_skeleton(k, j_l=j_l) + new_j_g = np.argmax(error_function(r_A, r_B)) + if new_j_g == j_g: + break + j_g = new_j_g + pivot_error = error_function(c_A[j_l], c_B[j_l]) + + if pivot_error >= cross_strategy.tol_pivot: + cross.update_indices(k, j_l=j_l, j_g=j_g) + cross.update_tensors(k, r=r_A, c=c_A) + + return pivot_error + + +def _check_local_convergence( + pivot_errors: np.ndarray, + cross_strategy: CrossStrategyGreedy, + logger: Logger, +) -> bool: + max_pivot_error = np.max(pivot_errors) + if logger: + logger(f"Max. pivot error={max_pivot_error}") + if max_pivot_error < cross_strategy.tol_pivot: + logger(f"State converged within tolerance {cross_strategy.tol_pivot}") + return True + return False diff --git a/src/seemps/analysis/cross/cross_maxvol.py b/src/seemps/analysis/cross/cross_maxvol.py index 11799e5..7ec25aa 100644 --- a/src/seemps/analysis/cross/cross_maxvol.py +++ b/src/seemps/analysis/cross/cross_maxvol.py @@ -1,6 +1,8 @@ import numpy as np +import scipy.linalg # type: ignore import dataclasses import functools +from typing import Optional, Callable from .cross import ( CrossInterpolation, @@ -10,84 +12,48 @@ maxvol_square, _check_convergence, ) -from ...state._contractions import _contract_last_and_first +from ..sampling import random_mps_indices from ...tools import make_logger +# TODO: Implement local error evaluation + @dataclasses.dataclass class CrossStrategyMaxvol(CrossStrategy): - maxvol_tol: float = 1.1 - maxvol_maxiter: int = 100 - maxvol_rect_tol: float = 1.1 - maxvol_rect_rank_change: tuple = (1, np.inf) + rank_kick: tuple = (0, 1) + maxiter_maxvol_square: int = 10 + tol_maxvol_square: float = 1.05 + tol_maxvol_rect: float = 1.05 fortran_order: bool = True """ - Dataclass containing the parameters for the maxvol-based TCI. + Dataclass containing the parameters for the rectangular maxvol-based TCI. The common parameters are documented in the base `CrossStrategy` class. Parameters ---------- - maxvol_tol : float, default = 1.1 - Sensibility for the square maxvol decomposition. - maxvol_maxiter : int, default = 100 + rank_kick : tuple, default=(0, 1) + Minimum and maximum rank increase or 'kick' at each rectangular maxvol decomposition. + maxiter_maxvol_square : int, default=10 Maximum number of iterations for the square maxvol decomposition. - maxvol_rect_tol : float, default = 1.1 + tol_maxvol_square : float, default=1.05 + Sensibility for the square maxvol decomposition. + tol_maxvol_rect : float, default=1.05 Sensibility for the rectangular maxvol decomposition. - maxvol_rect_rank_change : tuple, default = (1, np.inf) - Minimum and maximum increase allowed for the bond dimension at each half sweep. - fortran_order: bool, default = True + fortran_order: bool, default=True Whether to use the Fortran order in the computation of the maxvol indices. - For some reason, the Fortran order converges better for some functions. """ -class CrossInterpolationMaxvol(CrossInterpolation): - def __init__(self, black_box: BlackBox, initial_point: np.ndarray): - super().__init__(black_box, initial_point) - - def sample_fiber(self, k: int) -> np.ndarray: - i_l, i_s, i_g = self.I_l[k], self.I_s[k], self.I_g[k] - mps_indices = self.combine_indices(i_l, i_s, i_g) - return self.black_box[mps_indices].reshape((len(i_l), len(i_s), len(i_g))) - - @staticmethod - def combine_indices_fortran(*indices: np.ndarray) -> np.ndarray: - """ - Computes the Cartesian product of a set of multi-indices arrays and arranges the - result as concatenated indices in Fortran order (column-major). - - Parameters - ---------- - indices : *np.ndarray - A variable number of arrays where each array is treated as a set of multi-indices. - fortran_order : bool, default=False - If True, the output is arranged in Fortran order where the first index changes fastest. - If False, the output is arranged in C order where the last index changes fastest. - - Example - ------- - >>> combine_indices(np.array([[1, 2, 3], [4, 5, 6]]), np.array([[0], [1]]), fortran_order=True) - array([[1, 2, 3, 0], - [4, 5, 6, 0], - [1, 2, 3, 1], - [4, 5, 6, 1]]) - """ - - def cartesian_fortran(A: np.ndarray, B: np.ndarray) -> np.ndarray: - A_tiled = np.tile(A, (B.shape[0], 1)) - B_repeated = np.repeat(B, repeats=A.shape[0], axis=0) - return np.hstack((A_tiled, B_repeated)) - - return functools.reduce(cartesian_fortran, indices) - - def cross_maxvol( black_box: BlackBox, cross_strategy: CrossStrategyMaxvol = CrossStrategyMaxvol(), + initial_points: Optional[np.ndarray] = None, + callback: Optional[Callable] = None, ) -> CrossResults: """ Computes the MPS representation of a black-box function using the tensor cross-approximation (TCI) algorithm based on one-site optimizations using the rectangular maxvol decomposition. + The black-box function can represent several different structures. See `black_box` for usage examples. Parameters ---------- @@ -95,17 +61,30 @@ def cross_maxvol( The black box to approximate as a MPS. cross_strategy : CrossStrategy, default=CrossStrategy() A dataclass containing the parameters of the algorithm. + initial_points : np.ndarray, optional + A collection of initial points used to initialize the algorithm. + If None, an initial random point is used. + callback : Callable, optional + A callable called on the MPS after each iteration. + The output of the callback is included in a list 'callback_output' in CrossResults. Returns ------- - mps : MPS - The MPS representation of the black-box function. + CrossResults + A dataclass containing the MPS representation of the black-box function, + among other useful information. """ - initial_point = cross_strategy.rng.integers( - low=0, high=black_box.base, size=black_box.sites - ) - cross = CrossInterpolationMaxvol(black_box, initial_point) + if initial_points is None: + initial_points = random_mps_indices( + black_box.physical_dimensions, + num_indices=1, + allowed_indices=getattr(black_box, "allowed_indices", None), + rng=cross_strategy.rng, + ) + + cross = CrossInterpolationMaxvol(black_box, initial_points) converged = False + callback_output = [] with make_logger(2) as logger: for i in range(cross_strategy.maxiter): # Forward sweep @@ -114,11 +93,51 @@ def cross_maxvol( # Backward sweep for k in reversed(range(cross.sites)): _update_maxvol(cross, k, False, cross_strategy) + if callback: + callback_output.append(callback(cross.mps, logger=logger)) if converged := _check_convergence(cross, i, cross_strategy, logger): break if not converged: logger("Maximum number of iterations reached") - return CrossResults(mps=cross.mps, evals=black_box.evals) + points = cross.indices_to_points(False) + return CrossResults( + mps=cross.mps, + points=points, + evals=black_box.evals, + callback_output=callback_output, + ) + + +class CrossInterpolationMaxvol(CrossInterpolation): + def __init__(self, black_box: BlackBox, initial_point: np.ndarray): + super().__init__(black_box, initial_point) + + @staticmethod + def combine_indices_fortran(*indices: np.ndarray) -> np.ndarray: + """ + Computes the Cartesian product of a set of multi-indices arrays and arranges the + result as concatenated indices in Fortran order (row-major). + + Parameters + ---------- + indices : *np.ndarray + A variable number of arrays where each array is treated as a set of multi-indices. + + Example + ------- + >>> combine_indices(np.array([[1, 2, 3], [4, 5, 6]]), np.array([[0], [1]]), fortran_order=True) + array([[1, 2, 3, 0], + [4, 5, 6, 0], + [1, 2, 3, 1], + [4, 5, 6, 1]]) + """ + + def cartesian_fortran(A: np.ndarray, B: np.ndarray) -> np.ndarray: + A_tiled = np.tile(A, (B.shape[0], 1)) + B_repeated = np.repeat(B, repeats=A.shape[0], axis=0) + return np.hstack((A_tiled, B_repeated)) + + return functools.reduce(cartesian_fortran, indices) def _update_maxvol( @@ -136,75 +155,74 @@ def _update_maxvol( fiber = cross.sample_fiber(k) r_l, s, r_g = fiber.shape if forward: - C = fiber.reshape(r_l * s, r_g, order=order) # type: ignore - Q, _ = np.linalg.qr(C) + C = fiber.reshape(r_l * s, r_g, order=order) + Q, _ = scipy.linalg.qr(C, mode="economic", overwrite_a=True, check_finite=False) # type: ignore I, _ = choose_maxvol( - Q, - cross_strategy.maxvol_maxiter, - cross_strategy.maxvol_tol, - cross_strategy.maxvol_rect_tol, - cross_strategy.maxvol_rect_rank_change, + Q, # type: ignore + cross_strategy.rank_kick, + cross_strategy.maxiter_maxvol_square, + cross_strategy.tol_maxvol_square, + cross_strategy.tol_maxvol_rect, ) if k < cross.sites - 1: cross.I_l[k + 1] = combine_indices(cross.I_l[k], cross.I_s[k])[I] else: - R = fiber.reshape(r_l, s * r_g, order=order) # type: ignore - Q, T = np.linalg.qr(R.T) - I, G = choose_maxvol( - Q, - cross_strategy.maxvol_maxiter, - cross_strategy.maxvol_tol, - cross_strategy.maxvol_rect_tol, - cross_strategy.maxvol_rect_rank_change, - ) - cross.mps[k] = (G.T).reshape(-1, s, r_g, order=order) # type: ignore if k > 0: + R = fiber.reshape(r_l, s * r_g, order=order) + Q, _ = scipy.linalg.qr( # type: ignore + R.T, mode="economic", overwrite_a=True, check_finite=False + ) + I, G = choose_maxvol( + Q, # type: ignore + cross_strategy.rank_kick, + cross_strategy.maxiter_maxvol_square, + cross_strategy.tol_maxvol_square, + cross_strategy.tol_maxvol_rect, + ) + cross.mps[k] = (G.T).reshape(-1, s, r_g, order=order) cross.I_g[k - 1] = combine_indices(cross.I_s[k], cross.I_g[k])[I] - elif k == 0: - cross.mps[0] = _contract_last_and_first((Q[I] @ T).T, cross.mps[0]) + else: + cross.mps[0] = fiber def choose_maxvol( A: np.ndarray, - maxiter: int = 100, + rank_kick: tuple = (0, np.inf), + maxiter: int = 10, tol: float = 1.1, - tol_rect: float = 1.05, - rank_change: tuple = (1, 1), + tol_rect: float = 0.1, ) -> tuple[np.ndarray, np.ndarray]: n, r = A.shape - min_rank_change, max_rank_change = rank_change - max_rank_change = min(max_rank_change, n - r) - min_rank_change = min(min_rank_change, max_rank_change) - if n <= r: - I, B = np.arange(n, dtype=int), np.eye(n) - elif max_rank_change == 0: - I, B = maxvol_square(A, maxiter, tol) + max_rank_kick = min(rank_kick[1], n - r) + min_rank_kick = min(rank_kick[0], max_rank_kick) + if n < r: + return np.arange(n, dtype=int), np.eye(n) + elif rank_kick == 0: + return maxvol_square(A, maxiter, tol) else: - I, B = maxvol_rectangular( - A, maxiter, tol, min_rank_change, max_rank_change, tol_rect + return maxvol_rectangular( + A, min_rank_kick, max_rank_kick, maxiter, tol, tol_rect ) - return I, B def maxvol_rectangular( A: np.ndarray, - maxiter: int = 100, + min_rank_kick: int = 0, + max_rank_kick: float = np.inf, + maxiter: int = 10, tol: float = 1.1, - min_rank_change: int = 1, - max_rank_change: int = 1, tol_rect: float = 1.05, ): n, r = A.shape - r_min = r + min_rank_change - r_max = r + max_rank_change if max_rank_change is not None else n - r_max = min(r_max, n) + r_min = r + min_rank_kick + r_max = min(r + max_rank_kick, n) if r_min < r or r_min > r_max or r_max > n: raise ValueError("Invalid minimum/maximum number of added rows") I0, B = maxvol_square(A, maxiter, tol) I = np.hstack([I0, np.zeros(r_max - r, dtype=I0.dtype)]) S = np.ones(n, dtype=int) S[I0] = 0 - F = S * np.linalg.norm(B, axis=1) ** 2 + F = S * np.linalg.norm(B) ** 2 for k in range(r, r_max): i = np.argmax(F) if k >= r_min and F[i] <= tol_rect**2: diff --git a/tests/test_analysis/test_cross.py b/tests/test_analysis/test_cross.py index 8a07022..187ebe8 100644 --- a/tests/test_analysis/test_cross.py +++ b/tests/test_analysis/test_cross.py @@ -1,23 +1,45 @@ import numpy as np +import functools + +import seemps +from seemps.state import MPS, scprod +from seemps.truncate.simplify_mpo import mps_as_mpo from seemps.analysis.mesh import Mesh, RegularInterval +from seemps.analysis.factories import mps_tensor_product +from seemps.analysis.integration import mps_fifth_order from seemps.analysis.cross import ( BlackBoxLoadMPS, + BlackBoxLoadTT, BlackBoxLoadMPO, BlackBoxComposeMPS, cross_maxvol, cross_dmrg, + cross_greedy, + CrossStrategyGreedy, ) - -import seemps -from seemps.state import MPS from seemps.analysis.cross.cross import maxvol_square from seemps.analysis.cross.cross_maxvol import maxvol_rectangular -from seemps.truncate.simplify_mpo import mps_as_mpo from .tools_analysis import reorder_tensor from ..tools import TestCase -seemps.tools.DEBUG = 1 +seemps.tools.DEBUG = 10 + + +def integration_callback(mps_quadrature: MPS): + """ + Returns a callback function that can be used to compute the + integral of the intermediate MPS in TCI. + """ + + def callback(mps: MPS, **kwargs) -> float: + integral = scprod(mps, mps_quadrature) + logger = kwargs.get("logger") + if logger: + logger(f"MPS integral={integral}") + return integral # type: ignore + + return callback def gaussian_setup_mps(dims, n=5, a=-1, b=1): @@ -49,51 +71,78 @@ def setUp(self, method): self.cross_method = cross_maxvol elif method == "dmrg": self.cross_method = cross_dmrg - - def _test_load_1d_mps(self): - func, mesh, _, y = gaussian_setup_mps(1, n=10) + elif method == "greedy_full": + strat = CrossStrategyGreedy(partial=False) + self.cross_method = functools.partial(cross_greedy, cross_strategy=strat) + elif method == "greedy_partial": + strat = CrossStrategyGreedy(partial=True) + self.cross_method = functools.partial(cross_greedy, cross_strategy=strat) + + def _test_load_1d_mps(self, n=5): + func, mesh, _, y = gaussian_setup_mps(1, n=n) black_box = BlackBoxLoadMPS(func, mesh) cross_results = self.cross_method(black_box) self.assertSimilar(y, cross_results.mps.to_vector()) - def _test_load_2d_mps(self): - func, mesh, _, y = gaussian_setup_mps(2) + def _test_load_2d_mps(self, n=5): + func, mesh, _, y = gaussian_setup_mps(2, n=n) black_box = BlackBoxLoadMPS(func, mesh) cross_results = self.cross_method(black_box) self.assertSimilar(y, cross_results.mps.to_vector()) - def _test_load_2d_mps_with_order_B(self): - func, mesh, _, y = gaussian_setup_mps(2) + def _test_load_2d_mps_with_order_B(self, n=5): + func, mesh, _, y = gaussian_setup_mps(2, n=n) black_box = BlackBoxLoadMPS(func, mesh, mps_order="B") cross_results = self.cross_method(black_box) qubits = [int(np.log2(s)) for s in mesh.dimensions] tensor = reorder_tensor(cross_results.mps.to_vector(), qubits) self.assertSimilar(y, tensor) - def _test_load_1d_mpo_diagonal(self): - func, x, mesh, mps_I = gaussian_setup_1d_mpo(is_diagonal=True) + def _test_load_2d_tt(self, n=5): + func, mesh, _, y = gaussian_setup_mps(2, n=n) + black_box = BlackBoxLoadTT(func, mesh) + cross_results = self.cross_method(black_box) + vector = cross_results.mps.to_vector() + self.assertSimilar(y, vector) + + def _test_2d_integration_callback(self, n=8): + a, b = -1, 1 + func = lambda tensor: np.exp(tensor[0] + tensor[1]) # f(x,y) = e^(x+y) + interval = RegularInterval(a, b, 2**n, endpoint_right=True) + mesh = Mesh([interval, interval]) + mps_quad_1d = mps_fifth_order(-1, 1, n) + mps_quad = mps_tensor_product([mps_quad_1d, mps_quad_1d]) + exact_integral = (np.exp(b) - np.exp(a)) ** 2 + callback = integration_callback(mps_quad) + black_box = BlackBoxLoadMPS(func, mesh) + cross_results = self.cross_method(black_box, callback=callback) + integral = cross_results.callback_output[-1] # type: ignore + self.assertAlmostEqual(integral, exact_integral) + + def _test_load_1d_mpo_diagonal(self, n=5): + func, x, mesh, mps_I = gaussian_setup_1d_mpo(is_diagonal=True, n=n) black_box = BlackBoxLoadMPO(func, mesh, is_diagonal=True) cross_results = self.cross_method(black_box) mps_diagonal = mps_as_mpo(cross_results.mps).apply(mps_I) self.assertSimilar(func(x, x), mps_diagonal.to_vector()) - def _test_load_1d_mpo_nondiagonal(self): - func, x, mesh, _ = gaussian_setup_1d_mpo(is_diagonal=False) + def _test_load_1d_mpo_nondiagonal(self, n=5): + func, x, mesh, _ = gaussian_setup_1d_mpo(is_diagonal=False, n=n) black_box = BlackBoxLoadMPO(func, mesh) cross_results = self.cross_method(black_box) y_mps = mps_as_mpo(cross_results.mps).to_matrix() xx, yy = np.meshgrid(x, x) self.assertSimilar(func(xx, yy), y_mps) - def _test_compose_1d_mps_list(self): - _, _, mps_0, y_0 = gaussian_setup_mps(1) + def _test_compose_1d_mps_list(self, n=5): + _, _, mps_0, y_0 = gaussian_setup_mps(1, n=n) func = lambda v: v[0] + np.sin(v[1]) + np.cos(v[2]) black_box = BlackBoxComposeMPS(func, [mps_0, mps_0, mps_0]) cross_results = self.cross_method(black_box) self.assertSimilar(func([y_0, y_0, y_0]), cross_results.mps.to_vector()) - def _test_compose_2d_mps_list(self): - _, _, mps_0, y_0 = gaussian_setup_mps(2) + def _test_compose_2d_mps_list(self, n=5): + _, _, mps_0, y_0 = gaussian_setup_mps(2, n=n) func = lambda v: v[0] + np.sin(v[1]) + np.cos(v[2]) black_box = BlackBoxComposeMPS(func, [mps_0, mps_0, mps_0]) cross_results = self.cross_method(black_box) @@ -113,6 +162,12 @@ def test_load_2d_mps(self): def test_load_2d_mps_with_order_B(self): super()._test_load_2d_mps_with_order_B() + def test_load_2d_tt(self): + super()._test_load_2d_tt() + + def test_2d_integration_callback(self): + super()._test_2d_integration_callback() + def test_load_1d_mpo_diagonal(self): super()._test_load_1d_mpo_diagonal() @@ -139,6 +194,76 @@ def test_load_2d_mps(self): def test_load_2d_mps_with_order_B(self): super()._test_load_2d_mps_with_order_B() + def test_load_2d_tt(self): + super()._test_load_2d_tt() + + def test_2d_integration_callback(self): + super()._test_2d_integration_callback() + + def test_load_1d_mpo_diagonal(self): + super()._test_load_1d_mpo_diagonal() + + def test_load_1d_mpo_nondiagonal(self): + super()._test_load_1d_mpo_nondiagonal() + + def test_compose_1d_mps_list(self): + super()._test_compose_1d_mps_list() + + def test_compose_2d_mps_list(self): + super()._test_compose_2d_mps_list() + + +class TestCrossGreedyFull(CrossTests): + def setUp(self): + super().setUp("greedy_full") + + def test_load_1d_mps(self): + super()._test_load_1d_mps() + + def test_load_2d_mps(self): + super()._test_load_2d_mps() + + def test_load_2d_mps_with_order_B(self): + super()._test_load_2d_mps_with_order_B() + + def test_load_2d_tt(self): + super()._test_load_2d_tt() + + def test_2d_integration_callback(self): + super()._test_2d_integration_callback() + + def test_load_1d_mpo_diagonal(self): + super()._test_load_1d_mpo_diagonal() + + def test_load_1d_mpo_nondiagonal(self): + super()._test_load_1d_mpo_nondiagonal() + + def test_compose_1d_mps_list(self): + super()._test_compose_1d_mps_list() + + def test_compose_2d_mps_list(self): + super()._test_compose_2d_mps_list() + + +class TestCrossGreedyPartial(CrossTests): + def setUp(self): + super().setUp("greedy_partial") + + def test_load_1d_mps(self): + super()._test_load_1d_mps() + + def test_load_2d_mps(self): + super()._test_load_2d_mps() + + def test_load_2d_mps_with_order_B(self): + super()._test_load_2d_mps_with_order_B() + + def test_load_2d_tt(self): + super()._test_load_2d_tt() + + def test_2d_integration_callback(self): + super()._test_2d_integration_callback() + def test_load_1d_mpo_diagonal(self): super()._test_load_1d_mpo_diagonal() @@ -174,9 +299,9 @@ def test_maxvol_rectangular(self): J = np.random.choice(A.shape[1], 1, replace=False) for _ in range(2): C = A[:, J] - I, _ = maxvol_rectangular(C) + I, _ = maxvol_rectangular(C, max_rank_kick=1) R = A[I, :] - J, _ = maxvol_rectangular(R.T) + J, _ = maxvol_rectangular(R.T, max_rank_kick=1) I, _ = maxvol_square(A[:, J]) A_new = A[:, J] @ np.linalg.inv(A[I, :][:, J]) @ A[I, :] self.assertSimilar(A, A_new) From 28ece790fc0a2bd934f5de1737a56b888ce41220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Rodr=C3=ADguez=20Aldavero?= Date: Fri, 5 Jul 2024 10:05:33 +0200 Subject: [PATCH 2/7] Rename 'integrals' to 'integration' --- src/seemps/analysis/__init__.py | 6 +- src/seemps/analysis/integrals.py | 335 -------------------- src/seemps/analysis/integration.py | 400 ++++++++++++++++++++++++ tests/test_analysis/test_integrals.py | 60 ---- tests/test_analysis/test_integration.py | 165 ++++++++++ 5 files changed, 569 insertions(+), 397 deletions(-) delete mode 100644 src/seemps/analysis/integrals.py create mode 100644 src/seemps/analysis/integration.py delete mode 100644 tests/test_analysis/test_integrals.py create mode 100644 tests/test_analysis/test_integration.py diff --git a/src/seemps/analysis/__init__.py b/src/seemps/analysis/__init__.py index 8220e0c..f8f2cc7 100644 --- a/src/seemps/analysis/__init__.py +++ b/src/seemps/analysis/__init__.py @@ -4,10 +4,11 @@ evolution, factories, finite_differences, - integrals, + integration, interpolation, mesh, operators, + optimization, polynomials, sampling, space, @@ -19,10 +20,11 @@ "evolution", "factories", "finite_differences", - "integrals", + "integration", "interpolation", "mesh", "operators", + "optimization", "polynomials", "sampling", "space", diff --git a/src/seemps/analysis/integrals.py b/src/seemps/analysis/integrals.py deleted file mode 100644 index 7032284..0000000 --- a/src/seemps/analysis/integrals.py +++ /dev/null @@ -1,335 +0,0 @@ -from __future__ import annotations -import numpy as np -from typing import Callable, Union -from math import sqrt -from ..truncate import simplify -from ..state import MPS, Strategy, scprod -from ..qft import iqft, qft_flip -from .mesh import RegularInterval, Mesh -from .factories import mps_tensor_product, mps_affine, COMPUTER_PRECISION -from .cross import cross_maxvol, BlackBoxLoadMPS, CrossStrategyMaxvol - - -def mps_midpoint(start: float, stop: float, sites: int) -> MPS: - """ - Returns a MPS representing the midpoint quadrature of an interval. - - Parameters - --------- - start : float - The starting point of the interval. - stop : float - The ending point of the interval. - sites : int - The number of sites or qubits for the MPS. - """ - step = (stop - start) / (2**sites - 1) - return step * MPS([np.ones((1, 2, 1))] * sites) - - -def mps_trapezoidal(start: float, stop: float, sites: int) -> MPS: - """ - Returns a MPS representing the trapezoidal quadrature of an interval. - - Parameters - --------- - start : float - The starting point of the interval. - stop : float - The ending point of the interval. - sites : int - The number of sites or qubits for the MPS. - """ - tensor_1 = np.zeros((1, 2, 3)) - tensor_1[0, 0, 0] = 1 - tensor_1[0, 1, 1] = 1 - tensor_1[0, 0, 2] = 1 - tensor_1[0, 1, 2] = 1 - tensor_bulk = np.zeros((3, 2, 3)) - tensor_bulk[0, 0, 0] = 1 - tensor_bulk[1, 1, 1] = 1 - tensor_bulk[2, 0, 2] = 1 - tensor_bulk[2, 1, 2] = 1 - tensor_2 = np.zeros((3, 2, 1)) - tensor_2[0, 0, 0] = -0.5 - tensor_2[1, 1, 0] = -0.5 - tensor_2[2, 0, 0] = 1 - tensor_2[2, 1, 0] = 1 - tensors = [tensor_1] + [tensor_bulk for _ in range(sites - 2)] + [tensor_2] - step = (stop - start) / (2**sites - 1) - return step * MPS(tensors) - - -def mps_simpson(start: float, stop: float, sites: int) -> MPS: - """ - Returns a MPS representing the Simpson quadrature of an interval. - Note that the number of sites must be even for Simpson's rule. - - Parameters - --------- - start : float - The starting point of the interval. - stop : float - The ending point of the interval. - sites : int - The number of sites or qubits for the MPS. Must be even. - """ - if sites % 2 != 0: - raise ValueError("The sites must be divisible by 2.") - - tensor_1 = np.zeros((1, 2, 4)) - tensor_1[0, 0, 0] = 1 - tensor_1[0, 1, 1] = 1 - tensor_1[0, 0, 2] = 1 - tensor_1[0, 1, 3] = 1 - if sites == 2: - tensor_2 = np.zeros((4, 2, 1)) - tensor_2[0, 0, 0] = -1 - tensor_2[1, 1, 0] = -1 - tensor_2[2, 0, 0] = 2 - tensor_2[2, 1, 0] = 3 - tensor_2[3, 0, 0] = 3 - tensor_2[3, 1, 0] = 2 - tensors = [tensor_1, tensor_2] - else: - tensor_2 = np.zeros((4, 2, 5)) - tensor_2[0, 0, 0] = 1 - tensor_2[1, 1, 1] = 1 - tensor_2[2, 0, 2] = 1 - tensor_2[2, 1, 3] = 1 - tensor_2[3, 0, 4] = 1 - tensor_2[3, 1, 2] = 1 - tensor_bulk = np.zeros((5, 2, 5)) - tensor_bulk[0, 0, 0] = 1 - tensor_bulk[1, 1, 1] = 1 - tensor_bulk[2, 0, 2] = 1 - tensor_bulk[2, 1, 3] = 1 - tensor_bulk[3, 0, 4] = 1 - tensor_bulk[3, 1, 2] = 1 - tensor_bulk[4, 0, 3] = 1 - tensor_bulk[4, 1, 4] = 1 - tensor_3 = np.zeros((5, 2, 1)) - tensor_3[0, 0, 0] = -1 - tensor_3[1, 1, 0] = -1 - tensor_3[2, 0, 0] = 2 - tensor_3[2, 1, 0] = 3 - tensor_3[3, 0, 0] = 3 - tensor_3[3, 1, 0] = 2 - tensor_3[4, 0, 0] = 3 - tensor_3[4, 1, 0] = 3 - tensors = ( - [tensor_1, tensor_2] + [tensor_bulk for _ in range(sites - 3)] + [tensor_3] - ) - step = (stop - start) / (2**sites - 1) - return (3 * step / 8) * MPS(tensors) - - -def mps_fifth_order(start: float, stop: float, sites: int) -> MPS: - """ - Returns a MPS representing the fifth-order quadrature of an interval. - Note that the number of sites must be divisible by 4 for this quadrature rule. - - Parameters - --------- - start : float - The starting point of the interval. - stop : float - The ending point of the interval. - sites : int - The number of sites or qubits for the MPS. Must be divisible by 4. - """ - if sites % 4 != 0: - raise ValueError("The sites must be divisible by 4.") - tensor_1 = np.zeros((1, 2, 4)) - tensor_1[0, 0, 0] = 1 - tensor_1[0, 1, 1] = 1 - tensor_1[0, 0, 2] = 1 - tensor_1[0, 1, 3] = 1 - tensor_2 = np.zeros((4, 2, 6)) - tensor_2[0, 0, 0] = 1 - tensor_2[1, 1, 1] = 1 - tensor_2[2, 0, 2] = 1 - tensor_2[2, 1, 3] = 1 - tensor_2[3, 0, 4] = 1 - tensor_2[3, 1, 5] = 1 - tensor_3 = np.zeros((6, 2, 7)) - tensor_3[0, 0, 0] = 1 - tensor_3[1, 1, 1] = 1 - tensor_3[2, 0, 2] = 1 - tensor_3[2, 1, 3] = 1 - tensor_3[3, 0, 4] = 1 - tensor_3[3, 1, 5] = 1 - tensor_3[4, 0, 6] = 1 - tensor_3[4, 1, 2] = 1 - tensor_3[5, 0, 3] = 1 - tensor_3[5, 1, 4] = 1 - tensor_bulk = np.zeros((7, 2, 7)) - tensor_bulk[0, 0, 0] = 1 - tensor_bulk[1, 1, 1] = 1 - tensor_bulk[2, 0, 2] = 1 - tensor_bulk[2, 1, 3] = 1 - tensor_bulk[3, 0, 4] = 1 - tensor_bulk[3, 1, 5] = 1 - tensor_bulk[4, 0, 6] = 1 - tensor_bulk[4, 1, 2] = 1 - tensor_bulk[5, 0, 3] = 1 - tensor_bulk[5, 1, 4] = 1 - tensor_bulk[6, 0, 5] = 1 - tensor_bulk[6, 1, 6] = 1 - tensor_4 = np.zeros((7, 2, 1)) - tensor_4[0, 0, 0] = -19 - tensor_4[1, 1, 0] = -19 - tensor_4[2, 0, 0] = 38 - tensor_4[2, 1, 0] = 75 - tensor_4[3, 0, 0] = 50 - tensor_4[3, 1, 0] = 50 - tensor_4[4, 0, 0] = 75 - tensor_4[4, 1, 0] = 38 - tensor_4[5, 0, 0] = 75 - tensor_4[5, 1, 0] = 50 - tensor_4[6, 0, 0] = 50 - tensor_4[6, 1, 0] = 75 - tensors = ( - [tensor_1, tensor_2, tensor_3] - + [tensor_bulk for _ in range(sites - 4)] - + [tensor_4] - ) - step = (stop - start) / (2**sites - 1) - return (5 * step / 288) * MPS(tensors) - - -def mps_fejer( - start: float, - stop: float, - sites: int, - strategy: Strategy = COMPUTER_PRECISION, - cross_strategy: CrossStrategyMaxvol = CrossStrategyMaxvol(tol_sampling=1e-15), -) -> MPS: - """ - Returns the MPS encoding of the Fejér first quadrature rule. - This is achieved using the formulation of Waldvogel (see waldvogel2006 formula 4.4) - by means of a direct encoding of the Féjer phase, tensor cross interpolation - for the term $1/(1-4*k**2)$, and the corrected inverse Quantum Fourier Transform (iQFT). - - Parameters - ---------- - start : float - The start of the interval. - stop : float - The end of the interval. - sites : int - The number of sites or qubits for the MPS. - strategy : Optional[Strategy], optional - The strategy for MPS simplification. Defaults to a tolerance of 1e-15. - cross_strategy : CrossStrategy, optional - The strategy for cross interpolation. Defaults to a tolerance of 1e-15. - - Returns - ------- - MPS - An MPS encoding of the Fejér first quadrature rule. - """ - - N = int(2**sites) - - # Encode 1/(1 - 4*k**2) term with cross interpolation - def func(k): - return np.where( - k < N / 2, - 2 / (1 - 4 * k**2), - 2 / (1 - 4 * (N - k) ** 2), - ) - - cross_results = cross_maxvol( - BlackBoxLoadMPS(func, Mesh([RegularInterval(0, N, N)])), - cross_strategy=cross_strategy, - ) - mps_k2 = simplify(cross_results.mps, strategy=strategy) - - # Encode phase term analytically - p = 1j * np.pi / N # prefactor - exponent = p * 2 ** (sites - 1) - tensor_1 = np.zeros((1, 2, 5), dtype=complex) - tensor_2 = np.zeros((5, 2, 1), dtype=complex) - tensors_bulk = [np.zeros((5, 2, 5), dtype=complex) for _ in range(sites - 2)] - tensor_1[0, 0, 0] = 1 - tensor_1[0, 1, 1] = np.exp(-exponent) - tensor_1[0, 1, 2] = np.exp(exponent) - tensor_1[0, 1, 3] = -np.exp(-exponent) - tensor_1[0, 1, 4] = -np.exp(exponent) - tensor_2[0, 0, 0] = 1 - tensor_2[0, 1, 0] = np.exp(p) - tensor_2[1, 0, 0] = 1 - tensor_2[1, 1, 0] = np.exp(p) - tensor_2[2, 0, 0] = 1 - tensor_2[3, 0, 0] = 1 - tensor_2[4, 0, 0] = 1 - for idx, tensor in enumerate(tensors_bulk): - exponent = p * 2 ** (sites - (idx + 2)) - tensor[0, 0, 0] = 1 - tensor[0, 1, 0] = np.exp(exponent) - tensor[1, 0, 1] = 1 - tensor[1, 1, 1] = np.exp(exponent) - tensor[2, 0, 2] = 1 - tensor[3, 0, 3] = 1 - tensor[4, 0, 4] = 1 - tensors = [tensor_1] + tensors_bulk + [tensor_2] - mps_phase = MPS(tensors) - - # Encode Fejér MPS with iQFT - mps_v = mps_k2 * mps_phase - mps = (1 / sqrt(2) ** sites) * qft_flip(iqft(mps_v, strategy=strategy)) - - return mps_affine(mps, (-1, 1), (start, stop)).as_mps() - - -def integrate_mps( - mps: MPS, mesh: Mesh, integral_type: str = "trapezoidal", mps_order: str = "A" -) -> Union[float, complex]: - """ - Returns the integral of a MPS representation of a function defined on a given mesh. - Supports multiple quadrature types, including midpoint, trapezoidal, Simpson's rule, - fifth-order, and Fejér's rule. - - Parameters - ---------- - mps : MPS - The input MPS to integrate representing a multivariate function. - mesh : Mesh - A Mesh object representing the intervals over which the function is defined. - integral_type : str - The type of numerical integration method to use. Options: - - "midpoint": Midpoint rule integration. - - "trapezoidal": Trapezoidal rule integration (default). - - "simpson": Simpson's rule integration (requires the qubits of each MPS to be a multiple of 2). - - "fifth_order": Fifth-order rule integration (requires the qubits of each MPS to be a multiple of 4). - - "fejer": Fejér's rule integration (requires the function to be discretized on Chebyshev zeros). - If an unsupported integral type is specified, a ValueError is raised. - mps_order : str, optional - The order in which to arrange the qubits of the quadrature before contraction. Options: - - 'A': The qubits are arranged by dimension (default). - - 'B': The qubits are arranged by significance. - - Returns - ------- - Union[float, complex] - The resulting integral. - """ - foo: Callable[[float, float, int], MPS] - if integral_type == "midpoint": - foo = mps_midpoint - elif integral_type == "trapezoidal": - foo = mps_trapezoidal - elif integral_type == "simpson" and len(mps) % 2 == 0: - foo = mps_simpson - elif integral_type == "fifth_order" and len(mps) % 4 == 0: - foo = mps_fifth_order - elif integral_type == "fejer": - foo = mps_fejer - else: - raise ValueError("Invalid integral_type or number of sites") - - mps_list = [] - for interval in mesh.intervals: - mps_list.append(foo(interval.start, interval.stop, int(np.log2(interval.size)))) - return scprod(mps, mps_tensor_product(mps_list, mps_order=mps_order)) diff --git a/src/seemps/analysis/integration.py b/src/seemps/analysis/integration.py new file mode 100644 index 0000000..240ccd9 --- /dev/null +++ b/src/seemps/analysis/integration.py @@ -0,0 +1,400 @@ +from __future__ import annotations +import numpy as np +from math import sqrt +from typing import Union + +from ..state import MPS, Strategy, scprod, DEFAULT_STRATEGY +from ..truncate import simplify +from ..qft import iqft, qft_flip +from .mesh import Mesh, Interval, RegularInterval, ChebyshevInterval, IntegerInterval +from .factories import mps_affine, mps_tensor_product +from .cross import cross_dmrg, BlackBoxLoadMPS, CrossStrategyDMRG + +# TODO: Express the quadratures in terms of a 'nodes' and 'quantize' arguments, and +# implement 'mps_trapezoidal' for any base. + + +def integrate_mps( + mps: MPS, domain: Union[Interval, Mesh], mps_order: str = "A" +) -> Union[complex, float]: + """ + Returns the integral of a multivariate function represented as a MPS. + Uses the 'best possible' quadrature rule according to the intervals that compose the mesh. + Intervals of type `RegularInterval` employ high-order Newton-Côtes rules, while + those of type `ChebyshevInterval` employ Clenshaw-Curtis rules. + + Parameters + ---------- + mps : MPS + The MPS representation of the multivariate function to be integrated. + domain : Union[Interval, Mesh] + An object defining the discretization domain of the function. + Can be either an `Interval` or a `Mesh` given by a collection of intervals. + The quadrature rules are selected based on the properties of these intervals. + mps_order : str, default='A' + Specifies the ordering of the qubits in the quadrature. Possible values:. + - 'A': Qubits are serially ordered (by variable). + - 'B': Qubits are interleaved (by significance). + + Returns + ------- + Union[complex, float] + The integral of the MPS representation of the function discretized in the given Mesh. + + Notes + ----- + - This algorithm assumes that all function variables are in the standard MPS form, i.e. + quantized in base 2, and are discretized either on a `RegularInterval or `ChebyshevInterval`. + + - For more general structures, the quadrature MPS can be constructed + using the univariate quadrature rules and the `mps_tensor_product` routine, which can be + subsequently contracted using the `scprod` routine. + + Examples + -------- + .. code-block:: python + + # Integrate a given bivariate function using the Clenshaw-Curtis quadrature. + # Assuming that the MPS is already loaded (for example, using TT-Cross or Chebyshev). + mps_function_2d = ... + + # Define a domain that matches the MPS to integrate. + start, stop = -1, 1 + n_qubits = 10 + interval = ChebyshevInterval(-1, 1, 2**n_qubits, endpoints=True) + mesh = Mesh([interval, interval]) + + # Integrate the MPS on the given discretization domain. + integral = integrate_mps(mps_function_2d, mesh) + """ + mesh = domain if isinstance(domain, Mesh) else Mesh([domain]) + quads = [] + for interval in mesh.intervals: + a, b, N = interval.start, interval.stop, interval.size + n = int(np.log2(N)) + if isinstance(interval, RegularInterval): + if n % 4 == 0: + quads.append(mps_fifth_order(a, b, n)) + elif n % 2 == 0: + quads.append(mps_simpson(a, b, n)) + else: + quads.append(mps_trapezoidal(a, b, n)) + elif isinstance(interval, ChebyshevInterval): + if interval.endpoints: + quads.append(mps_clenshaw_curtis(a, b, n)) + else: + quads.append(mps_fejer(a, b, n)) + else: + raise ValueError("Invalid interval in mesh") + mps_quad = quads[0] if len(quads) == 1 else mps_tensor_product(quads, mps_order) + return scprod(mps, mps_quad) + + +# TODO: Consider removing this (trapezoidal is strictly superior) +def mps_midpoint(start: float, stop: float, sites: int) -> MPS: + """ + Returns the binary MPS representation of the midpoint quadrature on an interval. + + Parameters + ---------- + start : float + The starting point of the interval. + stop : float + The ending point of the interval. + sites : int + The number of sites or qubits for the MPS. + """ + step = (stop - start) / (2**sites - 1) + return step * MPS([np.ones((1, 2, 1))] * sites) + + +def mps_trapezoidal(start: float, stop: float, sites: int) -> MPS: + """ + Returns the binary MPS representation of the trapezoidal quadrature on an interval. + + Parameters + ---------- + start : float + The starting point of the interval. + stop : float + The ending point of the interval. + sites : int + The number of sites or qubits for the MPS. + """ + tensor_L = np.zeros((1, 2, 3)) # Left + tensor_L[0, 0, 0] = 1 + tensor_L[0, 1, 1] = 1 + tensor_L[0, 0, 2] = 1 + tensor_L[0, 1, 2] = 1 + tensor_C = np.zeros((3, 2, 3)) # Center + tensor_C[0, 0, 0] = 1 + tensor_C[1, 1, 1] = 1 + tensor_C[2, 0, 2] = 1 + tensor_C[2, 1, 2] = 1 + tensor_R = np.zeros((3, 2, 1)) # Right + tensor_R[0, 0, 0] = -0.5 + tensor_R[1, 1, 0] = -0.5 + tensor_R[2, 0, 0] = 1 + tensor_R[2, 1, 0] = 1 + tensors = [tensor_L] + [tensor_C for _ in range(sites - 2)] + [tensor_R] + step = (stop - start) / (2**sites - 1) + return step * MPS(tensors) + + +def mps_simpson(start: float, stop: float, sites: int) -> MPS: + """ + Returns the binary MPS representation of the Simpson quadrature on an interval. + Note that the number of sites must be even for Simpson's rule. + + Parameters + ---------- + start : float + The starting point of the interval. + stop : float + The ending point of the interval. + sites : int + The number of sites or qubits for the MPS. Must be even. + """ + if sites % 2 != 0: + raise ValueError("The sites must be divisible by 2.") + + tensor_L1 = np.zeros((1, 2, 4)) + tensor_L1[0, 0, 0] = 1 + tensor_L1[0, 1, 1] = 1 + tensor_L1[0, 0, 2] = 1 + tensor_L1[0, 1, 3] = 1 + if sites == 2: + tensor_R = np.zeros((4, 2, 1)) + tensor_R[0, 0, 0] = -1 + tensor_R[1, 1, 0] = -1 + tensor_R[2, 0, 0] = 2 + tensor_R[2, 1, 0] = 3 + tensor_R[3, 0, 0] = 3 + tensor_R[3, 1, 0] = 2 + tensors = [tensor_L1, tensor_R] + else: + tensor_L2 = np.zeros((4, 2, 5)) + tensor_L2[0, 0, 0] = 1 + tensor_L2[1, 1, 1] = 1 + tensor_L2[2, 0, 2] = 1 + tensor_L2[2, 1, 3] = 1 + tensor_L2[3, 0, 4] = 1 + tensor_L2[3, 1, 2] = 1 + tensor_C = np.zeros((5, 2, 5)) + tensor_C[0, 0, 0] = 1 + tensor_C[1, 1, 1] = 1 + tensor_C[2, 0, 2] = 1 + tensor_C[2, 1, 3] = 1 + tensor_C[3, 0, 4] = 1 + tensor_C[3, 1, 2] = 1 + tensor_C[4, 0, 3] = 1 + tensor_C[4, 1, 4] = 1 + tensor_R = np.zeros((5, 2, 1)) + tensor_R[0, 0, 0] = -1 + tensor_R[1, 1, 0] = -1 + tensor_R[2, 0, 0] = 2 + tensor_R[2, 1, 0] = 3 + tensor_R[3, 0, 0] = 3 + tensor_R[3, 1, 0] = 2 + tensor_R[4, 0, 0] = 3 + tensor_R[4, 1, 0] = 3 + tensors = ( + [tensor_L1, tensor_L2] + [tensor_C for _ in range(sites - 3)] + [tensor_R] + ) + step = (stop - start) / (2**sites - 1) + return (3 * step / 8) * MPS(tensors) + + +def mps_fifth_order(start: float, stop: float, sites: int) -> MPS: + """ + Returns the binary MPS representation of the fifth-order quadrature on an interval. + Note that the number of sites must be divisible by 4 for this quadrature rule. + + Parameters + ---------- + start : float + The starting point of the interval. + stop : float + The ending point of the interval. + sites : int + The number of sites or qubits for the MPS. Must be divisible by 4. + """ + if sites % 4 != 0: + raise ValueError("The sites must be divisible by 4.") + tensor_L1 = np.zeros((1, 2, 4)) + tensor_L1[0, 0, 0] = 1 + tensor_L1[0, 1, 1] = 1 + tensor_L1[0, 0, 2] = 1 + tensor_L1[0, 1, 3] = 1 + tensor_L2 = np.zeros((4, 2, 6)) + tensor_L2[0, 0, 0] = 1 + tensor_L2[1, 1, 1] = 1 + tensor_L2[2, 0, 2] = 1 + tensor_L2[2, 1, 3] = 1 + tensor_L2[3, 0, 4] = 1 + tensor_L2[3, 1, 5] = 1 + tensor_L3 = np.zeros((6, 2, 7)) + tensor_L3[0, 0, 0] = 1 + tensor_L3[1, 1, 1] = 1 + tensor_L3[2, 0, 2] = 1 + tensor_L3[2, 1, 3] = 1 + tensor_L3[3, 0, 4] = 1 + tensor_L3[3, 1, 5] = 1 + tensor_L3[4, 0, 6] = 1 + tensor_L3[4, 1, 2] = 1 + tensor_L3[5, 0, 3] = 1 + tensor_L3[5, 1, 4] = 1 + tensor_C = np.zeros((7, 2, 7)) + tensor_C[0, 0, 0] = 1 + tensor_C[1, 1, 1] = 1 + tensor_C[2, 0, 2] = 1 + tensor_C[2, 1, 3] = 1 + tensor_C[3, 0, 4] = 1 + tensor_C[3, 1, 5] = 1 + tensor_C[4, 0, 6] = 1 + tensor_C[4, 1, 2] = 1 + tensor_C[5, 0, 3] = 1 + tensor_C[5, 1, 4] = 1 + tensor_C[6, 0, 5] = 1 + tensor_C[6, 1, 6] = 1 + tensor_R = np.zeros((7, 2, 1)) + tensor_R[0, 0, 0] = -19 + tensor_R[1, 1, 0] = -19 + tensor_R[2, 0, 0] = 38 + tensor_R[2, 1, 0] = 75 + tensor_R[3, 0, 0] = 50 + tensor_R[3, 1, 0] = 50 + tensor_R[4, 0, 0] = 75 + tensor_R[4, 1, 0] = 38 + tensor_R[5, 0, 0] = 75 + tensor_R[5, 1, 0] = 50 + tensor_R[6, 0, 0] = 50 + tensor_R[6, 1, 0] = 75 + tensors = ( + [tensor_L1, tensor_L2, tensor_L3] + + [tensor_C for _ in range(sites - 4)] + + [tensor_R] + ) + step = (stop - start) / (2**sites - 1) + return (5 * step / 288) * MPS(tensors) + + +def mps_fejer( + start: float, + stop: float, + sites: int, + strategy: Strategy = DEFAULT_STRATEGY, + cross_strategy: CrossStrategyDMRG = CrossStrategyDMRG(), +) -> MPS: + """ + Returns the binary MPS representation of the Fejér first quadrature rule on an interval. + The integration nodes are given by the `d` zeros of the `d`-th Chebyshev polynomial. + This is achieved using the formulation of Waldvogel (see waldvogel2006 formula 4.4) + by means of a direct encoding of the Féjer phase, tensor-cross interpolation + for the term $1/(1-4*k**2)$, and the bit-flipped inverse Quantum Fourier Transform (iQFT). + + Parameters + ---------- + start : float + The start of the interval. + stop : float + The end of the interval. + sites : int + The number of sites or qubits for the MPS. + strategy : Strategy, default=DEFAULT_STRATEGY + The strategy for MPS simplification. + cross_strategy : CrossStrategyDMRG, default=CrossStrategyDMRG. + The strategy for tensor cross-interpolation. + """ + + N = int(2**sites) + + # Encode 1/(1 - 4*k**2) term with TCI + func = lambda k: np.where(k < N / 2, 2 / (1 - 4 * k**2), 2 / (1 - 4 * (N - k) ** 2)) + mps_k2 = cross_dmrg( + BlackBoxLoadMPS(func, IntegerInterval(0, N)), cross_strategy=cross_strategy + ).mps + mps_k2 = simplify(mps_k2, strategy=strategy) + + # Encode phase term analytically + p = 1j * np.pi / N # prefactor + exponent = p * 2 ** (sites - 1) + tensor_L = np.zeros((1, 2, 5), dtype=complex) + tensor_L[0, 0, 0] = 1 + tensor_L[0, 1, 1] = np.exp(-exponent) + tensor_L[0, 1, 2] = np.exp(exponent) + tensor_L[0, 1, 3] = -np.exp(-exponent) + tensor_L[0, 1, 4] = -np.exp(exponent) + tensor_R = np.zeros((5, 2, 1), dtype=complex) + tensor_R[0, 0, 0] = 1 + tensor_R[0, 1, 0] = np.exp(p) + tensor_R[1, 0, 0] = 1 + tensor_R[1, 1, 0] = np.exp(p) + tensor_R[2, 0, 0] = 1 + tensor_R[3, 0, 0] = 1 + tensor_R[4, 0, 0] = 1 + tensors_C = [np.zeros((5, 2, 5), dtype=complex) for _ in range(sites - 2)] + for idx, tensor_C in enumerate(tensors_C): + exponent = p * 2 ** (sites - (idx + 2)) + tensor_C[0, 0, 0] = 1 + tensor_C[0, 1, 0] = np.exp(exponent) + tensor_C[1, 0, 1] = 1 + tensor_C[1, 1, 1] = np.exp(exponent) + tensor_C[2, 0, 2] = 1 + tensor_C[3, 0, 3] = 1 + tensor_C[4, 0, 4] = 1 + tensors = [tensor_L] + tensors_C + [tensor_R] + mps_phase = MPS(tensors) + + # Encode Fejér quadrature with iQFT + mps = (1 / sqrt(2) ** sites) * qft_flip(iqft(mps_k2 * mps_phase, strategy=strategy)) + + return mps_affine(mps, (-1, 1), (start, stop)) # type: ignore + + +def mps_clenshaw_curtis( + start: float, + stop: float, + sites: int, + strategy: Strategy = DEFAULT_STRATEGY, +) -> MPS: + """ + Returns the binary MPS representation of the Clenshaw-Curtis quadrature rule on an interval. + The integration nodes are given by the `d+1` extrema of the `d`-th Chebyshev polynomial. + This is achieved using the formulation of Waldvogel (see waldvogel2006 formula 4.2) using + the Schmidt decomposition. + + Parameters + ---------- + start : float + The start of the interval. + stop : float + The end of the interval. + sites : int + The number of sites or qubits for the MPS. + strategy : Strategy, default=DEFAULT_STRATEGY. + The strategy for the Schmidt decomposition. + """ + # TODO: Find a way to construct the MPS analytically without using SVD. + # Problem: it cannot be directly computed as the iFFT of a vector of size 2**n + # thus, it cannot be constructed as the iQFT of another MPS. + N = int(2**sites) - 1 + + # Construct the quadrature vector using the iFFT + v = np.zeros(N) + g = np.zeros(N) + w0 = 1 / (N**2 - 1 + (N % 2)) + for k in range(N // 2): + v[k] = 2 / (1 - 4 * k**2) + g[k] = -w0 + v[N // 2] = (N - 3) / (2 * (N // 2) - 1) - 1 + g[N // 2] = w0 * ((2 - (N % 2)) * N - 1) + for k in range(1, N // 2 + 1): + v[-k] = v[k] + g[-k] = g[k] + w = np.fft.ifft(v + g).real + w = np.hstack((w, w[0])) + + # Decompose the quadrature vector with the Schmidt decomposition + mps = MPS.from_vector(w, [2] * sites, strategy=strategy, normalize=False) + return mps_affine(mps, (-1, 1), (start, stop)) # type: ignore diff --git a/tests/test_analysis/test_integrals.py b/tests/test_analysis/test_integrals.py deleted file mode 100644 index 37ae030..0000000 --- a/tests/test_analysis/test_integrals.py +++ /dev/null @@ -1,60 +0,0 @@ -import numpy as np -from scipy.fft import ifft -from seemps.analysis.integrals import ( - mps_midpoint, - mps_trapezoidal, - mps_simpson, - mps_fifth_order, - mps_fejer, -) -from ..tools import TestCase - - -class TestMPSIntegrals(TestCase): - def test_mps_midpoint(self): - a, b, n = -1, 1, 3 - h = (b - a) / (2**n - 1) - vector_quad = h * np.array([1, 1, 1, 1, 1, 1, 1, 1]) - mps_quad = mps_midpoint(a, b, n) - self.assertSimilar(vector_quad, mps_quad.to_vector()) - - def test_mps_trapezoidal(self): - a, b, n = -1, 1, 3 - h = (b - a) / (2**n - 1) - vector_quad = (h / 2) * np.array([1, 2, 2, 2, 2, 2, 2, 1]) - mps_quad = mps_trapezoidal(-1, 1, 3) - self.assertSimilar(vector_quad, mps_quad.to_vector()) - - def test_mps_simpson(self): - a, b, n = -1, 1, 4 # Multiple of 2 - h = (b - a) / (2**n - 1) - vector_quad = (3 * h / 8) * np.array( - [1, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 3, 1] - ) - mps_quad = mps_simpson(a, b, n) - self.assertSimilar(vector_quad, mps_quad.to_vector()) - - def test_mps_fifth_order(self): - a, b, n = -1, 1, 4 # Multiple of 4 - h = (b - a) / (2**n - 1) - vector_quad = (5 * h / 288) * np.array( - [19, 75, 50, 50, 75, 38, 75, 50, 50, 75, 38, 75, 50, 50, 75, 19] - ) - mps_quad = mps_fifth_order(a, b, n) - self.assertSimilar(vector_quad, mps_quad.to_vector()) - - def test_mps_fejer(self): - a, b, n = -1, 1, 3 - h = (b - a) / 2 - # Implement Fejér vector with iFFT - N = 2**n - v = np.zeros(N, dtype=complex) - for k in range(N // 2): - v[k] = 2 / (1 - 4 * k**2) * np.exp(1j * k * np.pi / N) - for k in range(1, N // 2 + 1): - v[-k] = np.conjugate(v[k]) - if N % 2 == 0: - v[N // 2] = 0 - vector_quad = h * ifft(v).flatten().real - mps_quad = mps_fejer(a, b, n) - self.assertSimilar(vector_quad, mps_quad.to_vector()) diff --git a/tests/test_analysis/test_integration.py b/tests/test_analysis/test_integration.py new file mode 100644 index 0000000..52809b0 --- /dev/null +++ b/tests/test_analysis/test_integration.py @@ -0,0 +1,165 @@ +import numpy as np +from scipy.fft import ifft +from seemps.state import MPS +from seemps.analysis.factories import mps_exponential, mps_tensor_product +from seemps.analysis.mesh import Mesh, RegularInterval, ChebyshevInterval +from seemps.analysis.integration import ( + mps_midpoint, + mps_trapezoidal, + mps_simpson, + mps_fifth_order, + mps_fejer, + mps_clenshaw_curtis, + integrate_mps, +) +from ..tools import TestCase + + +class TestMPSQuadratures(TestCase): + def test_mps_midpoint(self): + a, b, n = -1, 1, 3 + h = (b - a) / (2**n - 1) + vector_quad = h * np.array([1, 1, 1, 1, 1, 1, 1, 1]) + mps_quad = mps_midpoint(a, b, n) + self.assertSimilar(vector_quad, mps_quad.to_vector()) + + def test_mps_trapezoidal(self): + a, b, n = -1, 1, 3 + h = (b - a) / (2**n - 1) + vector_quad = (h / 2) * np.array([1, 2, 2, 2, 2, 2, 2, 1]) + mps_quad = mps_trapezoidal(-1, 1, n) + self.assertSimilar(vector_quad, mps_quad.to_vector()) + + def test_mps_simpson(self): + a, b, n = -1, 1, 4 # Multiple of 2 + h = (b - a) / (2**n - 1) + vector_quad = (3 * h / 8) * np.array( + [1, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 3, 1] + ) + mps_quad = mps_simpson(a, b, n) + self.assertSimilar(vector_quad, mps_quad.to_vector()) + + def test_mps_fifth_order(self): + a, b, n = -1, 1, 4 # Multiple of 4 + h = (b - a) / (2**n - 1) + vector_quad = (5 * h / 288) * np.array( + [19, 75, 50, 50, 75, 38, 75, 50, 50, 75, 38, 75, 50, 50, 75, 19] + ) + mps_quad = mps_fifth_order(a, b, n) + self.assertSimilar(vector_quad, mps_quad.to_vector()) + + def test_mps_fejer(self): + a, b, n = -2, 2, 5 + h = (b - a) / 2 + # Implement Fejér vector with iFFT + N = 2**n + v = np.zeros(N, dtype=complex) + for k in range(N // 2): + v[k] = 2 / (1 - 4 * k**2) * np.exp(1j * k * np.pi / N) + for k in range(1, N // 2 + 1): + v[-k] = np.conjugate(v[k]) + if N % 2 == 0: + v[N // 2] = 0 + vector_quad = h * ifft(v).real # type: ignore + mps_quad = mps_fejer(a, b, n) + self.assertSimilar(vector_quad, mps_quad.to_vector()) + + def test_mps_clenshaw_curtis(self): + a, b, n = -2, 2, 5 + h = (b - a) / 2 + # Implement Clenshaw-Curtis vector with iFFT + N = int(2**n) - 1 + v = np.zeros(N) + g = np.zeros(N) + w0 = 1 / (N**2 - 1 + (N % 2)) + for k in range(N // 2): + v[k] = 2 / (1 - 4 * k**2) + g[k] = -w0 + v[N // 2] = (N - 3) / (2 * (N // 2) - 1) - 1 + g[N // 2] = w0 * ((2 - (N % 2)) * N - 1) + for k in range(1, N // 2 + 1): + v[-k] = v[k] + g[-k] = g[k] + w = np.fft.ifft(v + g).real + vector_quad = h * np.hstack((w, w[0])) + mps_quad = mps_clenshaw_curtis(a, b, n) + self.assertSimilar(vector_quad, mps_quad.to_vector()) + + +class TestMPSIntegrals(TestCase): + def setUp(self): + self.a, self.b = -2, 2 + self.func = lambda x: np.exp(x) + self.integral = self.func(self.b) - self.func(self.a) + self.step = lambda n: (self.b - self.a) / (2**n - 1) + + def test_trapezoidal_integral(self): + n = 11 + mps = mps_exponential(self.a, self.b + self.step(n), n) + interval = RegularInterval(self.a, self.b, 2**n, endpoint_right=True) + integral = integrate_mps(mps, interval) + self.assertAlmostEqual(self.integral, integral, places=5) + + def test_simpson_integral(self): + n = 10 + mps = mps_exponential(self.a, self.b + self.step(n), n) + interval = RegularInterval(self.a, self.b, 2**n) + integral = integrate_mps(mps, interval) + self.assertAlmostEqual(self.integral, integral) + + def test_fifth_order_integral(self): + n = 8 + mps = mps_exponential(self.a, self.b + self.step(n), n) + interval = RegularInterval(self.a, self.b, 2**n) + integral = integrate_mps(mps, interval) + self.assertAlmostEqual(self.integral, integral) + + def test_fejer_integral(self): + n = 4 + interval = ChebyshevInterval(self.a, self.b, 2**n) + mps = MPS.from_vector(self.func(interval.to_vector()), [2] * n, normalize=False) + integral = integrate_mps(mps, interval) + self.assertAlmostEqual(self.integral, integral) + + def test_clenshaw_curtis_integral(self): + n = 4 + interval = ChebyshevInterval(self.a, self.b, 2**n, endpoints=True) + mps = MPS.from_vector(self.func(interval.to_vector()), [2] * n, normalize=False) + integral = integrate_mps(mps, interval) + self.assertAlmostEqual(self.integral, integral) + + def test_multivariate_integral_order_A(self): + n = 4 + integral_2d = self.integral**2 + # Fejér quadrature in first variable + interval_fj = ChebyshevInterval(self.a, self.b, 2**n) + mps_fj = MPS.from_vector( + self.func(interval_fj.to_vector()), [2] * n, normalize=False + ) + # CC quadrature in second variable + interval_cc = ChebyshevInterval(self.a, self.b, 2**n, endpoints=True) + mps_cc = MPS.from_vector( + self.func(interval_cc.to_vector()), [2] * n, normalize=False + ) + mps = mps_tensor_product([mps_fj, mps_cc], mps_order="A") + mesh = Mesh([interval_fj, interval_cc]) + integral = integrate_mps(mps, mesh, mps_order="A") + self.assertAlmostEqual(integral_2d, integral) + + def test_multivariate_integral_order_B(self): + n = 4 + integral_2d = self.integral**2 + # Fejér quadrature in first variable + interval_fj = ChebyshevInterval(self.a, self.b, 2**n) + mps_fj = MPS.from_vector( + self.func(interval_fj.to_vector()), [2] * n, normalize=False + ) + # CC quadrature in second variable + interval_cc = ChebyshevInterval(self.a, self.b, 2**n, endpoints=True) + mps_cc = MPS.from_vector( + self.func(interval_cc.to_vector()), [2] * n, normalize=False + ) + mps = mps_tensor_product([mps_fj, mps_cc], mps_order="B") + mesh = Mesh([interval_fj, interval_cc]) + integral = integrate_mps(mps, mesh, mps_order="B") + self.assertAlmostEqual(integral_2d, integral) From edfbb2d6f69f08c3e7d258acd824f0d6649c80ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Rodr=C3=ADguez=20Aldavero?= Date: Fri, 5 Jul 2024 10:05:46 +0200 Subject: [PATCH 3/7] Add optimization method --- src/seemps/analysis/optimization.py | 156 +++++++++++++++++++++++ tests/test_analysis/test_optimization.py | 30 +++++ 2 files changed, 186 insertions(+) create mode 100644 src/seemps/analysis/optimization.py create mode 100644 tests/test_analysis/test_optimization.py diff --git a/src/seemps/analysis/optimization.py b/src/seemps/analysis/optimization.py new file mode 100644 index 0000000..9137487 --- /dev/null +++ b/src/seemps/analysis/optimization.py @@ -0,0 +1,156 @@ +import numpy as np + +from ..state import MPS, CanonicalMPS, MPSSum +from .sampling import evaluate_mps + +# TODO: Profile and optimize +# TODO: Implement TT-Opt + + +def optimize_mps(mps: MPS, num_indices: int = 100, make_canonical: bool = True): + """ + Returns the minimum and maximum values of a given MPS, together with their indices. + Performs two full sweeps using `optima_tt`, one for the left-to-right and right-to-left + directions respectively. + + Parameters + ---------- + mps : MPS + The MPS to optimize. + num_indices : int, default=100 + The maximum amount of indices to retain from each tensor. A larger number increases + the probability of finding the global maxima, but has a larger cost. + make_canonical : bool, default=True + Whether to canonicalize the MPS prior to the search and orthogonalize its tensors. + + Returns + ------- + (i_1, y_1) : tuple + A tuple with the index and minimum value in the MPS. + (i_2, y_2) : tuple + A tuple with the index and maximum value in the MPS. + + Example + ------- + .. code-block:: python + # Compute the two extrema of a given univariate function. + # Assume that the function is already loaded. + mps_function_1d = ... + (i_min, y_min), (i_max, y_max) = optimize_mps(mps) + """ + # TODO: Optimize (consider simplifying mps_2 and avoiding the product) + s = mps.physical_dimensions()[0] + i_1, y_1 = _optima_tt_sweep(mps, num_indices, make_canonical) + mps_2 = MPSSum( + weights=[1, -y_1], + states=[mps, MPS([np.ones((1, s, 1))] * len(mps))], + check_args=False, + ).join() + mps_2 = mps_2 * mps_2 + i_2, _ = _optima_tt_sweep(mps_2, num_indices, make_canonical) + y_2 = evaluate_mps(mps, i_2)[0] + if y_1 < y_2: + return (i_1, y_1), (i_2, y_2) + else: + return (i_2, y_2), (i_1, y_1) + + +def optima_tt( + mps: MPS, + num_indices: int = 100, + make_canonical: bool = True, + left_to_right: bool = True, +) -> np.ndarray: + """ + Returns a set of k indices representing k potentially maximum in modulo values of the MPS. + Performs a probabilistic search traversing the MPS tensors from left-to-right or right-to-left. + Source: https://arxiv.org/pdf/2209.14808 + + Parameters + ---------- + mps : MPS + The MPS to optimize. + num_indices : int, default=100 + The maximum amount of indices to retain from each tensor and return at the end. A larger number + increases the probability of finding the global maxima, but has a larger cost. + make_canonical : bool, default=True + Whether to canonicalize the MPS prior to the search and orthogonalize its tensors. + left_to_right: bool, default=True + The direction of the MPS traversal. + + Returns + ------- + I : np.ndarray + An array containing `num_indices` indices whose MPS values are potentially maximum in modulo. + """ + + def choose_indices(Q: np.ndarray) -> np.ndarray: + """Returns the indices of the k rows or columns of Q that are largest norm-2.""" + axis = 1 if left_to_right else 0 + Q_norm = (Q / np.max(np.abs(Q))) ** 2 # Normalize in [0, 1] + Q_sum = np.sum(Q_norm, axis=axis) + I_sort = np.argsort(Q_sum)[::-1] # Indices from largest to smallest norm-2 + return I_sort[:num_indices] + + if left_to_right: + if make_canonical: + mps = CanonicalMPS(mps, center=0) + r_l, s, r_g = mps[0].shape + Q = mps[0].reshape(r_l * s, r_g) + I = np.arange(s).reshape(-1, 1) + ind = choose_indices(Q) + Q = Q[ind] + I = I[ind] + for site in mps[1:]: + r_l, s, r_g = site.shape + G = site.reshape(r_l, s * r_g) + Q = np.matmul(Q, G).reshape(-1, r_g) + I_old = np.kron(I, np.ones((s, 1), dtype=int)) + I_cur = np.kron( + np.ones((I.shape[0], 1), dtype=int), np.arange(s).reshape(-1, 1) + ) + I = np.hstack((I_old, I_cur)) + ind = choose_indices(Q) + Q = Q[ind] + I = I[ind] + else: + if make_canonical: + mps = CanonicalMPS(mps, center=-1) + r_l, s, r_g = mps[-1].shape + Q = mps[-1].reshape(r_l, s * r_g) + I = np.arange(s).reshape(-1, 1) + ind = choose_indices(Q) + Q = Q[:, ind] + I = I[ind] + for site in mps[:-1][::-1]: + r_l, s, r_g = site.shape + G = site.reshape(r_l * s, r_g) + Q = np.matmul(G, Q).reshape(r_l, -1) + I_old = np.kron( + np.arange(s).reshape(-1, 1), + np.ones((I.shape[0], 1), dtype=int), + ) + I_cur = np.kron(np.ones((s, 1), dtype=int), I) + I = np.hstack((I_old, I_cur)) + ind = choose_indices(Q) + Q = Q[:, ind] + I = I[ind] + return I + + +def _optima_tt_sweep( + mps: MPS, + num_indices: int, + make_canonical: bool = True, +) -> tuple[np.ndarray, float]: + """ + Performs a full sweep (left-right-left) using `optima_tt` and keeps the + best value from the two. + """ + i_max_list = [ + optima_tt(mps, num_indices, make_canonical, True)[0], + optima_tt(mps, num_indices, make_canonical, False)[0], + ] + y_max_list = [evaluate_mps(mps, i)[0] for i in i_max_list] + idx = np.argmax(np.array([abs(y) for y in y_max_list])) + return i_max_list[idx], y_max_list[idx] diff --git a/tests/test_analysis/test_optimization.py b/tests/test_analysis/test_optimization.py new file mode 100644 index 0000000..5f5476a --- /dev/null +++ b/tests/test_analysis/test_optimization.py @@ -0,0 +1,30 @@ +import numpy as np + +from seemps.analysis.factories import mps_sin +from seemps.analysis.optimization import optimize_mps + +from ..tools import TestCase + + +def _bits_to_int(bits: np.ndarray) -> int: + return int("".join(str(x) for x in bits), 2) + + +class TestMPSOptimization(TestCase): + def test_optimize_mps_sin(self): + mps = mps_sin(-2, 2, 6) + # Vector optimization + y = mps.to_vector() + i_min_vec = np.argmin(y) + y_min_vec = y[i_min_vec] + i_max_vec = np.argmax(y) + y_max_vec = y[i_max_vec] + # MPS optimization + (j_min, y_min), (j_max, y_max) = optimize_mps(mps) + i_min = _bits_to_int(j_min) + i_max = _bits_to_int(j_max) + # TODO: There is an error of 10**(-16) of unknown source. + self.assertEqual(i_min_vec, i_min) + self.assertAlmostEqual(y_min_vec, y_min, places=15) + self.assertEqual(i_max_vec, i_max) + self.assertAlmostEqual(y_max_vec, y_max, places=15) From 604fb12da77d89e6ab85a8e2bea3a11503ea5a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Rodr=C3=ADguez=20Aldavero?= Date: Fri, 5 Jul 2024 10:07:21 +0200 Subject: [PATCH 4/7] Improve overall documentation of analysis and remove unused variables --- src/seemps/analysis/chebyshev.py | 116 ++++++++++++----------- src/seemps/analysis/factories.py | 129 ++++++++++++++------------ src/seemps/analysis/lagrange.py | 68 +++++++------- src/seemps/analysis/mesh.py | 72 ++++++++++---- src/seemps/analysis/sampling.py | 26 +++--- src/seemps/truncate/simplify_mpo.py | 25 +++-- tests/test_analysis/test_chebyshev.py | 5 +- 7 files changed, 247 insertions(+), 194 deletions(-) diff --git a/src/seemps/analysis/chebyshev.py b/src/seemps/analysis/chebyshev.py index 22dff4f..0b3f4c3 100644 --- a/src/seemps/analysis/chebyshev.py +++ b/src/seemps/analysis/chebyshev.py @@ -5,8 +5,8 @@ from scipy.fft import dct # type: ignore from ..tools import make_logger -from ..state import CanonicalMPS, MPS, MPSSum, Strategy -from ..truncate import simplify, SIMPLIFICATION_STRATEGY +from ..state import CanonicalMPS, MPS, MPSSum, Strategy, DEFAULT_STRATEGY +from ..truncate import simplify from ..truncate.simplify_mpo import simplify_mpo from ..operators import MPO, MPOList, MPOSum from .mesh import ( @@ -18,14 +18,11 @@ from .factories import mps_interval, mps_affine -DEFAULT_CHEBYSHEV_STRATEGY = SIMPLIFICATION_STRATEGY.replace(normalize=False) - - def interpolation_coefficients( func: Callable, order: Optional[int] = None, - start: float = -1, - stop: float = +1, + start: float = -1.0, + stop: float = +1.0, domain: Optional[Interval] = None, interpolated_nodes: str = "zeros", ) -> np.polynomial.Chebyshev: @@ -37,14 +34,14 @@ def interpolation_coefficients( ---------- func : Callable The target function to approximate with Chebyshev polynomials. - order : Optional[int], default = None + order : int, optional The number of Chebyshev coefficients to compute. If None, estimates an order that results in an error below machine precision. - domain : Optional[Interval], default = None + domain : Interval, optional The domain on which the function is defined and in which the approximation is desired. - start : float, default = -1 - stop : float, default = +1 + start : float, default=-1.0 + stop : float, default=+1.0 Alternative way to specify the function's domain. interpolated_nodes : str, default = "zeros" The nodes on which the function is interpolated. Use "zeros" for @@ -72,8 +69,8 @@ def interpolation_coefficients( def projection_coefficients( func: Callable, order: Optional[int] = None, - start: float = -1, - stop: float = +1, + start: float = -1.0, + stop: float = +1.0, domain: Optional[Interval] = None, ) -> np.polynomial.Chebyshev: """ @@ -84,14 +81,13 @@ def projection_coefficients( ---------- func : Callable The target function to approximate with Chebyshev polynomials. - order : Optional[int], default = None + order : int, optional The number of Chebyshev projection coefficients to compute. If None, estimates an order that results in an error below machine precision. - start : float, default = -1 - stop : float, default = +1 - The domain on which the function is defined and in which the - approximation is desired. - domain : Optional[Interval], default = None + start : float, default=-1.0 + stop : float, default=+1.0 + The domain on which the function is defined and in which the approximation is desired. + domain : Interval, optional Alternative way to specify the function's domain. Returns @@ -149,34 +145,34 @@ def cheb2mps( coefficients: np.polynomial.Chebyshev, initial_mps: Optional[MPS] = None, domain: Optional[Interval] = None, - strategy: Strategy = DEFAULT_CHEBYSHEV_STRATEGY, + strategy: Strategy = DEFAULT_STRATEGY, clenshaw: bool = True, rescale: bool = True, ) -> MPS: """ - Constructs a MPS representation of a function by expanding it on the basis - of Chebyshev polynomials. It takes as input the Chebyshev coefficients of a function - `f(x)` defined in an interval `[a, b]` and, optionally, an initial MPS representing a - function `g(x)` that is taken as the first order polynomial of the expansion. - With this information, it constructs the MPS that approximates `f(g(x))`. + Composes a function on an initial MPS by expanding it on the basis of Chebyshev polynomials. + Allows to load functions on MPS by providing a suitable initial MPS for a given interval. + Takes as input the Chebyshev coefficients of a function `f(x)` defined in an interval `[a, b]` + and, optionally, an initial MPS representing a function `g(x)` that is taken as the first order + polynomial of the expansion. With this information, it constructs the MPS that approximates `f(g(x))`. Parameters ---------- coefficients : np.polynomial.Chebyshev The Chebyshev expansion coefficients representing the target function that is defined on a given interval `[a, b]`. - initial_mps: Optional[MPS], default = None + initial_mps : MPS, optional The initial MPS on which to apply the expansion. - By default (if `rescale` is True), it must have a support inside the domain of + By default (if ``rescale`` is ``True``), it must have a support inside the domain of definition of the function `[a, b]`. - If `rescale` is False, it must have a support inside `[-1, 1]`. - domain : Optional[Interval], default = None + If ``rescale`` is ``False``, it must have a support inside `[-1, 1]`. + domain : Interval, optional An alternative way to specify the initial MPS by constructing it from the given Interval. - strategy : Strategy + strategy : Strategy, default=DEFAULT_STRATEGY The simplification strategy for operations between MPS. - clenshaw : bool, default = True + clenshaw : bool, default=True Whether to use the Clenshaw algorithm for polynomial evaluation. - rescale : bool, default = True + rescale : bool, default=True Whether to perform an affine transformation of the initial MPS from the domain `[a, b]` of the Chebyshev coefficients to the canonical Chebyshev interval `[-1, 1]`. @@ -187,13 +183,25 @@ def cheb2mps( Notes ----- - - The complexity of the expansion depends on the complexity (e.g. maximum bond dimension) - of the intermediate states which are general hard to anticipate and may depend on - the type of coefficients (interpolation, projection) or evaluation method used (Clenshaw, polynomial). + - The computational complexity of the expansion depends on bond dimensions of the intermediate + states. For the case of loading univariate functions on `RegularInterval` domains, these are + bounded by the polynomial order of each intermediate step. In general, these are determined by + the function, the bond dimensions of the initial state and the simplification strategy used. - The Clenshaw evaluation method has a better performance overall, but performs worse when - the interpolation order is overestimated, meaning that the expected accuracy is below - machine precision. + the expansion order is overestimated. This can be avoided using the `estimate_order` method. + + Examples + -------- + .. code-block:: python + + # Load an univariate Gaussian in an equispaced domain. + start, stop = -1, 1 + n_qubits = 10 + func = lambda x: np.exp(-x**2) + coefficients = interpolation_coefficients(func, start=start, stop=stop) + domain = RegularInterval(start, stop, 2**n_qubits) + mps = cheb2mps(coefficients, domain=domain) """ if isinstance(initial_mps, MPS): pass @@ -216,7 +224,7 @@ def cheb2mps( normalized_x = CanonicalMPS( initial_mps, center=0, normalize=True, strategy=strategy ) - logger = make_logger() + logger = make_logger(1) if clenshaw: steps = len(c) logger("MPS Clenshaw evaluation started") @@ -233,7 +241,7 @@ def cheb2mps( strategy=strategy, ) logger( - f"MPS Clenshaw step {i+1}/{steps} with maximum bond dimension {y_i.max_bond_dimension()} and error {y_i.error():6e}" + f"MPS Clenshaw step {i+1}/{steps}, maxbond={y_i.max_bond_dimension()}, error={y_i.error():6e}" ) f_mps = simplify( MPSSum( @@ -246,7 +254,6 @@ def cheb2mps( else: steps = len(c) logger("MPS Chebyshev expansion started") - f_mps = simplify( MPSSum( weights=[c[0] * I_norm, c[1] * x_norm], @@ -270,7 +277,7 @@ def cheb2mps( strategy=strategy, ) logger( - f"MPS expansion step {i+1}/{steps} with maximum bond dimension {f_mps.max_bond_dimension()} and error {f_mps.error():6e}" + f"MPS expansion step {i+1}/{steps}, maxbond={f_mps.max_bond_dimension()}, error={f_mps.error():6e}" ) T_i, T_i_plus_1 = T_i_plus_1, T_i_plus_2 logger.close() @@ -280,29 +287,28 @@ def cheb2mps( def cheb2mpo( coefficients: np.polynomial.Chebyshev, initial_mpo: MPO, - strategy: Strategy = DEFAULT_CHEBYSHEV_STRATEGY, - clenshaw=True, - rescale=True, + strategy: Strategy = DEFAULT_STRATEGY, + clenshaw: bool = True, + rescale: bool = True, ) -> MPO: """ - Constructs a MPO representation of a function by expanding it on the basis - of Chebyshev polynomials. + Composes a function on an initial MPO by expanding it on the basis of Chebyshev polynomials. Parameters ---------- coefficients : np.polynomial.Chebyshev The Chebyshev expansion coefficients representing the target function that is defined on a given interval `[a, b]`. - initial_mpo: MPO + initial_mpo : MPO The initial MPO on which to apply the expansion. - By default (if `rescale` is True), it must have a support inside the domain of + By default (if ``rescale`` is ``True``), it must have a support inside the domain of definition of the function `[a, b]`. - If `rescale` is False, it must have a support inside `[-1, 1]`. - strategy : Strategy + If ``rescale`` is ``False``, it must have a support inside `[-1, 1]`. + strategy : Strategy, default=DEFAULT_STRATEGY The simplification strategy for operations between MPS. - clenshaw : bool, default = True + clenshaw : bool, default=True Whether to use the Clenshaw algorithm for polynomial evaluation. - rescale : bool, default = True + rescale : bool, default=True Whether to perform an affine transformation of the initial MPO from the domain `[a, b]` of the Chebyshev coefficients to the canonical Chebyshev interval `[-1, 1]`. @@ -316,7 +322,7 @@ def cheb2mpo( initial_mpo = mpo_affine(initial_mpo, orig, (-1, 1)) c = coefficients.coef I = MPO([np.eye(2).reshape(1, 2, 2, 1)] * len(initial_mpo)) - logger = make_logger() + logger = make_logger(1) if clenshaw: steps = len(c) logger("MPO Clenshaw evaluation started") @@ -331,7 +337,7 @@ def cheb2mpo( strategy=strategy, ) logger( - f"MPO Clenshaw step {i+1}/{steps} with maximum bond dimension {y_i.max_bond_dimension()}" + f"MPO Clenshaw step {i+1}/{steps}, maxbond={y_i.max_bond_dimension()}" ) f_mpo = simplify_mpo( MPOSum([y_i, MPOList([initial_mpo, y_i_plus_1])], weights=[1, -1]), @@ -355,7 +361,7 @@ def cheb2mpo( strategy=strategy, ) logger( - f"MPO expansion step {i+1}/{steps} with maximum bond dimension {f_mpo.max_bond_dimension()}" + f"MPO expansion step {i+1}/{steps}, maxbond={f_mpo.max_bond_dimension()}" ) T_i, T_i_plus_1 = T_i_plus_1, T_i_plus_2 return f_mpo diff --git a/src/seemps/analysis/factories.py b/src/seemps/analysis/factories.py index 181cf93..83dba8f 100644 --- a/src/seemps/analysis/factories.py +++ b/src/seemps/analysis/factories.py @@ -1,25 +1,11 @@ from __future__ import annotations import numpy as np -from typing import TypeVar, Union +from typing import TypeVar, Union, Optional from ..typing import Tensor3 -from ..state import ( - MPS, - MPSSum, - Strategy, - Truncation, - Simplification, -) -from ..truncate import simplify, SIMPLIFICATION_STRATEGY +from ..state import Strategy, MPS, MPSSum, CanonicalMPS, DEFAULT_STRATEGY +from ..truncate import simplify from .mesh import Interval, RegularInterval, ChebyshevInterval -COMPUTER_PRECISION = SIMPLIFICATION_STRATEGY.replace( - tolerance=float(np.finfo(np.double).eps), - simplification_tolerance=float(np.finfo(np.double).eps), - simplify=Simplification.DO_NOT_SIMPLIFY, - method=Truncation.RELATIVE_SINGULAR_VALUE, - normalize=False, -) - def mps_equispaced(start: float, stop: float, sites: int) -> MPS: """ @@ -55,7 +41,8 @@ def mps_equispaced(start: float, stop: float, sites: int) -> MPS: def mps_exponential(start: float, stop: float, sites: int, c: complex = 1) -> MPS: """ - Returns an MPS representing an exponential function discretized over an interval. + Returns an MPS representing an exponential function discretized over a + half-open interval [start, stop). Parameters ---------- @@ -65,7 +52,7 @@ def mps_exponential(start: float, stop: float, sites: int, c: complex = 1) -> MP The end of the interval. sites : int The number of sites or qubits for the MPS. - c : complex + c : complex, default=1 The coefficient in the exponent of the exponential function. Returns @@ -89,10 +76,14 @@ def mps_exponential(start: float, stop: float, sites: int, c: complex = 1) -> MP def mps_sin( - start: float, stop: float, sites: int, strategy: Strategy = COMPUTER_PRECISION + start: float, + stop: float, + sites: int, + strategy: Strategy = DEFAULT_STRATEGY, ) -> MPS: """ - Returns an MPS representing a sine function discretized over an interval. + Returns an MPS representing a sine function discretized over a + half-open interval [start, stop). Parameters ---------- @@ -102,7 +93,7 @@ def mps_sin( The end of the interval. sites : int The number of sites or qubits for the MPS. - strategy : Strategy, default = DEFAULT_STRATEGY + strategy : Strategy, default=DEFAULT_STRATEGY The MPS simplification strategy to apply. Returns @@ -117,10 +108,14 @@ def mps_sin( def mps_cos( - start: float, stop: float, sites: int, strategy: Strategy = COMPUTER_PRECISION + start: float, + stop: float, + sites: int, + strategy: Strategy = DEFAULT_STRATEGY, ) -> MPS: """ - Returns an MPS representing a cosine function discretized over an interval. + Returns an MPS representing a cosine function discretized over a + half-open interval [start, stop). Parameters ---------- @@ -130,7 +125,7 @@ def mps_cos( The end of the interval. sites : int The number of sites or qubits for the MPS. - strategy : Strategy, default = DEFAULT_STRATEGY + strategy : Strategy, default=DEFAULT_STRATEGY The MPS simplification strategy to apply. Returns @@ -155,7 +150,7 @@ def mps_affine(mps: _State, orig: tuple, dest: tuple) -> _State: Parameters ---------- - mps : MPS + mps : Union[MPS, MPSSum] The MPS to be transformed. orig : tuple A tuple (x0, x1) representing the original interval. @@ -164,8 +159,8 @@ def mps_affine(mps: _State, orig: tuple, dest: tuple) -> _State: Returns ------- - MPS - The transformed MPS. + mps_affine : Union[MPS, MPSSum] + The MPS affinely transformed from (x0, x1) to (u0, u1). """ x0, x1 = orig u0, u1 = dest @@ -181,7 +176,7 @@ def mps_affine(mps: _State, orig: tuple, dest: tuple) -> _State: return mps_affine -def mps_interval(interval: Interval, strategy: Strategy = COMPUTER_PRECISION): +def mps_interval(interval: Interval, strategy: Strategy = DEFAULT_STRATEGY): """ Returns an MPS corresponding to a specific type of interval. @@ -190,12 +185,12 @@ def mps_interval(interval: Interval, strategy: Strategy = COMPUTER_PRECISION): interval : Interval The interval object containing start and stop points and the interval type. Currently supports `RegularInterval` and `ChebyshevInterval`. - strategy : Strategy, default = DEFAULT_STRATEGY + strategy : Strategy, default=DEFAULT_STRATEGY The MPS simplification strategy to apply. Returns ------- - mps : MPS + MPS An MPS representing the interval according to its type. """ start = interval.start @@ -221,7 +216,9 @@ def mps_interval(interval: Interval, strategy: Strategy = COMPUTER_PRECISION): raise ValueError(f"Unsupported interval type {type(interval)}") -def map_mps_locations(mps_list: list[MPS], mps_order: str) -> list[tuple[int, Tensor3]]: +def _map_mps_locations( + mps_list: list[MPS], mps_order: str +) -> list[tuple[int, Tensor3]]: """Create a vector that lists which MPS and which tensor is associated to which position in the joint Hilbert space. """ @@ -246,12 +243,11 @@ def map_mps_locations(mps_list: list[MPS], mps_order: str) -> list[tuple[int, Te return tensors # type: ignore -def mps_tensor_terms(mps_list: list[MPS], mps_order: str) -> list[MPS]: +def _mps_tensor_terms(mps_list: list[MPS], mps_order: str) -> list[MPS]: """ - Extends each MPS of a given input list by appending identity tensors to it - according to the specified MPS order ('A' or 'B'). - The resulting list of MPS can be given as terms to a tensorized operation between MPS, - such as a tensor product or tensor sum. + Extends each MPS of a given input list by appending identity tensors to it according + to the specified MPS order ('A' or 'B'). The resulting list of MPS can be given as terms + to a tensorized operation between MPS, such as a tensor product or tensor sum. Parameters ---------- @@ -278,15 +274,16 @@ def extend_mps(mps_id: int, mps_map: list[tuple[int, Tensor3]]) -> MPS: output[k] = np.eye(D).reshape(D, 1, D) * np.ones((1, site_dimension, 1)) return MPS(output) - mps_map = map_mps_locations(mps_list, mps_order) + mps_map = _map_mps_locations(mps_list, mps_order) return [extend_mps(mps_id, mps_map) for mps_id, _ in enumerate(mps_list)] def mps_tensor_product( mps_list: list[MPS], mps_order: str = "A", - strategy: Strategy = COMPUTER_PRECISION, -) -> MPS: + strategy: Optional[Strategy] = None, + simplify_steps: bool = False, +) -> Union[MPS, CanonicalMPS]: """ Returns the tensor product of a list of MPS, with the sites arranged according to the specified MPS order. @@ -297,12 +294,15 @@ def mps_tensor_product( The list of MPS objects to multiply. mps_order : str The order in which to arrange the resulting MPS ('A' or 'B'). - strategy : optional - The strategy to use when multiplying the MPS. + strategy : Strategy, optional + The strategy to use when multiplying the MPS. If None, the tensor product is not simplified. + simplify_steps : bool, default=False + Whether to simplify the intermediate steps with `strategy` (if provided) + or simplify at the end. Returns ------- - MPS + result : MPS | CanonicalMPS The resulting MPS from the tensor product of the input list. """ if mps_order == "A": @@ -310,20 +310,27 @@ def mps_tensor_product( flattened_sites = [site for sites in nested_sites for site in sites] result = MPS(flattened_sites) elif mps_order == "B": - terms = mps_tensor_terms(mps_list, mps_order) + terms = _mps_tensor_terms(mps_list, mps_order) result = terms[0] for _, mps in enumerate(terms[1:]): - result = result * mps + result = ( + simplify(result * mps, strategy=strategy) + if (strategy and simplify_steps) + else result * mps + ) else: raise ValueError(f"Invalid mps order {mps_order}") - return simplify(result, strategy=strategy) + if strategy and not simplify_steps: + result = simplify(result, strategy=strategy) + return result def mps_tensor_sum( mps_list: list[MPS], mps_order: str = "A", - strategy: Strategy = COMPUTER_PRECISION, -) -> MPS: + strategy: Optional[Strategy] = None, + simplify_steps: bool = False, +) -> Union[MPS, CanonicalMPS]: """ Returns the tensor sum of a list of MPS, with the sites arranged according to the specified MPS order. @@ -332,26 +339,34 @@ def mps_tensor_sum( ---------- mps_list : list[MPS] The list of MPS objects to sum. - mps_order : str + mps_order : str, default='A' The order in which to arrange the resulting MPS ('A' or 'B'). - strategy : optional - The strategy to use when summing the MPS. + strategy : Strategy, optional + The strategy to use when summing the MPS. If None, the tensor sum is not simplified. + simplify_steps : bool, default=False + Whether to simplify the intermediate steps with `strategy` (if provided) + or simplify at the end. Returns ------- - MPS + result : MPS | CanonicalMPS The resulting MPS from the tensor sum of the input list. """ if mps_order == "A": result = _mps_tensor_sum_serial_order(mps_list) elif mps_order == "B": - result = MPSSum( - [1.0] * len(mps_list), mps_tensor_terms(mps_list, mps_order) - ).join() + terms = _mps_tensor_terms(mps_list, mps_order) + result = terms[0] + for _, mps in enumerate(terms[1:]): + result = ( + simplify(result + mps, strategy=strategy) + if (strategy and simplify_steps) + else (result + mps).join() + ) else: raise ValueError(f"Invalid mps order {mps_order}") - if strategy.get_simplify_flag(): - return simplify(result, strategy=strategy) + if strategy and not simplify_steps: + result = simplify(result, strategy=strategy) return result diff --git a/src/seemps/analysis/lagrange.py b/src/seemps/analysis/lagrange.py index a3c865e..b14168e 100644 --- a/src/seemps/analysis/lagrange.py +++ b/src/seemps/analysis/lagrange.py @@ -1,28 +1,26 @@ import numpy as np -from scipy.sparse import dok_matrix, csr_matrix # type: ignore +from scipy.sparse import dok_matrix, csc_array # type: ignore from typing import Callable, Optional from functools import lru_cache -from ..state import MPS, Strategy +from ..state import MPS, Strategy, DEFAULT_STRATEGY from ..state.schmidt import _destructive_svd from ..state._contractions import _contract_last_and_first from ..state.core import destructively_truncate_vector -from ..truncate import simplify, SIMPLIFICATION_STRATEGY +from ..truncate import simplify from .mesh import array_affine # TODO: Implement multivariate Lagrange interpolation and multirresolution constructions -DEFAULT_LAGRANGE_STRATEGY = SIMPLIFICATION_STRATEGY.replace(normalize=False) - def lagrange_basic( func: Callable, order: int, sites: int, - start: float = -1, - stop: float = 1, - strategy: Strategy = DEFAULT_LAGRANGE_STRATEGY, + start: float = -1.0, + stop: float = 1.0, + strategy: Strategy = DEFAULT_STRATEGY, use_logs: bool = True, ) -> MPS: """ @@ -36,15 +34,15 @@ def lagrange_basic( The order of the Chebyshev interpolation. sites : int The number of qubits of the MPS. - start : float + start : float, default=-1.0 The starting point of the function's domain. - stop : float + stop : float, default=1.0 The end point of the function's domain. - strategy : Strategy + strategy : Strategy, default=DEFAULT_STRATEGY The MPS simplification strategy. - use_logs : bool + use_logs : bool, default=True Whether to compute the Chebyshev cardinal function using - logarithms to avoid overflow (default True). + logarithms to avoid overflow. Returns ------- @@ -64,9 +62,9 @@ def lagrange_rank_revealing( func: Callable, order: int, sites: int, - start: float = -1, - stop: float = 1, - strategy: Strategy = DEFAULT_LAGRANGE_STRATEGY, + start: float = -1.0, + stop: float = 1.0, + strategy: Strategy = DEFAULT_STRATEGY, use_logs: bool = True, ) -> MPS: """ @@ -80,15 +78,15 @@ def lagrange_rank_revealing( The order of the Chebyshev interpolation. sites : int The number of qubits of the MPS. - start : float + start : float, default=-1.0 The starting point of the function's domain. - stop : float + stop : float, default=1.0 The end point of the function's domain. - strategy : Strategy + strategy : Strategy, default=DEFAULT_STRATEGY The MPS simplification strategy. - use_logs : bool + use_logs : bool, default=True Whether to compute the Chebyshev cardinal function using - logarithms to avoid overflow (default True). + logarithms to avoid overflow. Returns ------- @@ -123,9 +121,9 @@ def lagrange_local_rank_revealing( order: int, local_order: int, sites: int, - start: float = -1, - stop: float = 1, - strategy: Strategy = DEFAULT_LAGRANGE_STRATEGY, + start: float = -1.0, + stop: float = 1.0, + strategy: Strategy = DEFAULT_STRATEGY, ) -> MPS: """ Performs a local rank-revealing Lagrange MPS Chebyshev interpolation of a function. @@ -142,11 +140,11 @@ def lagrange_local_rank_revealing( The local order of the Chebyshev interpolation. sites : int The number of qubits of the MPS. - start : float + start : float, default=-1.0 The starting point of the function's domain. - stop : float + stop : float, default=1.0 The end point of the function's domain. - strategy : Strategy + strategy : Strategy, default=DEFAULT_STRATEGY The MPS simplification strategy. Returns @@ -275,13 +273,11 @@ def A_L(self, func: Callable, start: float, stop: float) -> np.ndarray: """ Returns the left-most MPS tensor required for Chebyshev interpolation. """ - - def affine_func(u): - return func(array_affine(u, orig=(0, 1), dest=(start, stop))) - A = np.zeros((1, 2, self.D)) for s in range(2): - A[0, s, :] = affine_func(0.5 * (s + self.c)) + A[0, s, :] = func( + array_affine(0.5 * (s + self.c), orig=(0, 1), dest=(start, stop)) + ) return A def A_C(self, use_logs: bool = True) -> np.ndarray: @@ -304,7 +300,7 @@ def A_R(self, use_logs: bool = True) -> np.ndarray: A[i, s, 0] = self.chebyshev_cardinal(np.array([0.5 * s]), i, use_logs) return A - def A_C_sparse(self) -> csr_matrix: + def A_C_sparse(self) -> csc_array: """ Returns the central MPS tensor required for local Chebyshev interpolation. For efficiency, it is represented as a (d+1, 2*(d+1)) sparse matrix (CSR). @@ -317,9 +313,9 @@ def A_C_sparse(self) -> csr_matrix: val = self.local_chebyshev_cardinal(0.5 * (s + c_j), i) if val != 0: A[i, s * self.D + j] = val - return A.tocsr() + return A.tocsc() - def A_R_sparse(self) -> csr_matrix: + def A_R_sparse(self) -> csc_array: """ Returns the right-most MPS tensor required for local Chebyshev interpolation. For efficiency, it is represented as a (d+1, 2) sparse matrix (CSR). @@ -330,4 +326,4 @@ def A_R_sparse(self) -> csr_matrix: val = self.local_chebyshev_cardinal(0.5 * s, i) if val != 0: A[i, s] = val - return A.tocsr() + return A.tocsc() diff --git a/src/seemps/analysis/mesh.py b/src/seemps/analysis/mesh.py index 9aafeab..3d5452b 100644 --- a/src/seemps/analysis/mesh.py +++ b/src/seemps/analysis/mesh.py @@ -8,13 +8,23 @@ class Interval(ABC): - """Interval Abstract Base Class. + """ + Interval Abstract Base Class. - This abstracts an Interval object, which represents implicitly an interval - discretized along N points within two endpoints start and stop. Intervals - act like sequences of numbers denoting points along the interval. They - can be accessed as in `i[0]`, `i[1]`,... up to `i[size-1]` and they can + This class represents implicitly a univariate discretization along `size` + points within two endpoints `start` and `stop`. The elements of an `Interval` + can be indexed as in `i[0]`, `i[1]`,... up to `i[size-1]` and they can be converted to other sequences, as in `list(i)`, or iterated over. + + Parameters + ---------- + start : float + The initial point of the interval. + stop : float + The ending point of the interval. + size : int + The discretization size, i.e. number of points of the interval within + `start` and `stop`. """ start: float @@ -61,9 +71,24 @@ def __iter__(self) -> Iterator: return (self[i] for i in range(self.size)) +class IntegerInterval(Interval): + """Equispaced integer discretization between `start` and `stop` with given `step`.""" + + def __init__(self, start: int, stop: int, step: int = 1): + self.step = step + size = (stop - start + step - 1) // step + super().__init__(start, stop, size) + + def __getitem__(self, idx: Union[int, np.ndarray]) -> Union[int, np.ndarray]: + super()._validate_index(idx) + return self.start + idx * self.step # type: ignore + + class RegularInterval(Interval): - """Equispaced discretization between start and stop. - The left and right boundary conditions can be set open or closed. + """ + Equispaced discretization between `start` and `stop` with `size` points. + The left and right boundary conditions can be set open or closed by + respectively setting the `endpoint_right` and `endpoint_left` flags. Defaults to a closed-left, open-right interval [start, stop). """ @@ -101,10 +126,13 @@ def __getitem__(self, idx: Union[int, np.ndarray]) -> Union[float, np.ndarray]: class ChebyshevInterval(Interval): - """Irregular discretization given by an affine map between the - nodes (zeros or extrema) of the N-th Chebyshev polynomial in [-1, 1] to (start, stop). - If `endpoints=True` returns the Chebyshev extrema defined in the closed interval [a, b]. - Else, returns the Chebyshev zeros defined in the open interval (a, b).""" + """ + Irregular discretization between `start` and `stop` given by the zeros or extrema + of a Chebyshev polynomial of order `size` or `size-1` respectively. + The nodes are affinely transformed from the canonical [-1, 1] interval to [start, stop]. + If `endpoints` is set, returns the Chebyshev extrema, defined in the closed interval [a, b]. + Else, returns the Chebyshev zeros defined in the open interval (start, stop). + """ def __init__(self, start: float, stop: float, size: int, endpoints: bool = False): super().__init__(start, stop, size) @@ -143,8 +171,6 @@ class Mesh: The supplied list of intervals. dimension : int Dimension of the space in which this mesh is embedded. - shape : tuple[int] - Shape of the equivalent tensor this Mesh can be converted to. dimensions : tuple[int] Tuple of the sizes of each interval """ @@ -200,19 +226,18 @@ def to_tensor(self): def array_affine( - x: np.ndarray, + array: np.ndarray, orig: tuple, dest: tuple, ) -> np.ndarray: """ - Performs an affine transformation of x as u = a*x + b from orig=(x0, x1) to dest=(u0, u1). + Performs an affine transformation of a given `array` as u = a*x + b from orig=(x0, x1) to dest=(u0, u1). """ - # TODO: Maybe combine the affine transformations for vectors, MPS and MPO in a single function? x0, x1 = orig u0, u1 = dest a = (u1 - u0) / (x1 - x0) b = 0.5 * ((u1 + u0) - a * (x0 + x1)) - x_affine = a * x + x_affine = a * array if abs(b) > np.finfo(np.float64).eps: x_affine = x_affine + b return x_affine @@ -222,8 +247,17 @@ def mps_to_mesh_matrix( sites_per_dimension: list[int], mps_order: str = "A", base: int = 2 ) -> np.ndarray: """ - Returns a matrix that transforms an array of MPS indices - to an array of Mesh indices based on the specified order and base. + Returns a matrix that transforms an array of `MPS` indices + to an array of `Mesh` indices based on the specified order and base. + + Parameters + ---------- + sites_per_dimension : list[int] + The number of MPS sites allocated to each spatial dimension. + mps_order : str, default='A' + The order of the MPS sites, either serial ('A') or interleaved ('B'). + base : int, default=2 + The base or physical dimension of the MPS. """ if mps_order == "A": T = np.zeros((sum(sites_per_dimension), len(sites_per_dimension)), dtype=int) diff --git a/src/seemps/analysis/sampling.py b/src/seemps/analysis/sampling.py index bf77866..5a51dc2 100644 --- a/src/seemps/analysis/sampling.py +++ b/src/seemps/analysis/sampling.py @@ -19,9 +19,10 @@ def evaluate_mps(mps: MPS, mps_indices: np.ndarray) -> np.ndarray: Returns ------- np.ndarray - The array of evaluations corresponding to the provided indices.""" + The array of evaluations corresponding to the provided indices. + """ if mps_indices.ndim == 1: - mps_indices = mps_indices[np.newaxis, :] + mps_indices = mps_indices.reshape(1, -1) A = mps[0][:, mps_indices[:, 0], :] for idx, site in enumerate(mps[1:]): B = site[:, mps_indices[:, idx + 1], :] @@ -31,7 +32,7 @@ def evaluate_mps(mps: MPS, mps_indices: np.ndarray) -> np.ndarray: def random_mps_indices( - mps: MPS, + physical_dimensions: list[int], num_indices: int = 1000, allowed_indices: Optional[list[int]] = None, rng: np.random.Generator = np.random.default_rng(), @@ -41,21 +42,24 @@ def random_mps_indices( Parameters ---------- - mps : MPS - The matrix product state to sample from. - num_indices : int, default 1000 + physical_dimensions : list[int] + The physical dimensions of the MPS. + num_indices : int, default=1000 The number of random indices to generate. + allowed_indices : list[int], optional + An optional list with allowed values for the random indices. rng : np.random.Generator, default=`numpy.random.default_rng()` The random number generator to be used. If None, uses Numpy's default random number generator without any predefined seed. - allowed_indices : Optional[tuple], default=None - An optional tuple with allowed values for the random indices. + Returns ------- - indices : np.ndarray - An array of random MPS indices.""" + np.ndarray + An array of random MPS indices. + """ + # TODO: Implement quasi-random sampling to reduce sampling variance. mps_indices = [] - for k in mps.physical_dimensions(): + for k in physical_dimensions: indices = list(range(k)) if allowed_indices is not None: indices = list(set(indices) & set(allowed_indices)) diff --git a/src/seemps/truncate/simplify_mpo.py b/src/seemps/truncate/simplify_mpo.py index 1e75747..323358b 100644 --- a/src/seemps/truncate/simplify_mpo.py +++ b/src/seemps/truncate/simplify_mpo.py @@ -27,8 +27,6 @@ def mps_as_mpo( ) -# TODO: As opposed to MPS, the MPO class does not have an error attribute to keep track -# of the simplification errors def simplify_mpo( operator: Union[MPO, MPOList, MPOSum], strategy: Strategy = SIMPLIFICATION_STRATEGY, @@ -36,26 +34,27 @@ def simplify_mpo( guess: Optional[MPS] = None, mpo_strategy: Strategy = DEFAULT_STRATEGY, ) -> MPO: - """Simplify an MPO transforming it into another one with a smaller bond + """Simplify an MPO state transforming it into another one with a smaller bond dimension, sweeping until convergence is achieved. Parameters ---------- operator : Union[MPO, MPOList, MPOSum] - Operator to approximate. - strategy : Strategy - Truncation strategy. Defaults to `SIMPLIFICATION_STRATEGY`. - direction : { +1, -1 } - Initial direction for the sweeping algorithm. Defaults to +1. - guess : MPS - A guess for the new state, to ease the optimization. Defaults to None. - mpo_strategy : Strategy - Strategy of the resulting MPO. Defaults to `DEFAULT_STRATEGY`. + MPO to simplify. If given as `MPOList` or `MPOSum`, it is joined to `MPO` + before the simplification. + strategy : Strategy, default=SIMPLIFICATION_STRATEGY + Truncation strategy to use in the simplification routine. + direction : int, default=1 + Initial direction for the sweeping algorithm. + guess : MPS, optional + Guess for the new state, to ease the optimization. + mpo_strategy : Strategy, default=DEFAULT_STRATEGY + Strategy of the resulting MPO. Returns ------- MPO - Approximation O to the operator. + Approximation O to the operator. """ if isinstance(operator, MPOList) or isinstance(operator, MPOSum): operator = operator.join() diff --git a/tests/test_analysis/test_chebyshev.py b/tests/test_analysis/test_chebyshev.py index e4e4a94..a0fe0fd 100644 --- a/tests/test_analysis/test_chebyshev.py +++ b/tests/test_analysis/test_chebyshev.py @@ -2,11 +2,10 @@ from numpy.polynomial import Chebyshev from scipy.special import erf -from seemps.state import MPS, NO_TRUNCATION +from seemps.state import MPS, NO_TRUNCATION, DEFAULT_STRATEGY from seemps.analysis.mesh import RegularInterval from seemps.analysis.factories import mps_tensor_sum, mps_interval from seemps.analysis.chebyshev import ( - DEFAULT_CHEBYSHEV_STRATEGY, interpolation_coefficients, projection_coefficients, cheb2mps, @@ -227,7 +226,7 @@ def test_gaussian_2d(self): [mps_interval(interval_y), mps_interval(interval_x)] # type: ignore ) tol = 1e-10 - strategy = DEFAULT_CHEBYSHEV_STRATEGY.replace( + strategy = DEFAULT_STRATEGY.replace( tolerance=tol**2, simplification_tolerance=tol**2 ) mps_cheb_clen = cheb2mps( From f1b1d7a0261f75ad3f2d42d8f4fd252f4fb03453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Rodr=C3=ADguez=20Aldavero?= Date: Fri, 5 Jul 2024 10:07:40 +0200 Subject: [PATCH 5/7] Add notebooks to examples --- examples/Chebyshev.ipynb | 369 ++++++++++++++++++++++++++++++++++++ examples/DMRG.ipynb | 4 +- examples/Integration.ipynb | 135 +++++++++++++ examples/Optimization.ipynb | 211 +++++++++++++++++++++ examples/TT-Cross.ipynb | 232 +++++++++++++++++++++++ 5 files changed, 948 insertions(+), 3 deletions(-) create mode 100644 examples/Chebyshev.ipynb create mode 100644 examples/Integration.ipynb create mode 100644 examples/Optimization.ipynb create mode 100644 examples/TT-Cross.ipynb diff --git a/examples/Chebyshev.ipynb b/examples/Chebyshev.ipynb new file mode 100644 index 0000000..55fc74f --- /dev/null +++ b/examples/Chebyshev.ipynb @@ -0,0 +1,369 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Approximating and composing functions with Chebyshev expansions\n", + "\n", + "This notebook shows how to compose univariate functions with generic matrix product states using the Chebyshev approximation algorithm. This algorithm can be used to load smooth univariate functions, as well as multivariate functions whose algebraic structure is given by univariate function composition. The main idea is that a Chebyshev expansion\n", + "\n", + "$$\n", + "f(x) = \\sum_{k=0}^{d} c_k T_k(x),\n", + "$$\n", + "\n", + "where $T_i$ are the Chebyshev polynomials and $c_i$ are the coefficients of the expansion, can be expressed in MPS form as \n", + "\n", + "$$\n", + "f(\\ket{\\psi}) = \\sum_{k=0}^d c_k T_k(\\ket{\\psi}).\n", + "$$\n", + "\n", + "The Chebyshev polynomials can be computed following a simple recurrence relation $T_{k+1}(x) = 2x T_k(x) - T_{k-1}(x)$ with $T_1(x) = x$ and $T_0(x) = 1$. In practice it is more efficient to use the Clenshaw summation method, which evaluates the partial sum in a more efficient manner and does not require to compute the polynomials $T_k(x)$ separately." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from seemps.analysis.mesh import RegularInterval\n", + "from seemps.analysis.factories import mps_interval\n", + "from seemps.analysis.chebyshev import (\n", + " interpolation_coefficients,\n", + " projection_coefficients,\n", + " cheb2mps,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 1: Loading univariate functions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first step is to compute the coefficients of the expansion. There are several types of coefficients, given by either the projection of the function on the Chebyshev basis or by interpolation or collocation of the function in a collection of nodes, either the Chebyshev zeros (defined in an open domain) or the Chebyshev extrema (defined in a closed domain). The former are computed by integration, while the latter can be computed by an efficient Discrete Cosine Transform (DCT).\n", + "\n", + "For this example we load a univariate Gaussian function. The number of coefficients of the expansion ---i.e. the approximation order $d$---can be given or estimated automatically with the function `estimate_order()`." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "func = lambda x: np.exp(-(x**2))\n", + "\n", + "# Define the domain of the function in (-1, 1):\n", + "start, stop = -1, 1\n", + "coeffs_interp = interpolation_coefficients(func, start=start, stop=stop)\n", + "coeffs_proj = projection_coefficients(func, start=start, stop=stop)\n", + "\n", + "# Alternatively, the domain of the function can be given by an `Interval object`:\n", + "num_qubits = 6\n", + "domain = RegularInterval(start, stop, 2**num_qubits)\n", + "coeffs_interp = interpolation_coefficients(func, domain=domain)\n", + "coeffs_proj = projection_coefficients(func, domain=domain)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, construct the initial MPS on which to compose the function. For the case of function loading, we construct the initial MPS from the interval, so that it represents a regular, equispaced interval $\\ket{x} \\in [-1, 1)$. However, the method is agnostic on the initial interval, as long as it is normalized to have a support in the canonical Chebyshev interval $[-1, 1]$." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "mps_x = mps_interval(domain)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, compose the function with the initial MPS using the `cheb2mps` function. By default, it uses the Clenshaw summation method. The cost of the algorithm depends on the bond dimension of the intermediate steps, which can be parameterized with the simplification strategy provided. For this example, we use the default `DEFAULT_STRATEGY`, which has an error of the order of $10^{-8}$." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "mps_gaussian = cheb2mps(coeffs_interp, initial_mps=mps_x)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error in Chebyshev norm: 6.906256677652323e-09\n" + ] + }, + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'f(x)')" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "x = domain.to_vector()\n", + "y_vec = func(x)\n", + "y_mps = mps_gaussian.to_vector()\n", + "print(\"Error in Chebyshev norm: \", np.max(np.abs(y_vec - y_mps)))\n", + "\n", + "plt.plot(x, y_vec, label=\"Exact\")\n", + "plt.plot(x, y_mps, \"o\", label=\"MPS\")\n", + "plt.legend()\n", + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"f(x)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 2: Loading multivariate functions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Because the method is agnostic to the initial MPS, it can be used to load some multivariate functions that are given by function composition. In this example, we show how to load a bivariate product Gaussian function,\n", + "\n", + "$$\n", + "f(x, y) = e^{-(x^2 + y^2)}\n", + "$$\n", + "\n", + "by composing a function $g(x) = e^{-x}$ with an initial polynomial $h(x, y) = x^2 + y^2$ expressed in MPS form." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "from seemps.analysis.factories import mps_tensor_sum" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We construct the initial MPS for $h(x,y)$ using the MPS tensor sum. Now, there are two possible qubit orders, *serial* or 'A' (by dimension) and *interleaved* or 'B' (by significance). The former is specially suitable for separable functions, such as the product Gaussian, while the latter is suitable for some multivariate functions with highly-correlated degrees of freedom." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "func = lambda x: np.exp(-x)\n", + "\n", + "start, stop = -1, 1\n", + "coeffs = interpolation_coefficients(func, start=start, stop=stop)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "num_qubits = 6\n", + "domain = RegularInterval(start, stop, 2**num_qubits)\n", + "mps_x = mps_interval(domain)\n", + "\n", + "mps_x_squared = mps_x * mps_x # x^2\n", + "mps_domain_A = mps_tensor_sum(\n", + " [mps_x_squared, mps_x_squared], mps_order=\"A\"\n", + ") # x^2 + y^2 serial\n", + "mps_domain_B = mps_tensor_sum(\n", + " [mps_x_squared, mps_x_squared], mps_order=\"B\"\n", + ") # x^2 + y^2 interleaved" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The \"tricky\" part is matching the domain of definition of the Chebyshev coefficients with the support of the initial MPS. By default, they must match, so that the initial MPS can be affinely transformed to the canonical Chebyshev interval $[-1, 1]$. It is more intuitive to compute first the domain MPS and define the Chebyshev coefficient afterwards on its support.\n", + "\n", + "In the example above, $h(x, y)$ has a support in $[0, x^2 + y^2) = [0, 2)$." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "func = lambda x: np.exp(-x)\n", + "coeffs = interpolation_coefficients(func, start=0, stop=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, load the function by composing $g(x)$ with the initial MPS for $h(x, y)$." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "mps_gaussian_2d_A = cheb2mps(coeffs, mps_domain_A)\n", + "mps_gaussian_2d_B = cheb2mps(coeffs, mps_domain_B)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [], + "source": [ + "def reorder_tensor(tensor, sites_per_dimension):\n", + " \"\"\"\n", + " Reorders a given tensor between the MPS orderings 'A' and 'B' by transposing its axes.\n", + " \"\"\"\n", + " dimensions = len(sites_per_dimension)\n", + " shape_orig = tensor.shape\n", + " tensor = tensor.reshape([2] * sum(sites_per_dimension))\n", + " axes = [\n", + " np.arange(idx, dimensions * n, dimensions)\n", + " for idx, n in enumerate(sites_per_dimension)\n", + " ]\n", + " axes = [item for items in axes for item in items]\n", + " tensor = np.transpose(tensor, axes=axes)\n", + " return tensor.reshape(shape_orig)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error in Chebyshev norm in serial order: 1.068270905069113e-08\n", + "Error in Chebyshev norm in interleaved order: 5.0399591944305655e-08\n" + ] + } + ], + "source": [ + "x = domain.to_vector()\n", + "X, Y = np.meshgrid(x, x)\n", + "y_vec = np.exp(-(X**2 + Y**2))\n", + "\n", + "shape = (x.size, x.size)\n", + "y_mps_A = mps_gaussian_2d_A.to_vector().reshape(shape)\n", + "y_mps_B = mps_gaussian_2d_B.to_vector().reshape(shape)\n", + "\n", + "# Note: the interleaved tensor must be reshaped to be compared with the serial tensor.\n", + "# This is not a good practice so the required function is not present in the library.\n", + "y_mps_B = reorder_tensor(y_mps_B, [num_qubits, num_qubits])\n", + "\n", + "print(\"Error in Chebyshev norm in serial order: \", np.max(np.abs(y_vec - y_mps_A)))\n", + "print(\"Error in Chebyshev norm in interleaved order: \", np.max(np.abs(y_vec - y_mps_B)))" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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GEjz5hsaGqK5udn7e8GCBVlJBuC5555DKcWyZcuSHo9Go96+bdvmi1/8Ij//+c85efIk0WiUaDSaJK654YYbePe73823v/1tAoEADz30EG9+85s9g9Xt27fzzDPPeBGMu99IJEI4HGbv3r3U1dV5JAOwcePGrOc905EjmlmK1N4YpRSHDh3i6NGjLF++nNra2hFHLE9lRPNCtaCZTed80003cdttt6FpGvfdd1/W7a688kq+9a1v4fP5mDdvnqdocyNry7Koq6vj+uuv581vfjO33347q1ev5uc//zk333xz0r4SiSUxxQaOSMB9zr2OI31npZRUV1cn1VJclJSUeP99NqWllBJd19m+fXvacD93ouzXvvY1vv71r3Pvvfdy4YUXkp+fz2233ZYkenj1q1+NlJL/+Z//4dJLL2Xz5s3cc889Sce5++67ecMb3pB2DsFgMKs/4GxFjmhmGTKNWI5EIuzcuZNYLMaGDRsoLCwccR+5iCaHVFx99dVereWaa67Jul1+fj5LlixJezxxzg4Mf08XLVpEXl4eXV1dDAwMpI1EyNYU6hJP4k2RS0x+v9+L4l2sWbOGtrY2DMNg4cKF474Oq1evxrZt2tvbueKKKzJus3nzZl772tfytre9zTvXgwcPsnLlSm+bUCjEG97wBh566CEOHTrEsmXLWLt2bdL57t+/P+O1BDj//PM5fvw4p06d8lJ4W7duHff7mm7kiGYWIVNvTHt7O7t27aKyspK1a9eOSiaai2hySIWu6zzzzDPk5eWNekx3Ku6++27C4TDXXXcdCxYsoKenh29+85uYpskrX/lKgsFgmjN1NBoF8P524f63GykBXsRTV1fHkSNH2LFjB7W1tRQWFvKyl72MjRs38rrXvY4vfelLLF++nFOnTvHoo4/yute9jnXr1o3qPSxbtowbbriBm266ia997WusXr2ajo4O/va3v3HhhRfyile8giVLlvDrX/+aLVu2UFpayj333ENbW1sS0YCTPnv1q1/N7t27PVJy8alPfYpXvepV1NbW8qY3vQlN09i5cydNTU189rOf5WUvexnLly/3zqOvr48777xzXJ/LTEBOdTZLkNobo5Ri7969NDU1cf7557Nq1apR9yJMd0TT19fHM888Q2NjI8eOHaOvr29U55OLaCYXRUVF52Sq+qIXvYgjR45w8803c8EFF/Cq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mZkxbty4KxYiNDc3c+bMGV566SUOHjyIEIK5c+cyb948br31VpM2p4K0dJLIGc0Nj67kderUqXrHp06danTdOz4+vt3506ZNIyEhgaamJpON9UbFzMyMCRMm8I9//IPz589z4MABhg8fzvLly/Hz8+PBBx/k008/paKigit979NFT+tSQTtT7aYLd4uIiMDR0ZE333yThoYGpk2bxuDBgzlz5kxP3apBXF1dGTFiRId/rK2tiYiIoKKigp9//lm99qeffqKiooKJEyd2+vNKSkrIysrCy8vLFLcj6QZSaG5wiouL0Wg07cpWPTw8jK575+fnGzy/ubn5it9MJR1jZmZGWFgYK1euJCUlhZ9//pnQ0FBWr16Nn58fDzzwAB999BElJSXtREej0XDixAmEEISEhHS6pFoIwZtvvsnatWvZt28fTz/9NFu2bKGwsJANGzYwdOhQU9xqlwkKCmL69OnMmzePI0eOcOTIEebNm8fMmTP1Ks5GjBjBtm3bgBbzz2effZb4+HgyMjKIi4tj1qxZuLq6MmfOnGt1K5I2SKG5SWi7PHKldW9D5xs6Luk+iqIwevRoXnnlFU6dOsXJkye57bbb1If/7Nmz2bhxI4WFhVRXV/PQQw+Rm5vbZZF55513WL16Nbt372bs2LHqa5aWlkRGRvYpZ+ZPPvmE0aNHM3XqVKZOncqYMWPYsmWL3jmpqalUVLREXPfr14/Tp08TFRVFYGAgjz76KIGBgcTHx0vfwD6EzGO9wXF1daVfv37tZi+FhYVGm/M8PT0Nnm9ubo6Li4vJxnozoygKI0aM4C9/+QsvvvgiFy9eJCYmho8//pjFixdjZ2eHra0tr7zySpdEZt26daxatYpdu3YRHh5u4ru4epydnfn44487PKf1TM/Gxobdu3ebeliSq0TOaG5wLC0tCQsLY+/evXrH9+7da3TdOyIiot35e/bsITw8XMb49gKKojBs2DCWLFnCgQMHmDx5Mvb29gwePJjx48dzzz338K9//YvMzEyjezpCCD744ANeffVVvv76ayZMmNDLdyGRtEJIbng+++wzYWFhITZu3CiSkpLEokWLRP/+/UVGRoYQQog///nP4re//a16flpamrC1tRWLFy8WSUlJYuPGjcLCwkJs3br1Wt3CTYlGoxFRUVEiPDxclJWVCa1WK7Kzs8U777wjpkyZIszN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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = plt.figure()\n", + "ax = fig.add_subplot(111, projection=\"3d\")\n", + "ax.plot_surface(X, Y, y_vec, label=\"Exact\")\n", + "ax.plot_surface(X, Y, y_mps_A, label=\"MPS serial\")\n", + "ax.plot_surface(X, Y, y_mps_B, label=\"MPS interleaved\")\n", + "ax.legend()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "seemps", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.1.-1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/DMRG.ipynb b/examples/DMRG.ipynb index d8d5ab2..0410bd1 100644 --- a/examples/DMRG.ipynb +++ b/examples/DMRG.ipynb @@ -162,7 +162,6 @@ "from time import process_time\n", "\n", "\n", - "\n", "def experiment_00(N: int = 30, steps: int = 21):\n", " \"\"\"Explore the quantum phase transition as a function of the transverse\n", " magnetic field $h$. Plots the magnetizations of the ground state, computed\n", @@ -210,7 +209,6 @@ " fig.tight_layout()\n", "\n", "\n", - "\n", "experiment_00(N=15)" ] }, @@ -288,7 +286,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.1.-1" } }, "nbformat": 4, diff --git a/examples/Integration.ipynb b/examples/Integration.ipynb new file mode 100644 index 0000000..8af4eec --- /dev/null +++ b/examples/Integration.ipynb @@ -0,0 +1,135 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Integration of functions loaded in MPS" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook shows how to integrate functions loaded in MPS by contracting them with predefined quadrature MPS. These quadrature MPS can be straightforwardly generalized to multivariate scenarios by means of tensor products in both qubit orders, either serial or interleaved. Moreover, this integration procedure avoids the *curse of dimensionality*, as it has a cost that scales polynomially with the dimension of the function instead than exponentially.\n", + "\n", + "The main prerequisite for this *quantum-inspired* integration method is to dispose of a prior multivariate function loaded in MPS. This can be done following several methods, such as MPS Chebyshev expansions (see the `chebyshev_composition.ipynb` example), tensor cross-interpolation (TCI, see the `tt-cross.ipynb` example), multiscale interpolative constructions, etc.\n", + "\n", + "For this example, we are going to load a trivially simple multivariate function \n", + "\n", + "$f(x_1, \\ldots, x_{10}) = \\prod_{i=1}^{10} x_i^3$.\n", + "\n", + "The integral of this function in $\\Omega = [-1, 1] \\times \\ldots \\times [-1, 1]$ is naively zero. Even though it can be analytically constructed, we use tensor cross-interpolation as it generalizes to a wider range of functions. Then, we integrate it following a Clenshaw-Curtis quadrature rule." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Cross sweep 1 with error(1000 samples in norm-inf)=3.2845046914370504, maxbond=2, evals(cumulative)=228\n", + " Cross sweep 1 with error(1000 samples in norm-inf)=2.842170943040401e-14, maxbond=3, evals(cumulative)=816\n", + " State converged within tolerance 1e-12\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "from seemps.analysis.mesh import ChebyshevInterval, Mesh\n", + "from seemps.analysis.cross import BlackBoxLoadMPS, cross_dmrg\n", + "import seemps.tools\n", + "\n", + "seemps.tools.DEBUG = 2\n", + "\n", + "start, stop = -1, 1\n", + "num_qubits = 3\n", + "interval = ChebyshevInterval(\n", + " start, stop, 2**num_qubits, endpoints=True\n", + ") # Chebyshev extrema\n", + "\n", + "dimension = 10\n", + "mesh = Mesh([interval] * dimension)\n", + "\n", + "func = lambda tensor: np.sum(tensor**3, axis=0) # x^3 * y^3 * ...\n", + "black_box = BlackBoxLoadMPS(func, mesh)\n", + "\n", + "mps_func = cross_dmrg(black_box).mps" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we construct the quadrature MPS. We can integrate the function directly using the auxiliar routine `integrate_mps`, but in this example we construct the quadrature manually." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "from seemps.analysis.integration import mps_clenshaw_curtis\n", + "from seemps.analysis.factories import mps_tensor_product\n", + "\n", + "mps_quad_1d = mps_clenshaw_curtis(start, stop, num_qubits)\n", + "mps_quad_10d = mps_tensor_product([mps_quad_1d] * dimension)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, integrate the MPS by taking the scalar product `scprod` of the function MPS with the quadrature MPS." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Integration error: 4.195754854663392e-12\n" + ] + } + ], + "source": [ + "from seemps.state import scprod\n", + "\n", + "integral_exact = 0\n", + "integral_mps = scprod(mps_func, mps_quad_10d)\n", + "\n", + "print(\"Integration error: \", np.max(np.abs(integral_exact - integral_mps)))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "seemps", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/Optimization.ipynb b/examples/Optimization.ipynb new file mode 100644 index 0000000..6996e34 --- /dev/null +++ b/examples/Optimization.ipynb @@ -0,0 +1,211 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Optimization of functions loaded in MPS" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook shows how to optimize a MPS, that is, find its minimum and maximum value together their corresponding indices, using the routine `optimize_mps`. The underlying optimization method is `optima_tt` (https://arxiv.org/abs/2209.14808). This method requires polynomial resources for multi-qubit states and hence avoids the curse of dimensionality.\n", + "\n", + "In this example, we are going to apply this method to compute the extrema of a linear combination of harmonic functions.\n", + "\n", + "$$\n", + " f(x) = \\sum_{k=1}^{N} a_k\\cos(\\omega_k x + \\phi_k),\n", + "$$\n", + "\n", + "where $a_k$, $\\omega_k$ and $\\phi_k$ are taken at random. For demonstration purposes, this function is loaded in MPS using the SVD decomposition. However, the scope of the method is the optimization of exponentially large functions whose tensors are computationally intractable, which can be built using other function loading algorithms such as Chebyshev approximations, tensor cross-interpolation, solutions of PDEs, etc." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from seemps.state import MPS\n", + "from seemps.analysis.mesh import RegularInterval\n", + "from seemps.analysis.optimization import optimize_mps" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "num_terms = 100\n", + "amplitudes = np.random.uniform(0.5, 2.0, num_terms)\n", + "frequencies = np.random.uniform(100.0, 200.0, num_terms)\n", + "phases = np.random.uniform(0, 2 * np.pi, num_terms)\n", + "\n", + "\n", + "def func(x):\n", + " y = np.zeros_like(x)\n", + " for a, φ, ω in zip(amplitudes, phases, frequencies):\n", + " y += a * np.cos(ω * x + φ)\n", + " return y" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Min: -34.68646224704873 Max: 33.87831269613038\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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1KlTcd5558HzPPzsZz/DAQccgO9+97s488wzcfrpp2Pu3LlYvnz5uJ9fBwcHh04F1/REpboK5YnR2qKrAh8O3/dRKBSw2267Yd68ebj99ttxyCGHAACKxSLuuusufPOb32zzKOuoVIAzzzSDHqD2mOcBZ50FfPCD45L2+u1vf4spU6Zoj51//vn4yle+AgD4+te/jt/97nf4+7//ezz99NM4+eST8aEPfSg49sMf/rD2tz/5yU+w00474ZlnnsGSJUvw85//HG+88QYefvhhzJw5EwCCIBUApkyZgkwmg3nz5sWOdebMmfjud7+LVCqFvffeG9/61rcwNDSEL33pSwBqLN03vvEN/PGPf8RJJ50EAPjyl78c/P2uu+6Kc889F7/4xS9w3nnnAQB23nlnXH311Tj55JPx+uuv4z//8z/x2GOPIZvNJj6HDg4ODhMNPLUVleoqlMLnRkrda3TYNYHPl770Jbz3ve/FokWLsH37dtxwww248847ceutt8LzPJx11lm45JJLsOeee2LPPffEJZdcgr6+Pnzyk59s99BruOceYPVq+/O+D6xaVTtuHDq3H3300bjqqqu0x1SAAtTYtH/7t3/DgQceiF122QVXXnmlduxLL72Er3zlK3jggQewYcMGVOt06MqVK7FkyRI8/vjjOOSQQ7TXHC32339/pFJhFnbu3LlYsmRJ8Hs6ncasWbOwfv364LFf/epXuPLKK/Hiiy9ix44dKJfLGBgY0F73ox/9KG6++WZceumluOqqq7DXXnuNeawODg4O3QxuWBhV1UXbWQw7xmf88frrr+Pkk0/G2rVrMW3aNBx44IG49dZbccwxxwAAzjvvPAwPD+OMM87A5s2b8da3vhW33XYbpk6d2uaR17E2oedB0uMaRH9/v8bASFAi8U2bNmHTpk3o7+8Pnjv++OOxaNEiXHPNNViwYAGq1SqWLFmCYrEmduvt7W3aWDkL43me+JgKvh544AGcdNJJuOiii3Dcccdh2rRpuOGGG3D55ZdrfzM0NIRHH30U6XQaL7zwQtPG6+Dg4NCtaITxoemtggt8xh8/+clPIp/3PA/Lly/vXM0G0R415bgm46WXXsLZZ5+Na665Br/85S/xqU99Cr///e+RSqWwceNGPPvss7j66qtx5JFHAgDuvfde7e8PPPBA/PjHP8amTZtE1ieXy6FSGZ8b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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "start = -1\n", + "stop = 1\n", + "num_qubits = 15\n", + "domain = RegularInterval(start, stop, 2**num_qubits)\n", + "\n", + "x = domain.to_vector()\n", + "y_vec = func(x)\n", + "\n", + "i_min_vec = np.argmin(y_vec)\n", + "y_min_vec = y_vec[i_min_vec]\n", + "i_max_vec = np.argmax(y_vec)\n", + "y_max_vec = y_vec[i_max_vec]\n", + "\n", + "print(\"Min: \", y_min_vec, \" Max: \", y_max_vec)\n", + "\n", + "plt.plot(x, y_vec)\n", + "plt.scatter(x[i_min_vec], y_min_vec, color=\"r\", label=\"Exact min.\")\n", + "plt.scatter(x[i_max_vec], y_max_vec, color=\"r\", label=\"Exact max.\")\n", + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"f(x)\")\n", + "plt.legend()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Maxbond: 16\n", + "Loading error: 1.9786703573387854e-06\n" + ] + } + ], + "source": [ + "# Load the function in MPS using SVD\n", + "mps = MPS.from_vector(y_vec, [2] * num_qubits, normalize=False)\n", + "y_mps = mps.to_vector()\n", + "err = np.max(np.abs(y_mps - y_vec))\n", + "print(\"Maxbond: \", mps.max_bond_dimension())\n", + "print(\"Loading error: \", err)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Min: -34.68646231285264 Max: 33.87831276218731\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj4AAAGwCAYAAACpYG+ZAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAADihklEQVR4nOz9ebxcVZU2jj813bpDcm8SQiZIGGQeRUAFVIJKAGlbmraVRhkceJumVVAQm3ZK9yvQDtjY2s4K/Lpb0a/g8EK3DSpjgwxhUEYZEhJIQgiZc4eazu+Pqn3O2muvferUvXVruHc9n08+ubfq3Kpdp87Ze+1nPetZqSAIAigUCoVCoVBMA6TbPQCFQqFQKBSKVkEDH4VCoVAoFNMGGvgoFAqFQqGYNtDAR6FQKBQKxbSBBj4KhUKhUCimDTTwUSgUCoVCMW2ggY9CoVAoFIppg2y7B9BpqFQqWLt2LWbOnIlUKtXu4SgUCoVCoUiAIAiwfft2LFq0COm0n9fRwIdh7dq1WLx4cbuHoVAoFAqFYhxYs2YNdt99d+/zGvgwzJw5E0D1xA0ODrZ5NAqFQqFQKJJg27ZtWLx4cbiO+6CBD4NJbw0ODmrgo1AoFApFl6GeTEXFzQqFQqFQKKYNNPBRKBQKhUIxbaCBj0KhUCgUimkD1fiMA5VKBYVCod3DUDQBuVwOmUym3cNQKBQKRYuggU+DKBQKWLlyJSqVSruHomgSZs2ahQULFqhvk0KhUEwDaODTAIIgwLp165DJZLB48eJYgyRF5yMIAgwPD2PDhg0AgIULF7Z5RAqFQqGYbGjg0wBKpRKGh4exaNEi9Pf3t3s4iiagr68PALBhwwbMmzdP014KhUIxxaGURQMol8sAgJ6enjaPRNFMmCC2WCy2eSQKhUKhmGxo4DMOqBZkakG/T4VCoZg+0FSXQqFQKBTTGZUy8MpdwMg6oG8hsOubgfTUTftr4KNQKBQKxXTFmhuBFRcCwy9Gj/XvDhz5NWDx6e0b1yRCU12Kjsa5556L0047rd3DUCgUiqmHNTcCd73bDnoAYPil6uNrbmzPuCYZGvi0A5Uy8PLtwKofV/+vlCf17c4991ykUimcf/75znMXXHABUqkUzj33XOf4VCqFXC6HvffeG5dccgl27twZHnPDDTfgDW94A4aGhjBz5kwcfPDBuPjii5s+9q997Wu49tprm/66CoVCMa1RKVeZHgQAgNFKDpXA6B2rj2HFRZO+PrUDGvi0GmtuBH61J/DbE4B7zqz+/6s9Jz2yXrx4Ma6//nqMjIyEj42OjuLHP/4xlixZ4hx/8sknY926dXj++efxhS98Ad/85jdxySWXAAB+85vf4IwzzsC73/1u3H///VixYgUuv/zySXGzHhoawqxZs5r+ugqFQjGt8cpdIdOzcmwRDn/8enx8Dd28BsDwmupxUwwa+LQSbaQVX/e612HJkiW48cboPW688UYsXrwYRxxxhHN8Pp/HggULsHjxYpx55pl43/veh1/84hcAgJtuuglvetOb8MlPfhL7778/9ttvP5x22mn4+te/7n3/VatWIZVK4ac//Sne/OY3o6+vD0cffTT+9Kc/4YEHHsBRRx2FGTNm4OSTT8Yrr7wS/h1PdS1duhQf+9jHcOmll2LOnDlYsGABli9fPuHzo1AoFNMKI+vCH2/e8iaMBXn8csvS2OOmCjTwaRUYrWijNbTiBz7wAVxzzTXh7z/84Q/xwQ9+MNHf9vX1hT43CxYswOOPP47HHnus4TF8/vOfx2c+8xk89NBDyGaz+Ou//mtceuml+NrXvoa77roLzz33HD73uc/FvsZ1112HgYEB3HffffjSl76Ef/qnf8Ktt97a8FgUCoVi2qIvcqp/qTgv/LkcpL3HTRVo4NMqEFpRxuTTimeddRbuvvturFq1Ci+88AL+93//F+9///vr/t3999+PH/3oR3jb294GAPjoRz+Ko48+Goceeij23HNPnHHGGfjhD3+IsbGxuq91ySWX4KSTTsKBBx6ICy+8EA899BA++9nP4rjjjsMRRxyBD33oQ7jttttiX+Owww7D5z//eey77744++yzcdRRR+G3v/1tspOgUCgUimrJev/uAGwfs+1l05UgBfQvrh43xaCBT6uQlC6cRFpx7ty5OPXUU3Hdddfhmmuuwamnnoq5c+eKx950002YMWMGent7ccwxx+Atb3lLmMoaGBjAzTffjGeffRaf+cxnMGPGDFx88cV4/etfj+Hh4dgxHHbYYeHP8+fPBwAceuih1mOmd1aS1wCqPbbq/Y1CoVAoCNKZask6gFIQefZsKw8gDIaOvHpK+vlo4NMqJKULJ5lW/OAHP4hrr70W1113XWya64QTTsAjjzyCp59+GqOjo7jxxhsxb94865jXvOY1+PCHP4zvf//7eOihh/DEE0/gJz/5Sez753K58GfjmMwfq9f5nh6f9G8UCoVCwbD4dODNP8NIelb40EjQW2WC3vyzKevjowaGrYKhFYdfgqzzSVWfn2Ra8eSTTw6rr0466STvcQMDA9hnn30Sv+6ee+6J/v5+q+RdoVAoFB2OxadjdN5i4NUqaz525A+AQ5dOSabHQAOfVsHQine9G1UakQY/raMVM5kMnnzyyfDn8WD58uUYHh7GO97xDuyxxx7YsmUL/vVf/xXFYhEnnnhiM4c7Lpx99tnYbbfdcOWVV7Z7KAqFQtHxGC1GjPno4BFTOugBNNXVWtRoRfTvZj/eYlpxcHAQg4OD4/77448/Hs8//zzOPvtsHHDAATjllFOwfv163HLLLdh///2bONLxYfXq1Vi3buqVYCoUCsVkYKQYVROPFqeeYSFHKggCKe8ybbFt2zYMDQ1h69atTnAwOjqKlStXYq+99kJvb+/432SaNYTrdDTte1UoFIouxDu+dheeWLcNAPC9s4/CiQfNb/OIxoe49ZtCU13tQDoDzF/a7lEoFAqFQmGxPNOB8dFUl0KhUCgU0xga+CgUCoVCoZg2oBqfsdLUtwbRwEehUCgUimmMAgl2NPBRKBQKhUIxpVGsRDVOpbIGPgqFQqFQKKYwaLBTqkz9Qu+uCXy+9a1v4bDDDgs9aI455hj893//d/h8EARYvnw5Fi1ahL6+PixduhSPP/54G0esUCgUCkVno1IJQGOdQp1U13//cR0++4vHMFwoTfLIJg9dE/jsvvvu+Od//mc8+OCDePDBB/HWt74V73rXu8Lg5ktf+hK++tWv4hvf+AYeeOABLFiwACeeeCK2b9/e5pErFAqFQtGZKLI+h6U6fQ8vvP4R/PvvX8BNj3avSWzXBD7vfOc78Y53vAP77bcf9ttvP1x++eWYMWMGfv/73yMIAlx99dX49Kc/jdNPPx2HHHIIrrvuOgwPD+NHP/pRu4euUCgUCkVHoli2U1ulsj/VVakEKNTSYsbwsBvRNYEPRblcxvXXX4+dO3fimGOOwcqVK7F+/XosW7YsPCafz+P444/HPffcE/taY2Nj2LZtm/VvquHcc89FKpXC+eef7zx3wQUXIJVK4dxzz3WOT6VSyOVy2HvvvXHJJZdYDUhvuOEGvOENb8DQ0BBmzpyJgw8+GBdffHErPo5CoVAomgQuZuaBEMW20WL4cyadmrQxTTa6KvD54x//iBkzZiCfz+P888/Hz3/+cxx00EFYv349AGD+fNtme/78+eFzPlx55ZUYGhoK/y1evHjSxh+iUgZevh1Y9ePq/5XJN4xavHgxrr/+eoyMjISPjY6O4sc//jGWLFniHH/yySdj3bp1eP755/GFL3wB3/zmN3HJJZcAAH7zm9/gjDPOwLvf/W7cf//9WLFiBS6//PKw67tCoVAougM80CnGVHVt2hnN8arxaRH2339/PPLII/j973+Pv/3bv8U555yDJ554Inw+lbIj0CAInMc4LrvsMmzdujX8t2bNmkkZe4g1NwK/2hP47QnAPWdW///VntXHJxGve93rsGTJEtx4Y/Q+N954IxYvXowjjjjCOT6fz2PBggVYvHgxzjzzTLzvfe/DL37xCwDATTfdhDe96U345Cc/if333x/77bcfTjvtNHz961/3vv+qVauQSqXw05/+FG9+85vR19eHo48+Gn/605/wwAMP4KijjsKMGTNw8skn45VXXgn/7oEHHsCJJ56IuXPnYmhoCMcffzweeuih8Pnbb78dPT09uOuuu8LHrrrqKsydO1cblSoUCkUdcE1PnMZnuFAWf+42dFXg09PTg3322QdHHXUUrrzyShx++OH42te+hgULFgCAw+5s2LDBYYE48vl8WCk20a7ldbHmRuCudwPDL9qPD79UfXySg58PfOADuOaaa8Lff/jDH+KDH/xgor/t6+tDsVilORcsWIDHH38cjz32WMNj+PznP4/PfOYzeOihh5DNZvHXf/3XuPTSS/G1r30Nd911F5577jl87nOfC4/fvn07zjnnHNx11134/e9/j3333RfveMc7QtH60qVLcdFFF+Gss87C1q1b8eijj+LTn/40vve972HhwoUNj0+hUCimE7imJy7VNVaaGq0tuirw4QiCAGNjY9hrr72wYMEC3HrrreFzhUIBd9xxB4499tg2jpCgUgZWXAhAuqhqj624aFLTXmeddRbuvvturFq1Ci+88AL+93//F+9///vr/t3999+PH/3oR3jb294GAPjoRz+Ko48+Goceeij23HNPnHHGGfjhD3+IsbGxuq91ySWX4KSTTsKBBx6ICy+8EA899BA++9nP4rjjjsMRRxyBD33oQ7jtttvC49/61rfi/e9/Pw488EAceOCB+M53voPh4WHccccd4TFf+MIXMGfOHPyf//N/8L73vQ9nnXUW/uIv/mIcZ0ihUCimF3hqKy7VNVaMnhstdq/RYdd0Z/+Hf/gHnHLKKVi8eDG2b9+O66+/Hrfffjt+/etfI5VK4aKLLsIVV1yBfffdF/vuuy+uuOIK9Pf348wzz2z30Kt45S6X6bEQAMNrqsdNUuf2uXPn4tRTT8V1112HIAhw6qmnYu7cueKxN910E2bMmIFSqYRisYh3vetdYSprYGAAN998M5577jncdttt+P3vf4+LL74YX/va13Dvvfeiv7/fO4bDDjss/NmwcYceeqj12IYNG8LfN2zYgM997nP43e9+h5dffhnlchnDw8NYvXp1eExPTw/+4z/+A4cddhj22GMPXH311eM6PwqFQjHdwA0L46q6aDuLkS5mfLom8Hn55Zdx1llnYd26dRgaGsJhhx2GX//61zjxxBMBAJdeeilGRkZwwQUXYPPmzXjDG96AW265BTNnzmzzyGsYSag3SXrcOPHBD34QH/nIRwAA//Zv/+Y97oQTTsC3vvUt5HI5LFq0CLlczjnmNa95DV7zmtfgwx/+MD796U9jv/32w09+8hN84AMf8L4ufR2jv+KPVUiO+dxzz8Urr7yCq6++GnvssQfy+TyOOeYYR0htqvc2bdqETZs2YWBgIO40KBQKhQKNMT40vTWmgc/k4wc/+EHs86lUCsuXL8fy5ctbM6BG0ZdQb5L0uHHi5JNPDoOGk046yXvcwMAA9tlnn8Svu+eee6K/v98qeW8G7rrrLnzzm9/EO97xDgDAmjVrsHHjRuuY5557Dh//+Mfxve99Dz/96U9x9tln47e//S3S6a7O5CoUCsWkgzM8cS0rpgrjoytDq7Drm4H+3QH4qsxSQP/i6nGTiEwmgyeffBJPPvkkMpnMuF5j+fLluPTSS3H77bdj5cqVePjhh/HBD34QxWIxZOCahX322Qf//u//jieffBL33Xcf3ve+96Gvry98vlwu46yzzsKyZctC8fZjjz2Gq666KjzmG9/4RqhPUigUCkUEXsUVq/GxxM3dq/HRwKdVSGeAI79W+4UHP7Xfj7y6etwkY6LVa8cffzyef/55nH322TjggANwyimnYP369bjllluw//77N3Gk1cqzzZs344gjjsBZZ52Fj33sY5g3b174/OWXX45V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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Optimize the MPS using `optimze_mps`\n", + "(j_min, y_min_mps), (j_max, y_max_mps) = optimize_mps(mps)\n", + "print(\"Min: \", y_min_mps, \" Max: \", y_max_mps)\n", + "\n", + "bitlist_to_int = lambda bitlist: int(\"\".join(str(x) for x in bitlist), 2)\n", + "\n", + "i_min_mps = bitlist_to_int(j_min)\n", + "i_max_mps = bitlist_to_int(j_max)\n", + "\n", + "plt.plot(x, y_vec)\n", + "plt.scatter(x[i_min_mps], y_min_mps, color=\"orange\", label=\"MPS min.\")\n", + "plt.scatter(x[i_max_mps], y_max_mps, color=\"orange\", label=\"MPS max.\")\n", + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"f(x)\")\n", + "plt.legend()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "seemps", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/TT-Cross.ipynb b/examples/TT-Cross.ipynb new file mode 100644 index 0000000..033a3af --- /dev/null +++ b/examples/TT-Cross.ipynb @@ -0,0 +1,232 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Loading black-box functions with TT-Cross\n", + "\n", + "TT-Cross, also known as tensor cross-interpolation (TCI), is a method to load black-box functions in tensor trains or MPS. This library implements several variants of TT-Cross, each with its strengths and weaknesses. In this example, we show the usage of the DMRG-based variant `cross_DMRG`, which is specially suitable for MPS with small physical dimension. However, these examples apply analogously to all other variants by just replacing the method.\n", + "\n", + "The black-box function given as input corresponds to a `BlackBox` object. There are several subclasses corresponding to different input black-boxes, applicable in different scenarios. In this notebook, we show a brief example for each type." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from seemps.analysis.mesh import Mesh, RegularInterval\n", + "from seemps.analysis.factories import mps_interval\n", + "from seemps.analysis.cross import cross_dmrg\n", + "import seemps.tools\n", + "\n", + "seemps.tools.DEBUG = 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. `BlackBoxLoadMPS`\n", + "\n", + "This black-box representation allows to load functions in a MPS by imposing a quantization on its degrees of freedom with a given base, or *physical dimension*. This enables to impose different tensor orders, such as *serial (A)* and *interleaved (B)*. In this example we load the function\n", + "\n", + "$$\n", + "f(x, y) = e^{-(x^2 + y^2)}\n", + "$$\n", + "\n", + "on a binary MPS with standard physical dimension 2 and serial order.\n", + "\n", + "The function `func` must act on the whole input tensor, following the convention that its first index labels each degree of freedom or dimension." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Cross sweep 1 with error(1000 samples in norm-inf)=0.08093730903765317, maxbond=2, evals(cumulative)=144\n", + " Cross sweep 1 with error(1000 samples in norm-inf)=1.4290195931743188e-05, maxbond=4, evals(cumulative)=624\n", + " Cross sweep 2 with error(1000 samples in norm-inf)=4.7628567756419216e-14, maxbond=8, evals(cumulative)=1920\n", + " State converged within tolerance 1e-12\n" + ] + } + ], + "source": [ + "from seemps.analysis.cross import BlackBoxLoadMPS\n", + "\n", + "func = lambda tensor: np.exp(-np.sum(tensor**2, axis=0))\n", + "\n", + "start, stop = -1, 1\n", + "num_qubits = 10\n", + "interval = RegularInterval(start, stop, 2**num_qubits)\n", + "dimension = 2\n", + "mesh = Mesh([interval] * dimension)\n", + "\n", + "black_box = BlackBoxLoadMPS(func, mesh, base=2, mps_order=\"A\")\n", + "mps = cross_dmrg(black_box).mps" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. `BlackBoxLoadTT`\n", + "\n", + "This black-box representation allows to load functions in a tensor-train with no such quantization, by assigning a full tensor to each function variable. \n", + "Even though we use it here, `cross_dmrg` is not optimal for this structure and it largely overestimates the bond dimension. Instead, its better to use `cross_maxvol` or `cross_greedy`." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Cross sweep 1 with error(1000 samples in norm-inf)=1.304512053934559e-14, maxbond=993, evals(cumulative)=1000000\n", + " State converged within tolerance 1e-12\n" + ] + } + ], + "source": [ + "from seemps.analysis.cross import BlackBoxLoadTT\n", + "\n", + "func = lambda tensor: np.exp(-np.sum(tensor**2, axis=0))\n", + "\n", + "start, stop = -1, 1\n", + "num_nodes = 1000\n", + "interval = RegularInterval(start, stop, num_nodes)\n", + "dimension = 2\n", + "mesh = Mesh([interval] * dimension)\n", + "\n", + "black_box = BlackBoxLoadTT(func, mesh)\n", + "mps = cross_dmrg(black_box).mps" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3. `BlackBoxLoadMPO`\n", + "\n", + "This black-box representation allows to load a bivariate function in a MPO, by loading its equivalent MPS and unfolding it at the end. In this example, we load in MPO the bivariate function\n", + "\n", + "$$\n", + "f(x, y) = e^{-(x^2 + y^2)}.\n", + "$$\n", + "\n", + "The function `func` must act on two input values labeling the rows and columns of the MPO respectively." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Cross sweep 1 with error(1000 samples in norm-inf)=0.09425112330182073, maxbond=4, evals(cumulative)=528\n", + " Cross sweep 1 with error(1000 samples in norm-inf)=2.4101306458801375e-05, maxbond=16, evals(cumulative)=6992\n", + " Cross sweep 2 with error(1000 samples in norm-inf)=2.537969834293108e-13, maxbond=59, evals(cumulative)=71216\n", + " State converged within tolerance 1e-12\n" + ] + } + ], + "source": [ + "from seemps.analysis.cross import BlackBoxLoadMPO\n", + "from seemps.truncate.simplify_mpo import mps_as_mpo\n", + "\n", + "func = lambda x, y: np.exp(-(x**2 + y**2))\n", + "\n", + "start, stop = -1, 1\n", + "num_qubits = 10\n", + "interval = RegularInterval(start, stop, 2**num_qubits)\n", + "dimension = 2\n", + "mesh = Mesh([interval] * dimension)\n", + "\n", + "black_box = BlackBoxLoadMPO(func, mesh)\n", + "mps = cross_dmrg(black_box).mps\n", + "mpo = mps_as_mpo(mps)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4. `BlackBoxComposeMPS`\n", + "\n", + "This black-box representation allows composing scalar functions on several MPS, given by `mps_list`. In this example we compose the function\n", + "\n", + "$$\n", + "f(x,y,z) = x\\cdot \\sin(y z) + y \\cdot \\cos(x z)\n", + "$$\n", + "\n", + "on three initial MPS representing $x$, $y$ and $z$. The function `func` must act on the whole list of MPS." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Cross sweep 1 with error(1000 samples in norm-inf)=0.11293525051035298, maxbond=2, evals(cumulative)=68\n", + " Cross sweep 1 with error(1000 samples in norm-inf)=1.723676025844334e-05, maxbond=4, evals(cumulative)=300\n", + " Cross sweep 2 with error(1000 samples in norm-inf)=1.354472090042691e-14, maxbond=8, evals(cumulative)=972\n", + " State converged within tolerance 1e-12\n" + ] + } + ], + "source": [ + "from seemps.analysis.cross import BlackBoxComposeMPS\n", + "\n", + "func = lambda mps: mps[0] * np.sin(mps[1] * mps[2]) + mps[1] * np.cos(mps[0] * mps[2])\n", + "\n", + "start, stop, num_qubits = -1, 1, 10\n", + "interval = RegularInterval(start, stop, 2**num_qubits)\n", + "mps_x = mps_interval(interval)\n", + "\n", + "black_box = BlackBoxComposeMPS(func, [mps_x, mps_x, mps_x])\n", + "mps = cross_dmrg(black_box).mps" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "seemps", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From da9fd5e84af2423913b13a31f136066a8d44e38e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Rodr=C3=ADguez=20Aldavero?= Date: Fri, 5 Jul 2024 10:08:03 +0200 Subject: [PATCH 6/7] Improve documentation --- docs/algorithms/chebyshev.rst | 89 +++++++++++++++++++ .../seemps.analysis.chebyshev.cheb2mpo.rst | 11 +++ .../seemps.analysis.chebyshev.cheb2mps.rst | 11 +++ ...emps.analysis.chebyshev.estimate_order.rst | 11 +++ ...s.chebyshev.interpolation_coefficients.rst | 11 +++ ...ysis.chebyshev.projection_coefficients.rst | 11 +++ ...eemps.analysis.lagrange.lagrange_basic.rst | 11 +++ ...lagrange.lagrange_local_rank_revealing.rst | 11 +++ ...lysis.lagrange.lagrange_rank_revealing.rst | 11 +++ docs/algorithms/index.rst | 3 + docs/algorithms/lagrange.rst | 23 +++++ docs/algorithms/tt-cross.rst | 27 ++++++ docs/seemps_analysis.rst | 13 +-- docs/seemps_analysis_integration.rst | 36 +++++++- docs/seemps_analysis_loading.rst | 73 +++++++++++++++ docs/seemps_analysis_operators.rst | 6 +- docs/seemps_analysis_optimization.rst | 16 ++++ docs/seemps_analysis_spaces.rst | 25 +++++- docs/seemps_analysis_states.rst | 21 +++++ docs/seemps_examples.rst | 4 + 20 files changed, 410 insertions(+), 14 deletions(-) create mode 100644 docs/algorithms/chebyshev.rst create mode 100644 docs/algorithms/generated/seemps.analysis.chebyshev.cheb2mpo.rst create mode 100644 docs/algorithms/generated/seemps.analysis.chebyshev.cheb2mps.rst create mode 100644 docs/algorithms/generated/seemps.analysis.chebyshev.estimate_order.rst create mode 100644 docs/algorithms/generated/seemps.analysis.chebyshev.interpolation_coefficients.rst create mode 100644 docs/algorithms/generated/seemps.analysis.chebyshev.projection_coefficients.rst create mode 100644 docs/algorithms/generated/seemps.analysis.lagrange.lagrange_basic.rst create mode 100644 docs/algorithms/generated/seemps.analysis.lagrange.lagrange_local_rank_revealing.rst create mode 100644 docs/algorithms/generated/seemps.analysis.lagrange.lagrange_rank_revealing.rst create mode 100644 docs/algorithms/lagrange.rst create mode 100644 docs/algorithms/tt-cross.rst create mode 100644 docs/seemps_analysis_loading.rst create mode 100644 docs/seemps_analysis_optimization.rst create mode 100644 docs/seemps_analysis_states.rst diff --git a/docs/algorithms/chebyshev.rst b/docs/algorithms/chebyshev.rst new file mode 100644 index 0000000..098789a --- /dev/null +++ b/docs/algorithms/chebyshev.rst @@ -0,0 +1,89 @@ +.. _analysis_chebyshev: + +*********************** +Chebyshev Approximation +*********************** + +Matrix product states (MPS) and operators (MPO) can be expanded on the basis of +Chebyshev polynomials, allowing to approximate and compose functions. + +In principle, this method can be performed for arbitrary multivariate functions. +However, its cost scales exponentially with the dimension of the function, and +the method converges efficiently only for highly-differentiable functions. + +For these reasons, currently the SeeMPS library contains methods to perform Chebyshev +expansions of univariate functions. The method works for both MPS and MPO initial states, +and converges efficiently for analytical or highly-differentiable functions. + +Computation of the expansion coefficients +========================================= +The expansion of an univariate function on the basis of Chebyshev polynomials is of the form + +.. math:: + p_d(x)=\sum_{k=0}^d c_k T_k(x), + +where + +.. math:: + T_{k+1}(x) =2 x T_k(x)-T_{k-1}(x),\;k\geq 1 + +are the Chebyshev polynomials of order :math:`c_k`. + +The coefficients :math:`c_k` contain the information of the function. These coefficients +can be given by the projection of the function on the basis, or by interpolation on a collection +of nodes. The library presents the method :func:`~seemps.analysis.chebyshev.projection_coefficients` for the former, and +:func:`~seemps.analysis.chebyshev.interpolation_coefficients` for the latter, both on Chebyshev-Gauss or Chebyshev-Lobatto nodes. +These methods only depend on the univariate function, its domain of definition, and optionally the +chosen interpolation order. This order can be estimated to machine precision using the :func:`~seemps.analysis.chebyshev.estimate_order` +routine. + +Expansion in the Chebyshev basis +================================ +Once the expansion coefficients are computed for the function, the series can be applied on a +generic initial condition. This condition can be an MPS or MPO. These expansions can be respectively +performed using the methods :func:`~seemps.analysis.chebyshev.cheb2mps` and :func:`~seemps.analysis.chebyshev.cheb2mpo`. + +The initial conditions must have a support on the canonical Chebyshev interval :math:`[-1, 1]`. +For MPS, this initial support is to be understood as the minimum and maximum values of the corresponding +vector, while for MPO it is to be understood as the smallest and largest eigenvalues. +If the initial condition has a larger support, it must be rescaled using an affine transformation. +This is performed by default by the main algorithm assuming that the initial condition is defined on +the domain of definition of the expansion coefficients. + +The standard evaluation of the partial sum is based on constructing the Chebyshev polynomials +:math:`T_k` using the recurrence relation, and then performing the linear combination with :math:`c_k` weights. +This can be performed by setting the flag ``clenshaw`` to ``False`` in :func:`~seemps.analysis.chebyshev.cheb2mps` or :func:`~seemps.analysis.chebyshev.cheb2mpo`. + +However, there is a more efficient evaluation routine based on Clenshaw's evaluation method. This +procedure avoids computing the intermediate Chebyshev coefficients, and shows a more robust and efficient +performance. It is set by default through the flag ``clenshaw`` set to ``True``. However, this method is very +susceptible to an overestimation of the interpolation order, showing a degrading performance in that case. + +Constructing the initial condition +================================== +This method requires an initial condition, either MPS or MPO, to perform function composition. This +initial condition must be passed to the argument ``initial_mps`` or ``initial_mpo``. However, the initial +conditions for the case of function "loading", which are given by discretized domains, can be built +automatically by passing an :class:`~seemps.analysis.mesh.Interval` object to the ``domain`` argument. + +These discretized domains can be alternatively built by creating an :class:`~seemps.analysis.mesh.Interval` object, such as a :class:`~seemps.analysis.mesh.RegularInterval` +or :class:`~seemps.analysis.mesh.ChebyshevInterval`, and constructing the corresponding MPS with the routine :py:func:`~seemps.analysis.factories.mps_interval`. + +Multivariate functions +====================== + +This method enables the construction of multivariate functions by composing functions on multivariate +initial conditions. These conditions can be constructed by tensorized products or sums on univariate states. +These operations can be performed with the methods :func:`~seemps.analysis.factories.mps_tensor_product` and/or :func:`~seemps.analysis.factories.mps_tensor_sum`. +These initial conditions may have a growing support, so they must be rescaled appropriately to fit in :math:`[-1, 1]`. + +An example on how to use these functions is shown in `Chebyshev.ipynb `_. + +.. autosummary:: + :toctree: generated/ + + ~seemps.analysis.chebyshev.projection_coefficients + ~seemps.analysis.chebyshev.interpolation_coefficients + ~seemps.analysis.chebyshev.estimate_order + ~seemps.analysis.chebyshev.cheb2mps + ~seemps.analysis.chebyshev.cheb2mpo \ No newline at end of file diff --git a/docs/algorithms/generated/seemps.analysis.chebyshev.cheb2mpo.rst b/docs/algorithms/generated/seemps.analysis.chebyshev.cheb2mpo.rst new file mode 100644 index 0000000..4bb3051 --- /dev/null +++ b/docs/algorithms/generated/seemps.analysis.chebyshev.cheb2mpo.rst @@ -0,0 +1,11 @@ + + +seemps.analysis.chebyshev.cheb2mpo +================================== + +.. currentmodule:: seemps.analysis.chebyshev + + + +.. autofunction:: seemps.analysis.chebyshev.cheb2mpo + diff --git a/docs/algorithms/generated/seemps.analysis.chebyshev.cheb2mps.rst b/docs/algorithms/generated/seemps.analysis.chebyshev.cheb2mps.rst new file mode 100644 index 0000000..7f8626a --- /dev/null +++ b/docs/algorithms/generated/seemps.analysis.chebyshev.cheb2mps.rst @@ -0,0 +1,11 @@ + + +seemps.analysis.chebyshev.cheb2mps +================================== + +.. currentmodule:: seemps.analysis.chebyshev + + + +.. autofunction:: seemps.analysis.chebyshev.cheb2mps + diff --git a/docs/algorithms/generated/seemps.analysis.chebyshev.estimate_order.rst b/docs/algorithms/generated/seemps.analysis.chebyshev.estimate_order.rst new file mode 100644 index 0000000..94ccf57 --- /dev/null +++ b/docs/algorithms/generated/seemps.analysis.chebyshev.estimate_order.rst @@ -0,0 +1,11 @@ + + +seemps.analysis.chebyshev.estimate\_order +========================================= + +.. currentmodule:: seemps.analysis.chebyshev + + + +.. autofunction:: seemps.analysis.chebyshev.estimate_order + diff --git a/docs/algorithms/generated/seemps.analysis.chebyshev.interpolation_coefficients.rst b/docs/algorithms/generated/seemps.analysis.chebyshev.interpolation_coefficients.rst new file mode 100644 index 0000000..4caee0e --- /dev/null +++ b/docs/algorithms/generated/seemps.analysis.chebyshev.interpolation_coefficients.rst @@ -0,0 +1,11 @@ + + +seemps.analysis.chebyshev.interpolation\_coefficients +===================================================== + +.. currentmodule:: seemps.analysis.chebyshev + + + +.. autofunction:: seemps.analysis.chebyshev.interpolation_coefficients + diff --git a/docs/algorithms/generated/seemps.analysis.chebyshev.projection_coefficients.rst b/docs/algorithms/generated/seemps.analysis.chebyshev.projection_coefficients.rst new file mode 100644 index 0000000..f904a04 --- /dev/null +++ b/docs/algorithms/generated/seemps.analysis.chebyshev.projection_coefficients.rst @@ -0,0 +1,11 @@ + + +seemps.analysis.chebyshev.projection\_coefficients +================================================== + +.. currentmodule:: seemps.analysis.chebyshev + + + +.. autofunction:: seemps.analysis.chebyshev.projection_coefficients + diff --git a/docs/algorithms/generated/seemps.analysis.lagrange.lagrange_basic.rst b/docs/algorithms/generated/seemps.analysis.lagrange.lagrange_basic.rst new file mode 100644 index 0000000..3eec2ed --- /dev/null +++ b/docs/algorithms/generated/seemps.analysis.lagrange.lagrange_basic.rst @@ -0,0 +1,11 @@ + + +seemps.analysis.lagrange.lagrange\_basic +======================================== + +.. currentmodule:: seemps.analysis.lagrange + + + +.. autofunction:: seemps.analysis.lagrange.lagrange_basic + diff --git a/docs/algorithms/generated/seemps.analysis.lagrange.lagrange_local_rank_revealing.rst b/docs/algorithms/generated/seemps.analysis.lagrange.lagrange_local_rank_revealing.rst new file mode 100644 index 0000000..cc5a877 --- /dev/null +++ b/docs/algorithms/generated/seemps.analysis.lagrange.lagrange_local_rank_revealing.rst @@ -0,0 +1,11 @@ + + +seemps.analysis.lagrange.lagrange\_local\_rank\_revealing +========================================================= + +.. currentmodule:: seemps.analysis.lagrange + + + +.. autofunction:: seemps.analysis.lagrange.lagrange_local_rank_revealing + diff --git a/docs/algorithms/generated/seemps.analysis.lagrange.lagrange_rank_revealing.rst b/docs/algorithms/generated/seemps.analysis.lagrange.lagrange_rank_revealing.rst new file mode 100644 index 0000000..c32b072 --- /dev/null +++ b/docs/algorithms/generated/seemps.analysis.lagrange.lagrange_rank_revealing.rst @@ -0,0 +1,11 @@ + + +seemps.analysis.lagrange.lagrange\_rank\_revealing +================================================== + +.. currentmodule:: seemps.analysis.lagrange + + + +.. autofunction:: seemps.analysis.lagrange.lagrange_rank_revealing + diff --git a/docs/algorithms/index.rst b/docs/algorithms/index.rst index b468c35..a441648 100644 --- a/docs/algorithms/index.rst +++ b/docs/algorithms/index.rst @@ -15,3 +15,6 @@ Index of algorithms runge_kutta crank_nicolson tebd_evolution + chebyshev + tt-cross + lagrange diff --git a/docs/algorithms/lagrange.rst b/docs/algorithms/lagrange.rst new file mode 100644 index 0000000..fa1fcc6 --- /dev/null +++ b/docs/algorithms/lagrange.rst @@ -0,0 +1,23 @@ +.. currentmodule:: seemps.analysis.lagrange + + +.. _alg_lagrange: + +************************************** +Multiscale interpolative constructions +************************************** + +The MPS representation of an univariate polynomial interpolant can be efficiently constructed using multiscale interpolative constructions, following Lindsey's method (see Ref. https://arxiv.org/pdf/2311.12554). These methods leverage the Lagrange interpolation framework, specifically utilizing Chebyshev-Lobatto nodes. The SeeMPS library implements the interpolative constructs for the univariate scenario. The extensions to the multivariate case have not been implemented yet. + +In the following, we provide an overview of the method. Essentially, the basic construct :func:`~lagrange_basic` implements an universal construction by assembling three distinct tensor cores corresponding to the left-most, bulk and right-most edges. The left-most core is function-dependent, while all the rest only depend on the interpolation order. After assembling, these cores are combined to form a Lagrange interpolant across two Chebyshev-Lobatto grids, one for each half of the domain. This approach severely overestimates the required bond dimension of the interpolant, requiring a large-scale final simplification. + +This method can be enhanced by performing rank-revealing optimizations using SVD decomposition. The corresponding method, :func:`~lagrange_rank_revealing`, avoids the need for a large-scale final simplification. + +Finally, the interpolative constructions can be developed on top of a local Lagrange interpolation framework, :func:`~lagrange_local_rank_revealing`. Then, the tensor cores become sparse, largely improving the overall efficiency of the algorithm. + +.. autosummary:: + :toctree: generated/ + + ~lagrange_basic + ~lagrange_rank_revealing + ~lagrange_local_rank_revealing \ No newline at end of file diff --git a/docs/algorithms/tt-cross.rst b/docs/algorithms/tt-cross.rst new file mode 100644 index 0000000..349d0d8 --- /dev/null +++ b/docs/algorithms/tt-cross.rst @@ -0,0 +1,27 @@ +.. _alg_ttcross: + +******************************************* +Tensor-train cross-interpolation (TT-Cross) +******************************************* + +Tensor-train cross-interpolation, known as TT-Cross or TCI, is a method that computes the tensor-train representation of a black-box function by sampling some of its elements along some patterns known as crosses. As it does not act on the explicit tensor representation, it provides an exponential advantage over the standard Schmidt decomposition and evades the *curse of dimensionality*. + +There are several variants available for TT-Cross. Each shows different advantages and disadvantages in terms of computational cost and accuracy for different initial conditions. This library implements three: + +1. :func:`~seemps.analysis.cross.cross_dmrg`: Based on two-site optimizations combining the skeleton decomposition and the Schmidt decomposition. It is efficient for structures of low physical dimension, such as binary MPS, as it can increase the bond dimension by several units for each sweep. Inefficient for structures of large physical dimension due to its computational complexity. Has an associated parameter dataclass given by :class:`~seemps.analysis.cross.CrossStrategyDMRG`. + +2. :func:`~seemps.analysis.cross.cross_greedy`: Based on two-site optimizations performing greedy searches for maximum-volume pivots. Efficient for structures of large physical dimension, such as tensor-trains with dense modes, due to its advantageous computational complexity. Inefficient for structures of reduced physical dimension, as it increases the bond dimension by one each sweep. Presents the ``full search`` variant or the ``partial search`` variants. Has an associated parameter dataclass given by :class:`~seemps.analysis.cross.CrossStrategyGreedy`. + +3. :func:`~seemps.analysis.cross.cross_maxvol`: Based on rank-adaptive one-site optimizations using the rectangular skeleton decomposition. Can be seen as a middle ground between the former two methods. Has an associated parameter dataclass given by :class:`~seemps.analysis.cross.CrossStrategyMaxvol`. + +Moreover, this method performs the decomposition of a given input black-box. This black-box can take several different forms and serve for different application domains. This library implements the class :class:`~seemps.analysis.cross.black_box.BlackBox` and the following subclasses: + +1. :class:`~seemps.analysis.cross.black_box.BlackBoxLoadMPS`: Required to load functions with quantized degrees of freedom in MPS. Allows for both the *serial* and *interleaved* qubit orders. + +2. :class:`~seemps.analysis.cross.black_box.BlackBoxLoadTT`: Required to load functions in tensor-trains, where each degree of freedom is assigned a full tensor of physical dimension equal to the density of the discretization. + +3. :class:`~seemps.analysis.cross.black_box.BlackBoxLoadMPO`: Required to load bivariate functions in MPO, by computing the equivalent MPS representation. This MPS is of square physical dimension (e.g. 4 for a MPO of dimenson 2) and can be unfolded in the end to the required MPO. + +4. :class:`~seemps.analysis.cross.black_box.BlackBoxComposeMPS`: Required to compose scalar functions on collections of MPS. + +An example on how to use TCI for all these scenarios is shown in `TT-Cross.ipynb `_. \ No newline at end of file diff --git a/docs/seemps_analysis.rst b/docs/seemps_analysis.rst index bbacfa9..5fdc76b 100644 --- a/docs/seemps_analysis.rst +++ b/docs/seemps_analysis.rst @@ -1,17 +1,18 @@ .. _seemps_analysis: -************************** -Quantum numerical analysis -************************** +*********************************** +Quantum-inspired numerical analysis +*********************************** .. toctree:: :maxdepth: 1 - seemps_analysis_spaces + seemps_analysis_states seemps_analysis_operators + seemps_analysis_spaces + seemps_analysis_loading seemps_analysis_differentiation seemps_analysis_integration seemps_analysis_interpolation - seemps_analysis_ttcross - seemps_analysis_chebyshev + seemps_analysis_optimization diff --git a/docs/seemps_analysis_integration.rst b/docs/seemps_analysis_integration.rst index 240f2d3..9f5ae0b 100644 --- a/docs/seemps_analysis_integration.rst +++ b/docs/seemps_analysis_integration.rst @@ -1,4 +1,4 @@ -.. currentmodule:: seemps.analysis.integrals +.. currentmodule:: seemps.analysis.integration .. _analysis_integration: @@ -11,14 +11,44 @@ Functions encoded in MPS can be efficiently integrated, by contracting those qua .. math:: \int f(x)\mathrm{d}x \simeq \sum_i f(x_i) \Delta{x} = \langle g | f\rangle. -In the following table we find both functions that construct the states associated to various quadratures---i.e. `mps_*` functions---and a function that implements the integral using any of those rules :py:func:`integrate_mps` +In this scenario, the quadrature corresponding to the state :math:`\langle g |` is given by the midpoint quadrature rule. More sophisticated quadrature rules result in more efficient convergence rates---i.e. requiring less nodes or tensor cores to compute an accurate estimation of the true integral. + +In the following table we find both functions that construct the states associated to various quadratures---i.e. ``mps_*`` functions---and a function that implements the integral using any of those rules :func:`integrate_mps`. These quadrature rules divide in two families: + +Newton-Côtes quadratures +------------------------ +These are useful to integrate equispaced discretizations, each of increasing order. Compatible with discretizations stemming from :class:`~seemps.analysis.mesh.RegularInterval` objects. The larger the order, the better the convergence rate. However, large-order quadratures impose restrictions on the amounts of qubits they support: + +- :func:`mps_simpson` requires a number of qubits divisible by 2. +- :func:`mps_fifth_order` requires a number of qubits divisible by 4. .. autosummary:: :toctree: generated/ - ~integrate_mps ~mps_midpoint ~mps_trapezoidal ~mps_simpson ~mps_fifth_order + +Clenshaw-Curtis quadratures +--------------------------- +These are useful to integrate irregular discretizations on either the Chebyshev zeros (Chebyshev-Gauss nodes) or the Chebyshev extrema (Chebyshev-Lobatto nodes). These have an exponentially better rate of convergence than the Newton-Côtes ones. Compatible with discretizations stemming from :class:`~seemps.analysis.mesh.ChebyshevInterval` objects. + +.. autosummary:: + :toctree: generated/ + ~mps_fejer + ~mps_clenshaw_curtis + +Integration +----------- +The standard method for integration consists in first constructing the multivariate quadrature rule using the previous routines, together with :class:`~seemps.analysis.factories.mps_tensor_product` and :class:`~seemps.analysis.factories.mps_tensor_sum` tensorized operations. Then, this quadrature is to be contracted with the desired MPS target using the scalar product routine :class:`~seemps.state.scprod`. However, for ease of use, a helper routine :class:`~seemps.analysis.integration.integrate_mps` is given that automatically computes the best possible quadrature rule associated to a :class:`~seemps.analysis.mesh.Mesh` object, and contracts with the target MPS to compute the integral: + +.. autosummary:: + :toctree: generated/ + + ~integrate_mps + +Note that this helper routine is only valid for standard function representations in MPS with binary quantization, while the former method is applicable in all cases. + +An example on how to use these functions is shown in `Integration.ipynb `_. \ No newline at end of file diff --git a/docs/seemps_analysis_loading.rst b/docs/seemps_analysis_loading.rst new file mode 100644 index 0000000..ab06771 --- /dev/null +++ b/docs/seemps_analysis_loading.rst @@ -0,0 +1,73 @@ +.. currentmodule:: seemps + +.. _analysis_loading: + +**************** +Function Loading +**************** + +The SeeMPS library provides several methods to load univariate and multivariate functions in MPS and MPO structures. In the following, the most important are listed. + +Tensorized operations +--------------------- +These methods are useful to construct MPS corresponding to domain discretizations, and compose them using tensor products and sums to construct multivariate domains. + +.. autosummary:: + :toctree: generated/ + + ~seemps.analysis.mesh.RegularInterval + ~seemps.analysis.mesh.ChebyshevInterval + ~seemps.analysis.factories.mps_interval + ~seemps.analysis.factories.mps_tensor_product + ~seemps.analysis.factories.mps_tensor_sum + +Tensor cross-interpolation (TT-Cross) +------------------------------------- +These methods are useful to compose MPS or MPO representations of black-box functions using tensor-train cross-interpolation (TT-Cross). See :doc:`algorithms/tt-cross` + +.. autosummary:: + :toctree: generated/ + + ~seemps.analysis.cross.black_box.BlackBoxLoadMPS + ~seemps.analysis.cross.black_box.BlackBoxLoadTT + ~seemps.analysis.cross.black_box.BlackBoxLoadMPO + ~seemps.analysis.cross.black_box.BlackBoxComposeMPS + ~seemps.analysis.cross.cross_maxvol + ~seemps.analysis.cross.cross_dmrg + ~seemps.analysis.cross.cross_greedy + +Chebyshev expansions +-------------------- +These methods are useful to compose univariate function on generic initial MPS or MPO and compute MPS approximations of functions. +See :doc:`algorithms/chebyshev`. + +.. autosummary:: + :toctree: generated/ + + ~seemps.analysis.chebyshev.cheb2mps + ~seemps.analysis.chebyshev.cheb2mpo + ~seemps.analysis.chebyshev.interpolation_coefficients + ~seemps.analysis.chebyshev.projection_coefficients + ~seemps.analysis.chebyshev.estimate_order + + +Multiscale interpolative constructions +-------------------------------------- +These methods are useful to construct polynomial interpolants of univariate functions in MPS using the Lagrange interpolation framework. +See :doc:`algorithms/lagrange`. + +.. autosummary:: + :toctree: generated/ + + ~seemps.analysis.lagrange.lagrange_basic + ~seemps.analysis.lagrange.lagrange_rank_revealing + ~seemps.analysis.lagrange.lagrange_local_rank_revealing + +Generic polynomial constructions +-------------------------------- +These methods are useful to construct generic polynomials in the monomial basis from a collection of coefficients. + +.. autosummary:: + :toctree: generated/ + + ~seemps.analysis.polynomials.mps_from_polynomial \ No newline at end of file diff --git a/docs/seemps_analysis_operators.rst b/docs/seemps_analysis_operators.rst index 6065a24..d422ca9 100644 --- a/docs/seemps_analysis_operators.rst +++ b/docs/seemps_analysis_operators.rst @@ -2,9 +2,9 @@ .. _analysis_operators: -********************* -Operators -********************* +************************** +Predefined Operators (MPO) +************************** The SeeMPS library provides a exact MPO representation of basic operators. diff --git a/docs/seemps_analysis_optimization.rst b/docs/seemps_analysis_optimization.rst new file mode 100644 index 0000000..b0ce1be --- /dev/null +++ b/docs/seemps_analysis_optimization.rst @@ -0,0 +1,16 @@ +.. currentmodule:: seemps + +.. _analysis_optimization: + +********************* +Function Optimization +********************* + +The SeeMPS library provides a method to find the minimum and maximum element on a given MPS, based on the ``optima_tt`` method (see https://arxiv.org/pdf/2209.14808) + +.. autosummary:: + :toctree: generated/ + + ~seemps.analysis.optimization.optimize_mps + +An example on how to use this function is shown in `Optimization.ipynb `_. \ No newline at end of file diff --git a/docs/seemps_analysis_spaces.rst b/docs/seemps_analysis_spaces.rst index b9d23e6..f36e8e0 100644 --- a/docs/seemps_analysis_spaces.rst +++ b/docs/seemps_analysis_spaces.rst @@ -3,7 +3,7 @@ .. _analysis_spaces: ***************************************** -Uniform grids and affine transformations +Uniform Grids and Affine Transformations ***************************************** Numerical analysis problems are defined on a discretized space. Given a one-dimensional @@ -49,4 +49,25 @@ The :class::`Space` creates an object to define the problem's coordinates for a :toctree: generated/ ~seemps.analysis.space.Space - ~seemps.analysis.space.mpo_flip \ No newline at end of file + ~seemps.analysis.space.mpo_flip + +Implicit representations +------------------------ +Alternatively, the multivariate spaces can be represented implicitly using the :class:`~seemps.analysis.mesh.Interval` and :class:`~seemps.analysis.mesh.Mesh` classes. + +Essentially, the :class:`~seemps.analysis.mesh.Interval` class represents an univariate discretization implicitly, and can be indexed similarly as an explicit array. Then, the :class:`~seemps.analysis.mesh.Mesh` class represents a multivariate space as a collection of :class:`~seemps.analysis.mesh.Interval` objects. These objects can be indexed using multidimensional indices similarly as explicit multivariate arrays, without explicitly containing them and avoiding an exponential memory overhead. + +Currently, there are three types of :class:`~seemps.analysis.mesh.Interval` implemented: + +- :class:`~seemps.analysis.mesh.RegularInterval`: An interval representing a regular discretization. +- :class:`~seemps.analysis.mesh.ChebyshevInterval`: An interval representing an irregular discretization on the Chebyshev zeros or extrema. +- :class:`~seemps.analysis.mesh.IntegerInterval`: An interval representing a regular discretization with integers. + +.. autosummary:: + :toctree: generated/ + + ~seemps.analysis.mesh.Mesh + ~seemps.analysis.mesh.Interval + ~seemps.analysis.mesh.RegularInterval + ~seemps.analysis.mesh.ChebyshevInterval + ~seemps.analysis.mesh.IntegerInterval \ No newline at end of file diff --git a/docs/seemps_analysis_states.rst b/docs/seemps_analysis_states.rst new file mode 100644 index 0000000..0398332 --- /dev/null +++ b/docs/seemps_analysis_states.rst @@ -0,0 +1,21 @@ +.. currentmodule:: seemps + +.. _analysis_states: + +*************************************** +Predefined States (MPS) and Tensor Operations +*************************************** + +The SeeMPS library provides an exact MPS representation of several basic states, as well as tensorized operations between them. + +.. autosummary:: + :toctree: generated/ + + ~seemps.analysis.factories.mps_equispaced + ~seemps.analysis.factories.mps_exponential + ~seemps.analysis.factories.mps_sin + ~seemps.analysis.factories.mps_cos + ~seemps.analysis.factories.mps_affine + ~seemps.analysis.factories.mps_interval + ~seemps.analysis.factories.mps_tensor_product + ~seemps.analysis.factories.mps_tensor_sum diff --git a/docs/seemps_examples.rst b/docs/seemps_examples.rst index c324d28..a8a582e 100644 --- a/docs/seemps_examples.rst +++ b/docs/seemps_examples.rst @@ -12,6 +12,10 @@ In the directory `examples` of the library, we have created various Jupyter note - `Solution of a Hamiltonian PDE `_. - `Function interpolation `_. - `Function differentiation `_. +- `Function integration `_. +- `Function optimization `_. +- `Function loading with Chebyshev expansions `_. +- `Function loading with TT-Cross `_. Further examples of use appear in the datasets and associated codes to the following publications: From 1107b4c887bb921d88a7264651b9ac043cf6f075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Rodr=C3=ADguez=20Aldavero?= Date: Fri, 5 Jul 2024 10:21:48 +0200 Subject: [PATCH 7/7] Fix mypy type issues --- src/seemps/analysis/cross/cross.py | 2 +- src/seemps/analysis/cross/cross_dmrg.py | 2 +- src/seemps/analysis/cross/cross_greedy.py | 5 +++-- src/seemps/analysis/cross/cross_maxvol.py | 10 +++++----- src/seemps/analysis/mesh.py | 10 ++++++++-- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/seemps/analysis/cross/cross.py b/src/seemps/analysis/cross/cross.py index deb394e..dd1f0f4 100644 --- a/src/seemps/analysis/cross/cross.py +++ b/src/seemps/analysis/cross/cross.py @@ -1,5 +1,5 @@ import numpy as np -import scipy.linalg +import scipy.linalg # type: ignore import dataclasses import functools diff --git a/src/seemps/analysis/cross/cross_dmrg.py b/src/seemps/analysis/cross/cross_dmrg.py index 805e543..88bd605 100644 --- a/src/seemps/analysis/cross/cross_dmrg.py +++ b/src/seemps/analysis/cross/cross_dmrg.py @@ -1,5 +1,5 @@ import numpy as np -import scipy.linalg +import scipy.linalg # type: ignore from dataclasses import dataclass from typing import Optional, Callable diff --git a/src/seemps/analysis/cross/cross_greedy.py b/src/seemps/analysis/cross/cross_greedy.py index a5d5173..b005bbd 100644 --- a/src/seemps/analysis/cross/cross_greedy.py +++ b/src/seemps/analysis/cross/cross_greedy.py @@ -1,5 +1,5 @@ import numpy as np -import scipy.linalg +import scipy.linalg # type: ignore from typing import TypeVar, Union, Optional, Callable from dataclasses import dataclass @@ -155,7 +155,8 @@ def get_row_indices(rows, all_rows): G_cores = [self.Q_to_G(Q, j_l) for Q, j_l in zip(self.Q_factors, self.J_l[1:])] self.mps = MPS(G_cores + [self.fibers[-1]]) - _Index = TypeVar("_Index", bound=Union[np.intp, np.ndarray, slice]) + # _Index = TypeVar("_Index", bound=Union[np.intp, np.ndarray, slice]) + _Index = Union[np.intp, np.ndarray, slice] def sample_superblock( self, k: int, j_l: _Index = slice(None), j_g: _Index = slice(None) diff --git a/src/seemps/analysis/cross/cross_maxvol.py b/src/seemps/analysis/cross/cross_maxvol.py index 7ec25aa..8d597be 100644 --- a/src/seemps/analysis/cross/cross_maxvol.py +++ b/src/seemps/analysis/cross/cross_maxvol.py @@ -155,7 +155,7 @@ def _update_maxvol( fiber = cross.sample_fiber(k) r_l, s, r_g = fiber.shape if forward: - C = fiber.reshape(r_l * s, r_g, order=order) + C = fiber.reshape(r_l * s, r_g, order=order) # type: ignore Q, _ = scipy.linalg.qr(C, mode="economic", overwrite_a=True, check_finite=False) # type: ignore I, _ = choose_maxvol( Q, # type: ignore @@ -168,7 +168,7 @@ def _update_maxvol( cross.I_l[k + 1] = combine_indices(cross.I_l[k], cross.I_s[k])[I] else: if k > 0: - R = fiber.reshape(r_l, s * r_g, order=order) + R = fiber.reshape(r_l, s * r_g, order=order) # type: ignore Q, _ = scipy.linalg.qr( # type: ignore R.T, mode="economic", overwrite_a=True, check_finite=False ) @@ -179,7 +179,7 @@ def _update_maxvol( cross_strategy.tol_maxvol_square, cross_strategy.tol_maxvol_rect, ) - cross.mps[k] = (G.T).reshape(-1, s, r_g, order=order) + cross.mps[k] = (G.T).reshape(-1, s, r_g, order=order) # type: ignore cross.I_g[k - 1] = combine_indices(cross.I_s[k], cross.I_g[k])[I] else: cross.mps[0] = fiber @@ -219,11 +219,11 @@ def maxvol_rectangular( if r_min < r or r_min > r_max or r_max > n: raise ValueError("Invalid minimum/maximum number of added rows") I0, B = maxvol_square(A, maxiter, tol) - I = np.hstack([I0, np.zeros(r_max - r, dtype=I0.dtype)]) + I = np.hstack([I0, np.zeros(r_max - r, dtype=I0.dtype)]) # type: ignore S = np.ones(n, dtype=int) S[I0] = 0 F = S * np.linalg.norm(B) ** 2 - for k in range(r, r_max): + for k in range(r, int(r_max)): i = np.argmax(F) if k >= r_min and F[i] <= tol_rect**2: break diff --git a/src/seemps/analysis/mesh.py b/src/seemps/analysis/mesh.py index 3d5452b..6fa4c2b 100644 --- a/src/seemps/analysis/mesh.py +++ b/src/seemps/analysis/mesh.py @@ -79,9 +79,15 @@ def __init__(self, start: int, stop: int, step: int = 1): size = (stop - start + step - 1) // step super().__init__(start, stop, size) - def __getitem__(self, idx: Union[int, np.ndarray]) -> Union[int, np.ndarray]: + @overload + def __getitem__(self, idx: np.ndarray) -> np.ndarray: ... + + @overload + def __getitem__(self, idx: int) -> float: ... + + def __getitem__(self, idx: Union[int, np.ndarray]) -> Union[float, np.ndarray]: super()._validate_index(idx) - return self.start + idx * self.step # type: ignore + return self.start + idx * self.step class RegularInterval(Interval):