Source code for seemps.analysis.space

from __future__ import annotations
import numpy as np
from ..operators import MPO, MPOList, MPOSum


# TODO: This might not be the place to have this function
# It should be under operators or in qft.
[docs] def mpo_flip(operator): """Swap the qubits in the quantum register, to fix the reversal suffered during the quantum Fourier transform.""" if isinstance(operator, MPO): return MPO( [np.moveaxis(op, [0, 1, 2, 3], [3, 1, 2, 0]) for op in reversed(operator)], strategy=operator.strategy, ) elif isinstance(operator, MPOList): return MPOList( [ MPO( [ np.moveaxis(op, [0, 1, 2, 3], [3, 1, 2, 0]) for op in reversed(mpo) ], strategy=operator.strategy, ) for mpo in operator.mpos ], strategy=operator.strategy, ) elif isinstance(operator, MPOSum): new_mpos = [] for weight, op in zip(operator.weights, operator.mpos): new_mpos.append(weight * mpo_flip(op)) return MPOSum( new_mpos, strategy=operator.strategy, )
[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 : list[list[floats]] 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. (Optional, defaults to True). order : str, optional The order in which sites are organized. Default is "A" (sequential). """ def __init__(self, qubits_per_dimension, L, closed=True, order="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 : str, 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 = L self.a = [L_i[0] for L_i in L] self.b = [L_i[1] for L_i in L] self.dx = np.array( [ (end - start) / ((d - 1) if closed else d) for (start, end), d in zip(L, self.grid_dimensions) ] ) self.x = [ self.a[i] + self.dx[i] * np.arange(dim) for i, dim in enumerate(self.grid_dimensions) ]
[docs] def increase_resolution(self, new_qubits_per_dimension): """ 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. """ if self.closed: new_space = Space( new_qubits_per_dimension, self.L, closed=self.closed, ) new_space.dx = np.array( [ dx * self.grid_dimensions[i] / new_space.grid_dimensions[i] for i, dx in enumerate(self.dx) ] ) new_space.x = [ new_space.a[i] + new_space.dx[i] * np.arange(dim) for i, dim in enumerate(new_space.grid_dimensions) ] else: new_space = Space( new_qubits_per_dimension, [ (an, an + dxn * (2**old_qubits)) for an, dxn, old_qubits in zip( self.a, self.dx, self.qubits_per_dimension ) ], closed=self.closed, ) return new_space
def __str__(self): """ Returns a string representation of the Space object. Returns ------- str String representation of the Space object. """ return f"Space(a={self.a}, b={self.b}, dx={self.dx}, closed={self.closed}, qubits={self.qubits_per_dimension})"
[docs] def get_coordinates_tuples(self): """ Creates a list of coordinate tuples for the qubits. Returns ------- list[tuple] List of tuples (n, k), where `n` is the dimension and `k` is the significant digit of the qubits used for storing that dimension. """ coordinates_tuples = [] coordinates_tuples = [ (n, k) for n, n_q in enumerate(self.qubits_per_dimension) for k in range(n_q) ] return coordinates_tuples
[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, dim): """ 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 ------- MPS The extended multi-dimensional MPS. """ return op.extend(self.n_sites, self.sites[dim])
[docs] def enlarge_dimension(self, dim, amount) -> Space: """ Enlarges the specified dimension by adding more qubits. 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 new positions from an old Space object to the current Space object. 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): new_positions[d] = new_positions[d][:n] new_positions = sum(new_positions, []) new_positions.sort() return new_positions