Source code for seemps.analysis.space

from __future__ import annotations
from collections.abc import Sequence
import numpy as np
from typing import TypeVar, cast
from ..typing import Real, MPSOrder
from ..operators import MPO, MPOList, MPOSum
from .mesh import Mesh, RegularInterval


_Operator = TypeVar("_Operator", MPOSum, MPO, MPOList)

# TODO: Remove this function


[docs] def mpo_flip(operator: _Operator) -> _Operator: """Swap the qubits in the quantum register, to fix the reversal suffered during the quantum Fourier transform.""" return operator.reverse()
[docs] class Space: """Coordinate grid class. Class to encode the definition space of a discretized multidimensional function. Parameters ---------- qubits_per_dimension : list[int] Number of qubits for each dimension. L : Sequence[tuple[Real, Real]] Position space intervals (a_i,b_i) for each dimension i. closed : bool If closed is True, the position space intervals are closed (symmetrically defined). If False, the interval is open. (defaults to True). order : MPSOrder, default = "A" The order in which sites are organized. Default is "A" (sequential). """ qubits_per_dimension: list[int] grid_dimensions: list[int] closed: bool n_sites: int order: MPSOrder sites: list[list[int]] L: list[tuple[float, float]] mesh: Mesh def __init__( self, qubits_per_dimension: list[int], L: Sequence[tuple[Real, Real]], closed: bool = True, order: MPSOrder = "A", ): """ Initializes the Space object. Parameters ---------- qubits_per_dimension : list[int] Number of qubits for each dimension. L : list[list[float]] Position space intervals [a_i, b_i] for each dimension i. closed : bool, optional If True, the intervals are closed; if False, they are open. Default is True. order : MPSOrder, optional The order in which sites are organized. Default is "A" (sequential). """ self.qubits_per_dimension = qubits_per_dimension self.grid_dimensions = [2**n for n in qubits_per_dimension] self.closed = closed self.n_sites = sum(qubits_per_dimension) self.order = order self.sites = self.get_sites() self.L = [(float(start), float(end)) for start, end in L] self.mesh = Mesh( [ RegularInterval(start, end, 2**n, endpoint_right=closed) for (start, end), n in zip(self.L, self.qubits_per_dimension) ] ) @property def dimensions(self) -> int: return len(self.qubits_per_dimension) @property def dx(self) -> np.ndarray: return np.asarray([cast(RegularInterval, I).step for I in self.mesh.intervals]) @property def x(self) -> list[np.ndarray]: return [I.to_vector() for I in self.mesh.intervals] def to_tensor(self) -> np.ndarray: return self.mesh.to_tensor().transpose([-1] + list(range(self.dimensions)))
[docs] def change_qubits(self, new_qubits_per_dimension: list[int]) -> Space: """ Creates a new Space object with increased resolution based on the new qubits per dimension. Parameters ---------- new_qubits_per_dimension : list[int] New number of qubits for each dimension. Returns ------- Space A new Space object with the increased resolution. """ return Space( new_qubits_per_dimension, self.L, closed=self.closed, order=self.order )
def __str__(self): """ Returns a string representation of the Space object. Returns ------- str String representation of the Space object. """ return f"Space(qubits={self.qubits_per_dimension}, L={self.L}, closed={self.closed}, order={self.order})"
[docs] def get_sites(self): """ Generates the sites for each dimension based on the order. Returns ------- list[list[int]] A list of lists containing site indices for each dimension. """ sites = [] index = 0 if self.order == "A": for n in self.qubits_per_dimension: sites.append(np.arange(index, index + n).tolist()) index += n else: sites = [[] for _ in self.qubits_per_dimension] for n in range(max(self.qubits_per_dimension)): for d, m in enumerate(self.qubits_per_dimension): if n < m: sites[d].append(index) index += 1 return sites
[docs] def extend(self, op: _Operator, dim: int) -> _Operator: """ Extends an MPO acting on a 1D space to a multi-dimensional MPS. Parameters ---------- op : MPO The MPO to extend. dim : int The dimension to extend along. Returns ------- MPO The extended multi-dimensional MPO. """ return op.extend(self.n_sites, self.sites[dim])
[docs] def enlarge_dimension(self, dim: int, amount: int) -> Space: """ Enlarges the specified dimension by adding more qubits to one dimension. Parameters ---------- dim : int The dimension to enlarge. amount : int The number of qubits to add. Returns ------- Space A new Space object with the enlarged dimension. """ new_qubits_per_dimension = self.qubits_per_dimension.copy() new_qubits_per_dimension[dim] += amount return Space(new_qubits_per_dimension, self.L, self.closed, self.order)
[docs] def new_positions_from_old_space(self, space: Space) -> list[int]: """ Maps the qubits from a smaller space, to their respective positions in the quantum register for a larger space. Parameters ---------- space : Space The old Space object to map positions from. Returns ------- list[int] List of new positions in the current Space object. """ new_positions = self.sites.copy() for d, n in enumerate(space.qubits_per_dimension): if n > self.qubits_per_dimension[d]: raise Exception( f"I cannot map a larger map into a smaller one.\nOld: {space}\nNew: {self}" ) new_positions[d] = new_positions[d][:n] return sorted(sum(new_positions, []))
__all__ = [ "mpo_flip", "Space", ]