From 5d4edf2e7b57359022d4220561b6618c690b205a Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 7 Nov 2023 12:18:12 +0000 Subject: [PATCH] Changes suggested by Iakov. --- docs/modules/tnstate.rst | 2 +- .../cutensornet/tnstate/__init__.py | 2 +- .../extensions/cutensornet/tnstate/general.py | 30 +--- pytket/extensions/cutensornet/tnstate/mps.py | 32 ++-- .../cutensornet/tnstate/simulation.py | 25 ++-- pytket/extensions/cutensornet/tnstate/ttn.py | 139 ++++++++++-------- tests/test_tnstate.py | 60 ++++---- 7 files changed, 141 insertions(+), 149 deletions(-) diff --git a/docs/modules/tnstate.rst b/docs/modules/tnstate.rst index 8b4ac456..18589ac7 100644 --- a/docs/modules/tnstate.rst +++ b/docs/modules/tnstate.rst @@ -9,7 +9,7 @@ Simulation .. autofunction:: pytket.extensions.cutensornet.tnstate.simulate -.. autoenum:: pytket.extensions.cutensornet.tnstate.ContractionAlg() +.. autoenum:: pytket.extensions.cutensornet.tnstate.SimulationAlgorithm() :members: .. autoclass:: pytket.extensions.cutensornet.tnstate.Config() diff --git a/pytket/extensions/cutensornet/tnstate/__init__.py b/pytket/extensions/cutensornet/tnstate/__init__.py index d85258c3..4fa995cb 100644 --- a/pytket/extensions/cutensornet/tnstate/__init__.py +++ b/pytket/extensions/cutensornet/tnstate/__init__.py @@ -19,7 +19,7 @@ """ from .general import CuTensorNetHandle, Config, TNState -from .simulation import ContractionAlg, simulate, prepare_circuit_mps +from .simulation import SimulationAlgorithm, simulate, prepare_circuit_mps from .mps import DirMPS, MPS from .mps_gate import MPSxGate diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index c35c5b21..398852b5 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -170,6 +170,10 @@ def __init__( UserWarning, ) + if leaf_size >= 65: # Imposed to avoid bond ID collisions + # More than 20 qubits is already unreasonable for a leaf anyway + raise ValueError("Maximum allowed leaf_size is 65.") + self.leaf_size = leaf_size self.k = k self.optim_delta = 1e-5 @@ -394,29 +398,3 @@ def copy(self) -> TNState: @abstractmethod def _flush(self) -> None: raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") - - -def _safe_qr( - libhandle: CuTensorNetHandle, indices: str, T: cp.ndarray -) -> tuple[cp.ndarray, cp.ndarray]: - """Wrapper of cuTensorNet QR decomposition to work around a bug in 23.6.0. - - The bug has been reported https://github.com/NVIDIA/cuQuantum/discussions/96. - """ - try: - Q, R = tensor.decompose( - indices, - T, - method=tensor.QRMethod(), - options={"handle": libhandle.handle, "device_id": libhandle.device_id}, - ) - except cutn.cuTensorNetError: - Q, S, R = tensor.decompose( - indices, - T, - method=tensor.SVDMethod(partition="U"), # Contracts S to Q - options={"handle": libhandle.handle, "device_id": libhandle.device_id}, - ) - assert S is None - - return (Q, R) diff --git a/pytket/extensions/cutensornet/tnstate/mps.py b/pytket/extensions/cutensornet/tnstate/mps.py index 5ba48b0c..7aac6763 100644 --- a/pytket/extensions/cutensornet/tnstate/mps.py +++ b/pytket/extensions/cutensornet/tnstate/mps.py @@ -93,24 +93,24 @@ def __init__( n_tensors = len(qubits) if n_tensors == 0: # There's no initialisation to be done - return None + pass elif n_tensors == 1: raise ValueError("Please, provide at least two qubits.") - - self.qubit_position = {q: i for i, q in enumerate(qubits)} - - # Create the list of tensors - self.tensors: list[Tensor] = [] - self.canonical_form = {i: None for i in range(n_tensors)} - - # Append each of the tensors initialised in state |0> - m_shape = (1, 1, 2) # Two virtual bonds (dim=1) and one physical - for i in range(n_tensors): - m_tensor = cp.empty(m_shape, dtype=self._cfg._complex_t) - # Initialise the tensor to ket 0 - m_tensor[0][0][0] = 1 - m_tensor[0][0][1] = 0 - self.tensors.append(m_tensor) + else: + self.qubit_position = {q: i for i, q in enumerate(qubits)} + + # Create the list of tensors + self.tensors: list[Tensor] = [] + self.canonical_form = {i: None for i in range(n_tensors)} + + # Append each of the tensors initialised in state |0> + m_shape = (1, 1, 2) # Two virtual bonds (dim=1) and one physical + for i in range(n_tensors): + m_tensor = cp.empty(m_shape, dtype=self._cfg._complex_t) + # Initialise the tensor to ket 0 + m_tensor[0][0][0] = 1 + m_tensor[0][0][1] = 0 + self.tensors.append(m_tensor) def is_valid(self) -> bool: """Verify that the MPS object is valid. diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py index ebbf6e21..66853e39 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/tnstate/simulation.py @@ -33,7 +33,7 @@ from .ttn_gate import TTNxGate -class ContractionAlg(Enum): +class SimulationAlgorithm(Enum): """An enum to refer to the TNState contraction algorithm. Each enum value corresponds to the class with the same name; see its docs for @@ -48,7 +48,7 @@ class ContractionAlg(Enum): def simulate( libhandle: CuTensorNetHandle, circuit: Circuit, - algorithm: ContractionAlg, + algorithm: SimulationAlgorithm, config: Config, ) -> TNState: """Simulates the circuit and returns the ``TNState`` representing the final state. @@ -65,7 +65,7 @@ def simulate( libhandle: The cuTensorNet library handle that will be used to carry out tensor operations. circuit: The pytket circuit to be simulated. - algorithm: Choose between the values of the ``ContractionAlg`` enum. + algorithm: Choose between the values of the ``SimulationAlgorithm`` enum. config: The configuration object for simulation. Returns: @@ -77,7 +77,7 @@ def simulate( logger.info( "Ordering the gates in the circuit to reduce canonicalisation overhead." ) - if algorithm == ContractionAlg.MPSxGate: + if algorithm == SimulationAlgorithm.MPSxGate: tnstate = MPSxGate( # type: ignore libhandle, circuit.qubits, @@ -85,7 +85,7 @@ def simulate( ) sorted_gates = _get_sorted_gates(circuit, algorithm) - elif algorithm == ContractionAlg.MPSxMPO: + elif algorithm == SimulationAlgorithm.MPSxMPO: tnstate = MPSxMPO( # type: ignore libhandle, circuit.qubits, @@ -93,7 +93,7 @@ def simulate( ) sorted_gates = _get_sorted_gates(circuit, algorithm) - elif algorithm == ContractionAlg.TTNxGate: + elif algorithm == SimulationAlgorithm.TTNxGate: qubit_partition = _get_qubit_partition(circuit, config.leaf_size) tnstate = TTNxGate( # type: ignore libhandle, @@ -226,7 +226,7 @@ def _get_qubit_partition( def _get_sorted_gates( circuit: Circuit, - algorithm: ContractionAlg, + algorithm: SimulationAlgorithm, qubit_partition: Optional[dict[int, list[Qubit]]] = None, ) -> list[Command]: """Sorts the list of gates so that there's less canonicalisation during simulation. @@ -251,7 +251,7 @@ def _get_sorted_gates( remaining = set(range(len(all_gates))) # Do some precomputation depending on the algorithm - if algorithm in [ContractionAlg.TTNxGate]: + if algorithm in [SimulationAlgorithm.TTNxGate]: if qubit_partition is None: raise RuntimeError("You must provide a qubit partition!") @@ -260,7 +260,7 @@ def _get_sorted_gates( for q in qubits: leaf_of_qubit[q] = leaf - elif algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: + elif algorithm in [SimulationAlgorithm.MPSxGate, SimulationAlgorithm.MPSxMPO]: idx_of_qubit = {q: i for i, q in enumerate(circuit.qubits)} else: @@ -301,14 +301,17 @@ def _get_sorted_gates( gate_qubits = all_gates[gate_idx].qubits # Criterion for distance depends on the simulation algorithm - if algorithm in [ContractionAlg.TTNxGate]: + if algorithm in [SimulationAlgorithm.TTNxGate]: gate_distance[gate_idx] = max( # Max common ancestor distance leaf_of_qubit[last_qubits[0]] ^ leaf_of_qubit[gate_qubits[0]], leaf_of_qubit[last_qubits[0]] ^ leaf_of_qubit[gate_qubits[1]], leaf_of_qubit[last_qubits[1]] ^ leaf_of_qubit[gate_qubits[0]], leaf_of_qubit[last_qubits[1]] ^ leaf_of_qubit[gate_qubits[1]], ) - elif algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: + elif algorithm in [ + SimulationAlgorithm.MPSxGate, + SimulationAlgorithm.MPSxMPO, + ]: gate_distance[gate_idx] = max( # Max linear distance between qubits abs(idx_of_qubit[last_qubits[0]] - idx_of_qubit[gate_qubits[0]]), abs(idx_of_qubit[last_qubits[0]] - idx_of_qubit[gate_qubits[1]]), diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index 8a17f441..d56dd4d5 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -25,6 +25,7 @@ warnings.warn("local settings failed to import cupy", ImportWarning) try: import cuquantum as cq # type: ignore + from cuquantum.cutensornet import tensor # type: ignore except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) @@ -33,7 +34,7 @@ from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle, Config, TNState, Tensor, _safe_qr +from .general import CuTensorNetHandle, Config, TNState, Tensor class DirTTN(IntEnum): @@ -109,9 +110,12 @@ def __init__( libhandle: The cuTensorNet library handle that will be used to carry out tensor operations on the TTN. qubit_partition: A partition of the qubits in the circuit into disjoint - groups, describing the hierarchical structure of the TTN. As a rule of - thumb, the cost of a gate between qubits on ``qubit_partition[i]`` and - ``qubit_partition[j]`` scales exponentially on ``i XOR j``. + groups, describing the hierarchical structure of the TTN. Each key + identifies a leaf of the TTN, with its corresponding value indicating + the list of qubits represented by the leaf. The leaves are numbered + from left to right on a planar representation of the tree. Hence, the + smaller half of the keys correspond to leaves in the left subtree and + the rest are in the right subtree; providing recursive bipartitions. config: The object describing the configuration for simulation. Raises: @@ -136,70 +140,70 @@ def __init__( n_groups = len(qubit_partition) if n_groups == 0: # There's no initialisation to be done - return None - if n_groups == 1: + pass + elif n_groups == 1: raise ValueError( "Only one entry to qubit_partition provided." "Introduce a finer partition of qubits." ) - - n_levels = math.floor(math.log2(n_groups)) - if n_groups != 2**n_levels: - raise ValueError( - "The number of entries in qubit_partition must be a power of two." - ) - - # Create the TreeNodes of the different groups of qubits - for k, qubits in qubit_partition.items(): - if k < 0 or k >= n_groups: + else: + n_levels = math.floor(math.log2(n_groups)) + if n_groups != 2**n_levels: raise ValueError( - f"The keys of qubit_partition must range from 0 to {n_groups-1}." + "The number of entries in qubit_partition must be a power of two." ) - # Calculate the root path of this group - path = [] - for l in reversed(range(n_levels)): - if k < 2**l: - path.append(DirTTN.LEFT) - else: - path.append(DirTTN.RIGHT) - k -= 2**l - - # Add each qubit to the qubit_position dictionary - for i, q in enumerate(qubits): - if q in self.qubit_position: + # Create the TreeNodes of the different groups of qubits + for k, qubits in qubit_partition.items(): + if k < 0 or k >= n_groups: raise ValueError( - f"Qubit {q} appears in multiple entries of qubit_partition." + f"Keys of qubit_partition must range from 0 to {n_groups-1}." ) - self.qubit_position[q] = (tuple(path), i) - - # This tensor has a physical bond per qubit and one virtual bond at the - # end for the parent (dim=1) - shape = tuple([2] * len(qubits) + [1]) - # Initialise the tensor of this group of qubits to |0> - tensor = cp.zeros(shape=shape, dtype=self._cfg._complex_t) - ket_zero_entry = tuple(0 for _ in shape) # Index 0 on all bonds - tensor[ket_zero_entry] = 1 # Amplitude of |0> set to 1 - - # Create the TreeNode - node = TreeNode(tensor, is_leaf=True) - self.nodes[tuple(path)] = node - - # Create the internal TreeNodes - paths: list[list[DirTTN]] = [[]] - for _ in range(n_levels): - # Create the TreeNode at this path - for p in paths: - tensor = cp.ones(shape=(1, 1, 1), dtype=self._cfg._complex_t) - self.nodes[tuple(p)] = TreeNode(tensor) - # Generate the paths for the next level - paths = [ - p + [direction] - for p in paths - for direction in [DirTTN.LEFT, DirTTN.RIGHT] - ] - self._logger.debug(f"qubit_position={self.qubit_position}") - self._logger.debug(f"All root paths: {list(self.nodes.keys())}") + + # Calculate the root path of this group + path = [] + for l in reversed(range(n_levels)): + if k < 2**l: + path.append(DirTTN.LEFT) + else: + path.append(DirTTN.RIGHT) + k -= 2**l + + # Add each qubit to the qubit_position dictionary + for i, q in enumerate(qubits): + if q in self.qubit_position: + raise ValueError( + f"Qubit {q} appears more than once in qubit_partition." + ) + self.qubit_position[q] = (tuple(path), i) + + # This tensor has a physical bond per qubit and one virtual bond at the + # end for the parent (dim=1) + shape = tuple([2] * len(qubits) + [1]) + # Initialise the tensor of this group of qubits to |0> + tensor = cp.zeros(shape=shape, dtype=self._cfg._complex_t) + ket_zero_entry = tuple(0 for _ in shape) # Index 0 on all bonds + tensor[ket_zero_entry] = 1 # Amplitude of |0> set to 1 + + # Create the TreeNode + node = TreeNode(tensor, is_leaf=True) + self.nodes[tuple(path)] = node + + # Create the internal TreeNodes + paths: list[list[DirTTN]] = [[]] + for _ in range(n_levels): + # Create the TreeNode at this path + for p in paths: + tensor = cp.ones(shape=(1, 1, 1), dtype=self._cfg._complex_t) + self.nodes[tuple(p)] = TreeNode(tensor) + # Generate the paths for the next level + paths = [ + p + [direction] + for p in paths + for direction in [DirTTN.LEFT, DirTTN.RIGHT] + ] + self._logger.debug(f"qubit_position={self.qubit_position}") + self._logger.debug(f"All root paths: {list(self.nodes.keys())}") def is_valid(self) -> bool: """Verify that the TTN object is valid. @@ -371,10 +375,11 @@ def canonicalise( Q_bonds = "lrs" R_bonds = "sp" - Q, R = _safe_qr( - self._lib, + Q, R = tensor.decompose( node_bonds + "->" + Q_bonds + "," + R_bonds, self.nodes[path].tensor, + method=tensor.QRMethod(), + options=options, ) # Update the tensor @@ -429,10 +434,11 @@ def canonicalise( R_bonds = "rs" node_bonds = "lrp" - Q, R = _safe_qr( - self._lib, + Q, R = tensor.decompose( node_bonds + "->" + Q_bonds + "," + R_bonds, self.nodes[path].tensor, + method=tensor.QRMethod(), + options=options, ) # If the child bond is not the center yet, contract R with child node @@ -495,8 +501,11 @@ def canonicalise( Q_bonds = node_bonds[:target_bond] + "s" + node_bonds[target_bond + 1 :] R_bonds = chr(target_bond) + "s" - Q, R = _safe_qr( - self._lib, node_bonds + "->" + Q_bonds + "," + R_bonds, leaf_node.tensor + Q, R = tensor.decompose( + node_bonds + "->" + Q_bonds + "," + R_bonds, + leaf_node.tensor, + method=tensor.QRMethod(), + options=options, ) # Note: Since R is not contracted with any other tensor, we cannot update # the leaf node to Q. That'd change the state represented by the TTN. diff --git a/tests/test_tnstate.py b/tests/test_tnstate.py index 6f85a473..12dc8c76 100644 --- a/tests/test_tnstate.py +++ b/tests/test_tnstate.py @@ -18,7 +18,7 @@ DirTTN, simulate, prepare_circuit_mps, - ContractionAlg, + SimulationAlgorithm, ) from pytket.extensions.cutensornet.tnstate.ttn import RootPath from pytket.extensions.cutensornet.utils import circuit_statevector_postselect @@ -234,13 +234,13 @@ def test_canonicalise_ttn(center: Union[RootPath, Qubit]) -> None: @pytest.mark.parametrize( "algorithm", [ - ContractionAlg.MPSxGate, - ContractionAlg.MPSxMPO, - ContractionAlg.TTNxGate, + SimulationAlgorithm.MPSxGate, + SimulationAlgorithm.MPSxMPO, + SimulationAlgorithm.TTNxGate, ], ) -def test_exact_circ_sim(circuit: Circuit, algorithm: ContractionAlg) -> None: - if algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: +def test_exact_circ_sim(circuit: Circuit, algorithm: SimulationAlgorithm) -> None: + if algorithm in [SimulationAlgorithm.MPSxGate, SimulationAlgorithm.MPSxMPO]: circuit, _ = prepare_circuit_mps(circuit) n_qubits = len(circuit.qubits) @@ -295,12 +295,14 @@ def test_exact_circ_sim(circuit: Circuit, algorithm: ContractionAlg) -> None: @pytest.mark.parametrize( "algorithm", [ - ContractionAlg.MPSxGate, - ContractionAlg.MPSxMPO, + SimulationAlgorithm.MPSxGate, + SimulationAlgorithm.MPSxMPO, ], ) -def test_approx_circ_sim_gate_fid(circuit: Circuit, algorithm: ContractionAlg) -> None: - if algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: +def test_approx_circ_sim_gate_fid( + circuit: Circuit, algorithm: SimulationAlgorithm +) -> None: + if algorithm in [SimulationAlgorithm.MPSxGate, SimulationAlgorithm.MPSxMPO]: circuit, _ = prepare_circuit_mps(circuit) with CuTensorNetHandle() as libhandle: @@ -339,13 +341,13 @@ def test_approx_circ_sim_gate_fid(circuit: Circuit, algorithm: ContractionAlg) - @pytest.mark.parametrize( "algorithm", [ - ContractionAlg.MPSxGate, - ContractionAlg.MPSxMPO, - ContractionAlg.TTNxGate, + SimulationAlgorithm.MPSxGate, + SimulationAlgorithm.MPSxMPO, + SimulationAlgorithm.TTNxGate, ], ) -def test_approx_circ_sim_chi(circuit: Circuit, algorithm: ContractionAlg) -> None: - if algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: +def test_approx_circ_sim_chi(circuit: Circuit, algorithm: SimulationAlgorithm) -> None: + if algorithm in [SimulationAlgorithm.MPSxGate, SimulationAlgorithm.MPSxMPO]: circuit, _ = prepare_circuit_mps(circuit) with CuTensorNetHandle() as libhandle: @@ -370,8 +372,8 @@ def test_approx_circ_sim_chi(circuit: Circuit, algorithm: ContractionAlg) -> Non @pytest.mark.parametrize( "algorithm", [ - ContractionAlg.MPSxGate, - ContractionAlg.MPSxMPO, + SimulationAlgorithm.MPSxGate, + SimulationAlgorithm.MPSxMPO, ], ) @pytest.mark.parametrize( @@ -382,9 +384,9 @@ def test_approx_circ_sim_chi(circuit: Circuit, algorithm: ContractionAlg) -> Non ], ) def test_float_point_options( - circuit: Circuit, algorithm: ContractionAlg, fp_precision: Any + circuit: Circuit, algorithm: SimulationAlgorithm, fp_precision: Any ) -> None: - if algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: + if algorithm in [SimulationAlgorithm.MPSxGate, SimulationAlgorithm.MPSxMPO]: circuit, _ = prepare_circuit_mps(circuit) with CuTensorNetHandle() as libhandle: @@ -436,7 +438,7 @@ def test_circ_approx_explicit_mps(circuit: Circuit) -> None: mps_gate = simulate( libhandle, circuit, - ContractionAlg.MPSxGate, + SimulationAlgorithm.MPSxGate, cfg, ) assert np.isclose(mps_gate.get_fidelity(), 0.4, atol=1e-1) @@ -447,7 +449,7 @@ def test_circ_approx_explicit_mps(circuit: Circuit) -> None: mps_mpo = simulate( libhandle, circuit, - ContractionAlg.MPSxMPO, + SimulationAlgorithm.MPSxMPO, cfg, ) assert np.isclose(mps_mpo.get_fidelity(), 0.6, atol=1e-1) @@ -457,13 +459,13 @@ def test_circ_approx_explicit_mps(circuit: Circuit) -> None: # Fixed virtual bond dimension # Check for MPSxGate cfg = Config(chi=8) - mps_gate = simulate(libhandle, circuit, ContractionAlg.MPSxGate, cfg) + mps_gate = simulate(libhandle, circuit, SimulationAlgorithm.MPSxGate, cfg) assert np.isclose(mps_gate.get_fidelity(), 0.03, atol=1e-2) assert mps_gate.is_valid() assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=cfg._atol) # Check for MPSxMPO - mps_mpo = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, cfg) + mps_mpo = simulate(libhandle, circuit, SimulationAlgorithm.MPSxMPO, cfg) assert np.isclose(mps_mpo.get_fidelity(), 0.05, atol=1e-2) assert mps_mpo.is_valid() assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=cfg._atol) @@ -482,7 +484,7 @@ def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: # Fixed virtual bond dimension # Check for TTNxGate cfg = Config(chi=120, leaf_size=3) - ttn_gate = simulate(libhandle, circuit, ContractionAlg.TTNxGate, cfg) + ttn_gate = simulate(libhandle, circuit, SimulationAlgorithm.TTNxGate, cfg) for g in circuit.get_commands(): ttn_gate.apply_gate(g) assert np.isclose(ttn_gate.get_fidelity(), 0.62, atol=1e-2) @@ -523,7 +525,7 @@ def test_postselect_2q_circ(circuit: Circuit, postselect_dict: dict) -> None: with CuTensorNetHandle() as libhandle: cfg = Config() - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, cfg) + mps = simulate(libhandle, circuit, SimulationAlgorithm.MPSxGate, cfg) prob = mps.postselect(postselect_dict) assert np.isclose(prob, sv_prob, atol=cfg._atol) assert np.allclose(mps.get_statevector(), sv, atol=cfg._atol) @@ -554,7 +556,7 @@ def test_postselect_circ(circuit: Circuit, postselect_dict: dict) -> None: with CuTensorNetHandle() as libhandle: cfg = Config() - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, cfg) + mps = simulate(libhandle, circuit, SimulationAlgorithm.MPSxGate, cfg) prob = mps.postselect(postselect_dict) assert np.isclose(prob, sv_prob, atol=cfg._atol) assert np.allclose(mps.get_statevector(), sv, atol=cfg._atol) @@ -600,7 +602,7 @@ def test_expectation_value(circuit: Circuit, observable: QubitPauliString) -> No # Simulate the circuit and obtain the expectation value with CuTensorNetHandle() as libhandle: cfg = Config() - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, cfg) + mps = simulate(libhandle, circuit, SimulationAlgorithm.MPSxGate, cfg) assert np.isclose( mps.expectation_value(observable), expectation_value, atol=cfg._atol ) @@ -630,7 +632,7 @@ def test_sample_circ_2q(circuit: Circuit) -> None: # Compute the samples sample_dict = {0: 0, 1: 0, 2: 0, 3: 0} with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config()) + mps = simulate(libhandle, circuit, SimulationAlgorithm.MPSxGate, Config()) # Take samples measuring both qubits at once for _ in range(n_samples): @@ -657,7 +659,7 @@ def test_measure_circ(circuit: Circuit) -> None: qB = circuit.qubits[-3] # Third list significant qubit with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config()) + mps = simulate(libhandle, circuit, SimulationAlgorithm.MPSxGate, Config()) # Compute the probabilities of each outcome p = {(0, 0): 0.0, (0, 1): 0.0, (1, 0): 0.0, (1, 1): 0.0}