From 19273db23b7a0b472a40cf1e2484bde6a4e5d22f Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Mon, 25 Sep 2023 15:32:32 +0100 Subject: [PATCH 01/83] Created new general.py file for logger and cutensornet handle. Updated copyright comment. --- pytket/extensions/cutensornet/__init__.py | 5 +++ pytket/extensions/cutensornet/mps/__init__.py | 6 +-- pytket/extensions/cutensornet/mps/mps.py | 41 +++---------------- pytket/extensions/cutensornet/mps/mps_gate.py | 6 +-- pytket/extensions/cutensornet/mps/mps_mpo.py | 8 ++-- .../extensions/cutensornet/mps/simulation.py | 16 +++++++- .../cutensornet/tensor_network_convert.py | 29 +------------ 7 files changed, 36 insertions(+), 75 deletions(-) diff --git a/pytket/extensions/cutensornet/__init__.py b/pytket/extensions/cutensornet/__init__.py index f716ff92..15e7095f 100644 --- a/pytket/extensions/cutensornet/__init__.py +++ b/pytket/extensions/cutensornet/__init__.py @@ -18,6 +18,11 @@ from .backends import CuTensorNetBackend +from .general import ( + set_logger, + CuTensorNetHandle, +) + from .tensor_network_convert import ( TensorNetwork, PauliOperatorTensorNetwork, diff --git a/pytket/extensions/cutensornet/mps/__init__.py b/pytket/extensions/cutensornet/mps/__init__.py index dd3abfc7..7eee1b05 100644 --- a/pytket/extensions/cutensornet/mps/__init__.py +++ b/pytket/extensions/cutensornet/mps/__init__.py @@ -1,11 +1,11 @@ -# Copyright 2019 Cambridge Quantum Computing +# Copyright 2019-2023 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +## # http://www.apache.org/licenses/LICENSE-2.0 -# +## # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/pytket/extensions/cutensornet/mps/mps.py b/pytket/extensions/cutensornet/mps/mps.py index 3514815b..287ee4ac 100644 --- a/pytket/extensions/cutensornet/mps/mps.py +++ b/pytket/extensions/cutensornet/mps/mps.py @@ -1,11 +1,11 @@ -# Copyright 2019 Cambridge Quantum Computing +# Copyright 2019-2023 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +## # http://www.apache.org/licenses/LICENSE-2.0 -# +## # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,7 +25,6 @@ warnings.warn("local settings failed to import cupy", ImportWarning) try: import cuquantum as cq # type: ignore - import cuquantum.cutensornet as cutn # type: ignore from cuquantum.cutensornet import tensor # type: ignore except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) @@ -33,6 +32,8 @@ from pytket.circuit import Command, Op, OpType, Qubit from pytket.pauli import Pauli, QubitPauliString +from pytket.extensions.cutensornet.general import CuTensorNetHandle + # An alias so that `intptr_t` from CuQuantum's API (which is not available in # base python) has some meaningful type name. Handle = int @@ -50,38 +51,6 @@ class DirectionMPS(Enum): RIGHT = 1 -class CuTensorNetHandle: - """Initialise the cuTensorNet library with automatic workspace memory - management. - - Note: - Always use as ``with CuTensorNetHandle() as libhandle:`` so that cuTensorNet - handles are automatically destroyed at the end of execution. - - Attributes: - handle (int): The cuTensorNet library handle created by this initialisation. - device_id (int): The ID of the device (GPU) where cuTensorNet is initialised. - If not provided, defaults to ``cp.cuda.Device()``. - """ - - def __init__(self, device_id: Optional[int] = None): - self.handle = cutn.create() - self._is_destroyed = False - - # Make sure CuPy uses the specified device - cp.cuda.Device(device_id).use() - - dev = cp.cuda.Device() - self.device_id = int(dev) - - def __enter__(self) -> CuTensorNetHandle: - return self - - def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None: - cutn.destroy(self.handle) - self._is_destroyed = True - - class MPS: """Represents a state as a Matrix Product State. diff --git a/pytket/extensions/cutensornet/mps/mps_gate.py b/pytket/extensions/cutensornet/mps/mps_gate.py index 3c522afd..de57c194 100644 --- a/pytket/extensions/cutensornet/mps/mps_gate.py +++ b/pytket/extensions/cutensornet/mps/mps_gate.py @@ -1,11 +1,11 @@ -# Copyright 2019 Cambridge Quantum Computing +# Copyright 2019-2023 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +## # http://www.apache.org/licenses/LICENSE-2.0 -# +## # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/pytket/extensions/cutensornet/mps/mps_mpo.py b/pytket/extensions/cutensornet/mps/mps_mpo.py index 42f82462..c0f62818 100644 --- a/pytket/extensions/cutensornet/mps/mps_mpo.py +++ b/pytket/extensions/cutensornet/mps/mps_mpo.py @@ -1,11 +1,11 @@ -# Copyright 2019 Cambridge Quantum Computing +# Copyright 2019-2023 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +## # http://www.apache.org/licenses/LICENSE-2.0 -# +## # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,8 +29,8 @@ warnings.warn("local settings failed to import cutensornet", ImportWarning) from pytket.circuit import Op, Qubit +from pytket.extensions.cutensornet.general import CuTensorNetHandle from .mps import ( - CuTensorNetHandle, DirectionMPS, Tensor, MPS, diff --git a/pytket/extensions/cutensornet/mps/simulation.py b/pytket/extensions/cutensornet/mps/simulation.py index a9224407..83bf2c49 100644 --- a/pytket/extensions/cutensornet/mps/simulation.py +++ b/pytket/extensions/cutensornet/mps/simulation.py @@ -1,3 +1,16 @@ +# Copyright 2019-2023 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +## +# http://www.apache.org/licenses/LICENSE-2.0 +## +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from typing import Any from enum import Enum from random import choice # type: ignore @@ -10,7 +23,8 @@ from pytket.passes import DefaultMappingPass from pytket.predicates import CompilationUnit -from .mps import CuTensorNetHandle, MPS +from pytket.extensions.cutensornet.general import CuTensorNetHandle +from .mps import MPS from .mps_gate import MPSxGate from .mps_mpo import MPSxMPO diff --git a/pytket/extensions/cutensornet/tensor_network_convert.py b/pytket/extensions/cutensornet/tensor_network_convert.py index f5bb7468..0d9aad71 100644 --- a/pytket/extensions/cutensornet/tensor_network_convert.py +++ b/pytket/extensions/cutensornet/tensor_network_convert.py @@ -27,34 +27,7 @@ from pytket.pauli import QubitPauliString # type: ignore from pytket.circuit import Circuit, Qubit # type: ignore from pytket.utils import permute_rows_cols_in_unitary - - -# TODO: decide whether to use logger. -def set_logger( - logger_name: str, - level: int = logging.INFO, - fmt: str = "%(name)s - %(levelname)s - %(message)s", -) -> Logger: - """Initialises and configures a logger object. - - Args: - logger_name: Name for the logger object. - level: Logger output level. - fmt: Logger output format. - - Returns: - New configured logger object. - """ - logger = logging.getLogger(logger_name) - logger.setLevel(level) - logger.propagate = False - if not logger.handlers: - handler = logging.StreamHandler() - handler.setLevel(level) - formatter = logging.Formatter(fmt) - handler.setFormatter(formatter) - logger.addHandler(handler) - return logger +from pytket.extensions.cutensornet.general import set_logger class TensorNetwork: From 2fde81ad24f2bbd34cf903b1c1ed1f65d7958127 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Mon, 25 Sep 2023 17:12:14 +0100 Subject: [PATCH 02/83] Setting logger at MPS. Also changed my mind and restored CuTensornetHandle back to mps.py --- pytket/extensions/cutensornet/__init__.py | 5 --- pytket/extensions/cutensornet/mps/mps.py | 39 ++++++++++++++++++- pytket/extensions/cutensornet/mps/mps_mpo.py | 3 +- .../extensions/cutensornet/mps/simulation.py | 4 +- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/pytket/extensions/cutensornet/__init__.py b/pytket/extensions/cutensornet/__init__.py index 15e7095f..f716ff92 100644 --- a/pytket/extensions/cutensornet/__init__.py +++ b/pytket/extensions/cutensornet/__init__.py @@ -18,11 +18,6 @@ from .backends import CuTensorNetBackend -from .general import ( - set_logger, - CuTensorNetHandle, -) - from .tensor_network_convert import ( TensorNetwork, PauliOperatorTensorNetwork, diff --git a/pytket/extensions/cutensornet/mps/mps.py b/pytket/extensions/cutensornet/mps/mps.py index 287ee4ac..906b8fac 100644 --- a/pytket/extensions/cutensornet/mps/mps.py +++ b/pytket/extensions/cutensornet/mps/mps.py @@ -13,6 +13,7 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings +import logging from typing import Any, Optional, Union from enum import Enum @@ -25,6 +26,7 @@ warnings.warn("local settings failed to import cupy", ImportWarning) try: import cuquantum as cq # type: ignore + import cuquantum.cutensornet as cutn # type: ignore from cuquantum.cutensornet import tensor # type: ignore except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) @@ -32,7 +34,7 @@ from pytket.circuit import Command, Op, OpType, Qubit from pytket.pauli import Pauli, QubitPauliString -from pytket.extensions.cutensornet.general import CuTensorNetHandle +from pytket.extensions.cutensornet.general import set_logger # An alias so that `intptr_t` from CuQuantum's API (which is not available in # base python) has some meaningful type name. @@ -51,6 +53,38 @@ class DirectionMPS(Enum): RIGHT = 1 +class CuTensorNetHandle: + """Initialise the cuTensorNet library with automatic workspace memory + management. + + Note: + Always use as ``with CuTensorNetHandle() as libhandle:`` so that cuTensorNet + handles are automatically destroyed at the end of execution. + + Attributes: + handle (int): The cuTensorNet library handle created by this initialisation. + device_id (int): The ID of the device (GPU) where cuTensorNet is initialised. + If not provided, defaults to ``cp.cuda.Device()``. + """ + + def __init__(self, device_id: Optional[int] = None): + self.handle = cutn.create() + self._is_destroyed = False + + # Make sure CuPy uses the specified device + cp.cuda.Device(device_id).use() + + dev = cp.cuda.Device() + self.device_id = int(dev) + + def __enter__(self) -> CuTensorNetHandle: + return self + + def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None: + cutn.destroy(self.handle) + self._is_destroyed = True + + class MPS: """Represents a state as a Matrix Product State. @@ -80,6 +114,7 @@ def __init__( chi: Optional[int] = None, truncation_fidelity: Optional[float] = None, float_precision: Optional[Union[np.float32, np.float64]] = None, + loglevel: int = logging.WARNING, ): """Initialise an MPS on the computational state ``|0>``. @@ -108,6 +143,7 @@ def __init__( choose from ``numpy`` types: ``np.float64`` or ``np.float32``. Complex numbers are represented using two of such ``float`` numbers. Default is ``np.float64``. + loglevel: Internal logger output level. Raises: ValueError: If less than two qubits are provided. @@ -142,6 +178,7 @@ def __init__( ) self._lib = libhandle + self._logger = set_logger("MPS", level=loglevel) ####################################### # Initialise the MPS with a |0> state # diff --git a/pytket/extensions/cutensornet/mps/mps_mpo.py b/pytket/extensions/cutensornet/mps/mps_mpo.py index c0f62818..acf9cbc7 100644 --- a/pytket/extensions/cutensornet/mps/mps_mpo.py +++ b/pytket/extensions/cutensornet/mps/mps_mpo.py @@ -29,8 +29,9 @@ warnings.warn("local settings failed to import cutensornet", ImportWarning) from pytket.circuit import Op, Qubit -from pytket.extensions.cutensornet.general import CuTensorNetHandle +from pytket.extensions.cutensornet.general import set_logger from .mps import ( + CuTensorNetHandle, DirectionMPS, Tensor, MPS, diff --git a/pytket/extensions/cutensornet/mps/simulation.py b/pytket/extensions/cutensornet/mps/simulation.py index 83bf2c49..db3d865a 100644 --- a/pytket/extensions/cutensornet/mps/simulation.py +++ b/pytket/extensions/cutensornet/mps/simulation.py @@ -23,8 +23,8 @@ from pytket.passes import DefaultMappingPass from pytket.predicates import CompilationUnit -from pytket.extensions.cutensornet.general import CuTensorNetHandle -from .mps import MPS +from pytket.extensions.cutensornet.general import set_logger +from .mps import CuTensorNetHandle, MPS from .mps_gate import MPSxGate from .mps_mpo import MPSxMPO From beded342bc7d4504089339560706247361ff1cc0 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 26 Sep 2023 15:46:17 +0100 Subject: [PATCH 03/83] Added debugging and info logging messages. --- pytket/extensions/cutensornet/mps/mps.py | 51 ++++++++++++++++++- pytket/extensions/cutensornet/mps/mps_gate.py | 24 ++++++++- pytket/extensions/cutensornet/mps/mps_mpo.py | 35 +++++++++++-- .../extensions/cutensornet/mps/simulation.py | 39 ++++++++++---- 4 files changed, 134 insertions(+), 15 deletions(-) diff --git a/pytket/extensions/cutensornet/mps/mps.py b/pytket/extensions/cutensornet/mps/mps.py index 906b8fac..73479c8b 100644 --- a/pytket/extensions/cutensornet/mps/mps.py +++ b/pytket/extensions/cutensornet/mps/mps.py @@ -231,6 +231,15 @@ def is_valid(self) -> bool: ds_ok = set(self.canonical_form.keys()) == set(range(len(self))) ds_ok = ds_ok and set(self.qubit_position.values()) == set(range(len(self))) + # Debugger logging + self._logger.debug( + "Checking validity of MPS... " + f"chi_ok={chi_ok}" + f"phys_ok={phys_ok}" + f"shape_ok={shape_ok}" + f"ds_ok={ds_ok}" + ) + return chi_ok and phys_ok and shape_ok and ds_ok def apply_gate(self, gate: Command) -> MPS: @@ -264,6 +273,7 @@ def apply_gate(self, gate: Command) -> MPS: "Gates can only be applied to tensors with physical" + " bond dimension of 2." ) + self._logger.debug(f"Applying gate {gate}...") if len(positions) == 1: self._apply_1q_gate(positions[0], gate.op) @@ -284,11 +294,17 @@ def apply_gate(self, gate: Command) -> MPS: self.canonical_form[positions[0]] = None self.canonical_form[positions[1]] = None + # If requested, provide info about memory usage. + if self._logger.isEnabledFor(logging.INFO): + # If-statetement used so that we only call `get_byte_size` if needed. + self._logger.info(f"MPS size (MiB)={self.get_byte_size() / 2**20}") + self._logger.info(f"MPS fidelity={self.fidelity}") else: raise RuntimeError( "Gates must act on only 1 or 2 qubits! " + f"This is not satisfied by {gate}." ) + return self def canonicalise(self, l_pos: int, r_pos: int) -> None: @@ -304,11 +320,15 @@ def canonicalise(self, l_pos: int, r_pos: int) -> None: r_pos: The position of the rightmost tensor that is not to be canonicalised. """ + self._logger.debug(f"Start canonicalisation... l_pos={l_pos}, r_pos={r_pos}") + for pos in range(l_pos): self.canonicalise_tensor(pos, form=DirectionMPS.LEFT) for pos in reversed(range(r_pos + 1, len(self))): self.canonicalise_tensor(pos, form=DirectionMPS.RIGHT) + self._logger.debug(f"Finished canonicalisation.") + def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: """Canonicalises a tensor from an MPS object. @@ -327,6 +347,7 @@ def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: """ if form == self.canonical_form[pos]: # Tensor already in canonical form, nothing needs to be done + self._logger.debug(f"Position {pos} already in {form}.") return None if self._lib._is_destroyed: @@ -335,6 +356,7 @@ def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: "See the documentation of update_libhandle and CuTensorNetHandle.", ) + self._logger.debug(f"Canonicalising {pos} to {form}.") # Glossary of bond IDs used here: # s -> shared virtual bond between T and Tnext # v -> the other virtual bond of T @@ -366,15 +388,19 @@ def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: raise ValueError("Argument form must be a value in DirectionMPS.") # Apply QR decomposition + self._logger.debug(f"QR decompose a {T.nbytes / 2**20} MiB tensor.") + subscripts = T_bonds + "->" + Q_bonds + "," + R_bonds options = {"handle": self._lib.handle, "device_id": self._lib.device_id} Q, R = tensor.decompose( subscripts, T, method=tensor.QRMethod(), options=options ) + self._logger.debug(f"QR decomposition finished.") # Contract R into Tnext subscripts = R_bonds + "," + Tnext_bonds + "->" + result_bonds result = cq.contract(subscripts, R, Tnext) + self._logger.debug(f"Contraction with {next_pos} applied.") # Update self.tensors self.tensors[pos] = Q @@ -427,9 +453,12 @@ def vdot(self, other: MPS) -> complex: # Special case if only one tensor remains if len(self) == 1: + self._logger.debug("Applying trivial vdot on single tensor MPS.") result = cq.contract("LRp,lrp->", self.tensors[0].conj(), other.tensors[0]) else: + self._logger.debug("Applying vdot between two MPS.") + # The two MPS will be contracted from left to right, storing the # ``partial_result`` tensor. partial_result = cq.contract( @@ -451,6 +480,7 @@ def vdot(self, other: MPS) -> complex: other.tensors[-1], ) + self._logger.debug(f"Result from vdot={result}") return complex(result) def sample(self) -> dict[Qubit, int]: @@ -498,6 +528,7 @@ def measure(self, qubits: set[Qubit]) -> dict[Qubit, int]: raise ValueError(f"Qubit {q} is not a qubit in the MPS.") position_qubit_map[self.qubit_position[q]] = q positions = sorted(position_qubit_map.keys()) + self._logger.debug(f"Measuring qubits={position_qubit_map}") # Tensor for postselection to |0> zero_tensor = cp.zeros(2, dtype=self._complex_t) @@ -530,6 +561,7 @@ def measure(self, qubits: set[Qubit]) -> dict[Qubit, int]: # Throw a coin to decide measurement outcome outcome = 0 if prob > random() else 1 result[position_qubit_map[pos]] = outcome + self._logger.debug(f"Outcome of qubit at {pos} is {outcome}.") # Postselect the MPS for this outcome, renormalising at the same time postselection_tensor = cp.zeros(2, dtype=self._complex_t) @@ -573,6 +605,7 @@ def postselect(self, qubit_outcomes: dict[Qubit, int]) -> float: raise ValueError( "Cannot postselect all qubits. You may want to use get_amplitude()." ) + self._logger.debug(f"Postselecting qubits={qubit_outcomes}") # Apply a postselection for each of the qubits for qubit, outcome in qubit_outcomes.items(): @@ -592,6 +625,7 @@ def postselect(self, qubit_outcomes: dict[Qubit, int]) -> float: self.tensors[0] = self.tensors[0] / np.sqrt(prob) self.canonical_form[0] = None + self._logger.debug(f"Probability of this postselection is {prob}.") return prob def _postselect_qubit(self, qubit: Qubit, postselection_tensor: cp.ndarray) -> None: @@ -657,6 +691,7 @@ def expectation_value(self, pauli_string: QubitPauliString) -> float: if q not in self.qubit_position: raise ValueError(f"Qubit {q} is not a qubit in the MPS.") + self._logger.debug(f"Calculating expectation value of {pauli_string}.") mps_copy = self.copy() pauli_optype = {Pauli.Z: OpType.Z, Pauli.X: OpType.X, Pauli.Y: OpType.Y} @@ -679,6 +714,7 @@ def expectation_value(self, pauli_string: QubitPauliString) -> float: value = self.vdot(mps_copy) assert np.isclose(value.imag, 0.0, atol=self._atol) + self._logger.debug(f"Expectation value is {value.real}.") return value.real def get_statevector(self) -> np.ndarray: @@ -752,7 +788,9 @@ def get_amplitude(self, state: int) -> complex: ) assert result_tensor.shape == (1,) - return complex(result_tensor[0]) + result = complex(result_tensor[0]) + self._logger.debug(f"Amplitude of state {state} is {result}.") + return result def get_qubits(self) -> set[Qubit]: """Returns the set of qubits that this MPS is defined on.""" @@ -795,6 +833,13 @@ def get_physical_dimension(self, position: int) -> int: physical_dim: int = self.tensors[position].shape[2] return physical_dim + def get_byte_size(self) -> int: + """ + Returns: + The number of bytes the MPS currently occupies in GPU memory. + """ + return sum(t.nbytes for t in self.tensors) + def get_device_id(self) -> int: """ Returns: @@ -839,6 +884,10 @@ def copy(self) -> MPS: new_mps._complex_t = self._complex_t new_mps._real_t = self._real_t + self._logger.debug( + "Successfully copied an MPS " + f"of size {new_mps.get_byte_size() / 2**20} MiB." + ) return new_mps def __len__(self) -> int: diff --git a/pytket/extensions/cutensornet/mps/mps_gate.py b/pytket/extensions/cutensornet/mps/mps_gate.py index de57c194..3e590627 100644 --- a/pytket/extensions/cutensornet/mps/mps_gate.py +++ b/pytket/extensions/cutensornet/mps/mps_gate.py @@ -138,12 +138,14 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: result_bonds = "acLR" # Contract + self._logger.debug("Contracting the two-qubit gate with its site tensors...") T = cq.contract( gate_bonds + "," + left_bonds + "," + right_bonds + "->" + result_bonds, gate_tensor, self.tensors[l_pos], self.tensors[r_pos], ) + self._logger.debug(f"Intermediate tensor of size (MiB)={T.nbytes / 2**20}") # Get the template of the MPS tensors involved L = self.tensors[l_pos] @@ -161,6 +163,9 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # uses REL_SUM2_CUTOFF. Then the code in the `else` block should # be run; i.e. use standard cuTensorNet API to do the SVD # including normalisation and contraction of S with L. + self._logger.debug( + f"Truncating to target fidelity={self.truncation_fidelity}" + ) options = {"handle": self._lib.handle, "device_id": self._lib.device_id} svd_method = tensor.SVDMethod(abs_cutoff=self._atol / 1000) @@ -179,6 +184,7 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # singular values that remain after truncation (before normalisation). denom = float(sum(cp.square(S))) # Element-wise squaring numer = 0.0 + old_dim = new_dim new_dim = 0 # Take singular values until we surpass the target fidelity @@ -222,11 +228,18 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # to keep track of a lower bound for the fidelity. self.fidelity *= this_fidelity + # Report to logger + self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") + self._logger.debug( + f"Reduced virtual bond dimension from {old_dim} to {new_dim}." + ) + elif new_dim > self.chi: # Apply SVD decomposition and truncate up to a `max_extent` (for the shared # bond) of `self.chi`. Ask cuTensorNet to contract S directly into the L # tensor and normalise the singular values so that the sum of its squares # is equal to one (i.e. the MPS is a normalised state after truncation). + self._logger.debug(f"Truncating to (or below) chosen chi={self.chi}") options = {"handle": self._lib.handle, "device_id": self._lib.device_id} svd_method = tensor.SVDMethod( @@ -252,16 +265,25 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # # We multiply the fidelity of the current step to the overall fidelity # to keep track of a lower bound for the fidelity. - self.fidelity *= 1.0 - svd_info.discarded_weight + this_fidelity = 1.0 - svd_info.discarded_weight + self.fidelity *= this_fidelity + + # Report to logger + self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") + self._logger.debug( + f"Reduced virtual bond dimension from {new_dim} to {R.shape[0]}." + ) else: # No truncation is necessary. In this case, simply apply a QR decomposition # to get back to MPS form. QR is cheaper than SVD. + self._logger.debug("No truncation is necessary, applying QR decomposition.") options = {"handle": self._lib.handle, "device_id": self._lib.device_id} L, R = tensor.decompose( "acLR->asL,scR", T, method=tensor.QRMethod(), options=options ) + self._logger.debug("QR decomposition applied.") self.tensors[l_pos] = L self.tensors[r_pos] = R diff --git a/pytket/extensions/cutensornet/mps/mps_mpo.py b/pytket/extensions/cutensornet/mps/mps_mpo.py index acf9cbc7..9f4ecc8a 100644 --- a/pytket/extensions/cutensornet/mps/mps_mpo.py +++ b/pytket/extensions/cutensornet/mps/mps_mpo.py @@ -13,6 +13,7 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings +import logging from typing import Optional, Union @@ -29,7 +30,6 @@ warnings.warn("local settings failed to import cutensornet", ImportWarning) from pytket.circuit import Op, Qubit -from pytket.extensions.cutensornet.general import set_logger from .mps import ( CuTensorNetHandle, DirectionMPS, @@ -54,6 +54,7 @@ def __init__( k: Optional[int] = None, optim_delta: Optional[float] = None, float_precision: Optional[Union[np.float32, np.float64]] = None, + loglevel: int = logging.WARNING, ): """Initialise an MPS on the computational state ``|0>``. @@ -89,8 +90,16 @@ def __init__( choose from ``numpy`` types: ``np.float64`` or ``np.float32``. Complex numbers are represented using two of such ``float`` numbers. Default is ``np.float64``. + loglevel: Internal logger output level. """ - super().__init__(libhandle, qubits, chi, truncation_fidelity, float_precision) + super().__init__( + libhandle=libhandle, + qubits=qubits, + chi=chi, + truncation_fidelity=truncation_fidelity, + float_precision=float_precision, + loglevel=loglevel, + ) # Initialise the MPO data structure. This will keep a list of the gates # batched for application to the MPS; all of them will be applied at @@ -110,7 +119,12 @@ def __init__( # Initialise the MPS that we will use as first approximation of the # variational algorithm. self._aux_mps = MPSxGate( - libhandle, qubits, chi, truncation_fidelity, float_precision + libhandle=libhandle, + qubits=qubits, + chi=chi, + truncation_fidelity=truncation_fidelity, + float_precision=float_precision, + loglevel=loglevel, ) if k is None: @@ -345,6 +359,8 @@ def _flush(self) -> None: The method applies variational optimisation of the MPS until it converges. Based on https://arxiv.org/abs/2207.05612. """ + self._logger.info("Applying variational optimisation.") + self._logger.info(f"Fidelity before optimisation={self._aux_mps.fidelity}") l_cached_tensors: list[Tensor] = [] r_cached_tensors: list[Tensor] = [] @@ -356,6 +372,7 @@ def update_sweep_cache(pos: int, direction: DirectionMPS) -> None: Update the cache accordingly. Applies canonicalisation on the vMPS tensor before contracting. """ + self._logger.debug("Updating the sweep cache...") # Canonicalise the tensor at ``pos`` if direction == DirectionMPS.LEFT: @@ -425,6 +442,8 @@ def update_sweep_cache(pos: int, direction: DirectionMPS) -> None: elif direction == DirectionMPS.RIGHT: l_cached_tensors.append(T) + self._logger.debug("Completed update of the sweep cache.") + def update_variational_tensor( pos: int, left_tensor: Optional[Tensor], right_tensor: Optional[Tensor] ) -> float: @@ -434,6 +453,8 @@ def update_variational_tensor( Contract these with the MPS-MPO column at ``pos``. Return the current fidelity of this sweep. """ + self._logger.debug(f"Optimising tensor at position={pos}") + interleaved_rep = [ # The tensor of the MPS self.tensors[pos], @@ -483,6 +504,7 @@ def update_variational_tensor( # Normalise F and update the variational MPS self._aux_mps.tensors[pos] = F / np.sqrt(optim_fidelity) + self._logger.debug(f"Tensor optimised. Current fidelity={optim_fidelity}") return optim_fidelity ################################## @@ -500,6 +522,7 @@ def update_variational_tensor( # Repeat sweeps until the fidelity converges sweep_direction = DirectionMPS.RIGHT while not np.isclose(prev_fidelity, sweep_fidelity, atol=self.optim_delta): + self._logger.info(f"Doing another optimisation sweep...") prev_fidelity = sweep_fidelity if sweep_direction == DirectionMPS.RIGHT: @@ -540,6 +563,10 @@ def update_variational_tensor( sweep_direction = DirectionMPS.RIGHT + self._logger.info( + f"Optimisation sweep completed. Current fidelity={self.fidelity}" + ) + # Clear out the MPO self._mpo = [list() for _ in range(len(self))] self._bond_ids = [list() for _ in range(len(self))] @@ -552,6 +579,8 @@ def update_variational_tensor( self.fidelity *= sweep_fidelity self._aux_mps.fidelity = self.fidelity + self._logger.info(f"Final fidelity after optimisation={self.fidelity}") + def _new_bond_id(self) -> int: self._mpo_bond_counter += 1 return self._mpo_bond_counter diff --git a/pytket/extensions/cutensornet/mps/simulation.py b/pytket/extensions/cutensornet/mps/simulation.py index db3d865a..6ac137d0 100644 --- a/pytket/extensions/cutensornet/mps/simulation.py +++ b/pytket/extensions/cutensornet/mps/simulation.py @@ -13,6 +13,8 @@ # limitations under the License. from typing import Any from enum import Enum +import logging + from random import choice # type: ignore from collections import defaultdict # type: ignore import numpy as np # type: ignore @@ -44,7 +46,7 @@ def simulate( libhandle: CuTensorNetHandle, circuit: Circuit, algorithm: ContractionAlg, - **kwargs: Any + **kwargs: Any, ) -> MPS: """Simulate the given circuit and return the ``MPS`` representing the final state. @@ -76,34 +78,50 @@ def simulate( chi = kwargs.get("chi", None) truncation_fidelity = kwargs.get("truncation_fidelity", None) float_precision = kwargs.get("float_precision", None) + loglevel = kwargs.get("loglevel", logging.WARNING) + logger = set_logger("Simulation", level=loglevel) if algorithm == ContractionAlg.MPSxGate: mps = MPSxGate( # type: ignore - libhandle, circuit.qubits, chi, truncation_fidelity, float_precision + libhandle=libhandle, + qubits=circuit.qubits, + chi=chi, + truncation_fidelity=truncation_fidelity, + float_precision=float_precision, + loglevel=loglevel, ) elif algorithm == ContractionAlg.MPSxMPO: k = kwargs.get("k", None) optim_delta = kwargs.get("optim_delta", None) mps = MPSxMPO( # type: ignore - libhandle, - circuit.qubits, - chi, - truncation_fidelity, - k, - optim_delta, - float_precision, + libhandle=libhandle, + qubits=circuit.qubits, + chi=chi, + truncation_fidelity=truncation_fidelity, + k=k, + optim_delta=optim_delta, + float_precision=float_precision, + loglevel=loglevel, ) # Sort the gates so there isn't much overhead from canonicalising back and forth. + logger.info( + "Ordering the gates in the circuit to reduce canonicalisation overhead." + ) sorted_gates = _get_sorted_gates(circuit) + logger.info("Running simulation...") # Apply the gates - for g in sorted_gates: + for i, g in enumerate(sorted_gates): mps.apply_gate(g) + logger.info(f"Progress... {(100*i) // len(sorted_gates)}%") # Apply the circuit's phase to the leftmost tensor (any would work) mps.tensors[0] = mps.tensors[0] * np.exp(1j * np.pi * circuit.phase) + logger.info("Simulation completed.") + logger.info(f"Final MPS size={mps.get_byte_size() / 2**20} MiB") + logger.info(f"Final MPS fidelity={mps.fidelity}") return mps @@ -157,6 +175,7 @@ def _get_sorted_gates(circuit: Circuit) -> list[Command]: Returns: The same gates, ordered in a beneficial way. """ + all_gates = circuit.get_commands() sorted_gates = [] # Keep track of the qubit at the center of the canonical form; start arbitrarily From fd362e15b1669077bc8f89a50d6c951aab7c40f3 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 26 Sep 2023 15:47:36 +0100 Subject: [PATCH 04/83] Forgot to add the general.py file --- pytket/extensions/cutensornet/general.py | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 pytket/extensions/cutensornet/general.py diff --git a/pytket/extensions/cutensornet/general.py b/pytket/extensions/cutensornet/general.py new file mode 100644 index 00000000..6e2b2207 --- /dev/null +++ b/pytket/extensions/cutensornet/general.py @@ -0,0 +1,42 @@ +# Copyright 2019-2023 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +## +# http://www.apache.org/licenses/LICENSE-2.0 +## +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import logging +from logging import Logger + + +def set_logger( + logger_name: str, + level: int = logging.WARNING, + fmt: str = "[%(asctime)s] %(name)s (%(levelname)-8s) - %(message)s", +) -> Logger: + """Initialises and configures a logger object. + + Args: + logger_name: Name for the logger object. + level: Logger output level. + fmt: Logger output format. + + Returns: + New configured logger object. + """ + logger = logging.getLogger(logger_name) + logger.setLevel(level) + logger.propagate = False + if not logger.handlers: + handler = logging.StreamHandler() + handler.setLevel(level) + formatter = logging.Formatter(fmt, datefmt="%H:%M:%S") + handler.setFormatter(formatter) + logger.addHandler(handler) + return logger From 6ebe2d02b4f253f3d6ea6e6dfd4547adfdd75a10 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 26 Sep 2023 09:16:48 -0700 Subject: [PATCH 05/83] Improved messages --- pytket/extensions/cutensornet/general.py | 2 +- pytket/extensions/cutensornet/mps/mps.py | 13 ++++--------- pytket/extensions/cutensornet/mps/mps_gate.py | 8 ++++++++ pytket/extensions/cutensornet/mps/mps_mpo.py | 3 +-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/pytket/extensions/cutensornet/general.py b/pytket/extensions/cutensornet/general.py index 6e2b2207..efa80aa8 100644 --- a/pytket/extensions/cutensornet/general.py +++ b/pytket/extensions/cutensornet/general.py @@ -18,7 +18,7 @@ def set_logger( logger_name: str, level: int = logging.WARNING, - fmt: str = "[%(asctime)s] %(name)s (%(levelname)-8s) - %(message)s", + fmt: str = "[%(asctime)s] %(name)s (%(levelname)s) - %(message)s", ) -> Logger: """Initialises and configures a logger object. diff --git a/pytket/extensions/cutensornet/mps/mps.py b/pytket/extensions/cutensornet/mps/mps.py index 73479c8b..004dad78 100644 --- a/pytket/extensions/cutensornet/mps/mps.py +++ b/pytket/extensions/cutensornet/mps/mps.py @@ -234,9 +234,9 @@ def is_valid(self) -> bool: # Debugger logging self._logger.debug( "Checking validity of MPS... " - f"chi_ok={chi_ok}" - f"phys_ok={phys_ok}" - f"shape_ok={shape_ok}" + f"chi_ok={chi_ok}, " + f"phys_ok={phys_ok}, " + f"shape_ok={shape_ok}, " f"ds_ok={ds_ok}" ) @@ -273,7 +273,7 @@ def apply_gate(self, gate: Command) -> MPS: "Gates can only be applied to tensors with physical" + " bond dimension of 2." ) - self._logger.debug(f"Applying gate {gate}...") + self._logger.debug(f"Applying gate {gate}") if len(positions) == 1: self._apply_1q_gate(positions[0], gate.op) @@ -294,11 +294,6 @@ def apply_gate(self, gate: Command) -> MPS: self.canonical_form[positions[0]] = None self.canonical_form[positions[1]] = None - # If requested, provide info about memory usage. - if self._logger.isEnabledFor(logging.INFO): - # If-statetement used so that we only call `get_byte_size` if needed. - self._logger.info(f"MPS size (MiB)={self.get_byte_size() / 2**20}") - self._logger.info(f"MPS fidelity={self.fidelity}") else: raise RuntimeError( "Gates must act on only 1 or 2 qubits! " diff --git a/pytket/extensions/cutensornet/mps/mps_gate.py b/pytket/extensions/cutensornet/mps/mps_gate.py index 3e590627..9b29d1e7 100644 --- a/pytket/extensions/cutensornet/mps/mps_gate.py +++ b/pytket/extensions/cutensornet/mps/mps_gate.py @@ -13,6 +13,7 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings +import logging import numpy as np # type: ignore @@ -287,4 +288,11 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: self.tensors[l_pos] = L self.tensors[r_pos] = R + + # If requested, provide info about memory usage. + if self._logger.isEnabledFor(logging.INFO): + # If-statetement used so that we only call `get_byte_size` if needed. + self._logger.info(f"MPS size (MiB)={self.get_byte_size() / 2**20}") + self._logger.info(f"MPS fidelity={self.fidelity}") + return self diff --git a/pytket/extensions/cutensornet/mps/mps_mpo.py b/pytket/extensions/cutensornet/mps/mps_mpo.py index 9f4ecc8a..4020e7b7 100644 --- a/pytket/extensions/cutensornet/mps/mps_mpo.py +++ b/pytket/extensions/cutensornet/mps/mps_mpo.py @@ -504,7 +504,6 @@ def update_variational_tensor( # Normalise F and update the variational MPS self._aux_mps.tensors[pos] = F / np.sqrt(optim_fidelity) - self._logger.debug(f"Tensor optimised. Current fidelity={optim_fidelity}") return optim_fidelity ################################## @@ -564,7 +563,7 @@ def update_variational_tensor( sweep_direction = DirectionMPS.RIGHT self._logger.info( - f"Optimisation sweep completed. Current fidelity={self.fidelity}" + f"Optimisation sweep completed. Current fidelity={self.fidelity*sweep_fidelity}" ) # Clear out the MPO From b348f14a94be0403b71f1324f47c08cf1c48cad8 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 26 Sep 2023 09:24:46 -0700 Subject: [PATCH 06/83] Fixed a bug on simulate I just found. --- pytket/extensions/cutensornet/mps/simulation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pytket/extensions/cutensornet/mps/simulation.py b/pytket/extensions/cutensornet/mps/simulation.py index 6ac137d0..c79d94c0 100644 --- a/pytket/extensions/cutensornet/mps/simulation.py +++ b/pytket/extensions/cutensornet/mps/simulation.py @@ -116,6 +116,9 @@ def simulate( mps.apply_gate(g) logger.info(f"Progress... {(100*i) // len(sorted_gates)}%") + # Apply the batched operations that are left (if any) + mps._flush() + # Apply the circuit's phase to the leftmost tensor (any would work) mps.tensors[0] = mps.tensors[0] * np.exp(1j * np.pi * circuit.phase) From cc92c0416b60b48f4ee3050419a2c69d71c5da5c Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 26 Sep 2023 09:41:45 -0700 Subject: [PATCH 07/83] Fixed linting and failing test --- pytket/extensions/cutensornet/mps/mps_mpo.py | 3 ++- tests/test_mps.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pytket/extensions/cutensornet/mps/mps_mpo.py b/pytket/extensions/cutensornet/mps/mps_mpo.py index 4020e7b7..e7f6be44 100644 --- a/pytket/extensions/cutensornet/mps/mps_mpo.py +++ b/pytket/extensions/cutensornet/mps/mps_mpo.py @@ -563,7 +563,8 @@ def update_variational_tensor( sweep_direction = DirectionMPS.RIGHT self._logger.info( - f"Optimisation sweep completed. Current fidelity={self.fidelity*sweep_fidelity}" + "Optimisation sweep completed. " + f"Current fidelity={self.fidelity*sweep_fidelity}" ) # Clear out the MPO diff --git a/tests/test_mps.py b/tests/test_mps.py index c8f8f440..9e920fa4 100644 --- a/tests/test_mps.py +++ b/tests/test_mps.py @@ -333,7 +333,7 @@ def test_circ_approx_explicit(circuit: Circuit) -> None: # Check for MPSxMPO mps_mpo = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, chi=8) - assert np.isclose(mps_mpo.fidelity, 0.06, atol=1e-2) + assert np.isclose(mps_mpo.fidelity, 0.04, atol=1e-2) assert mps_mpo.is_valid() assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=mps_mpo._atol) From f8b41d68372ec0788c0fba94640001855a608119 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 27 Sep 2023 14:03:42 +0100 Subject: [PATCH 08/83] Updated Jupyter notebook with example of logger. --- examples/mps_tutorial.ipynb | 1199 ++++++++++++++++++++++++++++++++++- 1 file changed, 1180 insertions(+), 19 deletions(-) diff --git a/examples/mps_tutorial.ipynb b/examples/mps_tutorial.ipynb index 984adfe4..f1a2a555 100644 --- a/examples/mps_tutorial.ipynb +++ b/examples/mps_tutorial.ipynb @@ -97,7 +97,7 @@ "\n", "\n", "\n", - " <div id="circuit-display-vue-container-f6589290-2356-4b8d-a825-fc39b4059b95" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-e13d14af-9a4e-4029-8388-8fb18c773bf6" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["q", [0]], ["q", [1]]], "op": {"type": "CZ"}}, {"args": [["q", [2]]], "op": {"type": "H"}}, {"args": [["q", [3]], ["q", [4]]], "op": {"type": "CX"}}, {"args": [["q", [0]]], "op": {"params": ["0.2"], "type": "Ry"}}, {"args": [["q", [2]], ["q", [1]]], "op": {"params": ["0.3", "0.5", "0.7"], "type": "TK2"}}, {"args": [["q", [4]], ["q", [3]]], "op": {"params": ["0.1"], "type": "ZZPhase"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["q", [0]], ["q", [0]]], [["q", [1]], ["q", [1]]], [["q", [2]], ["q", [2]]], [["q", [3]], ["q", [3]]], [["q", [4]], ["q", [4]]]], "phase": "0.0", "qubits": [["q", [0]], ["q", [1]], ["q", [2]], ["q", [3]], ["q", [4]]]}</div>\n", " </div>\n", @@ -107,7 +107,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "f6589290-2356-4b8d-a825-fc39b4059b95";\n", + " const circuitRendererUid = "e13d14af-9a4e-4029-8388-8fb18c773bf6";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -285,7 +285,7 @@ "outputs": [ { "data": { - "image/png": "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TEFBgbp166ZFixZVOk9CQoKysrJcw6pVq+qwQgAA4Mmq9a6lmjJw4EANHDjwovP4+PgoMjKyjioCAABW4vFtZJKTkxUeHq4OHTpo/PjxOn78+EXndzqdysvLcxsAAED95NFBJiEhQcuXL9fGjRv1wgsvKCUlRQMHDlRxcXGlyyQmJsput7uG2NjYOqwYAADUJZthGIbZRUiSzWbT2rVrNXTo0Ern+de//qW2bdvqiy++UL9+/Sqcx+l0yul0uj7n5eUpNjZWDodDwcHBNV02AACoBXl5ebLb7Zf8/fboKzIXatOmjZo1a6aDBw9WOo+Pj4+Cg4PdBgAAUD9ZKsgcPXpUx48fV1RUlNmlAAAAD2DqU0v5+fluV1fS09O1c+dOhYaGKjQ0VHPmzNHw4cMVGRmpQ4cO6cknn1S7du00YMAAE6sGAACewtQgs337dt12222uz1OnTpUkjR49WosXL9auXbv01ltv6dSpU4qOjlb//v01d+5c+fj4mFUyAADwIB7T2Le2VLWxEAAA8Bz1srEvAADA+QgyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAsggyAADAskwNMl999ZUGDx6s6Oho2Ww2ffDBB27TDcPQM888o6ioKPn5+Sk+Pl4HDhwwp1gAAOBxTA0yBQUF6tatmxYtWlTh9BdffFELFy7U66+/ri1btiggIEADBgxQYWFhHVcKAAA8UWMzv3zgwIEaOHBghdMMw9D8+fM1c+ZMDRkyRJK0fPlyRURE6IMPPtB9991Xl6UCAAAP5LFtZNLT05Wdna34+HjXOLvdrl69eik1NbXS5ZxOp/Ly8twGAABQP3lskMnOzpYkRUREuI2PiIhwTatIYmKi7Ha7a4iNja3VOgEAgHk8NshU14wZM+RwOFxDZmam2SUBAIBa4rFBJjIyUpKUk5PjNj4nJ8c1rSI+Pj4KDg52GwAAQP3ksUGmdevWioyM1MaNG13j8vLytGXLFsXFxZlYGQAA8BSmPrWUn5+vgwcPuj6np6dr586dCg0NVYsWLTR58mT9+c9/1tVXX63WrVvr6aefVnR0tIYOHWpe0QAAwGOYGmS2b9+u2267zfV56tSpkqTRo0crKSlJTz75pAoKCvTII4/o1KlTuvnmm7Vu3Tr5+vqaVTIAAPAgNsMwDLOLqE15eXmy2+1yOBy0lwEAwCKq+vvtsW1kAAAALoUgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgAwAALIsgg4oVOiTHsYqnOY6VTgc8Dect0OAQZFBeoUNaMVxKGiQ5jrpPcxwtHb9iOD8K8Cyct0CDRJBBec58qeAn6eRhKenOX38UHEdLP588XDrdmW9mlYA7zlugQSLIoDz7VdKYT6WmrX79UcjY8uuPQdNWpdPtV5lbJ3A+zlugQbIZhmGYXURtysvLk91ul8PhUHBwsNnlWMv5/5It4/oxiDGrKuDiOG+BeqGqv99ckUHl7DHSXW+4j7vrDX4M4Nk4b4EGhSCDyjmOSmsfcR+39pHyDSkBT8J5CzQoBBlU7PzL801bSQ997t72gB8FeCLOW6DBIcigPMex8g0kW/Qq35Cysv464HkaQv8qnLdAg+TRQWb27Nmy2WxuQ8eOHc0uq/7zCZQCmpdvIGmP+fVHIaB56XzwfA2lfxXOW6BBamx2AZdy7bXX6osvvnB9btzY40u2Pl+7NOr90v42LnxU1R4jjflb6Y+Br92c+nB5LuxfpexH/sKne5z51j6mnLdAg+TxqaBx48aKjIw0u4yGx9de+V/49MNhLWX9q5SFlqQ7S5/iWftI/etfhfMWaHA8+taSJB04cEDR0dFq06aNRo4cqYyMjIvO73Q6lZeX5zYADd75t1dOHpb+0v+CEMOjyQCsyaODTK9evZSUlKR169Zp8eLFSk9P1y233KLTp09XukxiYqLsdrtriI2NrcOK65/iEkOph47rw53HlHrouIpL6nX/ifWbPUbFQ5e4jSoeuqRehhjO2/qHY4rKWKpn31OnTqlly5Z65ZVX9PDDD1c4j9PplNPpdH3Oy8tTbGwsPftWw7o9WZrz8T5lOQpd46Lsvpo1uJMSOkeZWBmqI3nrDrX7232KUY5r3FFF6OCg1erbs7uJldUsztv6h2PaMNXLnn1DQkLUvn17HTx4sNJ5fHx8FBwc7Dbg8q3bk6XxK3a4/cUhSdmOQo1fsUPr9mSZVBmqI3nrDrX+5F7FKEdHSsI1zDlbR0rCFaMctf7kXiVv3WF2iTWC87b+4ZjiUiwVZPLz83Xo0CFFRZHAa1NxiaE5H+9TRZfqysbN+Xgfl3YtovjUUbX7231q2ShXR0rCdV/R09phtNd9RU/rSEm4WjbKVbu/3afiU9buLI7ztv7hmKIqPDrI/PGPf1RKSooOHz6sb7/9VnfddZe8vLw0YsQIs0ur17amnyj3r5/zGZKyHIXamn6i7opCtaVlnVNuSZArxGQpTJKUpTBXmMktCVJa1jmTK70ynLf1D8cUVeHRj18fPXpUI0aM0PHjx9W8eXPdfPPN2rx5s5o3b252afVa7unK/+KoznwwV5bTWzOLpitAZ5X97xDjmqYw3Vv0tArkpz87vU2qsGZw3tY/HFNUhUcHmdWrV5tdQoMUHuRbo/PBXOFBvjotf52Wf4XTy8KN1Y8n5239wzFFVXj0rSWYo2frUEXZfWWrZLpNpU8M9GwdWpdloZoayvFsKNvZkHBMURUEGZTj1cimWYM7SVK5v0DKPs8a3ElejSr76wWepKEcz4aynQ0JxxRVQZBBhRI6R2nxqO6KtLtfso20+2rxqO703WAxDeV4NpTtbEg4prgUS3WIVx1V7VAHFSsuMbQ1/YRyTxcqPKj0Ei7/+rGuhnI8G8p2NiQc04anqr/fBBkAgOcqdFT8RnNJchzjjeb1WL3s2RcA0IAUOqQVw6WkQZLjgg4bHUdLx68YXjofGiyCDADAMznzpYKfSt/UnnTnr2HGcbT088nDpdOd+WZWCZMRZAAAnsl+lTTmU6lpq1/DTMaWX0NM01al0yu67YQGgyADAPBc9hj3MPOX/heEmBhz64PpCDIAAM9mj5HuesN93F1vEGIgiSADAPB0jqPS2kfcx619pHwDYDRIBBkAgOc6v2Fv01bSQ5+7t5khzDR4BBkAgGdyHCvfsLdFr/INgB3HzK0TpiLIAAA8k0+gFNC8fMPe8xsABzQvnQ8NVmOzCwAAoEK+dmnU+xX37GuPkcb8jZ59QZABAHgwX3vlQYX+YyBuLQEAAAsjyAAAAMsiyAAAAMsiyAAAAMsiyAAAAMsiyACA1RQ6Ku8EznGsdDrQQBBkAMBKCh3SiuFS0qDy3fM7jpaOXzGcMIMGgyADAFbizJcKfir/rqHz30lU8FPpfEADQJABACuxX1X+XUMZW8q/k4jO4tBA0LMvAFjNv981ZCTdKdvJw9Jf+kuSjKatZDv/nUSwnOISQ1vTTyj3dKHCg3zVs3WovBrZzC7LoxFkAMCC1mV6aU3+I3pD/+Ua95/5j2hYppcSePWQJa3bk6U5H+9TlqPQNS7K7qtZgzspoXOUiZV5Nm4tAYDFrNuTpWdXbNBTzvlu459yztezKzZo3Z4scwpDta3bk6XxK3a4hRhJynYUavyKHRzTiyDIAICFFJcYWvzRV1rlPVctG+XqSEm4hjln60hJuFo2ytUq77la/NFXKi4xzC4VVVRcYmjOx/tU0RErGzfn430c00pwa6muFDokZ76Kg6LL3/88/ePFX0Vf3WWv5DvZTrbTzHqtdEzreDt37tmrhYUzXSHmvqKnlaUw3Vf0tFb/O9wsLJypnXuuVY+unS27nVfMQn9Gt6afUL7jhCJ1VtkKKzc9QseV7/DT1vQTimt7wXQLbWdtsUSQWbRokV566SVlZ2erW7duevXVV9WzZ0+zy6q6f/f7cOZktkace1rf5wW6JnULzteqJnPl3zRSGvV++YNf3WWv5DvZTrbTzHqtdExN2M5sZ2PZFCyVyBViJLmFmeMKVrazgr/eLbSdV8Rif0ZPnPhJb3k/rzDluR1TSYrScdcxzTrRRTo/yFhsO2uLx99aeueddzR16lTNmjVLO3bsULdu3TRgwADl5uaaXVrVOfN15mS2/AsytbBwpqJ0XFLpCbqwcKb8CzJ15mR2xf0+VHfZK/lOtpPtNLNeKx1TE7YzNLS5RhdN170X/OBJpWHm3qKnNbpoukJDm1t6O6+Ixf6MRvr8ojDlqWWjXK32nuu2bNlVtjDlKdLnF0tvZ23x+CDzyiuvaNy4cRo7dqw6deqk119/Xf7+/vrLX/5idmlVVhwUrRHnnnbdw17tPVfdbf90naBHSsI14tzTKg6KrrFlr+Q72U6208x6rXRMzdjOnq1DFWgPVU4FtyAkKUdhCrSHqmfrUEtv55Ww2p/R6zpfq8d9/3zRZR/3/bOu63ytpbeztnh0kCkqKlJaWpri4+Nd4xo1aqT4+HilpqZWuIzT6VReXp7bYLat6Sf0fV6g7iv69eCv8Zntdo/7+7xAbU0/UWPLXsl3sp1sp5n1WumYmrGdXo1smjW4kyTpwt5Fyj7PGtypwr5HrLSdV8Jqf0a9Gtk0/nd9NKKSZUcUPa3xv+tT7phabTtri0cHmZ9//lnFxcWKiIhwGx8REaHs7OwKl0lMTJTdbncNsbGxdVHqReWeLn2cLkthmnLuD27Tppz7g+vycNl8NbHslXxndbGd9Ws7zarXSsfUrO1M6BylxaO6K9Lu6zY+0u6rxaO6V9rniNW2s7qs+Gc0oXOUnhl1h57zmew2/jmfyXpm1B0VHlMrbmdt8OggUx0zZsyQw+FwDZmZmWaXpPCg0r9sonRc85q85jZtXpPXXPcYy+ariWWv5Duri+2sX9tpVr1WOqZmbmdC5yj9fdrtWjWutxbcd51Wjeutv0+7/aIdp1lxO6vDqn9GE2KLtSTwDbdxSwLfUEJscYXzW3U7a5pHB5lmzZrJy8tLOTk5buNzcnIUGRlZ4TI+Pj4KDg52G8zWs3WougXnu91DPL/fh9Xec9UtOL/Ce9rVXfZKvpPtZDvNrNdKx9Ts7fRqZFNc2zANue4qxbUNu2RX9lbdzstlyT+j/37pp63sfVkPfS41bVX6+fyXg1p9O2uBzTAMj+5hp1evXurZs6deffVVSVJJSYlatGihiRMnavr06ZdcPi8vT3a7XQ6Hw7xQ4zimM28MkH9BpuseYpbC3FqknwmIlf8j68u/6K26y17Jd7KdbKeZ9VrpmLKd9Ws7TdxHShp0wUs/Y9zfaN60lTTmb+Yflzo8nlX9/fboKzKSNHXqVC1dulRvvfWWfvjhB40fP14FBQUaO3as2aVVnU+g/JtG6kxArB73/bNbvw+P+/659KA3jSztRKimlr2S72Q72U4z67XSMWU769d2mriPFNDcPcRIrpeDqmmr0umecFzM+rvoIjz+iowk/c///I+rQ7zrrrtOCxcuVK9evaq0rEdckZEaTu+LbGf92k6z6rXSMWU769d2XsmyNfCdFV7FcBzzrONSR8ezqr/flggyV8JjggwAAKiyenNrCQAAoDIEGQAAYFkEGQAAYFkEGQAAYFkEGQAAYFkEGQAAYFkEGQAAYFkEGQAAYFkEGQAAYFmNzS6gtpV1XJyXl2dyJQAAoKrKfrcv9QKCeh9kTp8+LUmKjY01uRIAAHC5Tp8+Lbu98nc31ft3LZWUlOjHH39UUFCQbDZbja03Ly9PsbGxyszM5B1OlWAfXRr76NLYRxfH/rk09tGleeI+MgxDp0+fVnR0tBo1qrwlTL2/ItOoUSPFxMTU2vqDg4M95qB7KvbRpbGPLo19dHHsn0tjH12ap+2ji12JKUNjXwAAYFkEGQAAYFkEmWry8fHRrFmz5OPjY3YpHot9dGnso0tjH10c++fS2EeXZuV9VO8b+wIAgPqLKzIAAMCyCDIAAMCyCDIAAMCyCDIAAMCyCDLVtGjRIrVq1Uq+vr7q1auXtm7danZJHmP27Nmy2WxuQ8eOHc0uy1RfffWVBg8erOjoaNlsNn3wwQdu0w3D0DPPPKOoqCj5+fkpPj5eBw4cMKdYE1xq/4wZM6bcOZWQkGBOsSZJTEzUjTfeqKCgIIWHh2vo0KHav3+/2zyFhYWaMGGCwsLCFBgYqOHDhysnJ8ekiutWVfZP3759y51Hjz76qEkV173Fixera9eurk7v4uLi9Nlnn7mmW/X8IchUwzvvvKOpU6dq1qxZ2rFjh7p166YBAwYoNzfX7NI8xrXXXqusrCzX8Pe//93skkxVUFCgbt26adGiRRVOf/HFF7Vw4UK9/vrr2rJliwICAjRgwAAVFhbWcaXmuNT+kaSEhAS3c2rVqlV1WKH5UlJSNGHCBG3evFkbNmzQuXPn1L9/fxUUFLjmmTJlij7++GO9++67SklJ0Y8//qhhw4aZWHXdqcr+kaRx48a5nUcvvviiSRXXvZiYGD3//PNKS0vT9u3bdfvtt2vIkCHau3evJAufPwYuW8+ePY0JEya4PhcXFxvR0dFGYmKiiVV5jlmzZhndunUzuwyPJclYu3at63NJSYkRGRlpvPTSS65xp06dMnx8fIxVq1aZUKG5Ltw/hmEYo0ePNoYMGWJKPZ4qNzfXkGSkpKQYhlF6zjRp0sR49913XfP88MMPhiQjNTXVrDJNc+H+MQzDuPXWW41JkyaZV5QHatq0qfHmm29a+vzhisxlKioqUlpamuLj413jGjVqpPj4eKWmpppYmWc5cOCAoqOj1aZNG40cOVIZGRlml+Sx0tPTlZ2d7XZO2e129erVi3PqPMnJyQoPD1eHDh00fvx4HT9+3OySTOVwOCRJoaGhkqS0tDSdO3fO7Tzq2LGjWrRo0SDPowv3T5m3335bzZo1U+fOnTVjxgydOXPGjPJMV1xcrNWrV6ugoEBxcXGWPn/q/Usja9rPP/+s4uJiRUREuI2PiIjQP/7xD5Oq8iy9evVSUlKSOnTooKysLM2ZM0e33HKL9uzZo6CgILPL8zjZ2dmSVOE5VTatoUtISNCwYcPUunVrHTp0SP/1X/+lgQMHKjU1VV5eXmaXV+dKSko0efJk3XTTTercubOk0vPI29tbISEhbvM2xPOoov0jSffff79atmyp6Oho7dq1S9OmTdP+/fu1Zs0aE6utW7t371ZcXJwKCwsVGBiotWvXqlOnTtq5c6dlzx+CDGrcwIEDXf/ftWtX9erVSy1bttRf//pXPfzwwyZWBqu67777XP/fpUsXde3aVW3btlVycrL69etnYmXmmDBhgvbs2dPg255VprL988gjj7j+v0uXLoqKilK/fv106NAhtW3btq7LNEWHDh20c+dOORwOvffeexo9erRSUlLMLuuKcGvpMjVr1kxeXl7lWnLn5OQoMjLSpKo8W0hIiNq3b6+DBw+aXYpHKjtvOKeqrk2bNmrWrFmDPKcmTpyoTz75RJs2bVJMTIxrfGRkpIqKinTq1Cm3+RvaeVTZ/qlIr169JKlBnUfe3t5q166devToocTERHXr1k0LFiyw9PlDkLlM3t7e6tGjhzZu3OgaV1JSoo0bNyouLs7EyjxXfn6+Dh06pKioKLNL8UitW7dWZGSk2zmVl5enLVu2cE5V4ujRozp+/HiDOqcMw9DEiRO1du1affnll2rdurXb9B49eqhJkyZu59H+/fuVkZHRIM6jS+2fiuzcuVOSGtR5dKGSkhI5nU5rnz9mtza2otWrVxs+Pj5GUlKSsW/fPuORRx4xQkJCjOzsbLNL8whPPPGEkZycbKSnpxvffPONER8fbzRr1szIzc01uzTTnD592vjuu++M7777zpBkvPLKK8Z3331nHDlyxDAMw3j++eeNkJAQ48MPPzR27dplDBkyxGjdurVx9uxZkyuvGxfbP6dPnzb++Mc/GqmpqUZ6errxxRdfGN27dzeuvvpqo7Cw0OzS68z48eMNu91uJCcnG1lZWa7hzJkzrnkeffRRo0WLFsaXX35pbN++3YiLizPi4uJMrLruXGr/HDx40Hj22WeN7du3G+np6caHH35otGnTxujTp4/Jlded6dOnGykpKUZ6erqxa9cuY/r06YbNZjM+//xzwzCse/4QZKrp1VdfNVq0aGF4e3sbPXv2NDZv3mx2SR7j3nvvNaKiogxvb2/jqquuMu69917j4MGDZpdlqk2bNhmSyg2jR482DKP0Eeynn37aiIiIMHx8fIx+/foZ+/fvN7foOnSx/XPmzBmjf//+RvPmzY0mTZoYLVu2NMaNG9fg/uFQ0f6RZCxbtsw1z9mzZ40//OEPRtOmTQ1/f3/jrrvuMrKysswrug5dav9kZGQYffr0MUJDQw0fHx+jXbt2xp/+9CfD4XCYW3gdeuihh4yWLVsa3t7eRvPmzY1+/fq5QoxhWPf8sRmGYdTd9R8AAICaQxsZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAKZKSkpSSEiI2WUAsCiCDIBKjRkzRjabzTWEhYUpISFBu3btqrHvuPfee/XPf/6zxtZ3vlatWmn+/PmXvVzfvn01efLkGq8HQM0jyAC4qISEBGVlZSkrK0sbN25U48aN9dvf/rbG1u/n56fw8PAaWx+AhoUgA+CifHx8FBkZqcjISF133XWaPn26MjMz9dNPP7nmmTZtmtq3by9/f3+1adNGTz/9tM6dO+ea/v333+u2225TUFCQgoOD1aNHD23fvl1S+VtLF5v3QoZhaPbs2WrRooV8fHwUHR2txx9/XFLpVZUjR45oypQpritKknT8+HGNGDFCV111lfz9/dWlSxetWrXKtc4xY8YoJSVFCxYscC13+PBhSdKePXs0cOBABQYGKiIiQg888IB+/vln17LvvfeeunTpIj8/P4WFhSk+Pl4FBQVXdgAAXBRBBkCV5efna8WKFWrXrp3CwsJc44OCgpSUlKR9+/ZpwYIFWrp0qebNm+eaPnLkSMXExGjbtm1KS0vT9OnT1aRJkwq/43Lmff/99zVv3jwtWbJEBw4c0AcffKAuXbpIktasWaOYmBg9++yzritKklRYWKgePXro008/1Z49e/TII4/ogQce0NatWyVJCxYsUFxcnMaNG+daLjY2VqdOndLtt9+u66+/Xtu3b9e6deuUk5Oje+65R5KUlZWlESNG6KGHHtIPP/yg5ORkDRs2TLyXF6hl5r58G4AnGz16tOHl5WUEBAQYAQEBhiQjKirKSEtLu+hyL730ktGjRw/X56CgICMpKanCeZctW2bY7fYqzXuhl19+2Wjfvr1RVFRU4fSWLVsa8+bNu+R67rzzTuOJJ55wfb711luNSZMmuc0zd+5co3///m7jMjMzDUnG/v37jbS0NEOScfjw4SrVDqBmcEUGwEXddttt2rlzp3bu3KmtW7dqwIABGjhwoI4cOeKa55133tFNN92kyMhIBQYGaubMmcrIyHBNnzp1qv7jP/5D8fHxev7553Xo0KFKv+9y5r377rt19uxZtWnTRuPGjdPatWv1yy+/XHR7iouLNXfuXHXp0kWhoaEKDAzU+vXr3eqtyPfff69NmzYpMDDQNXTs2FGSdOjQIXXr1k39+vVTly5ddPfdd2vp0qU6efLkRdcJ4MoRZABcVEBAgNq1a6d27drpxhtv1JtvvqmCggItXbpUkpSamqqRI0dq0KBB+uSTT/Tdd9/pqaeeUlFRkWsds2fP1t69e3XnnXfqyy+/VKdOnbR27doKv+9y5o2NjdX+/fv12muvyc/PT3/4wx/Up08ft/Y5F3rppZe0YMECTZs2TZs2bdLOnTs1YMAAt3orkp+fr8GDB7tCXdlw4MAB9enTR15eXtqwYYM+++wzderUSa+++qo6dOig9PT0S+1iAFeAIAPgsthsNjVq1Ehnz56VJH377bdq2bKlnnrqKd1www26+uqr3a7WlGnfvr2mTJmizz//XMOGDdOyZcsq/Y7LmdfPz0+DBw/WwoULlZycrNTUVO3evVuS5O3treLiYrf5v/nmGw0ZMkSjRo1St27d1KZNm3KPf1e0XPfu3bV37161atXKFezKhoCAANe+uemmmzRnzhx999138vb2rjSEAagZBBkAF+V0OpWdna3s7Gz98MMPeuyxx1xXJyTp6quvVkZGhlavXq1Dhw5p4cKFbj/eZ8+e1cSJE5WcnKwjR47om2++0bZt23TNNdeU+67LmVcqfeLpf//3f7Vnzx7961//0ooVK+Tn56eWLVtKKu1H5quvvtKxY8dcTxddffXV2rBhg7799lv98MMP+s///E/l5OS4rbdVq1basmWLDh8+rJ9//lklJSWaMGGCTpw4oREjRmjbtm06dOiQ1q9fr7Fjx6q4uFhbtmzRf//3f2v79u3KyMjQmjVr9NNPP1VaO4AaYnYjHQCea/To0YYk1xAUFGTceOONxnvvvec235/+9CcjLCzMCAwMNO69915j3rx5rga8TqfTuO+++4zY2FjD29vbiI6ONiZOnGicPXvWMAz3xr6XmvdCa9euNXr16mUEBwcbAQEBRu/evY0vvvjCNT01NdXo2rWr4ePjY5T9dXf8+HFjyJAhRmBgoBEeHm7MnDnTePDBB40hQ4a4ltu/f7/Ru3dvw8/Pz5BkpKenG4ZhGP/85z+Nu+66ywgJCTH8/PyMjh07GpMnTzZKSkqMffv2GQMGDDCaN29u+Pj4GO3btzdeffXVGjgKAC7GZhg8GwgAAKyJW0sAAMCyCDIAAMCyCDIAAMCyCDIAAMCyCDIAAMCyCDIAAMCyCDIAAMCyCDIAAMCyCDIAAMCyCDIAAMCyCDIAAMCy/j+6quX6igBkOwAAAABJRU5ErkJggg==", + "image/png": "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GzdOJ06c0EMPPaTMzEx16dJFK1assGaMGQAA4JccpqyhBmuIrKwsuVwuud1uhYeHW10OAEBSbm6uUlJS1LRp07P+CAQ129mOg8p8f1s+zgwAAMC5IMwAAABbI8wAAFBDDB8+XH/84x+tLuO8I8wAAFAJw4cPl8PhKPFKTEy0ujS9+OKLWrhwodVlSCoa9PaDDz44L5/l179mAgCgVLluKe+Y5GpUcpr7sOQMrdYn3icmJmrBggVebVYO2FpQUCCHwyGXq/r67M84MwMAsJdct7ToT9LCfpL7R+9p7h+L2hf9qWi+auJ0OhUdHe31atCggVatWqXAwEB9+eWXnnlnzJihCy+8UGlpaZKkXr16afTo0Ro9erTq16+viIgIPfHEE17Pb8rPz9e4cePUqFEjhYSEqEuXLl4D4i1cuFD169fXRx99pNatW8vpdOrgwYMlLjP16tVLDz/8sJKSktSgQQNFRUVp7ty5ysnJ0d13362wsDA1b95cn376qVf/du/erX79+ik0NFRRUVEaMmSIfv31V6/1PvLIIxo3bpwaNmyo6OhoTZ482TO9SZMmkqRbbrlFDofD8766EGYAAPaSd0zK+UXKPCAtvPH3QOP+seh95oGi6XnHzntpvXr1UlJSkoYMGSK3261vv/1WEydO1Lx587wezfPGG2/oggsu0Pr16zV79mzNnDlT8+fP90y/++679fXXX2vx4sXavn27br31ViUmJmrfvn2eeY4fP65p06Zp/vz52rVrlyIjI0ut6Y033tCFF16oDRs26OGHH9aDDz6oW2+9Vd26ddOWLVvUp08fDRkyRMePH5dU9JDKnj176oorrtCmTZu0fPly/fzzz7rttttKrDckJETr16/X9OnT9dRTTyk5OVmSPA+QXrBggdLS0kp9oHSVMjWc2+02kozb7ba6FADA/5w4ccLs3r3bnDhxwrcV/JZqzKx2xjwZXvTfg+u83/+WWrUFn2bYsGEmICDAhISEeL2eeuopY4wxeXl55sorrzS33Xabufzyy829997rtXzPnj3NZZddZgoLCz1t48ePN5dddpkxxpjvv//eOBwOc/jwYa/lrr/+ejNhwgRjjDELFiwwksy2bdtK1DZgwACvz7rmmms870+dOmVCQkLMkCFDPG1paWlGkvnmm2+MMcZMmjTJJCQkeK03NTXVSDJ79+4tdb3GGHPVVVeZ8ePHe95LMkuXLi1jKxY523FQme9v7pkBANiPK04a/vHvZ2JeTyhqb9CkqN0VV60ff+211+rVV1/1amvYsKEkKTAwUIsWLVK7du3UuHFjzZo1q8TyV199tRwOh+d9165dNWPGDBUUFGjLli0yxqhVq1Zey+Tl5Ski4vcnhAcGBqpdu3bl1nr6PAEBAYqIiFDbtm09bcUPb87IyJBU9CDolStXKjQ0tMS6fvjhB09dZ352TEyMZx3nG2EGAGBPrjjplrm/Bxmp6H01Bxmp6FmCLVq0KHP62rVrJUlHjx7V0aNHFRISUuF1FxYWKiAgQJs3b1ZAQIDXtNMDRlBQkFcgKkvdunW93jscDq+24nUUFhZ6/tu/f/9SH+p8+qWy0tZbvI7zjTADALAn94/S0vu925bef17OzJzNDz/8oDFjxmjevHl67733NHToUH3++eeqU+f321TXrVvntcy6devUsmVLBQQE6Morr1RBQYEyMjLUvXv3812+OnTooH//+99q0qSJLrjA95hQt25dFRQUVGFlZeMGYACA/Zx+s2+DJtKIFUX/PfOm4GqSl5en9PR0r9evv/6qgoICDRkyRAkJCbr77ru1YMEC7dy5UzNmzPBaPjU1VWPHjtXevXv1zjvv6KWXXtKjjz4qSWrVqpXuuusuDR06VEuWLFFKSoo2btyo559/Xp988km19kuSRo0apaNHj2rQoEHasGGD9u/frxUrVmjEiBGVCidNmjTR559/rvT0dGVmZlZjxYQZAIDduA97B5nhH0sXdyn6r1egOVxtJSxfvlwxMTFer2uuuUbPPvusDhw4oLlz50qSoqOjNX/+fD3xxBPatm2bZ/mhQ4fqxIkT6ty5s0aNGqWHH35Y99//+1mmBQsWaOjQoXrsscd0ySWX6Oabb9b69esVHx9fbX0qFhsbq6+//loFBQXq06eP2rRpo0cffVQul8vr7FJ5ZsyYoeTkZMXHx+vKK6+sxop5ajYAwALn9NTs4nFmcn4peUmp+IxNyEXS4H9X68B5vurVq5euuOKKUm8Mrm2q6qnZ3DMDALCXeq6ioFLaCMCuOGn4J9U+AjD8C2EGAGA/9Vxlh5XSHnGAGo0wAwDAeXT6YwlQNbgBGAAA2BphBgBgmRr+GxSUo6r2P2EGAHDeFY8eW/xwQ9ROxfv/zNGEK4t7ZgAA511AQIDq16/veZZPcHBwhYbmR81gjNHx48eVkZGh+vXrl3hsQ2URZgAAloiOjpYkyx5OCOvVr1/fcxycC8IMAMASDodDMTExioyM1MmTJ60uB+dZ3bp1z/mMTDHCDADAUgEBAVX2pYbaiRuAAQCArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArVkaZl599VW1a9dO4eHhCg8PV9euXfXpp596phtjNHnyZMXGxiooKEi9evXSrl27LKwYAAD4G0vDTFxcnJ577jlt2rRJmzZt0nXXXacBAwZ4Asv06dP1wgsvaM6cOdq4caOio6PVu3dvZWdnW1k2AADwIw5jjLG6iNM1bNhQf//73zVixAjFxsYqKSlJ48ePlyTl5eUpKipKzz//vEaOHFmh9WVlZcnlcsntdis8PLw6SwcAAFWkMt/ffnPPTEFBgRYvXqycnBx17dpVKSkpSk9PV0JCgmcep9Opnj17au3atWWuJy8vT1lZWV4vAABQc1keZnbs2KHQ0FA5nU498MADWrp0qVq3bq309HRJUlRUlNf8UVFRnmmlmTZtmlwul+cVHx9frfUDAABrWR5mLrnkEm3btk3r1q3Tgw8+qGHDhmn37t2e6Q6Hw2t+Y0yJttNNmDBBbrfb80pNTa222gEAgPUusLqAwMBAtWjRQpLUqVMnbdy4US+++KLnPpn09HTFxMR45s/IyChxtuZ0TqdTTqezeosGAAB+w/IzM2cyxigvL09NmzZVdHS0kpOTPdPy8/O1evVqdevWzcIKAQCAP7H0zMxf//pX9e3bV/Hx8crOztbixYu1atUqLV++XA6HQ0lJSZo6dapatmypli1baurUqQoODtadd95pZdkAAMCPWBpmfv75Zw0ZMkRpaWlyuVxq166dli9frt69e0uSxo0bpxMnTuihhx5SZmamunTpohUrVigsLMzKsgEAgB/xu3FmqhrjzAAAYD+2HGcGAADAF4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABgaz6FmdTUVP3444+e9xs2bFBSUpLmzp1bZYUBAABUhE9h5s4779TKlSslSenp6erdu7c2bNigv/71r3rqqaeqtEAAAICz8SnM7Ny5U507d5Ykvffee2rTpo3Wrl2rt99+WwsXLqzK+gAAAM7KpzBz8uRJOZ1OSdJnn32mm2++WZJ06aWXKi0treqqAwAAKIdPYebyyy/XP/7xD3355ZdKTk5WYmKiJOmnn35SRERElRYIAABwNj6Fmeeff16vvfaaevXqpUGDBql9+/aSpGXLlnkuPwEAAJwPDmOM8WXBgoICZWVlqUGDBp62AwcOKDg4WJGRkVVW4LnKysqSy+WS2+1WeHi41eUAAIAKqMz3t8/jzBhjtHnzZr322mvKzs6WJAUGBio4ONjXVQIAAFTaBb4sdPDgQSUmJurQoUPKy8tT7969FRYWpunTpys3N1f/+Mc/qrpOAACAUvl0ZubRRx9Vp06dlJmZqaCgIE/7Lbfcos8//7zKigMAACiPT2dmvvrqK3399dcKDAz0am/cuLEOHz5cJYUBAABUhE9nZgoLC1VQUFCi/ccff1RYWNg5FwUAAFBRPoWZ3r17a9asWZ73DodDx44d05NPPql+/fpVVW0AAADl8umn2T/99JOuvfZaBQQEaN++ferUqZP27dunCy+8UGvWrOGn2QAA4JxU5vvbp3tmYmNjtW3bNr3zzjvasmWLCgsLdc899+iuu+7yuiEYAACguvk8aJ5dcGYGAAD7qZYzM8uWLatwAcUPngQAAKhuFQ4zf/zjHys0n8PhKPWXTgAAANWhwmGmsLCwOusAAADwic/PZgIAAPAHPoeZzz//XDfddJOaN2+uFi1a6KabbtJnn31WlbUBAACUy6cwM2fOHCUmJiosLEyPPvqoHnnkEYWHh6tfv36aM2dOVdcIAABQJp9+mt2oUSNNmDBBo0eP9mp/+eWX9eyzz+qnn36qsgLPFT/NBgDAfirz/e3TmZmsrCwlJiaWaE9ISFBWVpYvqwQAAPCJT2Hm5ptv1tKlS0u0/+c//1H//v3PuSgAAICK8ulxBpdddpmeffZZrVq1Sl27dpUkrVu3Tl9//bUee+wxzZ492zPvI488UjWVAgAAlMKne2aaNm1asZU7HNq/f3+li6pK3DMDAID9VPuDJlNSUnwq7EzTpk3TkiVL9P/+3/9TUFCQunXrpueff16XXHKJZx5jjKZMmaK5c+cqMzNTXbp00csvv6zLL7+8SmoAAAD2ZumgeatXr9aoUaO0bt06JScn69SpU0pISFBOTo5nnunTp+uFF17QnDlztHHjRkVHR6t3797Kzs62sHIAAOAvfLrMZIzR+++/r5UrVyojI6PEow6WLFniUzG//PKLIiMjtXr1avXo0UPGGMXGxiopKUnjx4+XJOXl5SkqKkrPP/+8Ro4cWe46ucwEAID9VPtPsx999FENGTJEKSkpCg0Nlcvl8nr5yu12S5IaNmwoqehyVnp6uhISEjzzOJ1O9ezZU2vXri11HXl5ecrKyvJ6AQCAmsune2YWLVqkJUuWqF+/flVWiDFGY8eO1TXXXKM2bdpIktLT0yVJUVFRXvNGRUXp4MGDpa5n2rRpmjJlSpXVBQAA/JtPZ2ZcLpeaNWtWpYWMHj1a27dv1zvvvFNimsPh8HpvjCnRVmzChAlyu92eV2pqapXWCQAA/ItPYWby5MmaMmWKTpw4USVFPPzww1q2bJlWrlypuLg4T3t0dLSk38/QFMvIyChxtqaY0+lUeHi41wsAANRcPoWZW2+9VZmZmYqMjFTbtm3VoUMHr1dFGWM0evRoLVmyRF988UWJ8WuaNm2q6OhoJScne9ry8/O1evVqdevWzZfSAQBADePTPTPDhw/X5s2bNXjwYEVFRZV5yac8o0aN0ttvv63//Oc/CgsL85yBcblcCgoKksPhUFJSkqZOnaqWLVuqZcuWmjp1qoKDg3XnnXf69JkAAKBm8emn2SEhIfrvf/+ra6655tw+vIwQtGDBAg0fPlzS74Pmvfbaa16D5hXfJFwefpoNAID9VOb726cwc+mll+q9995Tu3btfC7yfCHMAABgP9U+zsyMGTM0btw4HThwwJfFAQAAqoxP98wMHjxYx48fV/PmzRUcHKy6det6TT969GiVFAcAAFAen8LMrFmzqrgMAAAA3/gUZoYNG1bVdQAAAPjEpzBzuhMnTujkyZNebdxoCwAAzhefbgDOycnR6NGjFRkZqdDQUDVo0MDrBQAAcL74FGbGjRunL774Qq+88oqcTqfmz5+vKVOmKDY2Vm+++WZV1wgAAFAmny4zffjhh3rzzTfVq1cvjRgxQt27d1eLFi3UuHFjvfXWW7rrrruquk4AAIBS+XRm5ujRo57nKIWHh3t+in3NNddozZo1VVcdAABAOXwKM82aNfMMmNe6dWu99957korO2NSvX7+qagMAACiXT2Hm7rvv1rfffitJmjBhgufemTFjxugvf/lLlRYIAABwNj49m+lMhw4d0qZNm9S8eXO1b9++KuqqMjybCQAA+6m2ZzOtX79en376qVfbm2++qZ49e+qBBx7Qyy+/rLy8vMpXDAAA4KNKhZnJkydr+/btnvc7duzQPffcoxtuuEETJkzQhx9+qGnTplV5kQAAAGWpVJjZtm2brr/+es/7xYsXq0uXLpo3b57GjBmj2bNne24GBgAAOB8qFWYyMzMVFRXleb969WolJiZ63l911VVKTU2tuuoAAADKUakwExUVpZSUFElSfn6+tmzZoq5du3qmZ2dnq27dulVbIQAAwFlUKswkJibq8ccf15dffqkJEyYoODhY3bt390zfvn27mjdvXuVFAgAAlKVSjzN45plnNHDgQPXs2VOhoaF64403FBgY6Jn++uuvKyEhocqLBAAAKItP48y43W6FhoYqICDAq/3o0aMKDQ31CjhWY5wZAADspzLf3z49aNLlcpXa3rBhQ19WBwAA4DOfHmcAAADgLwgzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggz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7Fjde++9uuGGG/Tcc8/phx9+KPPzKjPvrbfeqhMnTqhZs2a67777tHTpUp06deqs/SkoKNCzzz6rdu3aKSIiQqGhoVqxYoVXvaXZvHmzVq5cqdDQUM/r0ksvlST98MMPat++va6//nq1bdtWt956q+bNm6fMzMyzrhNA1SDMADirkJAQtWjRQi1atFDnzp31z3/+Uzk5OZo3b54kad26dbrjjjvUt29fffTRR9q6dasmTpyo/Px8zzomT56sXbt26cYbb9QXX3yh1q1ba+nSpaV+XmXmjY+P1969e/Xyyy8rKChIDz30kHr06OF1v86ZZsyYoZkzZ2rcuHH64osvtG3bNvXp08er3tIUFhaqf//+nmBX/Nq3b5969OihgIAAJScn69NPP1Xr1q310ksv6ZJLLlFKSkp5mxjAOSLMAKgUh8OhOnXq6MSJE5Kkr7/+Wo0bN9bEiRPVqVMntWzZ0uusTbFWrVppzJgxWrFihQYOHKgFCxaU+RmVmTcoKEg333yzZs+erVWrVumbb77Rjh07JEmBgYEqKCjwmv/LL7/UgAEDNHjwYLVv317NmjXTvn37vOYpbbkOHTpo165datKkiSfcFb9CQkI82+YPf/iDpkyZoq1btyowMLDMIAag6hBmAJxVXl6e0tPTlZ6erj179ujhhx/WsWPH1L9/f0lSixYtdOjQIS1evFg//PCDZs+e7fUFfuLECY0ePVqrVq3SwYMH9fXXX2vjxo267LLLSnxWZeaVin4J9c9//lM7d+7U/v379a9//UtBQUFq3LixpKJxZtasWaPDhw97fnXUokULJScna+3atdqzZ49Gjhyp9PR0r/U2adJE69ev14EDB/Trr7+qsLBQo0aN0tGjRzVo0CBt2LBB+/fv14oVKzRixAgVFBRo/fr1mjp1qjZt2qRDhw5pyZIl+uWXX8qsHUAVsvqmHQD+a9iwYUaS5xUWFmauuuoq8/7773vN95e//MVERESY0NBQc/vtt5uZM2d6burNy8szd9xxh4mPjzeBgYEmNjbWjB492pw4ccIY430DcHnznmnp0qWmS5cuJjw83ISEhJirr77afPbZZ57p33zzjWnXrp1xOp2m+K+7I0eOmAEDBpjQ0FATGRlpnnjiCTN06FAzYMAAz3J79+41V199tQkKCjKSTEpKijHGmO+++87ccsstpn79+iYoKMhceumlJikpyRQWFprdu3ebPn36mIsuusg4nU7TqlUr89JLL1XBXgBQHocx/G4QAADYF5eZAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArf1/hYs84yYwXkgAAAAASUVORK5CYII=", "text/plain": [ "
" ] @@ -473,7 +473,7 @@ "\n", "\n", "\n", - " <div id="circuit-display-vue-container-827efb1f-a27d-49d8-83f1-beb6c80c4a56" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-067d69bc-bdde-4810-ac70-5b0fe1129460" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["q", [1]]], "op": {"type": "H"}}, {"args": [["q", [2]], ["q", [3]]], "op": {"params": ["0.3"], "type": "ZZPhase"}}, {"args": [["q", [4]]], "op": {"params": ["0.8"], "type": "Ry"}}, {"args": [["q", [0]], ["q", [1]]], "op": {"type": "CX"}}, {"args": [["q", [3]], ["q", [4]]], "op": {"type": "CZ"}}, {"args": [["q", [1]], ["q", [2]]], "op": {"params": ["0.7"], "type": "XXPhase"}}, {"args": [["q", [1]], ["q", [4]]], "op": {"params": ["0.1", "0.2", "0.4"], "type": "TK2"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["q", [0]], ["q", [0]]], [["q", [1]], ["q", [1]]], [["q", [2]], ["q", [2]]], [["q", [3]], ["q", [3]]], [["q", [4]], ["q", [4]]]], "phase": "0.0", "qubits": [["q", [0]], ["q", [1]], ["q", [2]], ["q", [3]], ["q", [4]]]}</div>\n", " </div>\n", @@ -483,7 +483,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "827efb1f-a27d-49d8-83f1-beb6c80c4a56";\n", + " const circuitRendererUid = "067d69bc-bdde-4810-ac70-5b0fe1129460";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -601,7 +601,7 @@ "\n", "\n", "\n", - " <div id="circuit-display-vue-container-63c69977-1f18-4780-a1c9-c5ffc874f18e" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-87b5c904-f430-4e00-b6c6-601952b6581f" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["node", [1]]], "op": {"params": ["0.8"], "type": "Ry"}}, {"args": [["node", [3]]], "op": {"type": "H"}}, {"args": [["node", [0]], ["node", [1]]], "op": {"type": "SWAP"}}, {"args": [["node", [4]], ["node", [3]]], "op": {"type": "CX"}}, {"args": [["node", [2]], ["node", [1]]], "op": {"params": ["0.3"], "type": "ZZPhase"}}, {"args": [["node", [1]], ["node", [0]]], "op": {"type": "CZ"}}, {"args": [["node", [3]], ["node", [2]]], "op": {"params": ["0.7"], "type": "XXPhase"}}, {"args": [["node", [3]], ["node", [2]]], "op": {"type": "SWAP"}}, {"args": [["node", [2]], ["node", [1]]], "op": {"type": "SWAP"}}, {"args": [["node", [1]], ["node", [0]]], "op": {"params": ["0.1", "0.2", "0.4"], "type": "TK2"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["node", [0]], ["node", [0]]], [["node", [1]], ["node", [1]]], [["node", [2]], ["node", [2]]], [["node", [3]], ["node", [3]]], [["node", [4]], ["node", [4]]]], "phase": "0.0", "qubits": [["node", [0]], ["node", [1]], ["node", [2]], ["node", [3]], ["node", [4]]]}</div>\n", " </div>\n", @@ -611,7 +611,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "63c69977-1f18-4780-a1c9-c5ffc874f18e";\n", + " const circuitRendererUid = "87b5c904-f430-4e00-b6c6-601952b6581f";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -774,10 +774,10 @@ "output_type": "stream", "text": [ "Time taken by approximate contraction with bound chi:\n", - "4.64 seconds\n", + "1.47 seconds\n", "\n", "Lower bound of the fidelity:\n", - "0.3834\n" + "0.3587\n" ] } ], @@ -811,10 +811,10 @@ "output_type": "stream", "text": [ "Time taken by approximate contraction with fixed truncation fidelity:\n", - "10.03 seconds\n", + "2.62 seconds\n", "\n", "Lower bound of the fidelity:\n", - "0.9367\n" + "0.9334\n" ] } ], @@ -866,8 +866,8 @@ "output_type": "stream", "text": [ "MPSxGate\n", - "\tTime taken: 4.71 seconds\n", - "\tLower bound of the fidelity: 0.3834\n" + "\tTime taken: 1.35 seconds\n", + "\tLower bound of the fidelity: 0.3589\n" ] } ], @@ -892,8 +892,8 @@ "output_type": "stream", "text": [ "MPSxMPO, default parameters\n", - "\tTime taken: 22.58 seconds\n", - "\tLower bound of the fidelity: 0.4275\n" + "\tTime taken: 12.6 seconds\n", + "\tLower bound of the fidelity: 0.3847\n" ] } ], @@ -918,8 +918,8 @@ "output_type": "stream", "text": [ "MPSxMPO, custom parameters\n", - "\tTime taken: 31.27 seconds\n", - "\tLower bound of the fidelity: 0.4603\n" + "\tTime taken: 22.52 seconds\n", + "\tLower bound of the fidelity: 0.3977\n" ] } ], @@ -940,13 +940,1174 @@ "source": [ "**Note**: `MPSxMPO` also admits truncation policy in terms of `truncation_fidelity` instead of `chi`." ] + }, + { + "cell_type": "markdown", + "id": "d1e0091f-e258-472f-aef5-bec2ad215e56", + "metadata": {}, + "source": [ + "# Using the logger" + ] + }, + { + "cell_type": "markdown", + "id": "7607b5bd-f332-4d97-963b-2a163d3fb194", + "metadata": {}, + "source": [ + "You can request a verbose log to be produced during simulation, by assigning the `loglevel` argument when calling `simulate`. Currently, two log levels are supported (other than default, which is silent): \n", + "- `logging.INFO` will print information about progress percent, memory currently occupied by the MPS and current fidelity. Additionally, some high level information of the current stage of the simulation is provided, such as when `MPSxMPO` is applying optimisation sweeps.\n", + "- `logging.DEBUG` provides all of the messages from the loglevel above plus detailed information of the current operation being carried out and the values of important variables.\n", + "\n", + "**Note**: Due to technical issues with the `logging` module and Jupyter notebooks we need to reload the `logging` module. When working with python scripts and command line, just doing `import logging` is enough." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "eb152bf3-a065-47bb-bc3e-2adb49277881", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from importlib import reload # Not needed in Python 2\n", + "import logging\n", + "reload(logging)" + ] + }, + { + "cell_type": "markdown", + "id": "fd8d8c7f-dcd2-40f9-940f-8dc8cfb31fac", + "metadata": {}, + "source": [ + "An example of the use of `logging.INFO` is provided below. " + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "318073fc-2ef4-492e-8c5a-1ba1ba0b7733", + "metadata": { + "collapsed": true, + "jupyter": { + "outputs_hidden": true + }, + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[14:01:12] Simulation (INFO) - Ordering the gates in the circuit to reduce canonicalisation overhead.\n", + "[14:01:12] Simulation (INFO) - Running simulation...\n", + "[14:01:12] Simulation (INFO) - Progress... 0%\n", + "[14:01:12] Simulation (INFO) - Progress... 0%\n", + "[14:01:12] Simulation (INFO) - Progress... 0%\n", + "[14:01:12] Simulation (INFO) - Progress... 0%\n", + "[14:01:12] Simulation (INFO) - Progress... 0%\n", + "[14:01:12] Simulation (INFO) - Progress... 0%\n", + "[14:01:12] Simulation (INFO) - Progress... 1%\n", + "[14:01:12] Simulation (INFO) - Progress... 1%\n", + "[14:01:12] Simulation (INFO) - Progress... 1%\n", + "[14:01:12] Simulation (INFO) - Progress... 1%\n", + "[14:01:12] Simulation (INFO) - Progress... 1%\n", + "[14:01:12] Simulation (INFO) - Progress... 1%\n", + "[14:01:12] Simulation (INFO) - Progress... 2%\n", + "[14:01:12] Simulation (INFO) - Progress... 2%\n", + "[14:01:12] Simulation (INFO) - Progress... 2%\n", + "[14:01:12] Simulation (INFO) - Progress... 2%\n", + "[14:01:12] Simulation (INFO) - Progress... 2%\n", + "[14:01:12] Simulation (INFO) - Progress... 2%\n", + "[14:01:12] Simulation (INFO) - Progress... 3%\n", + "[14:01:12] Simulation (INFO) - Progress... 3%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00067138671875\n", + "[14:01:12] MPS (INFO) - MPS fidelity=1.0\n", + "[14:01:12] Simulation (INFO) - Progress... 3%\n", + "[14:01:12] Simulation (INFO) - Progress... 3%\n", + "[14:01:12] Simulation (INFO) - Progress... 3%\n", + "[14:01:12] Simulation (INFO) - Progress... 3%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00067138671875\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", + "[14:01:12] Simulation (INFO) - Progress... 4%\n", + "[14:01:12] Simulation (INFO) - Progress... 4%\n", + "[14:01:12] Simulation (INFO) - Progress... 4%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.000762939453125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344604\n", + "[14:01:12] Simulation (INFO) - Progress... 4%\n", + "[14:01:12] Simulation (INFO) - Progress... 4%\n", + "[14:01:12] Simulation (INFO) - Progress... 4%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.000762939453125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", + "[14:01:12] Simulation (INFO) - Progress... 5%\n", + "[14:01:12] Simulation (INFO) - Progress... 5%\n", + "[14:01:12] Simulation (INFO) - Progress... 5%\n", + "[14:01:12] Simulation (INFO) - Progress... 5%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.000823974609375\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", + "[14:01:12] Simulation (INFO) - Progress... 5%\n", + "[14:01:12] Simulation (INFO) - Progress... 5%\n", + "[14:01:12] Simulation (INFO) - Progress... 6%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00091552734375\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", + "[14:01:12] Simulation (INFO) - Progress... 6%\n", + "[14:01:12] Simulation (INFO) - Progress... 6%\n", + "[14:01:12] Simulation (INFO) - Progress... 6%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00103759765625\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", + "[14:01:12] Simulation (INFO) - Progress... 6%\n", + "[14:01:12] Simulation (INFO) - Progress... 6%\n", + "[14:01:12] Simulation (INFO) - Progress... 7%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00128173828125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", + "[14:01:12] Simulation (INFO) - Progress... 7%\n", + "[14:01:12] Simulation (INFO) - Progress... 7%\n", + "[14:01:12] Simulation (INFO) - Progress... 7%\n", + "[14:01:12] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:12] MPS (INFO) - Fidelity before optimisation=0.9990283071344602\n", + "[14:01:12] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:12] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9997023479978765\n", + "[14:01:12] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:12] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9997024059075587\n", + "[14:01:12] MPS (INFO) - Final fidelity after optimisation=0.9997024059075587\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00128173828125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 7%\n", + "[14:01:12] Simulation (INFO) - Progress... 7%\n", + "[14:01:12] Simulation (INFO) - Progress... 8%\n", + "[14:01:12] Simulation (INFO) - Progress... 8%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0013427734375\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 8%\n", + "[14:01:12] Simulation (INFO) - Progress... 8%\n", + "[14:01:12] Simulation (INFO) - Progress... 8%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00146484375\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 8%\n", + "[14:01:12] Simulation (INFO) - Progress... 9%\n", + "[14:01:12] Simulation (INFO) - Progress... 9%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.001708984375\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 9%\n", + "[14:01:12] Simulation (INFO) - Progress... 9%\n", + "[14:01:12] Simulation (INFO) - Progress... 9%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0020751953125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 10%\n", + "[14:01:12] Simulation (INFO) - Progress... 10%\n", + "[14:01:12] Simulation (INFO) - Progress... 10%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0025634765625\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 10%\n", + "[14:01:12] Simulation (INFO) - Progress... 10%\n", + "[14:01:12] Simulation (INFO) - Progress... 10%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0025634765625\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 11%\n", + "[14:01:12] Simulation (INFO) - Progress... 11%\n", + "[14:01:12] Simulation (INFO) - Progress... 11%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0025634765625\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 11%\n", + "[14:01:12] Simulation (INFO) - Progress... 11%\n", + "[14:01:12] Simulation (INFO) - Progress... 11%\n", + "[14:01:12] Simulation (INFO) - Progress... 12%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00262451171875\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 12%\n", + "[14:01:12] Simulation (INFO) - Progress... 12%\n", + "[14:01:12] Simulation (INFO) - Progress... 12%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00274658203125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 12%\n", + "[14:01:12] Simulation (INFO) - Progress... 12%\n", + "[14:01:12] Simulation (INFO) - Progress... 13%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00299072265625\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 13%\n", + "[14:01:12] Simulation (INFO) - Progress... 13%\n", + "[14:01:12] Simulation (INFO) - Progress... 13%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00347900390625\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", + "[14:01:12] Simulation (INFO) - Progress... 13%\n", + "[14:01:12] Simulation (INFO) - Progress... 13%\n", + "[14:01:12] Simulation (INFO) - Progress... 14%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00396728515625\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9993396700769984\n", + "[14:01:12] Simulation (INFO) - Progress... 14%\n", + "[14:01:12] Simulation (INFO) - Progress... 14%\n", + "[14:01:12] Simulation (INFO) - Progress... 14%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0048828125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9984418366672726\n", + "[14:01:12] Simulation (INFO) - Progress... 14%\n", + "[14:01:12] Simulation (INFO) - Progress... 14%\n", + "[14:01:12] Simulation (INFO) - Progress... 15%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.005889892578125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9978683217610371\n", + "[14:01:12] Simulation (INFO) - Progress... 15%\n", + "[14:01:12] Simulation (INFO) - Progress... 15%\n", + "[14:01:12] Simulation (INFO) - Progress... 15%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.005889892578125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9978683217610371\n", + "[14:01:12] Simulation (INFO) - Progress... 15%\n", + "[14:01:12] Simulation (INFO) - Progress... 15%\n", + "[14:01:12] Simulation (INFO) - Progress... 16%\n", + "[14:01:12] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:12] MPS (INFO) - Fidelity before optimisation=0.9978683217610371\n", + "[14:01:12] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:12] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.99809024854532\n", + "[14:01:12] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:12] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9981132810448355\n", + "[14:01:12] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:12] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9981209535027262\n", + "[14:01:12] MPS (INFO) - Final fidelity after optimisation=0.9981209535027262\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.005889892578125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9981209535027262\n", + "[14:01:12] Simulation (INFO) - Progress... 16%\n", + "[14:01:12] Simulation (INFO) - Progress... 16%\n", + "[14:01:12] Simulation (INFO) - Progress... 16%\n", + "[14:01:12] Simulation (INFO) - Progress... 16%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.005950927734375\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9981209535027262\n", + "[14:01:12] Simulation (INFO) - Progress... 16%\n", + "[14:01:12] Simulation (INFO) - Progress... 17%\n", + "[14:01:12] Simulation (INFO) - Progress... 17%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.006072998046875\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9981209535027262\n", + "[14:01:12] Simulation (INFO) - Progress... 17%\n", + "[14:01:12] Simulation (INFO) - Progress... 17%\n", + "[14:01:12] Simulation (INFO) - Progress... 17%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.006317138671875\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9981209535027262\n", + "[14:01:12] Simulation (INFO) - Progress... 17%\n", + "[14:01:12] Simulation (INFO) - Progress... 18%\n", + "[14:01:12] Simulation (INFO) - Progress... 18%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.006805419921875\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9981209535027261\n", + "[14:01:12] Simulation (INFO) - Progress... 18%\n", + "[14:01:12] Simulation (INFO) - Progress... 18%\n", + "[14:01:12] Simulation (INFO) - Progress... 18%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.007049560546875\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.997423409781103\n", + "[14:01:12] Simulation (INFO) - Progress... 18%\n", + "[14:01:12] Simulation (INFO) - Progress... 19%\n", + "[14:01:12] Simulation (INFO) - Progress... 19%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.008392333984375\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.997423409781103\n", + "[14:01:12] Simulation (INFO) - Progress... 19%\n", + "[14:01:12] Simulation (INFO) - Progress... 19%\n", + "[14:01:12] Simulation (INFO) - Progress... 19%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.009765625\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9969717765474623\n", + "[14:01:12] Simulation (INFO) - Progress... 20%\n", + "[14:01:12] Simulation (INFO) - Progress... 20%\n", + "[14:01:12] Simulation (INFO) - Progress... 20%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.01123046875\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.996255622087102\n", + "[14:01:12] Simulation (INFO) - Progress... 20%\n", + "[14:01:12] Simulation (INFO) - Progress... 20%\n", + "[14:01:12] Simulation (INFO) - Progress... 20%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.01165771484375\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.996255622087102\n", + "[14:01:12] Simulation (INFO) - Progress... 21%\n", + "[14:01:12] Simulation (INFO) - Progress... 21%\n", + "[14:01:12] Simulation (INFO) - Progress... 21%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.01165771484375\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9962556220871019\n", + "[14:01:12] Simulation (INFO) - Progress... 21%\n", + "[14:01:12] Simulation (INFO) - Progress... 21%\n", + "[14:01:12] Simulation (INFO) - Progress... 21%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.01165771484375\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9962556220871019\n", + "[14:01:12] Simulation (INFO) - Progress... 22%\n", + "[14:01:12] Simulation (INFO) - Progress... 22%\n", + "[14:01:12] Simulation (INFO) - Progress... 22%\n", + "[14:01:12] Simulation (INFO) - Progress... 22%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.01171875\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9962556220871019\n", + "[14:01:12] Simulation (INFO) - Progress... 22%\n", + "[14:01:12] Simulation (INFO) - Progress... 22%\n", + "[14:01:12] Simulation (INFO) - Progress... 23%\n", + "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0118408203125\n", + "[14:01:12] MPS (INFO) - MPS fidelity=0.9962556220871019\n", + "[14:01:12] Simulation (INFO) - Progress... 23%\n", + "[14:01:13] Simulation (INFO) - Progress... 23%\n", + "[14:01:13] Simulation (INFO) - Progress... 23%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0120849609375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9962556220871019\n", + "[14:01:13] Simulation (INFO) - Progress... 23%\n", + "[14:01:13] Simulation (INFO) - Progress... 23%\n", + "[14:01:13] Simulation (INFO) - Progress... 24%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0125732421875\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9962556220871019\n", + "[14:01:13] Simulation (INFO) - Progress... 24%\n", + "[14:01:13] Simulation (INFO) - Progress... 24%\n", + "[14:01:13] Simulation (INFO) - Progress... 24%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0130615234375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9955811734143832\n", + "[14:01:13] Simulation (INFO) - Progress... 24%\n", + "[14:01:13] Simulation (INFO) - Progress... 24%\n", + "[14:01:13] Simulation (INFO) - Progress... 25%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.01373291015625\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9954580715406015\n", + "[14:01:13] Simulation (INFO) - Progress... 25%\n", + "[14:01:13] Simulation (INFO) - Progress... 25%\n", + "[14:01:13] Simulation (INFO) - Progress... 25%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0150146484375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9954129745430442\n", + "[14:01:13] Simulation (INFO) - Progress... 25%\n", + "[14:01:13] Simulation (INFO) - Progress... 25%\n", + "[14:01:13] Simulation (INFO) - Progress... 26%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.01708984375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9946104246997917\n", + "[14:01:13] Simulation (INFO) - Progress... 26%\n", + "[14:01:13] Simulation (INFO) - Progress... 26%\n", + "[14:01:13] Simulation (INFO) - Progress... 26%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0211181640625\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.993986081692407\n", + "[14:01:13] Simulation (INFO) - Progress... 26%\n", + "[14:01:13] Simulation (INFO) - Progress... 26%\n", + "[14:01:13] Simulation (INFO) - Progress... 27%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02392578125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9932754206084036\n", + "[14:01:13] Simulation (INFO) - Progress... 27%\n", + "[14:01:13] Simulation (INFO) - Progress... 27%\n", + "[14:01:13] Simulation (INFO) - Progress... 27%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02392578125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9932754206084036\n", + "[14:01:13] Simulation (INFO) - Progress... 27%\n", + "[14:01:13] Simulation (INFO) - Progress... 27%\n", + "[14:01:13] Simulation (INFO) - Progress... 28%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02392578125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9932754206084036\n", + "[14:01:13] Simulation (INFO) - Progress... 28%\n", + "[14:01:13] Simulation (INFO) - Progress... 28%\n", + "[14:01:13] Simulation (INFO) - Progress... 28%\n", + "[14:01:13] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:13] MPS (INFO) - Fidelity before optimisation=0.9932754206084036\n", + "[14:01:13] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:13] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9948146155456611\n", + "[14:01:13] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:13] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9948360895424706\n", + "[14:01:13] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:13] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9948431533380159\n", + "[14:01:13] MPS (INFO) - Final fidelity after optimisation=0.9948431533380159\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02392578125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9948431533380159\n", + "[14:01:13] Simulation (INFO) - Progress... 28%\n", + "[14:01:13] Simulation (INFO) - Progress... 28%\n", + "[14:01:13] Simulation (INFO) - Progress... 29%\n", + "[14:01:13] Simulation (INFO) - Progress... 29%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02398681640625\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9948431533380159\n", + "[14:01:13] Simulation (INFO) - Progress... 29%\n", + "[14:01:13] Simulation (INFO) - Progress... 29%\n", + "[14:01:13] Simulation (INFO) - Progress... 29%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02410888671875\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9948431533380157\n", + "[14:01:13] Simulation (INFO) - Progress... 30%\n", + "[14:01:13] Simulation (INFO) - Progress... 30%\n", + "[14:01:13] Simulation (INFO) - Progress... 30%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02435302734375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9948431533380157\n", + "[14:01:13] Simulation (INFO) - Progress... 30%\n", + "[14:01:13] Simulation (INFO) - Progress... 30%\n", + "[14:01:13] Simulation (INFO) - Progress... 30%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02484130859375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9948431533380157\n", + "[14:01:13] Simulation (INFO) - Progress... 31%\n", + "[14:01:13] Simulation (INFO) - Progress... 31%\n", + "[14:01:13] Simulation (INFO) - Progress... 31%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02484130859375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9948396534426794\n", + "[14:01:13] Simulation (INFO) - Progress... 31%\n", + "[14:01:13] Simulation (INFO) - Progress... 31%\n", + "[14:01:13] Simulation (INFO) - Progress... 31%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0257568359375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9946407082338863\n", + "[14:01:13] Simulation (INFO) - Progress... 32%\n", + "[14:01:13] Simulation (INFO) - Progress... 32%\n", + "[14:01:13] Simulation (INFO) - Progress... 32%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.026947021484375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9939915775333915\n", + "[14:01:13] Simulation (INFO) - Progress... 32%\n", + "[14:01:13] Simulation (INFO) - Progress... 32%\n", + "[14:01:13] Simulation (INFO) - Progress... 32%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02850341796875\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9930726984365036\n", + "[14:01:13] Simulation (INFO) - Progress... 33%\n", + "[14:01:13] Simulation (INFO) - Progress... 33%\n", + "[14:01:13] Simulation (INFO) - Progress... 33%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.029144287109375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9925894686689639\n", + "[14:01:13] Simulation (INFO) - Progress... 33%\n", + "[14:01:13] Simulation (INFO) - Progress... 33%\n", + "[14:01:13] Simulation (INFO) - Progress... 33%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.030609130859375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9922594965497078\n", + "[14:01:13] Simulation (INFO) - Progress... 34%\n", + "[14:01:13] Simulation (INFO) - Progress... 34%\n", + "[14:01:13] Simulation (INFO) - Progress... 34%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.036590576171875\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161947\n", + "[14:01:13] Simulation (INFO) - Progress... 34%\n", + "[14:01:13] Simulation (INFO) - Progress... 34%\n", + "[14:01:13] Simulation (INFO) - Progress... 34%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038421630859375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161949\n", + "[14:01:13] Simulation (INFO) - Progress... 35%\n", + "[14:01:13] Simulation (INFO) - Progress... 35%\n", + "[14:01:13] Simulation (INFO) - Progress... 35%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038421630859375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", + "[14:01:13] Simulation (INFO) - Progress... 35%\n", + "[14:01:13] Simulation (INFO) - Progress... 35%\n", + "[14:01:13] Simulation (INFO) - Progress... 35%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038421630859375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", + "[14:01:13] Simulation (INFO) - Progress... 36%\n", + "[14:01:13] Simulation (INFO) - Progress... 36%\n", + "[14:01:13] Simulation (INFO) - Progress... 36%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038421630859375\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", + "[14:01:13] Simulation (INFO) - Progress... 36%\n", + "[14:01:13] Simulation (INFO) - Progress... 36%\n", + "[14:01:13] Simulation (INFO) - Progress... 36%\n", + "[14:01:13] Simulation (INFO) - Progress... 37%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038482666015625\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", + "[14:01:13] Simulation (INFO) - Progress... 37%\n", + "[14:01:13] Simulation (INFO) - Progress... 37%\n", + "[14:01:13] Simulation (INFO) - Progress... 37%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038604736328125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", + "[14:01:13] Simulation (INFO) - Progress... 37%\n", + "[14:01:13] Simulation (INFO) - Progress... 37%\n", + "[14:01:13] Simulation (INFO) - Progress... 38%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038848876953125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", + "[14:01:13] Simulation (INFO) - Progress... 38%\n", + "[14:01:13] Simulation (INFO) - Progress... 38%\n", + "[14:01:13] Simulation (INFO) - Progress... 38%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.039337158203125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", + "[14:01:13] Simulation (INFO) - Progress... 38%\n", + "[14:01:13] Simulation (INFO) - Progress... 38%\n", + "[14:01:13] Simulation (INFO) - Progress... 39%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.040069580078125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9914013449577964\n", + "[14:01:13] Simulation (INFO) - Progress... 39%\n", + "[14:01:13] Simulation (INFO) - Progress... 39%\n", + "[14:01:13] Simulation (INFO) - Progress... 39%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.04107666015625\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9909200464032397\n", + "[14:01:13] Simulation (INFO) - Progress... 39%\n", + "[14:01:13] Simulation (INFO) - Progress... 40%\n", + "[14:01:13] Simulation (INFO) - Progress... 40%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.04278564453125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9909200464032397\n", + "[14:01:13] Simulation (INFO) - Progress... 40%\n", + "[14:01:13] Simulation (INFO) - Progress... 40%\n", + "[14:01:13] Simulation (INFO) - Progress... 40%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.045379638671875\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9904815074905157\n", + "[14:01:13] Simulation (INFO) - Progress... 40%\n", + "[14:01:13] Simulation (INFO) - Progress... 41%\n", + "[14:01:13] Simulation (INFO) - Progress... 41%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.049224853515625\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9895385170678038\n", + "[14:01:13] Simulation (INFO) - Progress... 41%\n", + "[14:01:13] Simulation (INFO) - Progress... 41%\n", + "[14:01:13] Simulation (INFO) - Progress... 41%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.054351806640625\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9893005128956965\n", + "[14:01:13] Simulation (INFO) - Progress... 41%\n", + "[14:01:13] Simulation (INFO) - Progress... 42%\n", + "[14:01:13] Simulation (INFO) - Progress... 42%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.059844970703125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.98888820372519\n", + "[14:01:13] Simulation (INFO) - Progress... 42%\n", + "[14:01:13] Simulation (INFO) - Progress... 42%\n", + "[14:01:13] Simulation (INFO) - Progress... 42%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.068878173828125\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9879401995661465\n", + "[14:01:13] Simulation (INFO) - Progress... 42%\n", + "[14:01:13] Simulation (INFO) - Progress... 43%\n", + "[14:01:13] Simulation (INFO) - Progress... 43%\n", + "[14:01:13] MPS (INFO) - MPS size (MiB)=0.075836181640625\n", + "[14:01:13] MPS (INFO) - MPS fidelity=0.9870682591461779\n", + "[14:01:13] Simulation (INFO) - Progress... 43%\n", + "[14:01:13] Simulation (INFO) - Progress... 43%\n", + "[14:01:13] Simulation (INFO) - Progress... 43%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.075836181640625\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9870682591461779\n", + "[14:01:14] Simulation (INFO) - Progress... 43%\n", + "[14:01:14] Simulation (INFO) - Progress... 44%\n", + "[14:01:14] Simulation (INFO) - Progress... 44%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.075836181640625\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9870682591461779\n", + "[14:01:14] Simulation (INFO) - Progress... 44%\n", + "[14:01:14] Simulation (INFO) - Progress... 44%\n", + "[14:01:14] Simulation (INFO) - Progress... 44%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.075836181640625\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9870682591461779\n", + "[14:01:14] Simulation (INFO) - Progress... 44%\n", + "[14:01:14] Simulation (INFO) - Progress... 45%\n", + "[14:01:14] Simulation (INFO) - Progress... 45%\n", + "[14:01:14] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:14] MPS (INFO) - Fidelity before optimisation=0.9870682591461779\n", + "[14:01:14] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:14] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9885243877420532\n", + "[14:01:14] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:14] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9885675777883345\n", + "[14:01:14] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:14] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9885807735732146\n", + "[14:01:14] MPS (INFO) - Final fidelity after optimisation=0.9885807735732146\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.075836181640625\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732146\n", + "[14:01:14] Simulation (INFO) - Progress... 45%\n", + "[14:01:14] Simulation (INFO) - Progress... 45%\n", + "[14:01:14] Simulation (INFO) - Progress... 45%\n", + "[14:01:14] Simulation (INFO) - Progress... 45%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.075897216796875\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732146\n", + "[14:01:14] Simulation (INFO) - Progress... 46%\n", + "[14:01:14] Simulation (INFO) - Progress... 46%\n", + "[14:01:14] Simulation (INFO) - Progress... 46%\n", + "[14:01:14] Simulation (INFO) - Progress... 46%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.076019287109375\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732146\n", + "[14:01:14] Simulation (INFO) - Progress... 46%\n", + "[14:01:14] Simulation (INFO) - Progress... 46%\n", + "[14:01:14] Simulation (INFO) - Progress... 47%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.076019287109375\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732146\n", + "[14:01:14] Simulation (INFO) - Progress... 47%\n", + "[14:01:14] Simulation (INFO) - Progress... 47%\n", + "[14:01:14] Simulation (INFO) - Progress... 47%\n", + "[14:01:14] Simulation (INFO) - Progress... 47%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.076263427734375\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732146\n", + "[14:01:14] Simulation (INFO) - Progress... 47%\n", + "[14:01:14] Simulation (INFO) - Progress... 48%\n", + "[14:01:14] Simulation (INFO) - Progress... 48%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.076629638671875\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732145\n", + "[14:01:14] Simulation (INFO) - Progress... 48%\n", + "[14:01:14] Simulation (INFO) - Progress... 48%\n", + "[14:01:14] Simulation (INFO) - Progress... 48%\n", + "[14:01:14] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:14] MPS (INFO) - Fidelity before optimisation=0.9885807735732145\n", + "[14:01:14] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:14] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9885807735732146\n", + "[14:01:14] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:14] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9885807735732127\n", + "[14:01:14] MPS (INFO) - Final fidelity after optimisation=0.9885807735732127\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.076629638671875\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732127\n", + "[14:01:14] Simulation (INFO) - Progress... 48%\n", + "[14:01:14] Simulation (INFO) - Progress... 49%\n", + "[14:01:14] Simulation (INFO) - Progress... 49%\n", + "[14:01:14] Simulation (INFO) - Progress... 49%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.077117919921875\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732127\n", + "[14:01:14] Simulation (INFO) - Progress... 49%\n", + "[14:01:14] Simulation (INFO) - Progress... 49%\n", + "[14:01:14] Simulation (INFO) - Progress... 50%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.077117919921875\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9885437435962636\n", + "[14:01:14] Simulation (INFO) - Progress... 50%\n", + "[14:01:14] Simulation (INFO) - Progress... 50%\n", + "[14:01:14] Simulation (INFO) - Progress... 50%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.077850341796875\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9876299149488592\n", + "[14:01:14] Simulation (INFO) - Progress... 50%\n", + "[14:01:14] Simulation (INFO) - Progress... 50%\n", + "[14:01:14] Simulation (INFO) - Progress... 51%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.079193115234375\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9876299149488592\n", + "[14:01:14] Simulation (INFO) - Progress... 51%\n", + "[14:01:14] Simulation (INFO) - Progress... 51%\n", + "[14:01:14] Simulation (INFO) - Progress... 51%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.080902099609375\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9876299149488592\n", + "[14:01:14] Simulation (INFO) - Progress... 51%\n", + "[14:01:14] Simulation (INFO) - Progress... 51%\n", + "[14:01:14] Simulation (INFO) - Progress... 52%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.083343505859375\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9872256262985958\n", + "[14:01:14] Simulation (INFO) - Progress... 52%\n", + "[14:01:14] Simulation (INFO) - Progress... 52%\n", + "[14:01:14] Simulation (INFO) - Progress... 52%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.08380126953125\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9867042570373467\n", + "[14:01:14] Simulation (INFO) - Progress... 52%\n", + "[14:01:14] Simulation (INFO) - Progress... 52%\n", + "[14:01:14] Simulation (INFO) - Progress... 53%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.08624267578125\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9860074263824546\n", + "[14:01:14] Simulation (INFO) - Progress... 53%\n", + "[14:01:14] Simulation (INFO) - Progress... 53%\n", + "[14:01:14] Simulation (INFO) - Progress... 53%\n", + "[14:01:14] MPS (INFO) - MPS size (MiB)=0.08868408203125\n", + "[14:01:14] MPS (INFO) - MPS fidelity=0.9857877466399374\n", + "[14:01:14] Simulation (INFO) - Progress... 53%\n", + "[14:01:14] Simulation (INFO) - Progress... 53%\n", + "[14:01:14] Simulation (INFO) - Progress... 54%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.09307861328125\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9853289590697893\n", + "[14:01:15] Simulation (INFO) - Progress... 54%\n", + "[14:01:15] Simulation (INFO) - Progress... 54%\n", + "[14:01:15] Simulation (INFO) - Progress... 54%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.09674072265625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9847593868171541\n", + "[14:01:15] Simulation (INFO) - Progress... 54%\n", + "[14:01:15] Simulation (INFO) - Progress... 54%\n", + "[14:01:15] Simulation (INFO) - Progress... 55%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.10498046875\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9839376637463282\n", + "[14:01:15] Simulation (INFO) - Progress... 55%\n", + "[14:01:15] Simulation (INFO) - Progress... 55%\n", + "[14:01:15] Simulation (INFO) - Progress... 55%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.110107421875\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.983070631353325\n", + "[14:01:15] Simulation (INFO) - Progress... 55%\n", + "[14:01:15] Simulation (INFO) - Progress... 55%\n", + "[14:01:15] Simulation (INFO) - Progress... 56%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.12353515625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9820965437215268\n", + "[14:01:15] Simulation (INFO) - Progress... 56%\n", + "[14:01:15] Simulation (INFO) - Progress... 56%\n", + "[14:01:15] Simulation (INFO) - Progress... 56%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.13287353515625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9813700217282061\n", + "[14:01:15] Simulation (INFO) - Progress... 56%\n", + "[14:01:15] Simulation (INFO) - Progress... 56%\n", + "[14:01:15] Simulation (INFO) - Progress... 57%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.15484619140625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9806263554164852\n", + "[14:01:15] Simulation (INFO) - Progress... 57%\n", + "[14:01:15] Simulation (INFO) - Progress... 57%\n", + "[14:01:15] Simulation (INFO) - Progress... 57%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.16436767578125\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9796957281177572\n", + "[14:01:15] Simulation (INFO) - Progress... 57%\n", + "[14:01:15] Simulation (INFO) - Progress... 57%\n", + "[14:01:15] Simulation (INFO) - Progress... 58%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.190460205078125\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9787753802493907\n", + "[14:01:15] Simulation (INFO) - Progress... 58%\n", + "[14:01:15] Simulation (INFO) - Progress... 58%\n", + "[14:01:15] Simulation (INFO) - Progress... 58%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.204498291015625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9779191475648064\n", + "[14:01:15] Simulation (INFO) - Progress... 58%\n", + "[14:01:15] Simulation (INFO) - Progress... 58%\n", + "[14:01:15] Simulation (INFO) - Progress... 59%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.227935791015625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9770858154529012\n", + "[14:01:15] Simulation (INFO) - Progress... 59%\n", + "[14:01:15] Simulation (INFO) - Progress... 59%\n", + "[14:01:15] Simulation (INFO) - Progress... 59%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.250579833984375\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9763732931498061\n", + "[14:01:15] Simulation (INFO) - Progress... 59%\n", + "[14:01:15] Simulation (INFO) - Progress... 60%\n", + "[14:01:15] Simulation (INFO) - Progress... 60%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.298919677734375\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.975569858851943\n", + "[14:01:15] Simulation (INFO) - Progress... 60%\n", + "[14:01:15] Simulation (INFO) - Progress... 60%\n", + "[14:01:15] Simulation (INFO) - Progress... 60%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.302093505859375\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.97482766334186\n", + "[14:01:15] Simulation (INFO) - Progress... 60%\n", + "[14:01:15] Simulation (INFO) - Progress... 61%\n", + "[14:01:15] Simulation (INFO) - Progress... 61%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.334991455078125\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", + "[14:01:15] Simulation (INFO) - Progress... 61%\n", + "[14:01:15] Simulation (INFO) - Progress... 61%\n", + "[14:01:15] Simulation (INFO) - Progress... 61%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.334991455078125\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", + "[14:01:15] Simulation (INFO) - Progress... 61%\n", + "[14:01:15] Simulation (INFO) - Progress... 62%\n", + "[14:01:15] Simulation (INFO) - Progress... 62%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", + "[14:01:15] Simulation (INFO) - Progress... 62%\n", + "[14:01:15] Simulation (INFO) - Progress... 62%\n", + "[14:01:15] Simulation (INFO) - Progress... 62%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", + "[14:01:15] Simulation (INFO) - Progress... 62%\n", + "[14:01:15] Simulation (INFO) - Progress... 63%\n", + "[14:01:15] Simulation (INFO) - Progress... 63%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", + "[14:01:15] Simulation (INFO) - Progress... 63%\n", + "[14:01:15] Simulation (INFO) - Progress... 63%\n", + "[14:01:15] Simulation (INFO) - Progress... 63%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", + "[14:01:15] Simulation (INFO) - Progress... 63%\n", + "[14:01:15] Simulation (INFO) - Progress... 64%\n", + "[14:01:15] Simulation (INFO) - Progress... 64%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892826\n", + "[14:01:15] Simulation (INFO) - Progress... 64%\n", + "[14:01:15] Simulation (INFO) - Progress... 64%\n", + "[14:01:15] Simulation (INFO) - Progress... 64%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", + "[14:01:15] Simulation (INFO) - Progress... 64%\n", + "[14:01:15] Simulation (INFO) - Progress... 65%\n", + "[14:01:15] Simulation (INFO) - Progress... 65%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", + "[14:01:15] Simulation (INFO) - Progress... 65%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", + "[14:01:15] Simulation (INFO) - Progress... 65%\n", + "[14:01:15] Simulation (INFO) - Progress... 65%\n", + "[14:01:15] Simulation (INFO) - Progress... 65%\n", + "[14:01:15] MPS (INFO) - MPS size (MiB)=0.340118408203125\n", + "[14:01:15] MPS (INFO) - MPS fidelity=0.9735596675843919\n", + "[14:01:15] Simulation (INFO) - Progress... 66%\n", + "[14:01:15] Simulation (INFO) - Progress... 66%\n", + "[14:01:15] Simulation (INFO) - Progress... 66%\n", + "[14:01:15] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:15] MPS (INFO) - Fidelity before optimisation=0.9735596675843919\n", + "[14:01:15] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:15] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9843516935412071\n", + "[14:01:15] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:15] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9848064428508081\n", + "[14:01:15] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:16] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9849304313856563\n", + "[14:01:16] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:16] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9849873035247502\n", + "[14:01:16] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:16] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9850185604266666\n", + "[14:01:16] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:16] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9850377284486574\n", + "[14:01:16] MPS (INFO) - Final fidelity after optimisation=0.9850377284486574\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.3406982421875\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9844304521445316\n", + "[14:01:16] Simulation (INFO) - Progress... 66%\n", + "[14:01:16] Simulation (INFO) - Progress... 66%\n", + "[14:01:16] Simulation (INFO) - Progress... 66%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.341339111328125\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9836453925132829\n", + "[14:01:16] Simulation (INFO) - Progress... 67%\n", + "[14:01:16] Simulation (INFO) - Progress... 67%\n", + "[14:01:16] Simulation (INFO) - Progress... 67%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.344635009765625\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9827521125621543\n", + "[14:01:16] Simulation (INFO) - Progress... 67%\n", + "[14:01:16] Simulation (INFO) - Progress... 67%\n", + "[14:01:16] Simulation (INFO) - Progress... 67%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.352752685546875\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9819763508005264\n", + "[14:01:16] Simulation (INFO) - Progress... 68%\n", + "[14:01:16] Simulation (INFO) - Progress... 68%\n", + "[14:01:16] Simulation (INFO) - Progress... 68%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.366485595703125\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9810077310496189\n", + "[14:01:16] Simulation (INFO) - Progress... 68%\n", + "[14:01:16] Simulation (INFO) - Progress... 68%\n", + "[14:01:16] Simulation (INFO) - Progress... 68%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.394256591796875\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9802244249339466\n", + "[14:01:16] Simulation (INFO) - Progress... 69%\n", + "[14:01:16] Simulation (INFO) - Progress... 69%\n", + "[14:01:16] Simulation (INFO) - Progress... 69%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.4154052734375\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9793217534714646\n", + "[14:01:16] Simulation (INFO) - Progress... 69%\n", + "[14:01:16] Simulation (INFO) - Progress... 69%\n", + "[14:01:16] Simulation (INFO) - Progress... 70%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.4249267578125\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9783526934928921\n", + "[14:01:16] Simulation (INFO) - Progress... 70%\n", + "[14:01:16] Simulation (INFO) - Progress... 70%\n", + "[14:01:16] Simulation (INFO) - Progress... 70%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.4468994140625\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9773801381930487\n", + "[14:01:16] Simulation (INFO) - Progress... 70%\n", + "[14:01:16] Simulation (INFO) - Progress... 70%\n", + "[14:01:16] Simulation (INFO) - Progress... 71%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.49566650390625\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9764511340458201\n", + "[14:01:16] Simulation (INFO) - Progress... 71%\n", + "[14:01:16] Simulation (INFO) - Progress... 71%\n", + "[14:01:16] Simulation (INFO) - Progress... 71%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9756161361580449\n", + "[14:01:16] Simulation (INFO) - Progress... 71%\n", + "[14:01:16] Simulation (INFO) - Progress... 71%\n", + "[14:01:16] Simulation (INFO) - Progress... 72%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9756161361580449\n", + "[14:01:16] Simulation (INFO) - Progress... 72%\n", + "[14:01:16] Simulation (INFO) - Progress... 72%\n", + "[14:01:16] Simulation (INFO) - Progress... 72%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9756161361580449\n", + "[14:01:16] Simulation (INFO) - Progress... 72%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9756161361580449\n", + "[14:01:16] Simulation (INFO) - Progress... 72%\n", + "[14:01:16] Simulation (INFO) - Progress... 73%\n", + "[14:01:16] Simulation (INFO) - Progress... 73%\n", + "[14:01:16] Simulation (INFO) - Progress... 73%\n", + "[14:01:16] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", + "[14:01:16] MPS (INFO) - MPS fidelity=0.9756161361580449\n", + "[14:01:16] Simulation (INFO) - Progress... 73%\n", + "[14:01:17] Simulation (INFO) - Progress... 73%\n", + "[14:01:17] Simulation (INFO) - Progress... 73%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9756161361580449\n", + "[14:01:17] Simulation (INFO) - Progress... 74%\n", + "[14:01:17] Simulation (INFO) - Progress... 74%\n", + "[14:01:17] Simulation (INFO) - Progress... 74%\n", + "[14:01:17] Simulation (INFO) - Progress... 74%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.513641357421875\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9756161361580449\n", + "[14:01:17] Simulation (INFO) - Progress... 74%\n", + "[14:01:17] Simulation (INFO) - Progress... 74%\n", + "[14:01:17] Simulation (INFO) - Progress... 75%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.513641357421875\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9756161361580449\n", + "[14:01:17] Simulation (INFO) - Progress... 75%\n", + "[14:01:17] Simulation (INFO) - Progress... 75%\n", + "[14:01:17] Simulation (INFO) - Progress... 75%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.51556396484375\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9748529131568249\n", + "[14:01:17] Simulation (INFO) - Progress... 75%\n", + "[14:01:17] Simulation (INFO) - Progress... 75%\n", + "[14:01:17] Simulation (INFO) - Progress... 76%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.51556396484375\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9748529131568249\n", + "[14:01:17] Simulation (INFO) - Progress... 76%\n", + "[14:01:17] Simulation (INFO) - Progress... 76%\n", + "[14:01:17] Simulation (INFO) - Progress... 76%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.51983642578125\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9742120807068472\n", + "[14:01:17] Simulation (INFO) - Progress... 76%\n", + "[14:01:17] Simulation (INFO) - Progress... 76%\n", + "[14:01:17] Simulation (INFO) - Progress... 77%\n", + "[14:01:17] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:17] MPS (INFO) - Fidelity before optimisation=0.9742120807068472\n", + "[14:01:17] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:17] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.975134847725609\n", + "[14:01:17] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:17] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9751851737337295\n", + "[14:01:17] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:17] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9752028508364771\n", + "[14:01:17] MPS (INFO) - Final fidelity after optimisation=0.9752028508364771\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.522216796875\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9747413039963226\n", + "[14:01:17] Simulation (INFO) - Progress... 77%\n", + "[14:01:17] Simulation (INFO) - Progress... 77%\n", + "[14:01:17] Simulation (INFO) - Progress... 77%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.530548095703125\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9737709267327275\n", + "[14:01:17] Simulation (INFO) - Progress... 77%\n", + "[14:01:17] Simulation (INFO) - Progress... 77%\n", + "[14:01:17] Simulation (INFO) - Progress... 78%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.542144775390625\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9730980087583336\n", + "[14:01:17] Simulation (INFO) - Progress... 78%\n", + "[14:01:17] Simulation (INFO) - Progress... 78%\n", + "[14:01:17] Simulation (INFO) - Progress... 78%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.555572509765625\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.972274693739747\n", + "[14:01:17] Simulation (INFO) - Progress... 78%\n", + "[14:01:17] Simulation (INFO) - Progress... 78%\n", + "[14:01:17] Simulation (INFO) - Progress... 79%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.58514404296875\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9714008152517145\n", + "[14:01:17] Simulation (INFO) - Progress... 79%\n", + "[14:01:17] Simulation (INFO) - Progress... 79%\n", + "[14:01:17] Simulation (INFO) - Progress... 79%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.5892333984375\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9705196501761234\n", + "[14:01:17] Simulation (INFO) - Progress... 79%\n", + "[14:01:17] Simulation (INFO) - Progress... 80%\n", + "[14:01:17] Simulation (INFO) - Progress... 80%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.63214111328125\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9697148601428947\n", + "[14:01:17] Simulation (INFO) - Progress... 80%\n", + "[14:01:17] Simulation (INFO) - Progress... 80%\n", + "[14:01:17] Simulation (INFO) - Progress... 80%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.65301513671875\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9688374595301655\n", + "[14:01:17] Simulation (INFO) - Progress... 80%\n", + "[14:01:17] Simulation (INFO) - Progress... 81%\n", + "[14:01:17] Simulation (INFO) - Progress... 81%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.731292724609375\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9679729595082353\n", + "[14:01:17] Simulation (INFO) - Progress... 81%\n", + "[14:01:17] Simulation (INFO) - Progress... 81%\n", + "[14:01:17] Simulation (INFO) - Progress... 81%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.756011962890625\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9670817720186894\n", + "[14:01:17] Simulation (INFO) - Progress... 81%\n", + "[14:01:17] Simulation (INFO) - Progress... 82%\n", + "[14:01:17] Simulation (INFO) - Progress... 82%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.851715087890625\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.9662304487130915\n", + "[14:01:17] Simulation (INFO) - Progress... 82%\n", + "[14:01:17] Simulation (INFO) - Progress... 82%\n", + "[14:01:17] Simulation (INFO) - Progress... 82%\n", + "[14:01:17] MPS (INFO) - MPS size (MiB)=0.903900146484375\n", + "[14:01:17] MPS (INFO) - MPS fidelity=0.96530121346801\n", + "[14:01:17] Simulation (INFO) - Progress... 82%\n", + "[14:01:17] Simulation (INFO) - Progress... 83%\n", + "[14:01:17] Simulation (INFO) - Progress... 83%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.074066162109375\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9644645141858508\n", + "[14:01:18] Simulation (INFO) - Progress... 83%\n", + "[14:01:18] Simulation (INFO) - Progress... 83%\n", + "[14:01:18] Simulation (INFO) - Progress... 83%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.13128662109375\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.963576178040663\n", + "[14:01:18] Simulation (INFO) - Progress... 83%\n", + "[14:01:18] Simulation (INFO) - Progress... 84%\n", + "[14:01:18] Simulation (INFO) - Progress... 84%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.375518798828125\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9627241232539026\n", + "[14:01:18] Simulation (INFO) - Progress... 84%\n", + "[14:01:18] Simulation (INFO) - Progress... 84%\n", + "[14:01:18] Simulation (INFO) - Progress... 84%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.4351806640625\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9617990818895198\n", + "[14:01:18] Simulation (INFO) - Progress... 84%\n", + "[14:01:18] Simulation (INFO) - Progress... 85%\n", + "[14:01:18] Simulation (INFO) - Progress... 85%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.738677978515625\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9610190784537106\n", + "[14:01:18] Simulation (INFO) - Progress... 85%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", + "[14:01:18] Simulation (INFO) - Progress... 85%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", + "[14:01:18] Simulation (INFO) - Progress... 85%\n", + "[14:01:18] Simulation (INFO) - Progress... 85%\n", + "[14:01:18] Simulation (INFO) - Progress... 86%\n", + "[14:01:18] Simulation (INFO) - Progress... 86%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877096\n", + "[14:01:18] Simulation (INFO) - Progress... 86%\n", + "[14:01:18] Simulation (INFO) - Progress... 86%\n", + "[14:01:18] Simulation (INFO) - Progress... 86%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", + "[14:01:18] Simulation (INFO) - Progress... 86%\n", + "[14:01:18] Simulation (INFO) - Progress... 87%\n", + "[14:01:18] Simulation (INFO) - Progress... 87%\n", + "[14:01:18] Simulation (INFO) - Progress... 87%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", + "[14:01:18] Simulation (INFO) - Progress... 87%\n", + "[14:01:18] Simulation (INFO) - Progress... 87%\n", + "[14:01:18] Simulation (INFO) - Progress... 87%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", + "[14:01:18] Simulation (INFO) - Progress... 88%\n", + "[14:01:18] Simulation (INFO) - Progress... 88%\n", + "[14:01:18] Simulation (INFO) - Progress... 88%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75701904296875\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", + "[14:01:18] Simulation (INFO) - Progress... 88%\n", + "[14:01:18] Simulation (INFO) - Progress... 88%\n", + "[14:01:18] Simulation (INFO) - Progress... 88%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75701904296875\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", + "[14:01:18] Simulation (INFO) - Progress... 89%\n", + "[14:01:18] Simulation (INFO) - Progress... 89%\n", + "[14:01:18] Simulation (INFO) - Progress... 89%\n", + "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75872802734375\n", + "[14:01:18] MPS (INFO) - MPS fidelity=0.9596341716247032\n", + "[14:01:18] Simulation (INFO) - Progress... 89%\n", + "[14:01:18] Simulation (INFO) - Progress... 89%\n", + "[14:01:18] Simulation (INFO) - Progress... 90%\n", + "[14:01:18] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:18] MPS (INFO) - Fidelity before optimisation=0.9596341716247032\n", + "[14:01:18] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:18] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9700420977488123\n", + "[14:01:18] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:18] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9703519467112257\n", + "[14:01:18] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:18] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9704374405302711\n", + "[14:01:18] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:19] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9704739545165699\n", + "[14:01:19] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:19] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9704925687713485\n", + "[14:01:19] MPS (INFO) - Final fidelity after optimisation=0.9704925687713485\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=1.75872802734375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9704925687713485\n", + "[14:01:19] Simulation (INFO) - Progress... 90%\n", + "[14:01:19] Simulation (INFO) - Progress... 90%\n", + "[14:01:19] Simulation (INFO) - Progress... 90%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=1.765777587890625\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9696216444258031\n", + "[14:01:19] Simulation (INFO) - Progress... 90%\n", + "[14:01:19] Simulation (INFO) - Progress... 90%\n", + "[14:01:19] Simulation (INFO) - Progress... 91%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=1.77117919921875\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9696216444258033\n", + "[14:01:19] Simulation (INFO) - Progress... 91%\n", + "[14:01:19] Simulation (INFO) - Progress... 91%\n", + "[14:01:19] Simulation (INFO) - Progress... 91%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=1.79656982421875\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9687315620521755\n", + "[14:01:19] Simulation (INFO) - Progress... 91%\n", + "[14:01:19] Simulation (INFO) - Progress... 91%\n", + "[14:01:19] Simulation (INFO) - Progress... 92%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=1.84222412109375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9679596222596152\n", + "[14:01:19] Simulation (INFO) - Progress... 92%\n", + "[14:01:19] Simulation (INFO) - Progress... 92%\n", + "[14:01:19] Simulation (INFO) - Progress... 92%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=1.870208740234375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9670763677407406\n", + "[14:01:19] Simulation (INFO) - Progress... 92%\n", + "[14:01:19] Simulation (INFO) - Progress... 92%\n", + "[14:01:19] Simulation (INFO) - Progress... 93%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=1.940521240234375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9661194670712572\n", + "[14:01:19] Simulation (INFO) - Progress... 93%\n", + "[14:01:19] Simulation (INFO) - Progress... 93%\n", + "[14:01:19] Simulation (INFO) - Progress... 93%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=1.999114990234375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9652231632846195\n", + "[14:01:19] Simulation (INFO) - Progress... 93%\n", + "[14:01:19] Simulation (INFO) - Progress... 93%\n", + "[14:01:19] Simulation (INFO) - Progress... 94%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=2.234954833984375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9642981707017143\n", + "[14:01:19] Simulation (INFO) - Progress... 94%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", + "[14:01:19] Simulation (INFO) - Progress... 94%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", + "[14:01:19] Simulation (INFO) - Progress... 94%\n", + "[14:01:19] Simulation (INFO) - Progress... 94%\n", + "[14:01:19] Simulation (INFO) - Progress... 94%\n", + "[14:01:19] Simulation (INFO) - Progress... 95%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", + "[14:01:19] Simulation (INFO) - Progress... 95%\n", + "[14:01:19] Simulation (INFO) - Progress... 95%\n", + "[14:01:19] Simulation (INFO) - Progress... 95%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", + "[14:01:19] Simulation (INFO) - Progress... 95%\n", + "[14:01:19] Simulation (INFO) - Progress... 95%\n", + "[14:01:19] Simulation (INFO) - Progress... 96%\n", + "[14:01:19] Simulation (INFO) - Progress... 96%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", + "[14:01:19] Simulation (INFO) - Progress... 96%\n", + "[14:01:19] Simulation (INFO) - Progress... 96%\n", + "[14:01:19] Simulation (INFO) - Progress... 96%\n", + "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", + "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", + "[14:01:19] Simulation (INFO) - Progress... 96%\n", + "[14:01:19] Simulation (INFO) - Progress... 97%\n", + "[14:01:19] Simulation (INFO) - Progress... 97%\n", + "[14:01:19] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:19] MPS (INFO) - Fidelity before optimisation=0.9633776196448067\n", + "[14:01:19] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.967414171617126\n", + "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.967533278009993\n", + "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9675675092087742\n", + "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9675846146681976\n", + "[14:01:20] MPS (INFO) - Final fidelity after optimisation=0.9675846146681976\n", + "[14:01:20] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", + "[14:01:20] MPS (INFO) - MPS fidelity=0.9675846146681976\n", + "[14:01:20] Simulation (INFO) - Progress... 97%\n", + "[14:01:20] Simulation (INFO) - Progress... 97%\n", + "[14:01:20] Simulation (INFO) - Progress... 97%\n", + "[14:01:20] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", + "[14:01:20] MPS (INFO) - MPS fidelity=0.9675846146681976\n", + "[14:01:20] Simulation (INFO) - Progress... 97%\n", + "[14:01:20] Simulation (INFO) - Progress... 98%\n", + "[14:01:20] Simulation (INFO) - Progress... 98%\n", + "[14:01:20] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", + "[14:01:20] MPS (INFO) - MPS fidelity=0.9675846146681976\n", + "[14:01:20] Simulation (INFO) - Progress... 98%\n", + "[14:01:20] Simulation (INFO) - Progress... 98%\n", + "[14:01:20] Simulation (INFO) - Progress... 98%\n", + "[14:01:20] MPS (INFO) - MPS size (MiB)=2.34918212890625\n", + "[14:01:20] MPS (INFO) - MPS fidelity=0.9667630952362768\n", + "[14:01:20] Simulation (INFO) - Progress... 98%\n", + "[14:01:20] Simulation (INFO) - Progress... 99%\n", + "[14:01:20] Simulation (INFO) - Progress... 99%\n", + "[14:01:20] MPS (INFO) - MPS size (MiB)=2.34918212890625\n", + "[14:01:20] MPS (INFO) - MPS fidelity=0.9667630952362768\n", + "[14:01:20] Simulation (INFO) - Progress... 99%\n", + "[14:01:20] MPS (INFO) - MPS size (MiB)=2.3427734375\n", + "[14:01:20] MPS (INFO) - MPS fidelity=0.9659837340863052\n", + "[14:01:20] Simulation (INFO) - Progress... 99%\n", + "[14:01:20] MPS (INFO) - MPS size (MiB)=2.3427734375\n", + "[14:01:20] MPS (INFO) - MPS fidelity=0.9659837340863054\n", + "[14:01:20] Simulation (INFO) - Progress... 99%\n", + "[14:01:20] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:20] MPS (INFO) - Fidelity before optimisation=0.9659837340863054\n", + "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9659853689734507\n", + "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9659862268998962\n", + "[14:01:20] MPS (INFO) - Final fidelity after optimisation=0.9659862268998962\n", + "[14:01:20] MPS (INFO) - Applying variational optimisation.\n", + "[14:01:20] MPS (INFO) - Fidelity before optimisation=0.9659862268998962\n", + "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:21] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.965986226899895\n", + "[14:01:21] MPS (INFO) - Doing another optimisation sweep...\n", + "[14:01:21] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9659862268998954\n", + "[14:01:21] MPS (INFO) - Final fidelity after optimisation=0.9659862268998954\n", + "[14:01:21] Simulation (INFO) - Simulation completed.\n", + "[14:01:21] Simulation (INFO) - Final MPS size=2.3427734375 MiB\n", + "[14:01:21] Simulation (INFO) - Final MPS fidelity=0.9659862268998954\n" + ] + } + ], + "source": [ + "with CuTensorNetHandle() as libhandle:\n", + " simulate(libhandle, circuit, ContractionAlg.MPSxMPO, truncation_fidelity=0.999, loglevel=logging.INFO)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d4a14fe-2ea2-435b-836a-acf9587faad7", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "py-cuquantum-23.06.0-mypich-py3.9", + "display_name": "Python 3 (ipykernel)", "language": "python", - "name": "py-cuquantum-23.06.0-mypich-py3.9" + "name": "python3" }, "language_info": { "codemirror_mode": { From deee46e11e7190e6c2cbee624288a51609568e58 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 27 Sep 2023 17:40:16 +0100 Subject: [PATCH 09/83] Now using a ConfigMPS object to provide the simulation settings. --- docs/modules/mps.rst | 3 + pytket/extensions/cutensornet/mps/__init__.py | 1 + pytket/extensions/cutensornet/mps/mps.py | 153 +++++++++++------- pytket/extensions/cutensornet/mps/mps_gate.py | 34 ++-- pytket/extensions/cutensornet/mps/mps_mpo.py | 60 ++----- .../extensions/cutensornet/mps/simulation.py | 24 +-- tests/test_mps.py | 101 +++++++----- 7 files changed, 191 insertions(+), 185 deletions(-) diff --git a/docs/modules/mps.rst b/docs/modules/mps.rst index 138c66b3..d4da1641 100644 --- a/docs/modules/mps.rst +++ b/docs/modules/mps.rst @@ -10,6 +10,9 @@ Simulation .. autoenum:: pytket.extensions.cutensornet.mps.ContractionAlg() :members: +.. autoclass:: pytket.extensions.cutensornet.mps.ConfigMPS() + .. automethod:: __init__ + .. autofunction:: pytket.extensions.cutensornet.mps.simulate diff --git a/pytket/extensions/cutensornet/mps/__init__.py b/pytket/extensions/cutensornet/mps/__init__.py index dd3abfc7..4dbf60b3 100644 --- a/pytket/extensions/cutensornet/mps/__init__.py +++ b/pytket/extensions/cutensornet/mps/__init__.py @@ -20,6 +20,7 @@ from .mps import ( CuTensorNetHandle, DirectionMPS, + ConfigMPS, Handle, Tensor, MPS, diff --git a/pytket/extensions/cutensornet/mps/mps.py b/pytket/extensions/cutensornet/mps/mps.py index 3430b5ed..f36afc62 100644 --- a/pytket/extensions/cutensornet/mps/mps.py +++ b/pytket/extensions/cutensornet/mps/mps.py @@ -82,74 +82,58 @@ def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None: self._is_destroyed = True -class MPS: - """Represents a state as a Matrix Product State. - - Attributes: - chi (int): The maximum allowed dimension of a virtual bond. - truncation_fidelity (float): The target fidelity of SVD truncation. - tensors (list[Tensor]): A list of tensors in the MPS; ``tensors[0]`` is - the leftmost and ``tensors[len(self)-1]`` is the rightmost; ``tensors[i]`` - and ``tensors[i+1]`` are connected in the MPS via a bond. All of the - tensors are rank three, with the dimensions listed in ``.shape`` matching - the left, right and physical bonds, in that order. - canonical_form (dict[int, Optional[DirectionMPS]]): A dictionary mapping - positions to the canonical form direction of the corresponding tensor, - or ``None`` if it the tensor is not canonicalised. - qubit_position (dict[pytket.circuit.Qubit, int]): A dictionary mapping circuit - qubits to the position its tensor is at in the MPS. - fidelity (float): A lower bound of the fidelity, obtained by multiplying - the fidelities after each contraction. The fidelity of a contraction - corresponds to ``||^2`` where ``|psi>`` and ``|phi>`` are the - states before and after truncation (assuming both are normalised). - """ +class ConfigMPS: + """Configuration class for simulation using MPS.""" def __init__( self, - libhandle: CuTensorNetHandle, - qubits: list[Qubit], chi: Optional[int] = None, truncation_fidelity: Optional[float] = None, - float_precision: Optional[Union[np.float32, np.float64]] = None, + k: int = 4, + optim_delta: float = 1e-5, + float_precision: Union[np.float32, np.float64] = np.float64, # type: ignore ): - """Initialise an MPS on the computational state ``|0>``. + """Instantiate a configuration object for MPS simulation. Note: - A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` - statement. The device where the MPS is stored will match the one specified - by the library handle. - Providing both a custom ``chi`` and ``truncation_fidelity`` will raise an exception. Choose one or the other (or neither, for exact simulation). Args: - libhandle: The cuTensorNet library handle that will be used to carry out - tensor operations on the MPS. - qubits: The list of qubits in the circuit to be simulated. chi: The maximum value allowed for the dimension of the virtual bonds. Higher implies better approximation but more - computational resources. If not provided, ``chi`` will be set - to ``2**(len(qubits) // 2)``, which is enough for exact contraction. + computational resources. If not provided, ``chi`` will be unbounded. truncation_fidelity: Every time a two-qubit gate is applied, the virtual bond will be truncated to the minimum dimension that satisfies ``||^2 >= trucantion_fidelity``, where ``|psi>`` and ``|phi>`` are the states before and after truncation (both normalised). If not provided, it will default to its maximum value 1. + k: If using MPSxMPO, the maximum number of layers the MPO is allowed to + have before being contracted. Increasing this might increase fidelity, + but it will also increase resource requirements exponentially. + Ignored if not using MPSxMPO. Default value is 4. + optim_delta: If using MPSxMPO, stopping criteria for the optimisation when + contracting the ``k`` layers of MPO. Stops when the increase of fidelity + between iterations is smaller than ``optim_delta``. + Ignored if not using MPSxMPO. Default value is ``1e-5``. float_precision: The floating point precision used in tensor calculations; choose from ``numpy`` types: ``np.float64`` or ``np.float32``. Complex numbers are represented using two of such ``float`` numbers. Default is ``np.float64``. Raises: - ValueError: If less than two qubits are provided. ValueError: If both ``chi`` and ``truncation_fidelity`` are fixed. ValueError: If the value of ``chi`` is set below 2. ValueError: If the value of ``truncation_fidelity`` is not in [0,1]. """ - if chi is not None and truncation_fidelity is not None: + if ( + chi is not None + and truncation_fidelity is not None + and truncation_fidelity != 1.0 + ): raise ValueError("Cannot fix both chi and truncation_fidelity.") if chi is None: - chi = max(2 ** (len(qubits) // 2), 2) + chi = 2**60 # In practice, this is like having it be unbounded if truncation_fidelity is None: truncation_fidelity = 1 @@ -158,6 +142,9 @@ def __init__( if truncation_fidelity < 0 or truncation_fidelity > 1: raise ValueError("Provide a value of truncation_fidelity in [0,1].") + self.chi = chi + self.truncation_fidelity = truncation_fidelity + if float_precision is None or float_precision == np.float64: # Double precision self._real_t = np.float64 # type: ignore self._complex_t = np.complex128 # type: ignore @@ -172,14 +159,64 @@ def __init__( f"Value of float_precision must be in {allowed_precisions}." ) - self._lib = libhandle + self.k = k + self.optim_delta = 1e-5 + + def copy(self) -> ConfigMPS: + """Standard copy of the contents.""" + return ConfigMPS( + chi=self.chi, + truncation_fidelity=self.truncation_fidelity, + k=self.k, + optim_delta=self.optim_delta, + float_precision=self._real_t, # type: ignore + ) - ####################################### - # Initialise the MPS with a |0> state # - ####################################### - self.chi = chi - self.truncation_fidelity = truncation_fidelity +class MPS: + """Represents a state as a Matrix Product State. + + Attributes: + tensors (list[Tensor]): A list of tensors in the MPS; ``tensors[0]`` is + the leftmost and ``tensors[len(self)-1]`` is the rightmost; ``tensors[i]`` + and ``tensors[i+1]`` are connected in the MPS via a bond. All of the + tensors are rank three, with the dimensions listed in ``.shape`` matching + the left, right and physical bonds, in that order. + canonical_form (dict[int, Optional[DirectionMPS]]): A dictionary mapping + positions to the canonical form direction of the corresponding tensor, + or ``None`` if it the tensor is not canonicalised. + qubit_position (dict[pytket.circuit.Qubit, int]): A dictionary mapping circuit + qubits to the position its tensor is at in the MPS. + fidelity (float): A lower bound of the fidelity, obtained by multiplying + the fidelities after each contraction. The fidelity of a contraction + corresponds to ``||^2`` where ``|psi>`` and ``|phi>`` are the + states before and after truncation (assuming both are normalised). + """ + + def __init__( + self, + libhandle: CuTensorNetHandle, + qubits: list[Qubit], + config: ConfigMPS, + ): + """Initialise an MPS on the computational state ``|0>``. + + Note: + A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` + statement. The device where the MPS is stored will match the one specified + by the library handle. + + Args: + libhandle: The cuTensorNet library handle that will be used to carry out + tensor operations on the MPS. + qubits: The list of qubits in the circuit to be simulated. + config: The object describing the configuration for simulation. + + Raises: + ValueError: If less than two qubits are provided. + """ + self._lib = libhandle + self._cfg = config self.fidelity = 1.0 n_tensors = len(qubits) @@ -197,7 +234,7 @@ def __init__( # 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._complex_t) + 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 @@ -216,7 +253,7 @@ def is_valid(self) -> bool: self._flush() chi_ok = all( - all(dim <= self.chi for dim in self.get_virtual_dimensions(pos)) + all(dim <= self._cfg.chi for dim in self.get_virtual_dimensions(pos)) for pos in range(len(self)) ) phys_ok = all(self.get_physical_dimension(pos) == 2 for pos in range(len(self))) @@ -494,7 +531,7 @@ def measure(self, qubits: set[Qubit]) -> dict[Qubit, int]: positions = sorted(position_qubit_map.keys()) # Tensor for postselection to |0> - zero_tensor = cp.zeros(2, dtype=self._complex_t) + zero_tensor = cp.zeros(2, dtype=self._cfg._complex_t) zero_tensor[0] = 1 # Measure and postselect each of the positions, one by one @@ -526,7 +563,7 @@ def measure(self, qubits: set[Qubit]) -> dict[Qubit, int]: result[position_qubit_map[pos]] = outcome # Postselect the MPS for this outcome, renormalising at the same time - postselection_tensor = cp.zeros(2, dtype=self._complex_t) + postselection_tensor = cp.zeros(2, dtype=self._cfg._complex_t) postselection_tensor[outcome] = 1 / np.sqrt( abs(outcome - prob) ) # Normalise @@ -571,18 +608,18 @@ def postselect(self, qubit_outcomes: dict[Qubit, int]) -> float: # Apply a postselection for each of the qubits for qubit, outcome in qubit_outcomes.items(): # Create the rank-1 postselection tensor - postselection_tensor = cp.zeros(2, dtype=self._complex_t) + postselection_tensor = cp.zeros(2, dtype=self._cfg._complex_t) postselection_tensor[outcome] = 1 # Apply postselection self._postselect_qubit(qubit, postselection_tensor) # Calculate the squared norm of the postselected state; this is its probability prob = self.vdot(self) - assert np.isclose(prob.imag, 0.0, atol=self._atol) + assert np.isclose(prob.imag, 0.0, atol=self._cfg._atol) prob = prob.real # Renormalise; it suffices to update the first tensor - if len(self) > 0 and not np.isclose(prob, 0.0, atol=self._atol): + if len(self) > 0 and not np.isclose(prob, 0.0, atol=self._cfg._atol): self.tensors[0] = self.tensors[0] / np.sqrt(prob) self.canonical_form[0] = None @@ -660,8 +697,8 @@ def expectation_value(self, pauli_string: QubitPauliString) -> float: pos = mps_copy.qubit_position[qubit] pauli_unitary = Op.create(pauli_optype[pauli]).get_unitary() pauli_tensor = cp.asarray( - pauli_unitary.astype(dtype=self._complex_t, copy=False), - dtype=self._complex_t, + pauli_unitary.astype(dtype=self._cfg._complex_t, copy=False), + dtype=self._cfg._complex_t, ) # Contract the Pauli to the MPS tensor of the corresponding qubit @@ -671,7 +708,7 @@ def expectation_value(self, pauli_string: QubitPauliString) -> float: # Obtain the inner product value = self.vdot(mps_copy) - assert np.isclose(value.imag, 0.0, atol=self._atol) + assert np.isclose(value.imag, 0.0, atol=self._cfg._atol) return value.real @@ -735,10 +772,10 @@ def get_amplitude(self, state: int) -> complex: mps_pos_bitvalue[pos] = bitvalue # Carry out the contraction, starting from a dummy tensor - result_tensor = cp.ones(1, dtype=self._complex_t) # rank-1, dimension 1 + result_tensor = cp.ones(1, dtype=self._cfg._complex_t) # rank-1, dimension 1 for pos in range(len(self)): - postselection_tensor = cp.zeros(2, dtype=self._complex_t) + postselection_tensor = cp.zeros(2, dtype=self._cfg._complex_t) postselection_tensor[mps_pos_bitvalue[pos]] = 1 # Contract postselection with qubit into the result_tensor result_tensor = cq.contract( @@ -822,16 +859,12 @@ def copy(self) -> MPS: self._flush() # Create a dummy object - new_mps = MPS(self._lib, qubits=[]) + new_mps = MPS(self._lib, qubits=[], config=self._cfg.copy()) # Copy all data - new_mps.chi = self.chi - new_mps.truncation_fidelity = self.truncation_fidelity new_mps.fidelity = self.fidelity new_mps.tensors = [t.copy() for t in self.tensors] new_mps.canonical_form = self.canonical_form.copy() new_mps.qubit_position = self.qubit_position.copy() - new_mps._complex_t = self._complex_t - new_mps._real_t = self._real_t return new_mps diff --git a/pytket/extensions/cutensornet/mps/mps_gate.py b/pytket/extensions/cutensornet/mps/mps_gate.py index 3c522afd..d51e75e2 100644 --- a/pytket/extensions/cutensornet/mps/mps_gate.py +++ b/pytket/extensions/cutensornet/mps/mps_gate.py @@ -51,8 +51,8 @@ def _apply_1q_gate(self, position: int, gate: Op) -> MPSxGate: """ # Load the gate's unitary to the GPU memory - gate_unitary = gate.get_unitary().astype(dtype=self._complex_t, copy=False) - gate_tensor = cp.asarray(gate_unitary, dtype=self._complex_t) + gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) + gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) # Glossary of bond IDs # p -> physical bond of the MPS tensor @@ -101,7 +101,7 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # Canonicalisation may be required if `new_dim` is larger than `chi` # or if set by `truncation_fidelity` - if new_dim > self.chi or self.truncation_fidelity < 1: + if new_dim > self._cfg.chi or self._cfg.truncation_fidelity < 1: # If truncation required, convert to canonical form before # contracting. Avoids the need to apply gauge transformations # to the larger tensor resulting from the contraction. @@ -115,8 +115,8 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: ) # Load the gate's unitary to the GPU memory - gate_unitary = gate.get_unitary().astype(dtype=self._complex_t, copy=False) - gate_tensor = cp.asarray(gate_unitary, dtype=self._complex_t) + gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) + gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) # Reshape into a rank-4 tensor gate_tensor = cp.reshape(gate_tensor, (2, 2, 2, 2)) @@ -151,7 +151,7 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: R = self.tensors[r_pos] r_shape = list(R.shape) - if self.truncation_fidelity < 1: + if self._cfg.truncation_fidelity < 1: # Carry out SVD decomposition first with NO truncation # to figure out where to apply the dimension cutoff. # Then, apply S normalisation and contraction of S and L manually. @@ -163,7 +163,7 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # including normalisation and contraction of S with L. options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - svd_method = tensor.SVDMethod(abs_cutoff=self._atol / 1000) + svd_method = tensor.SVDMethod(abs_cutoff=self._cfg._atol / 1000) L, S, R = tensor.decompose( "acLR->asL,scR", T, method=svd_method, options=options ) @@ -182,7 +182,7 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: new_dim = 0 # Take singular values until we surpass the target fidelity - while self.truncation_fidelity > numer / denom: + while self._cfg.truncation_fidelity > numer / denom: numer += float(S[new_dim] ** 2) new_dim += 1 this_fidelity = numer / denom @@ -193,7 +193,7 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # pylint: disable = unexpected-keyword-arg # Disable pylint for next line L = cp.ndarray( l_shape, - dtype=self._complex_t, + dtype=self._cfg._complex_t, memptr=L.data, strides=L.strides, ) @@ -201,18 +201,18 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # pylint: disable = unexpected-keyword-arg # Disable pylint for next line R = cp.ndarray( r_shape, - dtype=self._complex_t, + dtype=self._cfg._complex_t, memptr=R.data, strides=R.strides, ) # pylint: disable = unexpected-keyword-arg # Disable pylint for next line - S = cp.ndarray(new_dim, dtype=self._real_t, memptr=S.data) + S = cp.ndarray(new_dim, dtype=self._cfg._real_t, memptr=S.data) # Normalise S *= np.sqrt(1 / this_fidelity) # Contract S into L - S = S.astype(dtype=self._complex_t, copy=False) + S = S.astype(dtype=self._cfg._complex_t, copy=False) # Use some einsum index magic: since the virtual bond "s" appears in the # list of bonds of the output, it is not summed over. # This causes S to act as the intended diagonal matrix. @@ -222,16 +222,16 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # to keep track of a lower bound for the fidelity. self.fidelity *= this_fidelity - elif new_dim > self.chi: + elif new_dim > self._cfg.chi: # Apply SVD decomposition and truncate up to a `max_extent` (for the shared - # bond) of `self.chi`. Ask cuTensorNet to contract S directly into the L - # tensor and normalise the singular values so that the sum of its squares + # bond) of `self._cfg.chi`. Ask cuTensorNet to contract S directly into the + # L tensor and normalise the singular values so that the sum of its squares # is equal to one (i.e. the MPS is a normalised state after truncation). options = {"handle": self._lib.handle, "device_id": self._lib.device_id} svd_method = tensor.SVDMethod( - abs_cutoff=self._atol / 1000, - max_extent=self.chi, + abs_cutoff=self._cfg._atol / 1000, + max_extent=self._cfg.chi, partition="U", # Contract S directly into U (named L in our case) normalization="L2", # Sum of squares equal 1 ) diff --git a/pytket/extensions/cutensornet/mps/mps_mpo.py b/pytket/extensions/cutensornet/mps/mps_mpo.py index 42f82462..fd4b2dd6 100644 --- a/pytket/extensions/cutensornet/mps/mps_mpo.py +++ b/pytket/extensions/cutensornet/mps/mps_mpo.py @@ -32,6 +32,7 @@ from .mps import ( CuTensorNetHandle, DirectionMPS, + ConfigMPS, Tensor, MPS, ) @@ -48,11 +49,7 @@ def __init__( self, libhandle: CuTensorNetHandle, qubits: list[Qubit], - chi: Optional[int] = None, - truncation_fidelity: Optional[float] = None, - k: Optional[int] = None, - optim_delta: Optional[float] = None, - float_precision: Optional[Union[np.float32, np.float64]] = None, + config: ConfigMPS, ): """Initialise an MPS on the computational state ``|0>``. @@ -61,35 +58,13 @@ def __init__( statement. The device where the MPS is stored will match the one specified by the library handle. - Providing both a custom ``chi`` and ``truncation_fidelity`` will raise an - exception. Choose one or the other (or neither, for exact simulation). - Args: libhandle: The cuTensorNet library handle that will be used to carry out tensor operations on the MPS. qubits: The list of qubits in the circuit to be simulated. - chi: The maximum value allowed for the dimension of the virtual - bonds. Higher implies better approximation but more - computational resources. If not provided, ``chi`` will be set - to ``2**(len(qubits) // 2)``, which is enough for exact contraction. - truncation_fidelity: Every time a two-qubit gate is applied, the virtual - bond will be truncated to the minimum dimension that satisfies - ``||^2 >= trucantion_fidelity``, where ``|psi>`` and ``|phi>`` - are the states before and after truncation (both normalised). - If not provided, it will default to its maximum value 1. - k: The maximum number of layers the MPO is allowed to have before - being contracted. Increasing this might increase fidelity, but - it will also increase resource requirements exponentially. - Default value is 4. - optim_delta: Stopping criteria for the optimisation when contracting the - ``k`` layers of MPO. Stops when the increase of fidelity between - iterations is smaller than ``optim_delta``. Default value is ``1e-5``. - float_precision: The floating point precision used in tensor calculations; - choose from ``numpy`` types: ``np.float64`` or ``np.float32``. - Complex numbers are represented using two of such - ``float`` numbers. Default is ``np.float64``. + config: The object describing the configuration for simulation. """ - super().__init__(libhandle, qubits, chi, truncation_fidelity, float_precision) + super().__init__(libhandle, qubits, config) # Initialise the MPO data structure. This will keep a list of the gates # batched for application to the MPS; all of them will be applied at @@ -108,18 +83,7 @@ def __init__( # Initialise the MPS that we will use as first approximation of the # variational algorithm. - self._aux_mps = MPSxGate( - libhandle, qubits, chi, truncation_fidelity, float_precision - ) - - if k is None: - self.k = 4 - else: - self.k = k - if optim_delta is None: - self.optim_delta = 1e-5 - else: - self.optim_delta = optim_delta + self._aux_mps = MPSxGate(libhandle, qubits, config) self._mpo_bond_counter = 0 @@ -155,8 +119,8 @@ def _apply_1q_gate(self, position: int, gate: Op) -> MPSxMPO: self._aux_mps._apply_1q_gate(position, gate) # Load the gate's unitary to the GPU memory - gate_unitary = gate.get_unitary().astype(dtype=self._complex_t, copy=False) - gate_tensor = cp.asarray(gate_unitary, dtype=self._complex_t) + gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) + gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) # Glossary of bond IDs # i -> input to the MPO tensor @@ -209,15 +173,15 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxMPO: r_pos = max(positions) # Check whether the MPO is large enough to flush it - if any(len(self._mpo[pos]) >= self.k for pos in [l_pos, r_pos]): + if any(len(self._mpo[pos]) >= self._cfg.k for pos in [l_pos, r_pos]): self._flush() # Apply the gate to the MPS with eager approximation self._aux_mps._apply_2q_gate(positions, gate) # Load the gate's unitary to the GPU memory - gate_unitary = gate.get_unitary().astype(dtype=self._complex_t, copy=False) - gate_tensor = cp.asarray(gate_unitary, dtype=self._complex_t) + gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) + gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) # Reshape into a rank-4 tensor gate_tensor = cp.reshape(gate_tensor, (2, 2, 2, 2)) @@ -476,7 +440,7 @@ def update_variational_tensor( # Get the fidelity optim_fidelity = complex(cq.contract("LRP,LRP->", F.conj(), F)) - assert np.isclose(optim_fidelity.imag, 0.0, atol=self._atol) + assert np.isclose(optim_fidelity.imag, 0.0, atol=self._cfg._atol) optim_fidelity = float(optim_fidelity.real) # Normalise F and update the variational MPS @@ -498,7 +462,7 @@ def update_variational_tensor( # Repeat sweeps until the fidelity converges sweep_direction = DirectionMPS.RIGHT - while not np.isclose(prev_fidelity, sweep_fidelity, atol=self.optim_delta): + while not np.isclose(prev_fidelity, sweep_fidelity, atol=self._cfg.optim_delta): prev_fidelity = sweep_fidelity if sweep_direction == DirectionMPS.RIGHT: diff --git a/pytket/extensions/cutensornet/mps/simulation.py b/pytket/extensions/cutensornet/mps/simulation.py index e138e78c..b9656c9c 100644 --- a/pytket/extensions/cutensornet/mps/simulation.py +++ b/pytket/extensions/cutensornet/mps/simulation.py @@ -10,7 +10,7 @@ from pytket.passes import DefaultMappingPass from pytket.predicates import CompilationUnit -from .mps import CuTensorNetHandle, MPS +from .mps import CuTensorNetHandle, ConfigMPS, MPS from .mps_gate import MPSxGate from .mps_mpo import MPSxMPO @@ -30,7 +30,7 @@ def simulate( libhandle: CuTensorNetHandle, circuit: Circuit, algorithm: ContractionAlg, - **kwargs: Any + config: ConfigMPS, ) -> MPS: """Simulate the given circuit and return the ``MPS`` representing the final state. @@ -51,33 +51,23 @@ def simulate( tensor operations on the MPS. circuit: The pytket circuit to be simulated. algorithm: Choose between the values of the ``ContractionAlg`` enum. - **kwargs: Any argument accepted by the initialisers of the chosen - ``algorithm`` class can be passed as a keyword argument. See the - documentation of the corresponding class for details. + config: The configuration object for simulation. Returns: An instance of ``MPS`` containing (an approximation of) the final state of the circuit. """ - chi = kwargs.get("chi", None) - truncation_fidelity = kwargs.get("truncation_fidelity", None) - float_precision = kwargs.get("float_precision", None) - if algorithm == ContractionAlg.MPSxGate: mps = MPSxGate( # type: ignore - libhandle, circuit.qubits, chi, truncation_fidelity, float_precision + libhandle, + circuit.qubits, + config, ) elif algorithm == ContractionAlg.MPSxMPO: - k = kwargs.get("k", None) - optim_delta = kwargs.get("optim_delta", None) mps = MPSxMPO( # type: ignore libhandle, circuit.qubits, - chi, - truncation_fidelity, - k, - optim_delta, - float_precision, + config, ) # Sort the gates so there isn't much overhead from canonicalising back and forth. diff --git a/tests/test_mps.py b/tests/test_mps.py index 9e920fa4..ab2c5b0d 100644 --- a/tests/test_mps.py +++ b/tests/test_mps.py @@ -10,6 +10,7 @@ from pytket.pauli import Pauli, QubitPauliString # type: ignore from pytket.extensions.cutensornet.mps import ( CuTensorNetHandle, + ConfigMPS, MPS, MPSxGate, MPSxMPO, @@ -25,8 +26,8 @@ def test_libhandle_manager() -> None: # Proper use of library handle with CuTensorNetHandle() as libhandle: - mps = MPS(libhandle, qubits=circ.qubits) - assert np.isclose(mps.vdot(mps), 1, atol=mps._atol) + mps = MPS(libhandle, circ.qubits, ConfigMPS()) + assert np.isclose(mps.vdot(mps), 1, atol=mps._cfg._atol) # Catch exception due to library handle out of scope with pytest.raises(RuntimeError): @@ -37,9 +38,9 @@ def test_init() -> None: circ = Circuit(5) with CuTensorNetHandle() as libhandle: - mps_gate = MPSxGate(libhandle, qubits=circ.qubits) + mps_gate = MPSxGate(libhandle, circ.qubits, ConfigMPS()) assert mps_gate.is_valid() - mps_mpo = MPSxMPO(libhandle, qubits=circ.qubits) + mps_mpo = MPSxMPO(libhandle, circ.qubits, ConfigMPS()) assert mps_mpo.is_valid() @@ -48,11 +49,11 @@ def test_canonicalise() -> None: circ = Circuit(5) with CuTensorNetHandle() as libhandle: - mps_gate = MPSxGate(libhandle, qubits=circ.qubits) + mps_gate = MPSxGate(libhandle, circ.qubits, ConfigMPS()) # Fill up the tensors with random entries # Leftmost tensor - T_d = cp.empty(shape=(1, 4, 2), dtype=mps_gate._complex_t) + T_d = cp.empty(shape=(1, 4, 2), dtype=mps_gate._cfg._complex_t) for i1 in range(T_d.shape[1]): for i2 in range(T_d.shape[2]): T_d[0][i1][i2] = cp.random.rand() + 1j * cp.random.rand() @@ -60,7 +61,7 @@ def test_canonicalise() -> None: # Middle tensors for pos in range(1, len(mps_gate) - 1): - T_d = cp.empty(shape=(4, 4, 2), dtype=mps_gate._complex_t) + T_d = cp.empty(shape=(4, 4, 2), dtype=mps_gate._cfg._complex_t) for i0 in range(T_d.shape[0]): for i1 in range(T_d.shape[1]): for i2 in range(T_d.shape[2]): @@ -68,7 +69,7 @@ def test_canonicalise() -> None: mps_gate.tensors[pos] = T_d # Rightmost tensor - T_d = cp.empty(shape=(4, 1, 2), dtype=mps_gate._complex_t) + T_d = cp.empty(shape=(4, 1, 2), dtype=mps_gate._cfg._complex_t) for i0 in range(T_d.shape[0]): for i2 in range(T_d.shape[2]): T_d[i0][0][i2] = cp.random.rand() + 1j * cp.random.rand() @@ -88,7 +89,7 @@ def test_canonicalise() -> None: # Check that canonicalisation did not change the vector overlap = mps_gate.vdot(mps_copy) - assert np.isclose(overlap, norm_sq, atol=mps_gate._atol) + assert np.isclose(overlap, norm_sq, atol=mps_gate._cfg._atol) # Check that the corresponding tensors are in orthogonal form for pos in range(len(mps_gate)): @@ -142,23 +143,23 @@ def test_exact_circ_sim(circuit: Circuit, algorithm: ContractionAlg) -> None: state = prep_circ.get_statevector() with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, prep_circ, algorithm) + mps = simulate(libhandle, prep_circ, algorithm, ConfigMPS()) assert mps.is_valid() # Check that there was no approximation - assert np.isclose(mps.fidelity, 1.0, atol=mps._atol) + assert np.isclose(mps.fidelity, 1.0, atol=mps._cfg._atol) # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._atol) + assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) # Check that all of the amplitudes are correct for b in range(2**n_qubits): assert np.isclose( mps.get_amplitude(b), state[b], - atol=mps._atol, + atol=mps._cfg._atol, ) # Check that the statevector is correct - assert np.allclose(mps.get_statevector(), state, atol=mps._atol) + assert np.allclose(mps.get_statevector(), state, atol=mps._cfg._atol) @pytest.mark.parametrize( @@ -194,10 +195,12 @@ def test_exact_circ_sim(circuit: Circuit, algorithm: ContractionAlg) -> None: def test_approx_circ_sim_gate_fid(circuit: Circuit, algorithm: ContractionAlg) -> None: prep_circ, _ = prepare_circuit(circuit) with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, prep_circ, algorithm, truncation_fidelity=0.99) + mps = simulate( + libhandle, prep_circ, algorithm, ConfigMPS(truncation_fidelity=0.99) + ) assert mps.is_valid() # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._atol) + assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) @pytest.mark.parametrize( @@ -233,10 +236,10 @@ def test_approx_circ_sim_gate_fid(circuit: Circuit, algorithm: ContractionAlg) - def test_approx_circ_sim_chi(circuit: Circuit, algorithm: ContractionAlg) -> None: prep_circ, _ = prepare_circuit(circuit) with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, prep_circ, algorithm, chi=4) + mps = simulate(libhandle, prep_circ, algorithm, ConfigMPS(chi=4)) assert mps.is_valid() # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._atol) + assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) @pytest.mark.parametrize( @@ -271,30 +274,34 @@ def test_float_point_options( with CuTensorNetHandle() as libhandle: # Exact - mps = simulate(libhandle, prep_circ, algorithm, float_precision=fp_precision) + mps = simulate( + libhandle, prep_circ, algorithm, ConfigMPS(float_precision=fp_precision) + ) assert mps.is_valid() # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._atol) + assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) # Approximate, bound truncation fidelity mps = simulate( libhandle, prep_circ, algorithm, - truncation_fidelity=0.99, - float_precision=fp_precision, + ConfigMPS(truncation_fidelity=0.99, float_precision=fp_precision), ) assert mps.is_valid() # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._atol) + assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) # Approximate, bound chi mps = simulate( - libhandle, prep_circ, algorithm, chi=4, float_precision=fp_precision + libhandle, + prep_circ, + algorithm, + ConfigMPS(chi=4, float_precision=fp_precision), ) assert mps.is_valid() # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._atol) + assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) @pytest.mark.parametrize( @@ -310,32 +317,40 @@ def test_circ_approx_explicit(circuit: Circuit) -> None: # Finite gate fidelity # Check for MPSxGate mps_gate = simulate( - libhandle, circuit, ContractionAlg.MPSxGate, truncation_fidelity=0.99 + libhandle, + circuit, + ContractionAlg.MPSxGate, + ConfigMPS(truncation_fidelity=0.99), ) assert np.isclose(mps_gate.fidelity, 0.4, atol=1e-1) assert mps_gate.is_valid() - assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=mps_gate._atol) + assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=mps_gate._cfg._atol) # Check for MPSxMPO mps_mpo = simulate( - libhandle, circuit, ContractionAlg.MPSxMPO, truncation_fidelity=0.99 + libhandle, + circuit, + ContractionAlg.MPSxMPO, + ConfigMPS(truncation_fidelity=0.99), ) assert np.isclose(mps_mpo.fidelity, 0.6, atol=1e-1) assert mps_mpo.is_valid() - assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=mps_mpo._atol) + assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=mps_mpo._cfg._atol) # Fixed virtual bond dimension # Check for MPSxGate - mps_gate = simulate(libhandle, circuit, ContractionAlg.MPSxGate, chi=8) + mps_gate = simulate( + libhandle, circuit, ContractionAlg.MPSxGate, ConfigMPS(chi=8) + ) assert np.isclose(mps_gate.fidelity, 0.03, atol=1e-2) assert mps_gate.is_valid() - assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=mps_gate._atol) + assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=mps_gate._cfg._atol) # Check for MPSxMPO - mps_mpo = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, chi=8) + mps_mpo = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, ConfigMPS(chi=8)) assert np.isclose(mps_mpo.fidelity, 0.04, atol=1e-2) assert mps_mpo.is_valid() - assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=mps_mpo._atol) + assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=mps_mpo._cfg._atol) @pytest.mark.parametrize( @@ -370,10 +385,10 @@ def test_postselect_2q_circ(circuit: Circuit, postselect_dict: dict) -> None: sv = sv / np.sqrt(sv_prob) # Normalise with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate) + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, ConfigMPS()) prob = mps.postselect(postselect_dict) - assert np.isclose(prob, sv_prob, atol=mps._atol) - assert np.allclose(mps.get_statevector(), sv, atol=mps._atol) + assert np.isclose(prob, sv_prob, atol=mps._cfg._atol) + assert np.allclose(mps.get_statevector(), sv, atol=mps._cfg._atol) @pytest.mark.parametrize( @@ -400,10 +415,10 @@ def test_postselect_circ(circuit: Circuit, postselect_dict: dict) -> None: sv = sv / np.sqrt(sv_prob) # Normalise with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate) + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, ConfigMPS()) prob = mps.postselect(postselect_dict) - assert np.isclose(prob, sv_prob, atol=mps._atol) - assert np.allclose(mps.get_statevector(), sv, atol=mps._atol) + assert np.isclose(prob, sv_prob, atol=mps._cfg._atol) + assert np.allclose(mps.get_statevector(), sv, atol=mps._cfg._atol) @pytest.mark.parametrize( @@ -445,9 +460,9 @@ def test_expectation_value(circuit: Circuit, observable: QubitPauliString) -> No # Simulate the circuit and obtain the expectation value with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate) + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, ConfigMPS()) assert np.isclose( - mps.expectation_value(observable), expectation_value, atol=mps._atol + mps.expectation_value(observable), expectation_value, atol=mps._cfg._atol ) @@ -475,7 +490,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) + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, ConfigMPS()) # Take samples measuring both qubits at once for _ in range(n_samples): @@ -502,7 +517,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) + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, ConfigMPS()) # Compute the probabilities of each outcome p = {(0, 0): 0.0, (0, 1): 0.0, (1, 0): 0.0, (1, 1): 0.0} From b8ee2445d82ccd6bdcda85a50946792b03bd3b5a Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 28 Sep 2023 11:45:31 +0100 Subject: [PATCH 10/83] Added value_of_zero parameter. --- pytket/extensions/cutensornet/mps/mps.py | 8 ++++ pytket/extensions/cutensornet/mps/mps_gate.py | 38 ++++++++++++++----- pytket/extensions/cutensornet/mps/mps_mpo.py | 1 - .../extensions/cutensornet/mps/simulation.py | 2 - 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/pytket/extensions/cutensornet/mps/mps.py b/pytket/extensions/cutensornet/mps/mps.py index 170b66f4..62ea80c4 100644 --- a/pytket/extensions/cutensornet/mps/mps.py +++ b/pytket/extensions/cutensornet/mps/mps.py @@ -95,6 +95,7 @@ def __init__( k: int = 4, optim_delta: float = 1e-5, float_precision: Union[np.float32, np.float64] = np.float64, # type: ignore + value_of_zero: float = 1e-16, loglevel: int = logging.WARNING, ): """Instantiate a configuration object for MPS simulation. @@ -124,6 +125,12 @@ def __init__( choose from ``numpy`` types: ``np.float64`` or ``np.float32``. Complex numbers are represented using two of such ``float`` numbers. Default is ``np.float64``. + value_of_zero: Any number below this value will be considered equal to zero. + Even when no ``chi`` or ``truncation_fidelity`` is provided, singular + values below this number will be truncated. + We suggest to use a value slightly below what your chosen + ``float_precision`` can reasonably achieve. For instance, ``1e-16`` for + ``np.float64`` precision (default) and ``1e-7`` for ``np.float32``. loglevel: Internal logger output level. Raises: @@ -163,6 +170,7 @@ def __init__( raise TypeError( f"Value of float_precision must be in {allowed_precisions}." ) + self.zero = value_of_zero self.k = k self.optim_delta = 1e-5 diff --git a/pytket/extensions/cutensornet/mps/mps_gate.py b/pytket/extensions/cutensornet/mps/mps_gate.py index dc5a64bf..fcc83c95 100644 --- a/pytket/extensions/cutensornet/mps/mps_gate.py +++ b/pytket/extensions/cutensornet/mps/mps_gate.py @@ -169,7 +169,7 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: ) options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - svd_method = tensor.SVDMethod(abs_cutoff=self._cfg._atol / 1000) + svd_method = tensor.SVDMethod(abs_cutoff=self._cfg.zero) L, S, R = tensor.decompose( "acLR->asL,scR", T, method=svd_method, options=options ) @@ -244,7 +244,7 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: options = {"handle": self._lib.handle, "device_id": self._lib.device_id} svd_method = tensor.SVDMethod( - abs_cutoff=self._cfg._atol / 1000, + abs_cutoff=self._cfg.zero, max_extent=self._cfg.chi, partition="U", # Contract S directly into U (named L in our case) normalization="L2", # Sum of squares equal 1 @@ -276,15 +276,35 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: ) else: - # No truncation is necessary. In this case, simply apply a QR decomposition - # to get back to MPS form. QR is cheaper than SVD. - self._logger.debug("No truncation is necessary, applying QR decomposition.") - + # The user did not explicitly ask for truncation, but it is advantageous to + # remove any singular values below ``self._cfg.zero``. + self._logger.debug(f"Truncating singular values below={self._cfg.zero}.") + if self._cfg.zero > self._cfg._atol / 1000: + self._logger.warning( + "Your chosen value_of_zero is relatively large. " + "Faithfulness of final fidelity estimate is not guaranteed." + ) + + # NOTE: There is no guarantee of canonical form in this case. This is fine + # since canonicalisation is just meant to detect the optimal singular values + # to truncate, but if we find values that are essentially zero, we are safe + # to remove them. options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - L, R = tensor.decompose( - "acLR->asL,scR", T, method=tensor.QRMethod(), options=options + svd_method = tensor.SVDMethod( + abs_cutoff=self._cfg.zero, + partition="U", # Contract S directly into U (named L in our case) + normalization=None, # Without canonicalisation we must not normalise + ) + L, S, R = tensor.decompose( + "acLR->asL,scR", T, method=svd_method, options=options + ) + assert S is None # Due to "partition" option in SVDMethod + + # Report to logger + self._logger.debug(f"Truncation done. Fidelity estimate unchanged.") + self._logger.debug( + f"Reduced virtual bond dimension from {new_dim} to {R.shape[0]}." ) - self._logger.debug("QR decomposition applied.") self.tensors[l_pos] = L self.tensors[r_pos] = R diff --git a/pytket/extensions/cutensornet/mps/mps_mpo.py b/pytket/extensions/cutensornet/mps/mps_mpo.py index 09f53a1a..47274262 100644 --- a/pytket/extensions/cutensornet/mps/mps_mpo.py +++ b/pytket/extensions/cutensornet/mps/mps_mpo.py @@ -13,7 +13,6 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings -import logging from typing import Optional, Union diff --git a/pytket/extensions/cutensornet/mps/simulation.py b/pytket/extensions/cutensornet/mps/simulation.py index d817f2f6..1d330013 100644 --- a/pytket/extensions/cutensornet/mps/simulation.py +++ b/pytket/extensions/cutensornet/mps/simulation.py @@ -11,9 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any from enum import Enum -import logging from random import choice # type: ignore from collections import defaultdict # type: ignore From 14353fc2a2e6ebd88b75e88b44ab375188cd1e24 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Thu, 28 Sep 2023 04:18:44 -0700 Subject: [PATCH 11/83] Small changes --- examples/mpi/mpi_overlap_bcast_mps.py | 3 ++- pytket/extensions/cutensornet/mps/mps.py | 6 ++++++ pytket/extensions/cutensornet/mps/mps_gate.py | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/mpi/mpi_overlap_bcast_mps.py b/examples/mpi/mpi_overlap_bcast_mps.py index 36993231..a0d662d9 100644 --- a/examples/mpi/mpi_overlap_bcast_mps.py +++ b/examples/mpi/mpi_overlap_bcast_mps.py @@ -44,6 +44,7 @@ from pytket.extensions.cutensornet.mps import ( simulate, + ConfigMPS, ContractionAlg, CuTensorNetHandle, ) @@ -108,7 +109,7 @@ this_proc_mps = [] with CuTensorNetHandle(device_id) as libhandle: # Different handle for each process for circ in this_proc_circs: - mps = simulate(libhandle, circ, ContractionAlg.MPSxGate) + mps = simulate(libhandle, circ, ContractionAlg.MPSxGate, ConfigMPS()) this_proc_mps.append(mps) if rank == root: diff --git a/pytket/extensions/cutensornet/mps/mps.py b/pytket/extensions/cutensornet/mps/mps.py index 62ea80c4..b934f8f3 100644 --- a/pytket/extensions/cutensornet/mps/mps.py +++ b/pytket/extensions/cutensornet/mps/mps.py @@ -172,6 +172,12 @@ def __init__( ) self.zero = value_of_zero + if value_of_zero > self._atol / 1000: + logging.warning( + "Your chosen value_of_zero is relatively large. " + "Faithfulness of final fidelity estimate is not guaranteed." + ) + self.k = k self.optim_delta = 1e-5 self.loglevel = loglevel diff --git a/pytket/extensions/cutensornet/mps/mps_gate.py b/pytket/extensions/cutensornet/mps/mps_gate.py index fcc83c95..c7dc496e 100644 --- a/pytket/extensions/cutensornet/mps/mps_gate.py +++ b/pytket/extensions/cutensornet/mps/mps_gate.py @@ -280,7 +280,7 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # remove any singular values below ``self._cfg.zero``. self._logger.debug(f"Truncating singular values below={self._cfg.zero}.") if self._cfg.zero > self._cfg._atol / 1000: - self._logger.warning( + self._logger.info( # This was raised as a warning in ConfigMPS already "Your chosen value_of_zero is relatively large. " "Faithfulness of final fidelity estimate is not guaranteed." ) From 460736cb59ac11437798c3443381f0cc1df105e4 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 28 Sep 2023 13:53:12 +0100 Subject: [PATCH 12/83] Fixing docs --- docs/modules/mps.rst | 7 ++++--- pytket/extensions/cutensornet/mps/mps.py | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/modules/mps.rst b/docs/modules/mps.rst index d4da1641..d6fff984 100644 --- a/docs/modules/mps.rst +++ b/docs/modules/mps.rst @@ -7,13 +7,16 @@ Matrix Product State (MPS) Simulation ~~~~~~~~~~ +.. autofunction:: pytket.extensions.cutensornet.mps.simulate + .. autoenum:: pytket.extensions.cutensornet.mps.ContractionAlg() :members: .. autoclass:: pytket.extensions.cutensornet.mps.ConfigMPS() + .. automethod:: __init__ -.. autofunction:: pytket.extensions.cutensornet.mps.simulate +.. autoclass:: pytket.extensions.cutensornet.mps.CuTensorNetHandle Classes @@ -50,8 +53,6 @@ Classes .. automethod:: __init__ -.. autoclass:: pytket.extensions.cutensornet.mps.CuTensorNetHandle - Miscellaneous ~~~~~~~~~~~~~ diff --git a/pytket/extensions/cutensornet/mps/mps.py b/pytket/extensions/cutensornet/mps/mps.py index b934f8f3..762958cc 100644 --- a/pytket/extensions/cutensornet/mps/mps.py +++ b/pytket/extensions/cutensornet/mps/mps.py @@ -131,7 +131,8 @@ def __init__( We suggest to use a value slightly below what your chosen ``float_precision`` can reasonably achieve. For instance, ``1e-16`` for ``np.float64`` precision (default) and ``1e-7`` for ``np.float32``. - loglevel: Internal logger output level. + loglevel: Internal logger output level. Use 30 for warnings only, 20 for + verbose and 10 for debug mode. Raises: ValueError: If both ``chi`` and ``truncation_fidelity`` are fixed. From 5b620d9479d7a985a550e5a085bb3ebe866305be Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Fri, 29 Sep 2023 12:45:16 +0100 Subject: [PATCH 13/83] Fixed some issues when merging --- pytket/extensions/cutensornet/mps/mps_gate.py | 1 - pytket/extensions/cutensornet/mps/mps_mpo.py | 1 - 2 files changed, 2 deletions(-) diff --git a/pytket/extensions/cutensornet/mps/mps_gate.py b/pytket/extensions/cutensornet/mps/mps_gate.py index df9b9418..c7dc496e 100644 --- a/pytket/extensions/cutensornet/mps/mps_gate.py +++ b/pytket/extensions/cutensornet/mps/mps_gate.py @@ -305,7 +305,6 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: self._logger.debug( f"Reduced virtual bond dimension from {new_dim} to {R.shape[0]}." ) - self._logger.debug("QR decomposition applied.") self.tensors[l_pos] = L self.tensors[r_pos] = R diff --git a/pytket/extensions/cutensornet/mps/mps_mpo.py b/pytket/extensions/cutensornet/mps/mps_mpo.py index 09f53a1a..47274262 100644 --- a/pytket/extensions/cutensornet/mps/mps_mpo.py +++ b/pytket/extensions/cutensornet/mps/mps_mpo.py @@ -13,7 +13,6 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings -import logging from typing import Optional, Union From 0d0af5c4689338b2f463abb067605381dd227b4f Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 3 Oct 2023 15:42:42 -0700 Subject: [PATCH 14/83] Updated Jupyter notebook --- examples/mps_tutorial.ipynb | 2243 ++++++++++++++++++----------------- 1 file changed, 1126 insertions(+), 1117 deletions(-) diff --git a/examples/mps_tutorial.ipynb b/examples/mps_tutorial.ipynb index f1a2a555..237504e7 100644 --- a/examples/mps_tutorial.ipynb +++ b/examples/mps_tutorial.ipynb @@ -15,6 +15,7 @@ "\n", "from pytket.extensions.cutensornet.mps import (\n", " CuTensorNetHandle,\n", + " ConfigMPS,\n", " ContractionAlg,\n", " simulate, \n", " prepare_circuit\n", @@ -97,7 +98,7 @@ "\n", "\n", "\n", - " <div id="circuit-display-vue-container-e13d14af-9a4e-4029-8388-8fb18c773bf6" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-6f3d27c2-9192-4cb0-a7ab-af175d1b97d3" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["q", [0]], ["q", [1]]], "op": {"type": "CZ"}}, {"args": [["q", [2]]], "op": {"type": "H"}}, {"args": [["q", [3]], ["q", [4]]], "op": {"type": "CX"}}, {"args": [["q", [0]]], "op": {"params": ["0.2"], "type": "Ry"}}, {"args": [["q", [2]], ["q", [1]]], "op": {"params": ["0.3", "0.5", "0.7"], "type": "TK2"}}, {"args": [["q", [4]], ["q", [3]]], "op": {"params": ["0.1"], "type": "ZZPhase"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["q", [0]], ["q", [0]]], [["q", [1]], ["q", [1]]], [["q", [2]], ["q", [2]]], [["q", [3]], ["q", [3]]], [["q", [4]], ["q", [4]]]], "phase": "0.0", "qubits": [["q", [0]], ["q", [1]], ["q", [2]], ["q", [3]], ["q", [4]]]}</div>\n", " </div>\n", @@ -107,7 +108,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "e13d14af-9a4e-4029-8388-8fb18c773bf6";\n", + " const circuitRendererUid = "6f3d27c2-9192-4cb0-a7ab-af175d1b97d3";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -168,7 +169,7 @@ "id": "a9ede80b-32d0-4d43-b910-099dcc2a8a95", "metadata": {}, "source": [ - "For **exact** simulation, simply call the `simulate` function on the circuit and choose a contraction algorithm. To learn more about the contraction algorithms we provide see the *Contraction algorithms* section of this notebook.\n", + "For **exact** simulation, simply call the `simulate` function on the circuit and choose a contraction algorithm. To learn more about the contraction algorithms we provide see the *Contraction algorithms* section of this notebook. You will also need to provide a configuration, the default one is provided by `ConfigMPS()`. Custom settings of `ConfigMPS` are discussed in the *Approximate simulation* section.\n", "\n", "**NOTE**: whenever you wish to generate an `MPS` object or execute calculations on it you must do so within a `with CuTensorNetHandle() as libhandle:` block; this will initialise the cuTensorNetwork library for you, and destroy its handles at the end of the `with` block. You will need to pass the `libhandle` to the `MPS` object via the method that generates it (in the snippet below, `simulate`), or if already initialised, pass it via the `update_libhandle` method.\n", "\n", @@ -183,7 +184,7 @@ "outputs": [], "source": [ "with CuTensorNetHandle() as libhandle:\n", - " my_mps = simulate(libhandle, my_circ, ContractionAlg.MPSxGate)" + " my_mps = simulate(libhandle, my_circ, ContractionAlg.MPSxGate, ConfigMPS())" ] }, { @@ -216,7 +217,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "(0.039688840897737394+0.05462700305610265j)\n" + "(0.03968884089773739+0.05462700305610267j)\n" ] } ], @@ -285,7 +286,7 @@ "outputs": [ { "data": { - "image/png": "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GzdOJ06c0EMPPaTMzEx16dJFK1assGaMGQAA4JccpqyhBmuIrKwsuVwuud1uhYeHW10OAEBSbm6uUlJS1LRp07P+CAQ129mOg8p8f1s+zgwAAMC5IMwAAABbI8wAAFBDDB8+XH/84x+tLuO8I8wAAFAJw4cPl8PhKPFKTEy0ujS9+OKLWrhwodVlSCoa9PaDDz44L5/l179mAgCgVLluKe+Y5GpUcpr7sOQMrdYn3icmJmrBggVebVYO2FpQUCCHwyGXq/r67M84MwMAsJdct7ToT9LCfpL7R+9p7h+L2hf9qWi+auJ0OhUdHe31atCggVatWqXAwEB9+eWXnnlnzJihCy+8UGlpaZKkXr16afTo0Ro9erTq16+viIgIPfHEE17Pb8rPz9e4cePUqFEjhYSEqEuXLl4D4i1cuFD169fXRx99pNatW8vpdOrgwYMlLjP16tVLDz/8sJKSktSgQQNFRUVp7ty5ysnJ0d13362wsDA1b95cn376qVf/du/erX79+ik0NFRRUVEaMmSIfv31V6/1PvLIIxo3bpwaNmyo6OhoTZ482TO9SZMmkqRbbrlFDofD8766EGYAAPaSd0zK+UXKPCAtvPH3QOP+seh95oGi6XnHzntpvXr1UlJSkoYMGSK3261vv/1WEydO1Lx587wezfPGG2/oggsu0Pr16zV79mzNnDlT8+fP90y/++679fXXX2vx4sXavn27br31ViUmJmrfvn2eeY4fP65p06Zp/vz52rVrlyIjI0ut6Y033tCFF16oDRs26OGHH9aDDz6oW2+9Vd26ddOWLVvUp08fDRkyRMePH5dU9JDKnj176oorrtCmTZu0fPly/fzzz7rttttKrDckJETr16/X9OnT9dRTTyk5OVmSPA+QXrBggdLS0kp9oHSVMjWc2+02kozb7ba6FADA/5w4ccLs3r3bnDhxwrcV/JZqzKx2xjwZXvTfg+u83/+WWrUFn2bYsGEmICDAhISEeL2eeuopY4wxeXl55sorrzS33Xabufzyy829997rtXzPnj3NZZddZgoLCz1t48ePN5dddpkxxpjvv//eOBwOc/jwYa/lrr/+ejNhwgRjjDELFiwwksy2bdtK1DZgwACvz7rmmms870+dOmVCQkLMkCFDPG1paWlGkvnmm2+MMcZMmjTJJCQkeK03NTXVSDJ79+4tdb3GGHPVVVeZ8ePHe95LMkuXLi1jKxY523FQme9v7pkBANiPK04a/vHvZ2JeTyhqb9CkqN0VV60ff+211+rVV1/1amvYsKEkKTAwUIsWLVK7du3UuHFjzZo1q8TyV199tRwOh+d9165dNWPGDBUUFGjLli0yxqhVq1Zey+Tl5Ski4vcnhAcGBqpdu3bl1nr6PAEBAYqIiFDbtm09bcUPb87IyJBU9CDolStXKjQ0tMS6fvjhB09dZ352TEyMZx3nG2EGAGBPrjjplrm/Bxmp6H01Bxmp6FmCLVq0KHP62rVrJUlHjx7V0aNHFRISUuF1FxYWKiAgQJs3b1ZAQIDXtNMDRlBQkFcgKkvdunW93jscDq+24nUUFhZ6/tu/f/9SH+p8+qWy0tZbvI7zjTADALAn94/S0vu925bef17OzJzNDz/8oDFjxmjevHl67733NHToUH3++eeqU+f321TXrVvntcy6devUsmVLBQQE6Morr1RBQYEyMjLUvXv3812+OnTooH//+99q0qSJLrjA95hQt25dFRQUVGFlZeMGYACA/Zx+s2+DJtKIFUX/PfOm4GqSl5en9PR0r9evv/6qgoICDRkyRAkJCbr77ru1YMEC7dy5UzNmzPBaPjU1VWPHjtXevXv1zjvv6KWXXtKjjz4qSWrVqpXuuusuDR06VEuWLFFKSoo2btyo559/Xp988km19kuSRo0apaNHj2rQoEHasGGD9u/frxUrVmjEiBGVCidNmjTR559/rvT0dGVmZlZjxYQZAIDduA97B5nhH0sXdyn6r1egOVxtJSxfvlwxMTFer2uuuUbPPvusDhw4oLlz50qSoqOjNX/+fD3xxBPatm2bZ/mhQ4fqxIkT6ty5s0aNGqWHH35Y99//+1mmBQsWaOjQoXrsscd0ySWX6Oabb9b69esVHx9fbX0qFhsbq6+//loFBQXq06eP2rRpo0cffVQul8vr7FJ5ZsyYoeTkZMXHx+vKK6+sxop5ajYAwALn9NTs4nFmcn4peUmp+IxNyEXS4H9X68B5vurVq5euuOKKUm8Mrm2q6qnZ3DMDALCXeq6ioFLaCMCuOGn4J9U+AjD8C2EGAGA/9Vxlh5XSHnGAGo0wAwDAeXT6YwlQNbgBGAAA2BphBgBgmRr+GxSUo6r2P2EGAHDeFY8eW/xwQ9ROxfv/zNGEK4t7ZgAA511AQIDq16/veZZPcHBwhYbmR81gjNHx48eVkZGh+vXrl3hsQ2URZgAAloiOjpYkyx5OCOvVr1/fcxycC8IMAMASDodDMTExioyM1MmTJ60uB+dZ3bp1z/mMTDHCDADAUgEBAVX2pYbaiRuAAQCArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArVkaZl599VW1a9dO4eHhCg8PV9euXfXpp596phtjNHnyZMXGxiooKEi9evXSrl27LKwYAAD4G0vDTFxcnJ577jlt2rRJmzZt0nXXXacBAwZ4Asv06dP1wgsvaM6cOdq4caOio6PVu3dvZWdnW1k2AADwIw5jjLG6iNM1bNhQf//73zVixAjFxsYqKSlJ48ePlyTl5eUpKipKzz//vEaOHFmh9WVlZcnlcsntdis8PLw6SwcAAFWkMt/ffnPPTEFBgRYvXqycnBx17dpVKSkpSk9PV0JCgmcep9Opnj17au3atWWuJy8vT1lZWV4vAABQc1keZnbs2KHQ0FA5nU498MADWrp0qVq3bq309HRJUlRUlNf8UVFRnmmlmTZtmlwul+cVHx9frfUDAABrWR5mLrnkEm3btk3r1q3Tgw8+qGHDhmn37t2e6Q6Hw2t+Y0yJttNNmDBBbrfb80pNTa222gEAgPUusLqAwMBAtWjRQpLUqVMnbdy4US+++KLnPpn09HTFxMR45s/IyChxtuZ0TqdTTqezeosGAAB+w/IzM2cyxigvL09NmzZVdHS0kpOTPdPy8/O1evVqdevWzcIKAQCAP7H0zMxf//pX9e3bV/Hx8crOztbixYu1atUqLV++XA6HQ0lJSZo6dapatmypli1baurUqQoODtadd95pZdkAAMCPWBpmfv75Zw0ZMkRpaWlyuVxq166dli9frt69e0uSxo0bpxMnTuihhx5SZmamunTpohUrVigsLMzKsgEAgB/xu3FmqhrjzAAAYD+2HGcGAADAF4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABga4QZAABgaz6FmdTUVP3444+e9xs2bFBSUpLmzp1bZYUBAABUhE9h5s4779TKlSslSenp6erdu7c2bNigv/71r3rqqaeqtEAAAICz8SnM7Ny5U507d5Ykvffee2rTpo3Wrl2rt99+WwsXLqzK+gAAAM7KpzBz8uRJOZ1OSdJnn32mm2++WZJ06aWXKi0treqqAwAAKIdPYebyyy/XP/7xD3355ZdKTk5WYmKiJOmnn35SRERElRYIAABwNj6Fmeeff16vvfaaevXqpUGDBql9+/aSpGXLlnkuPwEAAJwPDmOM8WXBgoICZWVlqUGDBp62AwcOKDg4WJGRkVVW4LnKysqSy+WS2+1WeHi41eUAAIAKqMz3t8/jzBhjtHnzZr322mvKzs6WJAUGBio4ONjXVQIAAFTaBb4sdPDgQSUmJurQoUPKy8tT7969FRYWpunTpys3N1f/+Mc/qrpOAACAUvl0ZubRRx9Vp06dlJmZqaCgIE/7Lbfcos8//7zKigMAACiPT2dmvvrqK3399dcKDAz0am/cuLEOHz5cJYUBAABUhE9nZgoLC1VQUFCi/ccff1RYWNg5FwUAAFBRPoWZ3r17a9asWZ73DodDx44d05NPPql+/fpVVW0AAADl8umn2T/99JOuvfZaBQQEaN++ferUqZP27dunCy+8UGvWrOGn2QAA4JxU5vvbp3tmYmNjtW3bNr3zzjvasmWLCgsLdc899+iuu+7yuiEYAACguvk8aJ5dcGYGAAD7qZYzM8uWLatwAcUPngQAAKhuFQ4zf/zjHys0n8PhKPWXTgAAANWhwmGmsLCwOusAAADwic/PZgIAAPAHPoeZzz//XDfddJOaN2+uFi1a6KabbtJnn31WlbUBAACUy6cwM2fOHCUmJiosLEyPPvqoHnnkEYWHh6tfv36aM2dOVdcIAABQJp9+mt2oUSNNmDBBo0eP9mp/+eWX9eyzz+qnn36qsgLPFT/NBgDAfirz/e3TmZmsrCwlJiaWaE9ISFBWVpYvqwQAAPCJT2Hm5ptv1tKlS0u0/+c//1H//v3PuSgAAICK8ulxBpdddpmeffZZrVq1Sl27dpUkrVu3Tl9//bUee+wxzZ492zPvI488UjWVAgAAlMKne2aaNm1asZU7HNq/f3+li6pK3DMDAID9VPuDJlNSUnwq7EzTpk3TkiVL9P/+3/9TUFCQunXrpueff16XXHKJZx5jjKZMmaK5c+cqMzNTXbp00csvv6zLL7+8SmoAAAD2ZumgeatXr9aoUaO0bt06JScn69SpU0pISFBOTo5nnunTp+uFF17QnDlztHHjRkVHR6t3797Kzs62sHIAAOAvfLrMZIzR+++/r5UrVyojI6PEow6WLFniUzG//PKLIiMjtXr1avXo0UPGGMXGxiopKUnjx4+XJOXl5SkqKkrPP/+8Ro4cWe46ucwEAID9VPtPsx999FENGTJEKSkpCg0Nlcvl8nr5yu12S5IaNmwoqehyVnp6uhISEjzzOJ1O9ezZU2vXri11HXl5ecrKyvJ6AQCAmsune2YWLVqkJUuWqF+/flVWiDFGY8eO1TXXXKM2bdpIktLT0yVJUVFRXvNGRUXp4MGDpa5n2rRpmjJlSpXVBQAA/JtPZ2ZcLpeaNWtWpYWMHj1a27dv1zvvvFNimsPh8HpvjCnRVmzChAlyu92eV2pqapXWCQAA/ItPYWby5MmaMmWKTpw4USVFPPzww1q2bJlWrlypuLg4T3t0dLSk38/QFMvIyChxtqaY0+lUeHi41wsAANRcPoWZW2+9VZmZmYqMjFTbtm3VoUMHr1dFGWM0evRoLVmyRF988UWJ8WuaNm2q6OhoJScne9ry8/O1evVqdevWzZfSAQBADePTPTPDhw/X5s2bNXjwYEVFRZV5yac8o0aN0ttvv63//Oc/CgsL85yBcblcCgoKksPhUFJSkqZOnaqWLVuqZcuWmjp1qoKDg3XnnXf69JkAAKBm8emn2SEhIfrvf/+ra6655tw+vIwQtGDBAg0fPlzS74Pmvfbaa16D5hXfJFwefpoNAID9VOb726cwc+mll+q9995Tu3btfC7yfCHMAABgP9U+zsyMGTM0btw4HThwwJfFAQAAqoxP98wMHjxYx48fV/PmzRUcHKy6det6TT969GiVFAcAAFAen8LMrFmzqrgMAAAA3/gUZoYNG1bVdQAAAPjEpzBzuhMnTujkyZNebdxoCwAAzhefbgDOycnR6NGjFRkZqdDQUDVo0MDrBQAAcL74FGbGjRunL774Qq+88oqcTqfmz5+vKVOmKDY2Vm+++WZV1wgAAFAmny4zffjhh3rzzTfVq1cvjRgxQt27d1eLFi3UuHFjvfXWW7rrrruquk4AAIBS+XRm5ujRo57nKIWHh3t+in3NNddozZo1VVcdAABAOXwKM82aNfMMmNe6dWu99957korO2NSvX7+qagMAACiXT2Hm7rvv1rfffitJmjBhgufemTFjxugvf/lLlRYIAABwNj49m+lMhw4d0qZNm9S8eXO1b9++KuqqMjybCQAA+6m2ZzOtX79en376qVfbm2++qZ49e+qBBx7Qyy+/rLy8vMpXDAAA4KNKhZnJkydr+/btnvc7duzQPffcoxtuuEETJkzQhx9+qGnTplV5kQAAAGWpVJjZtm2brr/+es/7xYsXq0uXLpo3b57GjBmj2bNne24GBgAAOB8qFWYyMzMVFRXleb969WolJiZ63l911VVKTU2tuuoAAADKUakwExUVpZSUFElSfn6+tmzZoq5du3qmZ2dnq27dulVbIQAAwFlUKswkJibq8ccf15dffqkJEyYoODhY3bt390zfvn27mjdvXuVFAgAAlKVSjzN45plnNHDgQPXs2VOhoaF64403FBgY6Jn++uuvKyEhocqLBAAAKItP48y43W6FhoYqICDAq/3o0aMKDQ31CjhWY5wZAADspzLf3z49aNLlcpXa3rBhQ19WBwAA4DOfHmcAAADgLwgzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggz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7Fjde++9uuGGG/Tcc8/phx9+KPPzKjPvrbfeqhMnTqhZs2a67777tHTpUp06deqs/SkoKNCzzz6rdu3aKSIiQqGhoVqxYoVXvaXZvHmzVq5cqdDQUM/r0ksvlST98MMPat++va6//nq1bdtWt956q+bNm6fMzMyzrhNA1SDMADirkJAQtWjRQi1atFDnzp31z3/+Uzk5OZo3b54kad26dbrjjjvUt29fffTRR9q6dasmTpyo/Px8zzomT56sXbt26cYbb9QXX3yh1q1ba+nSpaV+XmXmjY+P1969e/Xyyy8rKChIDz30kHr06OF1v86ZZsyYoZkzZ2rcuHH64osvtG3bNvXp08er3tIUFhaqf//+nmBX/Nq3b5969OihgIAAJScn69NPP1Xr1q310ksv6ZJLLlFKSkp5mxjAOSLMAKgUh8OhOnXq6MSJE5Kkr7/+Wo0bN9bEiRPVqVMntWzZ0uusTbFWrVppzJgxWrFihQYOHKgFCxaU+RmVmTcoKEg333yzZs+erVWrVumbb77Rjh07JEmBgYEqKCjwmv/LL7/UgAEDNHjwYLVv317NmjXTvn37vOYpbbkOHTpo165datKkiSfcFb9CQkI82+YPf/iDpkyZoq1btyowMLDMIAag6hBmAJxVXl6e0tPTlZ6erj179ujhhx/WsWPH1L9/f0lSixYtdOjQIS1evFg//PCDZs+e7fUFfuLECY0ePVqrVq3SwYMH9fXXX2vjxo267LLLSnxWZeaVin4J9c9//lM7d+7U/v379a9//UtBQUFq3LixpKJxZtasWaPDhw97fnXUokULJScna+3atdqzZ49Gjhyp9PR0r/U2adJE69ev14EDB/Trr7+qsLBQo0aN0tGjRzVo0CBt2LBB+/fv14oVKzRixAgVFBRo/fr1mjp1qjZt2qRDhw5pyZIl+uWXX8qsHUAVsvqmHQD+a9iwYUaS5xUWFmauuuoq8/7773vN95e//MVERESY0NBQc/vtt5uZM2d6burNy8szd9xxh4mPjzeBgYEmNjbWjB492pw4ccIY430DcHnznmnp0qWmS5cuJjw83ISEhJirr77afPbZZ57p33zzjWnXrp1xOp2m+K+7I0eOmAEDBpjQ0FATGRlpnnjiCTN06FAzYMAAz3J79+41V199tQkKCjKSTEpKijHGmO+++87ccsstpn79+iYoKMhceumlJikpyRQWFprdu3ebPn36mIsuusg4nU7TqlUr89JLL1XBXgBQHocx/G4QAADYF5eZAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArRFmAACArf1/hYs84yYwXkgAAAAASUVORK5CYII=", + "image/png": "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wYIFuuOOO1RcXKzjx4+7nZU5cuSIIiMjK5yfv7+//P39a7tsAADgBUw/I3O+0tJSFRUVqWfPnvL19dX69etdw/bs2aPMzEzFx8ebWCEAAPAWpp6RmT17tgYNGqRWrVrpxIkTeuutt5SSkqJPP/1Udrtd99xzj2bOnKlmzZopJCRE999/v+Lj4yt9xxIAAKjfTA0yubm5Gjt2rLKzs2W329WtWzd9+umnuummmyRJ8+fPV6NGjTRixAgVFRVp4MCBeuWVV8wsGQAAeBGve45MTeM5MgAAWI/lniMDAABQVQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWR4FmaysLB08eND1fdOmTZo+fboWL15cY4UBAABcjEdB5q677tKGDRskSTk5Obrpppu0adMmPfzww3riiSdqtEAAAICKeBRkduzYoV69ekmS/vnPf6pLly765ptv9OabbyopKakm6wMAAKiQR0Hm9OnT8vf3lyR9/vnn+v3vfy9J6tixo7Kzs2uuOgAAgAvwKMhcfvnleu211/TVV19p3bp1SkxMlCQdPnxY4eHhNVogAABARTwKMs8++6wWLVqkfv36aeTIkerevbsk6aOPPnJdcgIAAKhtNsMwDE8mLCkpUV5ensLCwlxt+/fvV9OmTdWyZcsaK7C68vLyZLfb5XA4FBISYnY5AACgEir7++3xc2QMw1B6eroWLVqkEydOSJL8/PzUtGlTT2cJAABQJY09mejAgQNKTExUZmamioqKdNNNNyk4OFjPPvusioqK9Nprr9V0nQAAAGV4dEZm2rRpuuqqq3Ts2DEFBAS42ocNG6b169fXWHEAAAAX4tEZma+++krffPON/Pz83Nrj4uJ06NChGikMAADgYjw6I1NaWqqSkpIy7QcPHlRwcHC1iwIAAKgMj4LMgAED9OKLL7q+22w25efna86cORo8eHBN1QYAAHBBHt1+ffDgQQ0cOFCGYWjv3r266qqrtHfvXjVv3lxffvklt18DAIBqqezvt8fPkfntt9+0cuVKbd++Xfn5+erRo4dGjRrl1vnXGxBkAACwnsr+fnvU2VeSGjdurNGjR3s6OQAAQLVVOsh89NFHlZ7pmZdIAgAA1KZKB5lbb721UuPZbLZy72gCAACoaZUOMqWlpbVZBwAAQJV5/K4lAAAAs3kcZNavX69bbrlFl156qS699FLdcsst+vzzz2uyNgAAgAvyKMi88sorSkxMVHBwsKZNm6Zp06YpJCREgwcP1ssvv1zTNQIAAJTLo+fIxMTEaNasWZo6dapb+8svv6y//vWvXvW+JZ4jAwCA9VT299ujMzLHjx9XYmJimfYBAwbI4XB4MksAAIAq8yjI/P73v1dycnKZ9g8//FC33HJLtYsCAACoDI+e7Nu5c2c9/fTTSklJUXx8vCTp22+/1ddff60HH3xQCxcudI37wAMP1EylAAAA5/Goj0ybNm0qN3ObTf/973+rXFRNoo8MAADWU6vvWsrIyPC4MAAAgJrCA/EAAIBleXRGxjAMvffee9qwYYNyc3PLvL5g1apVNVIcAADAhXgUZKZPn65FixbphhtuUEREhGw2W03XBQAAcFEeBZn//d//1apVqzR48OBqLXzevHlatWqVfvzxRwUEBOh3v/udnn32WXXo0ME1TmFhoR588EGtXLlSRUVFGjhwoF555RVFRERUa9kAAMD6POojY7fb1bZt22ovPDU1VVOmTNG3336rdevW6fTp0xowYIAKCgpc48yYMUOrV6/Wu+++q9TUVB0+fFjDhw+v9rIBAID1eXT79RtvvKG1a9fq9ddfV0BAQI0V8/PPP6tly5ZKTU1V37595XA41KJFC7311lv6wx/+IEn68ccf1alTJ6WlpalPnz4XnSe3XwMAYD21evv17bffrrffflstW7ZUXFycfH193YZv3brVk9m6Xm/QrFkzSVJ6erpOnz6thIQE1zgdO3ZUq1atKgwyRUVFKioqcn3Py8vzqBYAAOD9PAoy48aNU3p6ukaPHl1jnX1LS0s1ffp0XXPNNerSpYskKScnR35+fgoNDXUbNyIiQjk5OeXOZ968eZo7d2616wEAAN7PoyCzZs0affrpp7r22mtrrJApU6Zox44d+ve//12t+cye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lRr3fC+88ILRvn17o7i4uNzhrVu3NubPn3/R+dx8883Ggw8+6Pp+/fXXG9OmTXMb58knnzQGDBjg1paVlWVIMvbs2WOkp6cbkoz9+/dXqnYANYMzMgAu6IYbbtC2bdu0bds2bdq0SQMHDtSgQYN04MAB1zjvvPOOrrnmGkVGRiooKEiPPPKIMjMzXcNnzpypP/7xj0pISNAzzzyjffv2Vbi8qox722236dSpU2rbtq0mTpyo5ORk/fbbbxdcn5KSEj355JPq2rWrmjVrpqCgIH366adu9Zbn+++/14YNGxQUFOT6dOzYUZK0b98+de/eXf3791fXrl112223acmSJTp27NgF5wmg+ggyAC4oMDBQ7dq1U7t27XT11Vdr6dKlKigo0JIlSyRJaWlpGjVqlAYPHqyPP/5Y3333nR5++GEVFxe75vH4449r586duvnmm/XFF1+oc+fOSk5OLnd5VRk3NjZWe/bs0SuvvKKAgADdd9996tu3r1v/nPM9//zzWrBggR566CFt2LBB27Zt08CBA93qLU9+fr6GDBniCnVnPnv37lXfvn3l4+OjdevW6ZNPPlHnzp310ksvqUOHDsrIyLjYJgZQDQQZAFVis9nUqFEjnTp1SpL0zTffqHXr1nr44Yd11VVX6bLLLnM7W3NG+/btNWPGDH322WcaPny4li1bVuEyqjJuQECAhgwZooULFyolJUVpaWn64YcfJEl+fn4qKSlxG//rr7/W0KFDNXr0aHXv3l1t27Ytc/t3edP16NFDO3fuVFxcnCvYnfkEBga6ts0111yjuXPn6rvvvpOfn1+FIQxAzSDIALigoqIi5eTkKCcnR7t379b999/vOjshSZdddpkyMzO1cuVK7du3TwsXLnT78T516pSmTp2qlJQUHThwQF9//bU2b96sTp06lVlWVcaVnHc8/eMf/9COHTv03//+VytWrFBAQIBat24tyfkcmS+//FKHDh1y3V102WWXad26dfrmm2+0e/du/b//9/905MgRt/nGxcVp48aN2r9/v3755ReVlpZqypQpOnr0qEaOHKnNmzdr3759+vTTTzVhwgSVlJRo48aN+utf/6otW7YoMzNTq1at0s8//1xh7QBqiNmddAB4r3HjxhmSXJ/g4GDj6quvNt577z238f785z8b4eHhRlBQkHHHHXcY8+fPd3XgLSoqMu68804jNjbW8PPzM6Kjo42pU6cap06dMgzDvbPvxcY9X3JystG7d28jJCTECAwMNPr06WN8/vnnruFpaWlGt27dDH9/f+PMP3e//vqrMXToUCMoKMho2bKl8cgjjxhjx441hg4d6ppuz549Rp8+fYyAgABDkpGRkWEYhmH85z//MYYNG2aEhoYaAQEBRseOHY3p06cbpaWlxq5du4yBAwcaLVq0MPz9/Y327dsbL730Ug3sBQAXYjMM7g0EAADWxKUlAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWf8frP1e4F+3sjkAAAAASUVORK5CYII=", "text/plain": [ "
" ] @@ -394,7 +395,7 @@ "\n", "# Simulate them\n", "with CuTensorNetHandle() as libhandle:\n", - " other_mps = simulate(libhandle, other_circ, ContractionAlg.MPSxGate)" + " other_mps = simulate(libhandle, other_circ, ContractionAlg.MPSxGate, ConfigMPS())" ] }, { @@ -473,7 +474,7 @@ "\n", "\n", "\n", - " <div id="circuit-display-vue-container-067d69bc-bdde-4810-ac70-5b0fe1129460" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-04ed427a-5ec8-4e3d-9fbf-e11d67078a06" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["q", [1]]], "op": {"type": "H"}}, {"args": [["q", [2]], ["q", [3]]], "op": {"params": ["0.3"], "type": "ZZPhase"}}, {"args": [["q", [4]]], "op": {"params": ["0.8"], "type": "Ry"}}, {"args": [["q", [0]], ["q", [1]]], "op": {"type": "CX"}}, {"args": [["q", [3]], ["q", [4]]], "op": {"type": "CZ"}}, {"args": [["q", [1]], ["q", [2]]], "op": {"params": ["0.7"], "type": "XXPhase"}}, {"args": [["q", [1]], ["q", [4]]], "op": {"params": ["0.1", "0.2", "0.4"], "type": "TK2"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["q", [0]], ["q", [0]]], [["q", [1]], ["q", [1]]], [["q", [2]], ["q", [2]]], [["q", [3]], ["q", [3]]], [["q", [4]], ["q", [4]]]], "phase": "0.0", "qubits": [["q", [0]], ["q", [1]], ["q", [2]], ["q", [3]], ["q", [4]]]}</div>\n", " </div>\n", @@ -483,7 +484,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "067d69bc-bdde-4810-ac70-5b0fe1129460";\n", + " const circuitRendererUid = "04ed427a-5ec8-4e3d-9fbf-e11d67078a06";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -557,7 +558,7 @@ "source": [ "with CuTensorNetHandle() as libhandle:\n", " try:\n", - " simulate(libhandle, bad_circ, ContractionAlg.MPSxGate)\n", + " simulate(libhandle, bad_circ, ContractionAlg.MPSxGate, ConfigMPS())\n", " except RuntimeError as e:\n", " print(e)" ] @@ -601,7 +602,7 @@ "\n", "\n", "\n", - " <div id="circuit-display-vue-container-87b5c904-f430-4e00-b6c6-601952b6581f" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-08a5a05d-8989-43a0-a5a9-8c83ea08f5a6" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["node", [1]]], "op": {"params": ["0.8"], "type": "Ry"}}, {"args": [["node", [3]]], "op": {"type": "H"}}, {"args": [["node", [0]], ["node", [1]]], "op": {"type": "SWAP"}}, {"args": [["node", [4]], ["node", [3]]], "op": {"type": "CX"}}, {"args": [["node", [2]], ["node", [1]]], "op": {"params": ["0.3"], "type": "ZZPhase"}}, {"args": [["node", [1]], ["node", [0]]], "op": {"type": "CZ"}}, {"args": [["node", [3]], ["node", [2]]], "op": {"params": ["0.7"], "type": "XXPhase"}}, {"args": [["node", [3]], ["node", [2]]], "op": {"type": "SWAP"}}, {"args": [["node", [2]], ["node", [1]]], "op": {"type": "SWAP"}}, {"args": [["node", [1]], ["node", [0]]], "op": {"params": ["0.1", "0.2", "0.4"], "type": "TK2"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["node", [0]], ["node", [0]]], [["node", [1]], ["node", [1]]], [["node", [2]], ["node", [2]]], [["node", [3]], ["node", [3]]], [["node", [4]], ["node", [4]]]], "phase": "0.0", "qubits": [["node", [0]], ["node", [1]], ["node", [2]], ["node", [3]], ["node", [4]]]}</div>\n", " </div>\n", @@ -611,7 +612,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "87b5c904-f430-4e00-b6c6-601952b6581f";\n", + " const circuitRendererUid = "08a5a05d-8989-43a0-a5a9-8c83ea08f5a6";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -694,7 +695,7 @@ ], "source": [ "with CuTensorNetHandle() as libhandle:\n", - " prep_mps = simulate(libhandle, prep_circ, ContractionAlg.MPSxGate)\n", + " prep_mps = simulate(libhandle, prep_circ, ContractionAlg.MPSxGate, ConfigMPS())\n", " print(\"Did simulation succeed?\")\n", " print(prep_mps.is_valid())" ] @@ -711,7 +712,7 @@ "* Bound the maximum value of the virtual bond dimension `chi`. If a bond dimension would increase past that point, we *truncate* (i.e. discard) the degrees of freedom that contribute the least to the state description. We can keep track of a lower bound of the error that this truncation causes.\n", "* Provide a value for acceptable two-qubit gate fidelity `truncation_fidelity`. After each two-qubit gate we truncate the dimension of virtual bonds as much as we can while guaranteeing the target gate fidelity. The more fidelity you require, the longer it will take to simulate. **Note**: this is *not* the final fidelity of the output state, but the fidelity per gate.\n", "\n", - "To showcase approximate simulation, let's define a circuit where exact MPS contraction starts struggling." + "Values for `chi` and `truncation_fidelity` can be set via `ConfigMPS`. To showcase approximate simulation, let's define a circuit where exact MPS contraction starts struggling." ] }, { @@ -774,17 +775,18 @@ "output_type": "stream", "text": [ "Time taken by approximate contraction with bound chi:\n", - "1.47 seconds\n", + "1.89 seconds\n", "\n", "Lower bound of the fidelity:\n", - "0.3587\n" + "0.3742\n" ] } ], "source": [ "start = time()\n", "with CuTensorNetHandle() as libhandle:\n", - " bound_chi_mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, chi=16)\n", + " config = ConfigMPS(chi=16)\n", + " bound_chi_mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, config)\n", "end = time()\n", "print(\"Time taken by approximate contraction with bound chi:\")\n", "print(f\"{round(end-start,2)} seconds\")\n", @@ -811,17 +813,18 @@ "output_type": "stream", "text": [ "Time taken by approximate contraction with fixed truncation fidelity:\n", - "2.62 seconds\n", + "2.89 seconds\n", "\n", "Lower bound of the fidelity:\n", - "0.9334\n" + "0.9298\n" ] } ], "source": [ "start = time()\n", "with CuTensorNetHandle() as libhandle:\n", - " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, truncation_fidelity=0.999)\n", + " config = ConfigMPS(truncation_fidelity=0.999)\n", + " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, config)\n", "end = time()\n", "print(\"Time taken by approximate contraction with fixed truncation fidelity:\")\n", "print(f\"{round(end-start,2)} seconds\")\n", @@ -852,7 +855,7 @@ "* **k**: The maximum number of layers the MPO is allowed to have before being contracted. Increasing this might increase fidelity, but it will also increase resource requirements exponentially. Default value is `4`.\n", "* **optim_delta**: Stopping criteria for the optimisation when contracting the `k` layers of MPO. Stops when the increase of fidelity between iterations is smaller than `optim_delta`. Default value is `1e-5`.\n", "\n", - "Below we compare `MPSxGate` versus `MPSxMPO` with default parameters and `MPSxMPO` with more resource-hungry parameters. The circuit used is the same as in the previous section." + "Both `k` and `optim_delta` can be set via `ConfigMPS`. Below we compare `MPSxGate` versus `MPSxMPO` with default parameters and `MPSxMPO` with more resource-hungry parameters. The circuit used is the same as in the previous section." ] }, { @@ -866,15 +869,16 @@ "output_type": "stream", "text": [ "MPSxGate\n", - "\tTime taken: 1.35 seconds\n", - "\tLower bound of the fidelity: 0.3589\n" + "\tTime taken: 1.89 seconds\n", + "\tLower bound of the fidelity: 0.3712\n" ] } ], "source": [ "start = time()\n", "with CuTensorNetHandle() as libhandle:\n", - " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, chi=16)\n", + " config = ConfigMPS(chi=16)\n", + " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, config)\n", "end = time()\n", "print(\"MPSxGate\")\n", "print(f\"\\tTime taken: {round(end-start,2)} seconds\")\n", @@ -892,15 +896,16 @@ "output_type": "stream", "text": [ "MPSxMPO, default parameters\n", - "\tTime taken: 12.6 seconds\n", - "\tLower bound of the fidelity: 0.3847\n" + "\tTime taken: 27.17 seconds\n", + "\tLower bound of the fidelity: 0.3956\n" ] } ], "source": [ "start = time()\n", "with CuTensorNetHandle() as libhandle:\n", - " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, chi=16)\n", + " config = ConfigMPS(chi=16)\n", + " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, config)\n", "end = time()\n", "print(\"MPSxMPO, default parameters\")\n", "print(f\"\\tTime taken: {round(end-start,2)} seconds\")\n", @@ -918,15 +923,16 @@ "output_type": "stream", "text": [ "MPSxMPO, custom parameters\n", - "\tTime taken: 22.52 seconds\n", - "\tLower bound of the fidelity: 0.3977\n" + "\tTime taken: 26.99 seconds\n", + "\tLower bound of the fidelity: 0.4209\n" ] } ], "source": [ "start = time()\n", "with CuTensorNetHandle() as libhandle:\n", - " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, k=8, optim_delta=1e-15, chi=16)\n", + " config = ConfigMPS(k=8, optim_delta=1e-15, chi=16)\n", + " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, config)\n", "end = time()\n", "print(\"MPSxMPO, custom parameters\")\n", "print(f\"\\tTime taken: {round(end-start,2)} seconds\")\n", @@ -954,7 +960,7 @@ "id": "7607b5bd-f332-4d97-963b-2a163d3fb194", "metadata": {}, "source": [ - "You can request a verbose log to be produced during simulation, by assigning the `loglevel` argument when calling `simulate`. Currently, two log levels are supported (other than default, which is silent): \n", + "You can request a verbose log to be produced during simulation, by assigning the `loglevel` argument when creating a `ConfigMPS` instance. Currently, two log levels are supported (other than default, which is silent): \n", "- `logging.INFO` will print information about progress percent, memory currently occupied by the MPS and current fidelity. Additionally, some high level information of the current stage of the simulation is provided, such as when `MPSxMPO` is applying optimisation sweeps.\n", "- `logging.DEBUG` provides all of the messages from the loglevel above plus detailed information of the current operation being carried out and the values of important variables.\n", "\n", @@ -970,7 +976,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 21, @@ -997,10 +1003,6 @@ "execution_count": 22, "id": "318073fc-2ef4-492e-8c5a-1ba1ba0b7733", "metadata": { - "collapsed": true, - "jupyter": { - "outputs_hidden": true - }, "tags": [] }, "outputs": [ @@ -1008,1090 +1010,1097 @@ "name": "stderr", "output_type": "stream", "text": [ - "[14:01:12] Simulation (INFO) - Ordering the gates in the circuit to reduce canonicalisation overhead.\n", - "[14:01:12] Simulation (INFO) - Running simulation...\n", - "[14:01:12] Simulation (INFO) - Progress... 0%\n", - "[14:01:12] Simulation (INFO) - Progress... 0%\n", - "[14:01:12] Simulation (INFO) - Progress... 0%\n", - "[14:01:12] Simulation (INFO) - Progress... 0%\n", - "[14:01:12] Simulation (INFO) - Progress... 0%\n", - "[14:01:12] Simulation (INFO) - Progress... 0%\n", - "[14:01:12] Simulation (INFO) - Progress... 1%\n", - "[14:01:12] Simulation (INFO) - Progress... 1%\n", - "[14:01:12] Simulation (INFO) - Progress... 1%\n", - "[14:01:12] Simulation (INFO) - Progress... 1%\n", - "[14:01:12] Simulation (INFO) - Progress... 1%\n", - "[14:01:12] Simulation (INFO) - Progress... 1%\n", - "[14:01:12] Simulation (INFO) - Progress... 2%\n", - "[14:01:12] Simulation (INFO) - Progress... 2%\n", - "[14:01:12] Simulation (INFO) - Progress... 2%\n", - "[14:01:12] Simulation (INFO) - Progress... 2%\n", - "[14:01:12] Simulation (INFO) - Progress... 2%\n", - "[14:01:12] Simulation (INFO) - Progress... 2%\n", - "[14:01:12] Simulation (INFO) - Progress... 3%\n", - "[14:01:12] Simulation (INFO) - Progress... 3%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00067138671875\n", - "[14:01:12] MPS (INFO) - MPS fidelity=1.0\n", - "[14:01:12] Simulation (INFO) - Progress... 3%\n", - "[14:01:12] Simulation (INFO) - Progress... 3%\n", - "[14:01:12] Simulation (INFO) - Progress... 3%\n", - "[14:01:12] Simulation (INFO) - Progress... 3%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00067138671875\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", - "[14:01:12] Simulation (INFO) - Progress... 4%\n", - "[14:01:12] Simulation (INFO) - Progress... 4%\n", - "[14:01:12] Simulation (INFO) - Progress... 4%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.000762939453125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344604\n", - "[14:01:12] Simulation (INFO) - Progress... 4%\n", - "[14:01:12] Simulation (INFO) - Progress... 4%\n", - "[14:01:12] Simulation (INFO) - Progress... 4%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.000762939453125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", - "[14:01:12] Simulation (INFO) - Progress... 5%\n", - "[14:01:12] Simulation (INFO) - Progress... 5%\n", - "[14:01:12] Simulation (INFO) - Progress... 5%\n", - "[14:01:12] Simulation (INFO) - Progress... 5%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.000823974609375\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", - "[14:01:12] Simulation (INFO) - Progress... 5%\n", - "[14:01:12] Simulation (INFO) - Progress... 5%\n", - "[14:01:12] Simulation (INFO) - Progress... 6%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00091552734375\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", - "[14:01:12] Simulation (INFO) - Progress... 6%\n", - "[14:01:12] Simulation (INFO) - Progress... 6%\n", - "[14:01:12] Simulation (INFO) - Progress... 6%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00103759765625\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", - "[14:01:12] Simulation (INFO) - Progress... 6%\n", - "[14:01:12] Simulation (INFO) - Progress... 6%\n", - "[14:01:12] Simulation (INFO) - Progress... 7%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00128173828125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9990283071344602\n", - "[14:01:12] Simulation (INFO) - Progress... 7%\n", - "[14:01:12] Simulation (INFO) - Progress... 7%\n", - "[14:01:12] Simulation (INFO) - Progress... 7%\n", - "[14:01:12] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:12] MPS (INFO) - Fidelity before optimisation=0.9990283071344602\n", - "[14:01:12] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:12] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9997023479978765\n", - "[14:01:12] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:12] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9997024059075587\n", - "[14:01:12] MPS (INFO) - Final fidelity after optimisation=0.9997024059075587\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00128173828125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 7%\n", - "[14:01:12] Simulation (INFO) - Progress... 7%\n", - "[14:01:12] Simulation (INFO) - Progress... 8%\n", - "[14:01:12] Simulation (INFO) - Progress... 8%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0013427734375\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 8%\n", - "[14:01:12] Simulation (INFO) - Progress... 8%\n", - "[14:01:12] Simulation (INFO) - Progress... 8%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00146484375\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 8%\n", - "[14:01:12] Simulation (INFO) - Progress... 9%\n", - "[14:01:12] Simulation (INFO) - Progress... 9%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.001708984375\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 9%\n", - "[14:01:12] Simulation (INFO) - Progress... 9%\n", - "[14:01:12] Simulation (INFO) - Progress... 9%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0020751953125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 10%\n", - "[14:01:12] Simulation (INFO) - Progress... 10%\n", - "[14:01:12] Simulation (INFO) - Progress... 10%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0025634765625\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 10%\n", - "[14:01:12] Simulation (INFO) - Progress... 10%\n", - "[14:01:12] Simulation (INFO) - Progress... 10%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0025634765625\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 11%\n", - "[14:01:12] Simulation (INFO) - Progress... 11%\n", - "[14:01:12] Simulation (INFO) - Progress... 11%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0025634765625\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 11%\n", - "[14:01:12] Simulation (INFO) - Progress... 11%\n", - "[14:01:12] Simulation (INFO) - Progress... 11%\n", - "[14:01:12] Simulation (INFO) - Progress... 12%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00262451171875\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 12%\n", - "[14:01:12] Simulation (INFO) - Progress... 12%\n", - "[14:01:12] Simulation (INFO) - Progress... 12%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00274658203125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 12%\n", - "[14:01:12] Simulation (INFO) - Progress... 12%\n", - "[14:01:12] Simulation (INFO) - Progress... 13%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00299072265625\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 13%\n", - "[14:01:12] Simulation (INFO) - Progress... 13%\n", - "[14:01:12] Simulation (INFO) - Progress... 13%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00347900390625\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9997024059075589\n", - "[14:01:12] Simulation (INFO) - Progress... 13%\n", - "[14:01:12] Simulation (INFO) - Progress... 13%\n", - "[14:01:12] Simulation (INFO) - Progress... 14%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.00396728515625\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9993396700769984\n", - "[14:01:12] Simulation (INFO) - Progress... 14%\n", - "[14:01:12] Simulation (INFO) - Progress... 14%\n", - "[14:01:12] Simulation (INFO) - Progress... 14%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0048828125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9984418366672726\n", - "[14:01:12] Simulation (INFO) - Progress... 14%\n", - "[14:01:12] Simulation (INFO) - Progress... 14%\n", - "[14:01:12] Simulation (INFO) - Progress... 15%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.005889892578125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9978683217610371\n", - "[14:01:12] Simulation (INFO) - Progress... 15%\n", - "[14:01:12] Simulation (INFO) - Progress... 15%\n", - "[14:01:12] Simulation (INFO) - Progress... 15%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.005889892578125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9978683217610371\n", - "[14:01:12] Simulation (INFO) - Progress... 15%\n", - "[14:01:12] Simulation (INFO) - Progress... 15%\n", - "[14:01:12] Simulation (INFO) - Progress... 16%\n", - "[14:01:12] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:12] MPS (INFO) - Fidelity before optimisation=0.9978683217610371\n", - "[14:01:12] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:12] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.99809024854532\n", - "[14:01:12] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:12] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9981132810448355\n", - "[14:01:12] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:12] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9981209535027262\n", - "[14:01:12] MPS (INFO) - Final fidelity after optimisation=0.9981209535027262\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.005889892578125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9981209535027262\n", - "[14:01:12] Simulation (INFO) - Progress... 16%\n", - "[14:01:12] Simulation (INFO) - Progress... 16%\n", - "[14:01:12] Simulation (INFO) - Progress... 16%\n", - "[14:01:12] Simulation (INFO) - Progress... 16%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.005950927734375\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9981209535027262\n", - "[14:01:12] Simulation (INFO) - Progress... 16%\n", - "[14:01:12] Simulation (INFO) - Progress... 17%\n", - "[14:01:12] Simulation (INFO) - Progress... 17%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.006072998046875\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9981209535027262\n", - "[14:01:12] Simulation (INFO) - Progress... 17%\n", - "[14:01:12] Simulation (INFO) - Progress... 17%\n", - "[14:01:12] Simulation (INFO) - Progress... 17%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.006317138671875\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9981209535027262\n", - "[14:01:12] Simulation (INFO) - Progress... 17%\n", - "[14:01:12] Simulation (INFO) - Progress... 18%\n", - "[14:01:12] Simulation (INFO) - Progress... 18%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.006805419921875\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9981209535027261\n", - "[14:01:12] Simulation (INFO) - Progress... 18%\n", - "[14:01:12] Simulation (INFO) - Progress... 18%\n", - "[14:01:12] Simulation (INFO) - Progress... 18%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.007049560546875\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.997423409781103\n", - "[14:01:12] Simulation (INFO) - Progress... 18%\n", - "[14:01:12] Simulation (INFO) - Progress... 19%\n", - "[14:01:12] Simulation (INFO) - Progress... 19%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.008392333984375\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.997423409781103\n", - "[14:01:12] Simulation (INFO) - Progress... 19%\n", - "[14:01:12] Simulation (INFO) - Progress... 19%\n", - "[14:01:12] Simulation (INFO) - Progress... 19%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.009765625\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9969717765474623\n", - "[14:01:12] Simulation (INFO) - Progress... 20%\n", - "[14:01:12] Simulation (INFO) - Progress... 20%\n", - "[14:01:12] Simulation (INFO) - Progress... 20%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.01123046875\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.996255622087102\n", - "[14:01:12] Simulation (INFO) - Progress... 20%\n", - "[14:01:12] Simulation (INFO) - Progress... 20%\n", - "[14:01:12] Simulation (INFO) - Progress... 20%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.01165771484375\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.996255622087102\n", - "[14:01:12] Simulation (INFO) - Progress... 21%\n", - "[14:01:12] Simulation (INFO) - Progress... 21%\n", - "[14:01:12] Simulation (INFO) - Progress... 21%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.01165771484375\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9962556220871019\n", - "[14:01:12] Simulation (INFO) - Progress... 21%\n", - "[14:01:12] Simulation (INFO) - Progress... 21%\n", - "[14:01:12] Simulation (INFO) - Progress... 21%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.01165771484375\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9962556220871019\n", - "[14:01:12] Simulation (INFO) - Progress... 22%\n", - "[14:01:12] Simulation (INFO) - Progress... 22%\n", - "[14:01:12] Simulation (INFO) - Progress... 22%\n", - "[14:01:12] Simulation (INFO) - Progress... 22%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.01171875\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9962556220871019\n", - "[14:01:12] Simulation (INFO) - Progress... 22%\n", - "[14:01:12] Simulation (INFO) - Progress... 22%\n", - "[14:01:12] Simulation (INFO) - Progress... 23%\n", - "[14:01:12] MPS (INFO) - MPS size (MiB)=0.0118408203125\n", - "[14:01:12] MPS (INFO) - MPS fidelity=0.9962556220871019\n", - "[14:01:12] Simulation (INFO) - Progress... 23%\n", - "[14:01:13] Simulation (INFO) - Progress... 23%\n", - "[14:01:13] Simulation (INFO) - Progress... 23%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0120849609375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9962556220871019\n", - "[14:01:13] Simulation (INFO) - Progress... 23%\n", - "[14:01:13] Simulation (INFO) - Progress... 23%\n", - "[14:01:13] Simulation (INFO) - Progress... 24%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0125732421875\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9962556220871019\n", - "[14:01:13] Simulation (INFO) - Progress... 24%\n", - "[14:01:13] Simulation (INFO) - Progress... 24%\n", - "[14:01:13] Simulation (INFO) - Progress... 24%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0130615234375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9955811734143832\n", - "[14:01:13] Simulation (INFO) - Progress... 24%\n", - "[14:01:13] Simulation (INFO) - Progress... 24%\n", - "[14:01:13] Simulation (INFO) - Progress... 25%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.01373291015625\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9954580715406015\n", - "[14:01:13] Simulation (INFO) - Progress... 25%\n", - "[14:01:13] Simulation (INFO) - Progress... 25%\n", - "[14:01:13] Simulation (INFO) - Progress... 25%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0150146484375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9954129745430442\n", - "[14:01:13] Simulation (INFO) - Progress... 25%\n", - "[14:01:13] Simulation (INFO) - Progress... 25%\n", - "[14:01:13] Simulation (INFO) - Progress... 26%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.01708984375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9946104246997917\n", - "[14:01:13] Simulation (INFO) - Progress... 26%\n", - "[14:01:13] Simulation (INFO) - Progress... 26%\n", - "[14:01:13] Simulation (INFO) - Progress... 26%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0211181640625\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.993986081692407\n", - "[14:01:13] Simulation (INFO) - Progress... 26%\n", - "[14:01:13] Simulation (INFO) - Progress... 26%\n", - "[14:01:13] Simulation (INFO) - Progress... 27%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02392578125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9932754206084036\n", - "[14:01:13] Simulation (INFO) - Progress... 27%\n", - "[14:01:13] Simulation (INFO) - Progress... 27%\n", - "[14:01:13] Simulation (INFO) - Progress... 27%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02392578125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9932754206084036\n", - "[14:01:13] Simulation (INFO) - Progress... 27%\n", - "[14:01:13] Simulation (INFO) - Progress... 27%\n", - "[14:01:13] Simulation (INFO) - Progress... 28%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02392578125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9932754206084036\n", - "[14:01:13] Simulation (INFO) - Progress... 28%\n", - "[14:01:13] Simulation (INFO) - Progress... 28%\n", - "[14:01:13] Simulation (INFO) - Progress... 28%\n", - "[14:01:13] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:13] MPS (INFO) - Fidelity before optimisation=0.9932754206084036\n", - "[14:01:13] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:13] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9948146155456611\n", - "[14:01:13] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:13] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9948360895424706\n", - "[14:01:13] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:13] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9948431533380159\n", - "[14:01:13] MPS (INFO) - Final fidelity after optimisation=0.9948431533380159\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02392578125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9948431533380159\n", - "[14:01:13] Simulation (INFO) - Progress... 28%\n", - "[14:01:13] Simulation (INFO) - Progress... 28%\n", - "[14:01:13] Simulation (INFO) - Progress... 29%\n", - "[14:01:13] Simulation (INFO) - Progress... 29%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02398681640625\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9948431533380159\n", - "[14:01:13] Simulation (INFO) - Progress... 29%\n", - "[14:01:13] Simulation (INFO) - Progress... 29%\n", - "[14:01:13] Simulation (INFO) - Progress... 29%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02410888671875\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9948431533380157\n", - "[14:01:13] Simulation (INFO) - Progress... 30%\n", - "[14:01:13] Simulation (INFO) - Progress... 30%\n", - "[14:01:13] Simulation (INFO) - Progress... 30%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02435302734375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9948431533380157\n", - "[14:01:13] Simulation (INFO) - Progress... 30%\n", - "[14:01:13] Simulation (INFO) - Progress... 30%\n", - "[14:01:13] Simulation (INFO) - Progress... 30%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02484130859375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9948431533380157\n", - "[14:01:13] Simulation (INFO) - Progress... 31%\n", - "[14:01:13] Simulation (INFO) - Progress... 31%\n", - "[14:01:13] Simulation (INFO) - Progress... 31%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02484130859375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9948396534426794\n", - "[14:01:13] Simulation (INFO) - Progress... 31%\n", - "[14:01:13] Simulation (INFO) - Progress... 31%\n", - "[14:01:13] Simulation (INFO) - Progress... 31%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.0257568359375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9946407082338863\n", - "[14:01:13] Simulation (INFO) - Progress... 32%\n", - "[14:01:13] Simulation (INFO) - Progress... 32%\n", - "[14:01:13] Simulation (INFO) - Progress... 32%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.026947021484375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9939915775333915\n", - "[14:01:13] Simulation (INFO) - Progress... 32%\n", - "[14:01:13] Simulation (INFO) - Progress... 32%\n", - "[14:01:13] Simulation (INFO) - Progress... 32%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.02850341796875\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9930726984365036\n", - "[14:01:13] Simulation (INFO) - Progress... 33%\n", - "[14:01:13] Simulation (INFO) - Progress... 33%\n", - "[14:01:13] Simulation (INFO) - Progress... 33%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.029144287109375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9925894686689639\n", - "[14:01:13] Simulation (INFO) - Progress... 33%\n", - "[14:01:13] Simulation (INFO) - Progress... 33%\n", - "[14:01:13] Simulation (INFO) - Progress... 33%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.030609130859375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9922594965497078\n", - "[14:01:13] Simulation (INFO) - Progress... 34%\n", - "[14:01:13] Simulation (INFO) - Progress... 34%\n", - "[14:01:13] Simulation (INFO) - Progress... 34%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.036590576171875\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161947\n", - "[14:01:13] Simulation (INFO) - Progress... 34%\n", - "[14:01:13] Simulation (INFO) - Progress... 34%\n", - "[14:01:13] Simulation (INFO) - Progress... 34%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038421630859375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161949\n", - "[14:01:13] Simulation (INFO) - Progress... 35%\n", - "[14:01:13] Simulation (INFO) - Progress... 35%\n", - "[14:01:13] Simulation (INFO) - Progress... 35%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038421630859375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", - "[14:01:13] Simulation (INFO) - Progress... 35%\n", - "[14:01:13] Simulation (INFO) - Progress... 35%\n", - "[14:01:13] Simulation (INFO) - Progress... 35%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038421630859375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", - "[14:01:13] Simulation (INFO) - Progress... 36%\n", - "[14:01:13] Simulation (INFO) - Progress... 36%\n", - "[14:01:13] Simulation (INFO) - Progress... 36%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038421630859375\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", - "[14:01:13] Simulation (INFO) - Progress... 36%\n", - "[14:01:13] Simulation (INFO) - Progress... 36%\n", - "[14:01:13] Simulation (INFO) - Progress... 36%\n", - "[14:01:13] Simulation (INFO) - Progress... 37%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038482666015625\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", - "[14:01:13] Simulation (INFO) - Progress... 37%\n", - "[14:01:13] Simulation (INFO) - Progress... 37%\n", - "[14:01:13] Simulation (INFO) - Progress... 37%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038604736328125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", - "[14:01:13] Simulation (INFO) - Progress... 37%\n", - "[14:01:13] Simulation (INFO) - Progress... 37%\n", - "[14:01:13] Simulation (INFO) - Progress... 38%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.038848876953125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", - "[14:01:13] Simulation (INFO) - Progress... 38%\n", - "[14:01:13] Simulation (INFO) - Progress... 38%\n", - "[14:01:13] Simulation (INFO) - Progress... 38%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.039337158203125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9917746740161948\n", - "[14:01:13] Simulation (INFO) - Progress... 38%\n", - "[14:01:13] Simulation (INFO) - Progress... 38%\n", - "[14:01:13] Simulation (INFO) - Progress... 39%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.040069580078125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9914013449577964\n", - "[14:01:13] Simulation (INFO) - Progress... 39%\n", - "[14:01:13] Simulation (INFO) - Progress... 39%\n", - "[14:01:13] Simulation (INFO) - Progress... 39%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.04107666015625\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9909200464032397\n", - "[14:01:13] Simulation (INFO) - Progress... 39%\n", - "[14:01:13] Simulation (INFO) - Progress... 40%\n", - "[14:01:13] Simulation (INFO) - Progress... 40%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.04278564453125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9909200464032397\n", - "[14:01:13] Simulation (INFO) - Progress... 40%\n", - "[14:01:13] Simulation (INFO) - Progress... 40%\n", - "[14:01:13] Simulation (INFO) - Progress... 40%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.045379638671875\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9904815074905157\n", - "[14:01:13] Simulation (INFO) - Progress... 40%\n", - "[14:01:13] Simulation (INFO) - Progress... 41%\n", - "[14:01:13] Simulation (INFO) - Progress... 41%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.049224853515625\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9895385170678038\n", - "[14:01:13] Simulation (INFO) - Progress... 41%\n", - "[14:01:13] Simulation (INFO) - Progress... 41%\n", - "[14:01:13] Simulation (INFO) - Progress... 41%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.054351806640625\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9893005128956965\n", - "[14:01:13] Simulation (INFO) - Progress... 41%\n", - "[14:01:13] Simulation (INFO) - Progress... 42%\n", - "[14:01:13] Simulation (INFO) - Progress... 42%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.059844970703125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.98888820372519\n", - "[14:01:13] Simulation (INFO) - Progress... 42%\n", - "[14:01:13] Simulation (INFO) - Progress... 42%\n", - "[14:01:13] Simulation (INFO) - Progress... 42%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.068878173828125\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9879401995661465\n", - "[14:01:13] Simulation (INFO) - Progress... 42%\n", - "[14:01:13] Simulation (INFO) - Progress... 43%\n", - "[14:01:13] Simulation (INFO) - Progress... 43%\n", - "[14:01:13] MPS (INFO) - MPS size (MiB)=0.075836181640625\n", - "[14:01:13] MPS (INFO) - MPS fidelity=0.9870682591461779\n", - "[14:01:13] Simulation (INFO) - Progress... 43%\n", - "[14:01:13] Simulation (INFO) - Progress... 43%\n", - "[14:01:13] Simulation (INFO) - Progress... 43%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.075836181640625\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9870682591461779\n", - "[14:01:14] Simulation (INFO) - Progress... 43%\n", - "[14:01:14] Simulation (INFO) - Progress... 44%\n", - "[14:01:14] Simulation (INFO) - Progress... 44%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.075836181640625\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9870682591461779\n", - "[14:01:14] Simulation (INFO) - Progress... 44%\n", - "[14:01:14] Simulation (INFO) - Progress... 44%\n", - "[14:01:14] Simulation (INFO) - Progress... 44%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.075836181640625\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9870682591461779\n", - "[14:01:14] Simulation (INFO) - Progress... 44%\n", - "[14:01:14] Simulation (INFO) - Progress... 45%\n", - "[14:01:14] Simulation (INFO) - Progress... 45%\n", - "[14:01:14] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:14] MPS (INFO) - Fidelity before optimisation=0.9870682591461779\n", - "[14:01:14] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:14] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9885243877420532\n", - "[14:01:14] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:14] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9885675777883345\n", - "[14:01:14] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:14] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9885807735732146\n", - "[14:01:14] MPS (INFO) - Final fidelity after optimisation=0.9885807735732146\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.075836181640625\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732146\n", - "[14:01:14] Simulation (INFO) - Progress... 45%\n", - "[14:01:14] Simulation (INFO) - Progress... 45%\n", - "[14:01:14] Simulation (INFO) - Progress... 45%\n", - "[14:01:14] Simulation (INFO) - Progress... 45%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.075897216796875\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732146\n", - "[14:01:14] Simulation (INFO) - Progress... 46%\n", - "[14:01:14] Simulation (INFO) - Progress... 46%\n", - "[14:01:14] Simulation (INFO) - Progress... 46%\n", - "[14:01:14] Simulation (INFO) - Progress... 46%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.076019287109375\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732146\n", - "[14:01:14] Simulation (INFO) - Progress... 46%\n", - "[14:01:14] Simulation (INFO) - Progress... 46%\n", - "[14:01:14] Simulation (INFO) - Progress... 47%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.076019287109375\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732146\n", - "[14:01:14] Simulation (INFO) - Progress... 47%\n", - "[14:01:14] Simulation (INFO) - Progress... 47%\n", - "[14:01:14] Simulation (INFO) - Progress... 47%\n", - "[14:01:14] Simulation (INFO) - Progress... 47%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.076263427734375\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732146\n", - "[14:01:14] Simulation (INFO) - Progress... 47%\n", - "[14:01:14] Simulation (INFO) - Progress... 48%\n", - "[14:01:14] Simulation (INFO) - Progress... 48%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.076629638671875\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732145\n", - "[14:01:14] Simulation (INFO) - Progress... 48%\n", - "[14:01:14] Simulation (INFO) - Progress... 48%\n", - "[14:01:14] Simulation (INFO) - Progress... 48%\n", - "[14:01:14] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:14] MPS (INFO) - Fidelity before optimisation=0.9885807735732145\n", - "[14:01:14] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:14] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9885807735732146\n", - "[14:01:14] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:14] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9885807735732127\n", - "[14:01:14] MPS (INFO) - Final fidelity after optimisation=0.9885807735732127\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.076629638671875\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732127\n", - "[14:01:14] Simulation (INFO) - Progress... 48%\n", - "[14:01:14] Simulation (INFO) - Progress... 49%\n", - "[14:01:14] Simulation (INFO) - Progress... 49%\n", - "[14:01:14] Simulation (INFO) - Progress... 49%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.077117919921875\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9885807735732127\n", - "[14:01:14] Simulation (INFO) - Progress... 49%\n", - "[14:01:14] Simulation (INFO) - Progress... 49%\n", - "[14:01:14] Simulation (INFO) - Progress... 50%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.077117919921875\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9885437435962636\n", - "[14:01:14] Simulation (INFO) - Progress... 50%\n", - "[14:01:14] Simulation (INFO) - Progress... 50%\n", - "[14:01:14] Simulation (INFO) - Progress... 50%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.077850341796875\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9876299149488592\n", - "[14:01:14] Simulation (INFO) - Progress... 50%\n", - "[14:01:14] Simulation (INFO) - Progress... 50%\n", - "[14:01:14] Simulation (INFO) - Progress... 51%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.079193115234375\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9876299149488592\n", - "[14:01:14] Simulation (INFO) - Progress... 51%\n", - "[14:01:14] Simulation (INFO) - Progress... 51%\n", - "[14:01:14] Simulation (INFO) - Progress... 51%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.080902099609375\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9876299149488592\n", - "[14:01:14] Simulation (INFO) - Progress... 51%\n", - "[14:01:14] Simulation (INFO) - Progress... 51%\n", - "[14:01:14] Simulation (INFO) - Progress... 52%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.083343505859375\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9872256262985958\n", - "[14:01:14] Simulation (INFO) - Progress... 52%\n", - "[14:01:14] Simulation (INFO) - Progress... 52%\n", - "[14:01:14] Simulation (INFO) - Progress... 52%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.08380126953125\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9867042570373467\n", - "[14:01:14] Simulation (INFO) - Progress... 52%\n", - "[14:01:14] Simulation (INFO) - Progress... 52%\n", - "[14:01:14] Simulation (INFO) - Progress... 53%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.08624267578125\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9860074263824546\n", - "[14:01:14] Simulation (INFO) - Progress... 53%\n", - "[14:01:14] Simulation (INFO) - Progress... 53%\n", - "[14:01:14] Simulation (INFO) - Progress... 53%\n", - "[14:01:14] MPS (INFO) - MPS size (MiB)=0.08868408203125\n", - "[14:01:14] MPS (INFO) - MPS fidelity=0.9857877466399374\n", - "[14:01:14] Simulation (INFO) - Progress... 53%\n", - "[14:01:14] Simulation (INFO) - Progress... 53%\n", - "[14:01:14] Simulation (INFO) - Progress... 54%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.09307861328125\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9853289590697893\n", - "[14:01:15] Simulation (INFO) - Progress... 54%\n", - "[14:01:15] Simulation (INFO) - Progress... 54%\n", - "[14:01:15] Simulation (INFO) - Progress... 54%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.09674072265625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9847593868171541\n", - "[14:01:15] Simulation (INFO) - Progress... 54%\n", - "[14:01:15] Simulation (INFO) - Progress... 54%\n", - "[14:01:15] Simulation (INFO) - Progress... 55%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.10498046875\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9839376637463282\n", - "[14:01:15] Simulation (INFO) - Progress... 55%\n", - "[14:01:15] Simulation (INFO) - Progress... 55%\n", - "[14:01:15] Simulation (INFO) - Progress... 55%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.110107421875\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.983070631353325\n", - "[14:01:15] Simulation (INFO) - Progress... 55%\n", - "[14:01:15] Simulation (INFO) - Progress... 55%\n", - "[14:01:15] Simulation (INFO) - Progress... 56%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.12353515625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9820965437215268\n", - "[14:01:15] Simulation (INFO) - Progress... 56%\n", - "[14:01:15] Simulation (INFO) - Progress... 56%\n", - "[14:01:15] Simulation (INFO) - Progress... 56%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.13287353515625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9813700217282061\n", - "[14:01:15] Simulation (INFO) - Progress... 56%\n", - "[14:01:15] Simulation (INFO) - Progress... 56%\n", - "[14:01:15] Simulation (INFO) - Progress... 57%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.15484619140625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9806263554164852\n", - "[14:01:15] Simulation (INFO) - Progress... 57%\n", - "[14:01:15] Simulation (INFO) - Progress... 57%\n", - "[14:01:15] Simulation (INFO) - Progress... 57%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.16436767578125\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9796957281177572\n", - "[14:01:15] Simulation (INFO) - Progress... 57%\n", - "[14:01:15] Simulation (INFO) - Progress... 57%\n", - "[14:01:15] Simulation (INFO) - Progress... 58%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.190460205078125\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9787753802493907\n", - "[14:01:15] Simulation (INFO) - Progress... 58%\n", - "[14:01:15] Simulation (INFO) - Progress... 58%\n", - "[14:01:15] Simulation (INFO) - Progress... 58%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.204498291015625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9779191475648064\n", - "[14:01:15] Simulation (INFO) - Progress... 58%\n", - "[14:01:15] Simulation (INFO) - Progress... 58%\n", - "[14:01:15] Simulation (INFO) - Progress... 59%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.227935791015625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9770858154529012\n", - "[14:01:15] Simulation (INFO) - Progress... 59%\n", - "[14:01:15] Simulation (INFO) - Progress... 59%\n", - "[14:01:15] Simulation (INFO) - Progress... 59%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.250579833984375\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9763732931498061\n", - "[14:01:15] Simulation (INFO) - Progress... 59%\n", - "[14:01:15] Simulation (INFO) - Progress... 60%\n", - "[14:01:15] Simulation (INFO) - Progress... 60%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.298919677734375\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.975569858851943\n", - "[14:01:15] Simulation (INFO) - Progress... 60%\n", - "[14:01:15] Simulation (INFO) - Progress... 60%\n", - "[14:01:15] Simulation (INFO) - Progress... 60%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.302093505859375\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.97482766334186\n", - "[14:01:15] Simulation (INFO) - Progress... 60%\n", - "[14:01:15] Simulation (INFO) - Progress... 61%\n", - "[14:01:15] Simulation (INFO) - Progress... 61%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.334991455078125\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", - "[14:01:15] Simulation (INFO) - Progress... 61%\n", - "[14:01:15] Simulation (INFO) - Progress... 61%\n", - "[14:01:15] Simulation (INFO) - Progress... 61%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.334991455078125\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", - "[14:01:15] Simulation (INFO) - Progress... 61%\n", - "[14:01:15] Simulation (INFO) - Progress... 62%\n", - "[14:01:15] Simulation (INFO) - Progress... 62%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", - "[14:01:15] Simulation (INFO) - Progress... 62%\n", - "[14:01:15] Simulation (INFO) - Progress... 62%\n", - "[14:01:15] Simulation (INFO) - Progress... 62%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", - "[14:01:15] Simulation (INFO) - Progress... 62%\n", - "[14:01:15] Simulation (INFO) - Progress... 63%\n", - "[14:01:15] Simulation (INFO) - Progress... 63%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", - "[14:01:15] Simulation (INFO) - Progress... 63%\n", - "[14:01:15] Simulation (INFO) - Progress... 63%\n", - "[14:01:15] Simulation (INFO) - Progress... 63%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", - "[14:01:15] Simulation (INFO) - Progress... 63%\n", - "[14:01:15] Simulation (INFO) - Progress... 64%\n", - "[14:01:15] Simulation (INFO) - Progress... 64%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892826\n", - "[14:01:15] Simulation (INFO) - Progress... 64%\n", - "[14:01:15] Simulation (INFO) - Progress... 64%\n", - "[14:01:15] Simulation (INFO) - Progress... 64%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", - "[14:01:15] Simulation (INFO) - Progress... 64%\n", - "[14:01:15] Simulation (INFO) - Progress... 65%\n", - "[14:01:15] Simulation (INFO) - Progress... 65%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", - "[14:01:15] Simulation (INFO) - Progress... 65%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.339019775390625\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9740459735892828\n", - "[14:01:15] Simulation (INFO) - Progress... 65%\n", - "[14:01:15] Simulation (INFO) - Progress... 65%\n", - "[14:01:15] Simulation (INFO) - Progress... 65%\n", - "[14:01:15] MPS (INFO) - MPS size (MiB)=0.340118408203125\n", - "[14:01:15] MPS (INFO) - MPS fidelity=0.9735596675843919\n", - "[14:01:15] Simulation (INFO) - Progress... 66%\n", - "[14:01:15] Simulation (INFO) - Progress... 66%\n", - "[14:01:15] Simulation (INFO) - Progress... 66%\n", - "[14:01:15] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:15] MPS (INFO) - Fidelity before optimisation=0.9735596675843919\n", - "[14:01:15] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:15] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9843516935412071\n", - "[14:01:15] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:15] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9848064428508081\n", - "[14:01:15] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:16] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9849304313856563\n", - "[14:01:16] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:16] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9849873035247502\n", - "[14:01:16] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:16] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9850185604266666\n", - "[14:01:16] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:16] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9850377284486574\n", - "[14:01:16] MPS (INFO) - Final fidelity after optimisation=0.9850377284486574\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.3406982421875\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9844304521445316\n", - "[14:01:16] Simulation (INFO) - Progress... 66%\n", - "[14:01:16] Simulation (INFO) - Progress... 66%\n", - "[14:01:16] Simulation (INFO) - Progress... 66%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.341339111328125\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9836453925132829\n", - "[14:01:16] Simulation (INFO) - Progress... 67%\n", - "[14:01:16] Simulation (INFO) - Progress... 67%\n", - "[14:01:16] Simulation (INFO) - Progress... 67%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.344635009765625\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9827521125621543\n", - "[14:01:16] Simulation (INFO) - Progress... 67%\n", - "[14:01:16] Simulation (INFO) - Progress... 67%\n", - "[14:01:16] Simulation (INFO) - Progress... 67%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.352752685546875\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9819763508005264\n", - "[14:01:16] Simulation (INFO) - Progress... 68%\n", - "[14:01:16] Simulation (INFO) - Progress... 68%\n", - "[14:01:16] Simulation (INFO) - Progress... 68%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.366485595703125\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9810077310496189\n", - "[14:01:16] Simulation (INFO) - Progress... 68%\n", - "[14:01:16] Simulation (INFO) - Progress... 68%\n", - "[14:01:16] Simulation (INFO) - Progress... 68%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.394256591796875\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9802244249339466\n", - "[14:01:16] Simulation (INFO) - Progress... 69%\n", - "[14:01:16] Simulation (INFO) - Progress... 69%\n", - "[14:01:16] Simulation (INFO) - Progress... 69%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.4154052734375\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9793217534714646\n", - "[14:01:16] Simulation (INFO) - Progress... 69%\n", - "[14:01:16] Simulation (INFO) - Progress... 69%\n", - "[14:01:16] Simulation (INFO) - Progress... 70%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.4249267578125\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9783526934928921\n", - "[14:01:16] Simulation (INFO) - Progress... 70%\n", - "[14:01:16] Simulation (INFO) - Progress... 70%\n", - "[14:01:16] Simulation (INFO) - Progress... 70%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.4468994140625\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9773801381930487\n", - "[14:01:16] Simulation (INFO) - Progress... 70%\n", - "[14:01:16] Simulation (INFO) - Progress... 70%\n", - "[14:01:16] Simulation (INFO) - Progress... 71%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.49566650390625\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9764511340458201\n", - "[14:01:16] Simulation (INFO) - Progress... 71%\n", - "[14:01:16] Simulation (INFO) - Progress... 71%\n", - "[14:01:16] Simulation (INFO) - Progress... 71%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9756161361580449\n", - "[14:01:16] Simulation (INFO) - Progress... 71%\n", - "[14:01:16] Simulation (INFO) - Progress... 71%\n", - "[14:01:16] Simulation (INFO) - Progress... 72%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9756161361580449\n", - "[14:01:16] Simulation (INFO) - Progress... 72%\n", - "[14:01:16] Simulation (INFO) - Progress... 72%\n", - "[14:01:16] Simulation (INFO) - Progress... 72%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9756161361580449\n", - "[14:01:16] Simulation (INFO) - Progress... 72%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9756161361580449\n", - "[14:01:16] Simulation (INFO) - Progress... 72%\n", - "[14:01:16] Simulation (INFO) - Progress... 73%\n", - "[14:01:16] Simulation (INFO) - Progress... 73%\n", - "[14:01:16] Simulation (INFO) - Progress... 73%\n", - "[14:01:16] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", - "[14:01:16] MPS (INFO) - MPS fidelity=0.9756161361580449\n", - "[14:01:16] Simulation (INFO) - Progress... 73%\n", - "[14:01:17] Simulation (INFO) - Progress... 73%\n", - "[14:01:17] Simulation (INFO) - Progress... 73%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.51324462890625\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9756161361580449\n", - "[14:01:17] Simulation (INFO) - Progress... 74%\n", - "[14:01:17] Simulation (INFO) - Progress... 74%\n", - "[14:01:17] Simulation (INFO) - Progress... 74%\n", - "[14:01:17] Simulation (INFO) - Progress... 74%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.513641357421875\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9756161361580449\n", - "[14:01:17] Simulation (INFO) - Progress... 74%\n", - "[14:01:17] Simulation (INFO) - Progress... 74%\n", - "[14:01:17] Simulation (INFO) - Progress... 75%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.513641357421875\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9756161361580449\n", - "[14:01:17] Simulation (INFO) - Progress... 75%\n", - "[14:01:17] Simulation (INFO) - Progress... 75%\n", - "[14:01:17] Simulation (INFO) - Progress... 75%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.51556396484375\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9748529131568249\n", - "[14:01:17] Simulation (INFO) - Progress... 75%\n", - "[14:01:17] Simulation (INFO) - Progress... 75%\n", - "[14:01:17] Simulation (INFO) - Progress... 76%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.51556396484375\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9748529131568249\n", - "[14:01:17] Simulation (INFO) - Progress... 76%\n", - "[14:01:17] Simulation (INFO) - Progress... 76%\n", - "[14:01:17] Simulation (INFO) - Progress... 76%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.51983642578125\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9742120807068472\n", - "[14:01:17] Simulation (INFO) - Progress... 76%\n", - "[14:01:17] Simulation (INFO) - Progress... 76%\n", - "[14:01:17] Simulation (INFO) - Progress... 77%\n", - "[14:01:17] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:17] MPS (INFO) - Fidelity before optimisation=0.9742120807068472\n", - "[14:01:17] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:17] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.975134847725609\n", - "[14:01:17] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:17] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9751851737337295\n", - "[14:01:17] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:17] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9752028508364771\n", - "[14:01:17] MPS (INFO) - Final fidelity after optimisation=0.9752028508364771\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.522216796875\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9747413039963226\n", - "[14:01:17] Simulation (INFO) - Progress... 77%\n", - "[14:01:17] Simulation (INFO) - Progress... 77%\n", - "[14:01:17] Simulation (INFO) - Progress... 77%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.530548095703125\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9737709267327275\n", - "[14:01:17] Simulation (INFO) - Progress... 77%\n", - "[14:01:17] Simulation (INFO) - Progress... 77%\n", - "[14:01:17] Simulation (INFO) - Progress... 78%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.542144775390625\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9730980087583336\n", - "[14:01:17] Simulation (INFO) - Progress... 78%\n", - "[14:01:17] Simulation (INFO) - Progress... 78%\n", - "[14:01:17] Simulation (INFO) - Progress... 78%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.555572509765625\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.972274693739747\n", - "[14:01:17] Simulation (INFO) - Progress... 78%\n", - "[14:01:17] Simulation (INFO) - Progress... 78%\n", - "[14:01:17] Simulation (INFO) - Progress... 79%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.58514404296875\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9714008152517145\n", - "[14:01:17] Simulation (INFO) - Progress... 79%\n", - "[14:01:17] Simulation (INFO) - Progress... 79%\n", - "[14:01:17] Simulation (INFO) - Progress... 79%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.5892333984375\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9705196501761234\n", - "[14:01:17] Simulation (INFO) - Progress... 79%\n", - "[14:01:17] Simulation (INFO) - Progress... 80%\n", - "[14:01:17] Simulation (INFO) - Progress... 80%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.63214111328125\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9697148601428947\n", - "[14:01:17] Simulation (INFO) - Progress... 80%\n", - "[14:01:17] Simulation (INFO) - Progress... 80%\n", - "[14:01:17] Simulation (INFO) - Progress... 80%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.65301513671875\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9688374595301655\n", - "[14:01:17] Simulation (INFO) - Progress... 80%\n", - "[14:01:17] Simulation (INFO) - Progress... 81%\n", - "[14:01:17] Simulation (INFO) - Progress... 81%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.731292724609375\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9679729595082353\n", - "[14:01:17] Simulation (INFO) - Progress... 81%\n", - "[14:01:17] Simulation (INFO) - Progress... 81%\n", - "[14:01:17] Simulation (INFO) - Progress... 81%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.756011962890625\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9670817720186894\n", - "[14:01:17] Simulation (INFO) - Progress... 81%\n", - "[14:01:17] Simulation (INFO) - Progress... 82%\n", - "[14:01:17] Simulation (INFO) - Progress... 82%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.851715087890625\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.9662304487130915\n", - "[14:01:17] Simulation (INFO) - Progress... 82%\n", - "[14:01:17] Simulation (INFO) - Progress... 82%\n", - "[14:01:17] Simulation (INFO) - Progress... 82%\n", - "[14:01:17] MPS (INFO) - MPS size (MiB)=0.903900146484375\n", - "[14:01:17] MPS (INFO) - MPS fidelity=0.96530121346801\n", - "[14:01:17] Simulation (INFO) - Progress... 82%\n", - "[14:01:17] Simulation (INFO) - Progress... 83%\n", - "[14:01:17] Simulation (INFO) - Progress... 83%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.074066162109375\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9644645141858508\n", - "[14:01:18] Simulation (INFO) - Progress... 83%\n", - "[14:01:18] Simulation (INFO) - Progress... 83%\n", - "[14:01:18] Simulation (INFO) - Progress... 83%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.13128662109375\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.963576178040663\n", - "[14:01:18] Simulation (INFO) - Progress... 83%\n", - "[14:01:18] Simulation (INFO) - Progress... 84%\n", - "[14:01:18] Simulation (INFO) - Progress... 84%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.375518798828125\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9627241232539026\n", - "[14:01:18] Simulation (INFO) - Progress... 84%\n", - "[14:01:18] Simulation (INFO) - Progress... 84%\n", - "[14:01:18] Simulation (INFO) - Progress... 84%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.4351806640625\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9617990818895198\n", - "[14:01:18] Simulation (INFO) - Progress... 84%\n", - "[14:01:18] Simulation (INFO) - Progress... 85%\n", - "[14:01:18] Simulation (INFO) - Progress... 85%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.738677978515625\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9610190784537106\n", - "[14:01:18] Simulation (INFO) - Progress... 85%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", - "[14:01:18] Simulation (INFO) - Progress... 85%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", - "[14:01:18] Simulation (INFO) - Progress... 85%\n", - "[14:01:18] Simulation (INFO) - Progress... 85%\n", - "[14:01:18] Simulation (INFO) - Progress... 86%\n", - "[14:01:18] Simulation (INFO) - Progress... 86%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877096\n", - "[14:01:18] Simulation (INFO) - Progress... 86%\n", - "[14:01:18] Simulation (INFO) - Progress... 86%\n", - "[14:01:18] Simulation (INFO) - Progress... 86%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", - "[14:01:18] Simulation (INFO) - Progress... 86%\n", - "[14:01:18] Simulation (INFO) - Progress... 87%\n", - "[14:01:18] Simulation (INFO) - Progress... 87%\n", - "[14:01:18] Simulation (INFO) - Progress... 87%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", - "[14:01:18] Simulation (INFO) - Progress... 87%\n", - "[14:01:18] Simulation (INFO) - Progress... 87%\n", - "[14:01:18] Simulation (INFO) - Progress... 87%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75592041015625\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", - "[14:01:18] Simulation (INFO) - Progress... 88%\n", - "[14:01:18] Simulation (INFO) - Progress... 88%\n", - "[14:01:18] Simulation (INFO) - Progress... 88%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75701904296875\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", - "[14:01:18] Simulation (INFO) - Progress... 88%\n", - "[14:01:18] Simulation (INFO) - Progress... 88%\n", - "[14:01:18] Simulation (INFO) - Progress... 88%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75701904296875\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9602283439877098\n", - "[14:01:18] Simulation (INFO) - Progress... 89%\n", - "[14:01:18] Simulation (INFO) - Progress... 89%\n", - "[14:01:18] Simulation (INFO) - Progress... 89%\n", - "[14:01:18] MPS (INFO) - MPS size (MiB)=1.75872802734375\n", - "[14:01:18] MPS (INFO) - MPS fidelity=0.9596341716247032\n", - "[14:01:18] Simulation (INFO) - Progress... 89%\n", - "[14:01:18] Simulation (INFO) - Progress... 89%\n", - "[14:01:18] Simulation (INFO) - Progress... 90%\n", - "[14:01:18] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:18] MPS (INFO) - Fidelity before optimisation=0.9596341716247032\n", - "[14:01:18] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:18] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9700420977488123\n", - "[14:01:18] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:18] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9703519467112257\n", - "[14:01:18] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:18] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9704374405302711\n", - "[14:01:18] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:19] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9704739545165699\n", - "[14:01:19] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:19] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9704925687713485\n", - "[14:01:19] MPS (INFO) - Final fidelity after optimisation=0.9704925687713485\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=1.75872802734375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9704925687713485\n", - "[14:01:19] Simulation (INFO) - Progress... 90%\n", - "[14:01:19] Simulation (INFO) - Progress... 90%\n", - "[14:01:19] Simulation (INFO) - Progress... 90%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=1.765777587890625\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9696216444258031\n", - "[14:01:19] Simulation (INFO) - Progress... 90%\n", - "[14:01:19] Simulation (INFO) - Progress... 90%\n", - "[14:01:19] Simulation (INFO) - Progress... 91%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=1.77117919921875\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9696216444258033\n", - "[14:01:19] Simulation (INFO) - Progress... 91%\n", - "[14:01:19] Simulation (INFO) - Progress... 91%\n", - "[14:01:19] Simulation (INFO) - Progress... 91%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=1.79656982421875\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9687315620521755\n", - "[14:01:19] Simulation (INFO) - Progress... 91%\n", - "[14:01:19] Simulation (INFO) - Progress... 91%\n", - "[14:01:19] Simulation (INFO) - Progress... 92%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=1.84222412109375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9679596222596152\n", - "[14:01:19] Simulation (INFO) - Progress... 92%\n", - "[14:01:19] Simulation (INFO) - Progress... 92%\n", - "[14:01:19] Simulation (INFO) - Progress... 92%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=1.870208740234375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9670763677407406\n", - "[14:01:19] Simulation (INFO) - Progress... 92%\n", - "[14:01:19] Simulation (INFO) - Progress... 92%\n", - "[14:01:19] Simulation (INFO) - Progress... 93%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=1.940521240234375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9661194670712572\n", - "[14:01:19] Simulation (INFO) - Progress... 93%\n", - "[14:01:19] Simulation (INFO) - Progress... 93%\n", - "[14:01:19] Simulation (INFO) - Progress... 93%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=1.999114990234375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9652231632846195\n", - "[14:01:19] Simulation (INFO) - Progress... 93%\n", - "[14:01:19] Simulation (INFO) - Progress... 93%\n", - "[14:01:19] Simulation (INFO) - Progress... 94%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=2.234954833984375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9642981707017143\n", - "[14:01:19] Simulation (INFO) - Progress... 94%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", - "[14:01:19] Simulation (INFO) - Progress... 94%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", - "[14:01:19] Simulation (INFO) - Progress... 94%\n", - "[14:01:19] Simulation (INFO) - Progress... 94%\n", - "[14:01:19] Simulation (INFO) - Progress... 94%\n", - "[14:01:19] Simulation (INFO) - Progress... 95%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", - "[14:01:19] Simulation (INFO) - Progress... 95%\n", - "[14:01:19] Simulation (INFO) - Progress... 95%\n", - "[14:01:19] Simulation (INFO) - Progress... 95%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", - "[14:01:19] Simulation (INFO) - Progress... 95%\n", - "[14:01:19] Simulation (INFO) - Progress... 95%\n", - "[14:01:19] Simulation (INFO) - Progress... 96%\n", - "[14:01:19] Simulation (INFO) - Progress... 96%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", - "[14:01:19] Simulation (INFO) - Progress... 96%\n", - "[14:01:19] Simulation (INFO) - Progress... 96%\n", - "[14:01:19] Simulation (INFO) - Progress... 96%\n", - "[14:01:19] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", - "[14:01:19] MPS (INFO) - MPS fidelity=0.9633776196448067\n", - "[14:01:19] Simulation (INFO) - Progress... 96%\n", - "[14:01:19] Simulation (INFO) - Progress... 97%\n", - "[14:01:19] Simulation (INFO) - Progress... 97%\n", - "[14:01:19] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:19] MPS (INFO) - Fidelity before optimisation=0.9633776196448067\n", - "[14:01:19] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.967414171617126\n", - "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.967533278009993\n", - "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9675675092087742\n", - "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9675846146681976\n", - "[14:01:20] MPS (INFO) - Final fidelity after optimisation=0.9675846146681976\n", - "[14:01:20] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", - "[14:01:20] MPS (INFO) - MPS fidelity=0.9675846146681976\n", - "[14:01:20] Simulation (INFO) - Progress... 97%\n", - "[14:01:20] Simulation (INFO) - Progress... 97%\n", - "[14:01:20] Simulation (INFO) - Progress... 97%\n", - "[14:01:20] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", - "[14:01:20] MPS (INFO) - MPS fidelity=0.9675846146681976\n", - "[14:01:20] Simulation (INFO) - Progress... 97%\n", - "[14:01:20] Simulation (INFO) - Progress... 98%\n", - "[14:01:20] Simulation (INFO) - Progress... 98%\n", - "[14:01:20] MPS (INFO) - MPS size (MiB)=2.35150146484375\n", - "[14:01:20] MPS (INFO) - MPS fidelity=0.9675846146681976\n", - "[14:01:20] Simulation (INFO) - Progress... 98%\n", - "[14:01:20] Simulation (INFO) - Progress... 98%\n", - "[14:01:20] Simulation (INFO) - Progress... 98%\n", - "[14:01:20] MPS (INFO) - MPS size (MiB)=2.34918212890625\n", - "[14:01:20] MPS (INFO) - MPS fidelity=0.9667630952362768\n", - "[14:01:20] Simulation (INFO) - Progress... 98%\n", - "[14:01:20] Simulation (INFO) - Progress... 99%\n", - "[14:01:20] Simulation (INFO) - Progress... 99%\n", - "[14:01:20] MPS (INFO) - MPS size (MiB)=2.34918212890625\n", - "[14:01:20] MPS (INFO) - MPS fidelity=0.9667630952362768\n", - "[14:01:20] Simulation (INFO) - Progress... 99%\n", - "[14:01:20] MPS (INFO) - MPS size (MiB)=2.3427734375\n", - "[14:01:20] MPS (INFO) - MPS fidelity=0.9659837340863052\n", - "[14:01:20] Simulation (INFO) - Progress... 99%\n", - "[14:01:20] MPS (INFO) - MPS size (MiB)=2.3427734375\n", - "[14:01:20] MPS (INFO) - MPS fidelity=0.9659837340863054\n", - "[14:01:20] Simulation (INFO) - Progress... 99%\n", - "[14:01:20] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:20] MPS (INFO) - Fidelity before optimisation=0.9659837340863054\n", - "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9659853689734507\n", - "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9659862268998962\n", - "[14:01:20] MPS (INFO) - Final fidelity after optimisation=0.9659862268998962\n", - "[14:01:20] MPS (INFO) - Applying variational optimisation.\n", - "[14:01:20] MPS (INFO) - Fidelity before optimisation=0.9659862268998962\n", - "[14:01:20] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:21] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.965986226899895\n", - "[14:01:21] MPS (INFO) - Doing another optimisation sweep...\n", - "[14:01:21] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9659862268998954\n", - "[14:01:21] MPS (INFO) - Final fidelity after optimisation=0.9659862268998954\n", - "[14:01:21] Simulation (INFO) - Simulation completed.\n", - "[14:01:21] Simulation (INFO) - Final MPS size=2.3427734375 MiB\n", - "[14:01:21] Simulation (INFO) - Final MPS fidelity=0.9659862268998954\n" + "[15:41:45] Simulation (INFO) - Ordering the gates in the circuit to reduce canonicalisation overhead.\n", + "[15:41:45] Simulation (INFO) - Running simulation...\n", + "[15:41:45] Simulation (INFO) - Progress... 0%\n", + "[15:41:45] Simulation (INFO) - Progress... 0%\n", + "[15:41:45] Simulation (INFO) - Progress... 0%\n", + "[15:41:45] Simulation (INFO) - Progress... 0%\n", + "[15:41:45] Simulation (INFO) - Progress... 0%\n", + "[15:41:45] Simulation (INFO) - Progress... 0%\n", + "[15:41:45] Simulation (INFO) - Progress... 1%\n", + "[15:41:45] Simulation (INFO) - Progress... 1%\n", + "[15:41:45] Simulation (INFO) - Progress... 1%\n", + "[15:41:45] Simulation (INFO) - Progress... 1%\n", + "[15:41:45] Simulation (INFO) - Progress... 1%\n", + "[15:41:45] Simulation (INFO) - Progress... 1%\n", + "[15:41:45] Simulation (INFO) - Progress... 2%\n", + "[15:41:45] Simulation (INFO) - Progress... 2%\n", + "[15:41:45] Simulation (INFO) - Progress... 2%\n", + "[15:41:45] Simulation (INFO) - Progress... 2%\n", + "[15:41:45] Simulation (INFO) - Progress... 2%\n", + "[15:41:45] Simulation (INFO) - Progress... 2%\n", + "[15:41:45] Simulation (INFO) - Progress... 3%\n", + "[15:41:45] Simulation (INFO) - Progress... 3%\n", + "[15:41:45] MPS (INFO) - MPS size (MiB)=0.00067138671875\n", + "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", + "[15:41:45] Simulation (INFO) - Progress... 3%\n", + "[15:41:45] Simulation (INFO) - Progress... 3%\n", + "[15:41:45] Simulation (INFO) - Progress... 3%\n", + "[15:41:45] Simulation (INFO) - Progress... 3%\n", + "[15:41:45] MPS (INFO) - MPS size (MiB)=0.000732421875\n", + "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", + "[15:41:45] Simulation (INFO) - Progress... 4%\n", + "[15:41:45] Simulation (INFO) - Progress... 4%\n", + "[15:41:45] Simulation (INFO) - Progress... 4%\n", + "[15:41:45] MPS (INFO) - MPS size (MiB)=0.0008544921875\n", + "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", + "[15:41:45] Simulation (INFO) - Progress... 4%\n", + "[15:41:45] Simulation (INFO) - Progress... 4%\n", + "[15:41:45] Simulation (INFO) - Progress... 4%\n", + "[15:41:45] MPS (INFO) - MPS size (MiB)=0.0008544921875\n", + "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", + "[15:41:45] Simulation (INFO) - Progress... 5%\n", + "[15:41:45] Simulation (INFO) - Progress... 5%\n", + "[15:41:45] Simulation (INFO) - Progress... 5%\n", + "[15:41:45] Simulation (INFO) - Progress... 5%\n", + "[15:41:45] MPS (INFO) - MPS size (MiB)=0.00091552734375\n", + "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", + "[15:41:45] Simulation (INFO) - Progress... 5%\n", + "[15:41:45] Simulation (INFO) - Progress... 5%\n", + "[15:41:45] Simulation (INFO) - Progress... 6%\n", + "[15:41:45] MPS (INFO) - MPS size (MiB)=0.00103759765625\n", + "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", + "[15:41:45] Simulation (INFO) - Progress... 6%\n", + "[15:41:45] Simulation (INFO) - Progress... 6%\n", + "[15:41:45] Simulation (INFO) - Progress... 6%\n", + "[15:41:45] MPS (INFO) - MPS size (MiB)=0.00128173828125\n", + "[15:41:45] MPS (INFO) - MPS fidelity=1.0000000000000002\n", + "[15:41:45] Simulation (INFO) - Progress... 6%\n", + "[15:41:45] Simulation (INFO) - Progress... 6%\n", + "[15:41:45] Simulation (INFO) - Progress... 7%\n", + "[15:41:45] MPS (INFO) - MPS size (MiB)=0.00164794921875\n", + "[15:41:45] MPS (INFO) - MPS fidelity=1.0000000000000002\n", + "[15:41:45] Simulation (INFO) - Progress... 7%\n", + "[15:41:45] Simulation (INFO) - Progress... 7%\n", + "[15:41:45] Simulation (INFO) - Progress... 7%\n", + "[15:41:45] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:45] MPS (INFO) - Fidelity before optimisation=1.0000000000000002\n", + "[15:41:45] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:45] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.999999999999996\n", + "[15:41:45] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:46] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9999999999999964\n", + "[15:41:46] MPS (INFO) - Final fidelity after optimisation=0.9999999999999964\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00164794921875\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9999999999999964\n", + "[15:41:46] Simulation (INFO) - Progress... 7%\n", + "[15:41:46] Simulation (INFO) - Progress... 7%\n", + "[15:41:46] Simulation (INFO) - Progress... 8%\n", + "[15:41:46] Simulation (INFO) - Progress... 8%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.001708984375\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9999999999999964\n", + "[15:41:46] Simulation (INFO) - Progress... 8%\n", + "[15:41:46] Simulation (INFO) - Progress... 8%\n", + "[15:41:46] Simulation (INFO) - Progress... 8%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0018310546875\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9999999999999964\n", + "[15:41:46] Simulation (INFO) - Progress... 8%\n", + "[15:41:46] Simulation (INFO) - Progress... 9%\n", + "[15:41:46] Simulation (INFO) - Progress... 9%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0020751953125\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9999999999999962\n", + "[15:41:46] Simulation (INFO) - Progress... 9%\n", + "[15:41:46] Simulation (INFO) - Progress... 9%\n", + "[15:41:46] Simulation (INFO) - Progress... 9%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0025634765625\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9999999999999962\n", + "[15:41:46] Simulation (INFO) - Progress... 10%\n", + "[15:41:46] Simulation (INFO) - Progress... 10%\n", + "[15:41:46] Simulation (INFO) - Progress... 10%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0030517578125\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", + "[15:41:46] Simulation (INFO) - Progress... 10%\n", + "[15:41:46] Simulation (INFO) - Progress... 10%\n", + "[15:41:46] Simulation (INFO) - Progress... 10%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0030517578125\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", + "[15:41:46] Simulation (INFO) - Progress... 11%\n", + "[15:41:46] Simulation (INFO) - Progress... 11%\n", + "[15:41:46] Simulation (INFO) - Progress... 11%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0030517578125\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", + "[15:41:46] Simulation (INFO) - Progress... 11%\n", + "[15:41:46] Simulation (INFO) - Progress... 11%\n", + "[15:41:46] Simulation (INFO) - Progress... 11%\n", + "[15:41:46] Simulation (INFO) - Progress... 12%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00311279296875\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", + "[15:41:46] Simulation (INFO) - Progress... 12%\n", + "[15:41:46] Simulation (INFO) - Progress... 12%\n", + "[15:41:46] Simulation (INFO) - Progress... 12%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00323486328125\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", + "[15:41:46] Simulation (INFO) - Progress... 12%\n", + "[15:41:46] Simulation (INFO) - Progress... 12%\n", + "[15:41:46] Simulation (INFO) - Progress... 13%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00347900390625\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", + "[15:41:46] Simulation (INFO) - Progress... 13%\n", + "[15:41:46] Simulation (INFO) - Progress... 13%\n", + "[15:41:46] Simulation (INFO) - Progress... 13%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00396728515625\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071943\n", + "[15:41:46] Simulation (INFO) - Progress... 13%\n", + "[15:41:46] Simulation (INFO) - Progress... 13%\n", + "[15:41:46] Simulation (INFO) - Progress... 14%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00494384765625\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071943\n", + "[15:41:46] Simulation (INFO) - Progress... 14%\n", + "[15:41:46] Simulation (INFO) - Progress... 14%\n", + "[15:41:46] Simulation (INFO) - Progress... 14%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0062255859375\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9992820662866865\n", + "[15:41:46] Simulation (INFO) - Progress... 14%\n", + "[15:41:46] Simulation (INFO) - Progress... 14%\n", + "[15:41:46] Simulation (INFO) - Progress... 15%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006561279296875\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9986887177546038\n", + "[15:41:46] Simulation (INFO) - Progress... 15%\n", + "[15:41:46] Simulation (INFO) - Progress... 15%\n", + "[15:41:46] Simulation (INFO) - Progress... 15%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006561279296875\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9986887177546038\n", + "[15:41:46] Simulation (INFO) - Progress... 15%\n", + "[15:41:46] Simulation (INFO) - Progress... 15%\n", + "[15:41:46] Simulation (INFO) - Progress... 16%\n", + "[15:41:46] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:46] MPS (INFO) - Fidelity before optimisation=0.9986887177546038\n", + "[15:41:46] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:46] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9992190736072919\n", + "[15:41:46] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:46] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9992335371417129\n", + "[15:41:46] MPS (INFO) - Final fidelity after optimisation=0.9992335371417129\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006561279296875\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9992335371417129\n", + "[15:41:46] Simulation (INFO) - Progress... 16%\n", + "[15:41:46] Simulation (INFO) - Progress... 16%\n", + "[15:41:46] Simulation (INFO) - Progress... 16%\n", + "[15:41:46] Simulation (INFO) - Progress... 16%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006622314453125\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9992335371417131\n", + "[15:41:46] Simulation (INFO) - Progress... 16%\n", + "[15:41:46] Simulation (INFO) - Progress... 17%\n", + "[15:41:46] Simulation (INFO) - Progress... 17%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006744384765625\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9992335371417131\n", + "[15:41:46] Simulation (INFO) - Progress... 17%\n", + "[15:41:46] Simulation (INFO) - Progress... 17%\n", + "[15:41:46] Simulation (INFO) - Progress... 17%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006988525390625\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9992335371417131\n", + "[15:41:46] Simulation (INFO) - Progress... 17%\n", + "[15:41:46] Simulation (INFO) - Progress... 18%\n", + "[15:41:46] Simulation (INFO) - Progress... 18%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.007476806640625\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9992335371417131\n", + "[15:41:46] Simulation (INFO) - Progress... 18%\n", + "[15:41:46] Simulation (INFO) - Progress... 18%\n", + "[15:41:46] Simulation (INFO) - Progress... 18%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.007476806640625\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9982345023466558\n", + "[15:41:46] Simulation (INFO) - Progress... 18%\n", + "[15:41:46] Simulation (INFO) - Progress... 19%\n", + "[15:41:46] Simulation (INFO) - Progress... 19%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.008209228515625\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9975515249441151\n", + "[15:41:46] Simulation (INFO) - Progress... 19%\n", + "[15:41:46] Simulation (INFO) - Progress... 19%\n", + "[15:41:46] Simulation (INFO) - Progress... 19%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00860595703125\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9967787323351995\n", + "[15:41:46] Simulation (INFO) - Progress... 20%\n", + "[15:41:46] Simulation (INFO) - Progress... 20%\n", + "[15:41:46] Simulation (INFO) - Progress... 20%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00958251953125\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.996089833981607\n", + "[15:41:46] Simulation (INFO) - Progress... 20%\n", + "[15:41:46] Simulation (INFO) - Progress... 20%\n", + "[15:41:46] Simulation (INFO) - Progress... 20%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.996089833981607\n", + "[15:41:46] Simulation (INFO) - Progress... 21%\n", + "[15:41:46] Simulation (INFO) - Progress... 21%\n", + "[15:41:46] Simulation (INFO) - Progress... 21%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9960898339816068\n", + "[15:41:46] Simulation (INFO) - Progress... 21%\n", + "[15:41:46] Simulation (INFO) - Progress... 21%\n", + "[15:41:46] Simulation (INFO) - Progress... 21%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9960898339816068\n", + "[15:41:46] Simulation (INFO) - Progress... 22%\n", + "[15:41:46] Simulation (INFO) - Progress... 22%\n", + "[15:41:46] Simulation (INFO) - Progress... 22%\n", + "[15:41:46] Simulation (INFO) - Progress... 22%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9955765571586642\n", + "[15:41:46] Simulation (INFO) - Progress... 22%\n", + "[15:41:46] Simulation (INFO) - Progress... 22%\n", + "[15:41:46] Simulation (INFO) - Progress... 23%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.01007080078125\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9955765571586642\n", + "[15:41:46] Simulation (INFO) - Progress... 23%\n", + "[15:41:46] Simulation (INFO) - Progress... 23%\n", + "[15:41:46] Simulation (INFO) - Progress... 23%\n", + "[15:41:46] MPS (INFO) - MPS size (MiB)=0.01031494140625\n", + "[15:41:46] MPS (INFO) - MPS fidelity=0.9955765571586642\n", + "[15:41:46] Simulation (INFO) - Progress... 23%\n", + "[15:41:46] Simulation (INFO) - Progress... 23%\n", + "[15:41:46] Simulation (INFO) - Progress... 24%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01080322265625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9955765571586642\n", + "[15:41:47] Simulation (INFO) - Progress... 24%\n", + "[15:41:47] Simulation (INFO) - Progress... 24%\n", + "[15:41:47] Simulation (INFO) - Progress... 24%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01153564453125\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9951866821980899\n", + "[15:41:47] Simulation (INFO) - Progress... 24%\n", + "[15:41:47] Simulation (INFO) - Progress... 24%\n", + "[15:41:47] Simulation (INFO) - Progress... 25%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.012542724609375\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9945684970193788\n", + "[15:41:47] Simulation (INFO) - Progress... 25%\n", + "[15:41:47] Simulation (INFO) - Progress... 25%\n", + "[15:41:47] Simulation (INFO) - Progress... 25%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.013336181640625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9937800765622566\n", + "[15:41:47] Simulation (INFO) - Progress... 25%\n", + "[15:41:47] Simulation (INFO) - Progress... 25%\n", + "[15:41:47] Simulation (INFO) - Progress... 26%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.015167236328125\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9933657156418472\n", + "[15:41:47] Simulation (INFO) - Progress... 26%\n", + "[15:41:47] Simulation (INFO) - Progress... 26%\n", + "[15:41:47] Simulation (INFO) - Progress... 26%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.016326904296875\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9926168168757035\n", + "[15:41:47] Simulation (INFO) - Progress... 26%\n", + "[15:41:47] Simulation (INFO) - Progress... 26%\n", + "[15:41:47] Simulation (INFO) - Progress... 27%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01806640625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9919907474019105\n", + "[15:41:47] Simulation (INFO) - Progress... 27%\n", + "[15:41:47] Simulation (INFO) - Progress... 27%\n", + "[15:41:47] Simulation (INFO) - Progress... 27%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01806640625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9919907474019105\n", + "[15:41:47] Simulation (INFO) - Progress... 27%\n", + "[15:41:47] Simulation (INFO) - Progress... 27%\n", + "[15:41:47] Simulation (INFO) - Progress... 28%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01806640625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9919907474019105\n", + "[15:41:47] Simulation (INFO) - Progress... 28%\n", + "[15:41:47] Simulation (INFO) - Progress... 28%\n", + "[15:41:47] Simulation (INFO) - Progress... 28%\n", + "[15:41:47] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:47] MPS (INFO) - Fidelity before optimisation=0.9919907474019105\n", + "[15:41:47] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:47] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9944986651228879\n", + "[15:41:47] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:47] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9945622863823858\n", + "[15:41:47] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:47] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9945744642325651\n", + "[15:41:47] MPS (INFO) - Final fidelity after optimisation=0.9945744642325651\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01806640625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9945744642325651\n", + "[15:41:47] Simulation (INFO) - Progress... 28%\n", + "[15:41:47] Simulation (INFO) - Progress... 28%\n", + "[15:41:47] Simulation (INFO) - Progress... 29%\n", + "[15:41:47] Simulation (INFO) - Progress... 29%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01812744140625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9945744642325651\n", + "[15:41:47] Simulation (INFO) - Progress... 29%\n", + "[15:41:47] Simulation (INFO) - Progress... 29%\n", + "[15:41:47] Simulation (INFO) - Progress... 29%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.018218994140625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9945744642325651\n", + "[15:41:47] Simulation (INFO) - Progress... 30%\n", + "[15:41:47] Simulation (INFO) - Progress... 30%\n", + "[15:41:47] Simulation (INFO) - Progress... 30%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.018341064453125\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9945744642325651\n", + "[15:41:47] Simulation (INFO) - Progress... 30%\n", + "[15:41:47] Simulation (INFO) - Progress... 30%\n", + "[15:41:47] Simulation (INFO) - Progress... 30%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.018707275390625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9945744642325651\n", + "[15:41:47] Simulation (INFO) - Progress... 31%\n", + "[15:41:47] Simulation (INFO) - Progress... 31%\n", + "[15:41:47] Simulation (INFO) - Progress... 31%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.019439697265625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9939252776724739\n", + "[15:41:47] Simulation (INFO) - Progress... 31%\n", + "[15:41:47] Simulation (INFO) - Progress... 31%\n", + "[15:41:47] Simulation (INFO) - Progress... 31%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.021148681640625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9939252776724739\n", + "[15:41:47] Simulation (INFO) - Progress... 32%\n", + "[15:41:47] Simulation (INFO) - Progress... 32%\n", + "[15:41:47] Simulation (INFO) - Progress... 32%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.02252197265625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9936102094018504\n", + "[15:41:47] Simulation (INFO) - Progress... 32%\n", + "[15:41:47] Simulation (INFO) - Progress... 32%\n", + "[15:41:47] Simulation (INFO) - Progress... 32%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.02447509765625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9932716193018882\n", + "[15:41:47] Simulation (INFO) - Progress... 33%\n", + "[15:41:47] Simulation (INFO) - Progress... 33%\n", + "[15:41:47] Simulation (INFO) - Progress... 33%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.027679443359375\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9926992945331796\n", + "[15:41:47] Simulation (INFO) - Progress... 33%\n", + "[15:41:47] Simulation (INFO) - Progress... 33%\n", + "[15:41:47] Simulation (INFO) - Progress... 33%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.031036376953125\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9917770056878091\n", + "[15:41:47] Simulation (INFO) - Progress... 34%\n", + "[15:41:47] Simulation (INFO) - Progress... 34%\n", + "[15:41:47] Simulation (INFO) - Progress... 34%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.034332275390625\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9910186053590768\n", + "[15:41:47] Simulation (INFO) - Progress... 34%\n", + "[15:41:47] Simulation (INFO) - Progress... 34%\n", + "[15:41:47] Simulation (INFO) - Progress... 34%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.035736083984375\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9903708169455654\n", + "[15:41:47] Simulation (INFO) - Progress... 35%\n", + "[15:41:47] Simulation (INFO) - Progress... 35%\n", + "[15:41:47] Simulation (INFO) - Progress... 35%\n", + "[15:41:47] MPS (INFO) - MPS size (MiB)=0.035736083984375\n", + "[15:41:47] MPS (INFO) - MPS fidelity=0.9903708169455654\n", + "[15:41:47] Simulation (INFO) - Progress... 35%\n", + "[15:41:47] Simulation (INFO) - Progress... 35%\n", + "[15:41:47] Simulation (INFO) - Progress... 35%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.035736083984375\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9903708169455654\n", + "[15:41:48] Simulation (INFO) - Progress... 36%\n", + "[15:41:48] Simulation (INFO) - Progress... 36%\n", + "[15:41:48] Simulation (INFO) - Progress... 36%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.035736083984375\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9903708169455652\n", + "[15:41:48] Simulation (INFO) - Progress... 36%\n", + "[15:41:48] Simulation (INFO) - Progress... 36%\n", + "[15:41:48] Simulation (INFO) - Progress... 36%\n", + "[15:41:48] Simulation (INFO) - Progress... 37%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.035797119140625\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9903708169455652\n", + "[15:41:48] Simulation (INFO) - Progress... 37%\n", + "[15:41:48] Simulation (INFO) - Progress... 37%\n", + "[15:41:48] Simulation (INFO) - Progress... 37%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.035919189453125\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9903708169455652\n", + "[15:41:48] Simulation (INFO) - Progress... 37%\n", + "[15:41:48] Simulation (INFO) - Progress... 37%\n", + "[15:41:48] Simulation (INFO) - Progress... 38%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.035919189453125\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9903532709139039\n", + "[15:41:48] Simulation (INFO) - Progress... 38%\n", + "[15:41:48] Simulation (INFO) - Progress... 38%\n", + "[15:41:48] Simulation (INFO) - Progress... 38%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.036163330078125\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9903532709139038\n", + "[15:41:48] Simulation (INFO) - Progress... 38%\n", + "[15:41:48] Simulation (INFO) - Progress... 38%\n", + "[15:41:48] Simulation (INFO) - Progress... 39%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.036651611328125\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9903532709139038\n", + "[15:41:48] Simulation (INFO) - Progress... 39%\n", + "[15:41:48] Simulation (INFO) - Progress... 39%\n", + "[15:41:48] Simulation (INFO) - Progress... 39%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.03765869140625\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9899182044513357\n", + "[15:41:48] Simulation (INFO) - Progress... 39%\n", + "[15:41:48] Simulation (INFO) - Progress... 40%\n", + "[15:41:48] Simulation (INFO) - Progress... 40%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.038116455078125\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9892830604520672\n", + "[15:41:48] Simulation (INFO) - Progress... 40%\n", + "[15:41:48] Simulation (INFO) - Progress... 40%\n", + "[15:41:48] Simulation (INFO) - Progress... 40%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.040313720703125\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9886878009898008\n", + "[15:41:48] Simulation (INFO) - Progress... 40%\n", + "[15:41:48] Simulation (INFO) - Progress... 41%\n", + "[15:41:48] Simulation (INFO) - Progress... 41%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.043121337890625\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9877332864162025\n", + "[15:41:48] Simulation (INFO) - Progress... 41%\n", + "[15:41:48] Simulation (INFO) - Progress... 41%\n", + "[15:41:48] Simulation (INFO) - Progress... 41%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.047698974609375\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9870492609117136\n", + "[15:41:48] Simulation (INFO) - Progress... 41%\n", + "[15:41:48] Simulation (INFO) - Progress... 42%\n", + "[15:41:48] Simulation (INFO) - Progress... 42%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.052581787109375\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9864155906572986\n", + "[15:41:48] Simulation (INFO) - Progress... 42%\n", + "[15:41:48] Simulation (INFO) - Progress... 42%\n", + "[15:41:48] Simulation (INFO) - Progress... 42%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.060150146484375\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.985553172457638\n", + "[15:41:48] Simulation (INFO) - Progress... 42%\n", + "[15:41:48] Simulation (INFO) - Progress... 43%\n", + "[15:41:48] Simulation (INFO) - Progress... 43%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.066925048828125\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9848524485301354\n", + "[15:41:48] Simulation (INFO) - Progress... 43%\n", + "[15:41:48] Simulation (INFO) - Progress... 43%\n", + "[15:41:48] Simulation (INFO) - Progress... 43%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.068695068359375\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9848524485301354\n", + "[15:41:48] Simulation (INFO) - Progress... 43%\n", + "[15:41:48] Simulation (INFO) - Progress... 44%\n", + "[15:41:48] Simulation (INFO) - Progress... 44%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.068695068359375\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9848524485301353\n", + "[15:41:48] Simulation (INFO) - Progress... 44%\n", + "[15:41:48] Simulation (INFO) - Progress... 44%\n", + "[15:41:48] Simulation (INFO) - Progress... 44%\n", + "[15:41:48] MPS (INFO) - MPS size (MiB)=0.068695068359375\n", + "[15:41:48] MPS (INFO) - MPS fidelity=0.9848524485301353\n", + "[15:41:48] Simulation (INFO) - Progress... 44%\n", + "[15:41:48] Simulation (INFO) - Progress... 45%\n", + "[15:41:48] Simulation (INFO) - Progress... 45%\n", + "[15:41:48] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:48] MPS (INFO) - Fidelity before optimisation=0.9848524485301353\n", + "[15:41:48] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:48] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9873602516768857\n", + "[15:41:48] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:48] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9874400577886869\n", + "[15:41:48] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:48] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9874665951742544\n", + "[15:41:48] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:49] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.987479443857063\n", + "[15:41:49] MPS (INFO) - Final fidelity after optimisation=0.987479443857063\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.068695068359375\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", + "[15:41:49] Simulation (INFO) - Progress... 45%\n", + "[15:41:49] Simulation (INFO) - Progress... 45%\n", + "[15:41:49] Simulation (INFO) - Progress... 45%\n", + "[15:41:49] Simulation (INFO) - Progress... 45%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.068756103515625\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", + "[15:41:49] Simulation (INFO) - Progress... 46%\n", + "[15:41:49] Simulation (INFO) - Progress... 46%\n", + "[15:41:49] Simulation (INFO) - Progress... 46%\n", + "[15:41:49] Simulation (INFO) - Progress... 46%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.068878173828125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", + "[15:41:49] Simulation (INFO) - Progress... 46%\n", + "[15:41:49] Simulation (INFO) - Progress... 46%\n", + "[15:41:49] Simulation (INFO) - Progress... 47%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.068878173828125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", + "[15:41:49] Simulation (INFO) - Progress... 47%\n", + "[15:41:49] Simulation (INFO) - Progress... 47%\n", + "[15:41:49] Simulation (INFO) - Progress... 47%\n", + "[15:41:49] Simulation (INFO) - Progress... 47%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.069122314453125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", + "[15:41:49] Simulation (INFO) - Progress... 47%\n", + "[15:41:49] Simulation (INFO) - Progress... 48%\n", + "[15:41:49] Simulation (INFO) - Progress... 48%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.069488525390625\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", + "[15:41:49] Simulation (INFO) - Progress... 48%\n", + "[15:41:49] Simulation (INFO) - Progress... 48%\n", + "[15:41:49] Simulation (INFO) - Progress... 48%\n", + "[15:41:49] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:49] MPS (INFO) - Fidelity before optimisation=0.987479443857063\n", + "[15:41:49] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:49] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9874794438570587\n", + "[15:41:49] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:49] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9874794438570632\n", + "[15:41:49] MPS (INFO) - Final fidelity after optimisation=0.9874794438570632\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.069488525390625\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570632\n", + "[15:41:49] Simulation (INFO) - Progress... 48%\n", + "[15:41:49] Simulation (INFO) - Progress... 49%\n", + "[15:41:49] Simulation (INFO) - Progress... 49%\n", + "[15:41:49] Simulation (INFO) - Progress... 49%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.069854736328125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570632\n", + "[15:41:49] Simulation (INFO) - Progress... 49%\n", + "[15:41:49] Simulation (INFO) - Progress... 49%\n", + "[15:41:49] Simulation (INFO) - Progress... 50%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.070831298828125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570634\n", + "[15:41:49] Simulation (INFO) - Progress... 50%\n", + "[15:41:49] Simulation (INFO) - Progress... 50%\n", + "[15:41:49] Simulation (INFO) - Progress... 50%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.070831298828125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570634\n", + "[15:41:49] Simulation (INFO) - Progress... 50%\n", + "[15:41:49] Simulation (INFO) - Progress... 50%\n", + "[15:41:49] Simulation (INFO) - Progress... 51%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.070831298828125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570637\n", + "[15:41:49] Simulation (INFO) - Progress... 51%\n", + "[15:41:49] Simulation (INFO) - Progress... 51%\n", + "[15:41:49] Simulation (INFO) - Progress... 51%\n", + "[15:41:49] Simulation (INFO) - Progress... 51%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.071319580078125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570637\n", + "[15:41:49] Simulation (INFO) - Progress... 51%\n", + "[15:41:49] Simulation (INFO) - Progress... 52%\n", + "[15:41:49] Simulation (INFO) - Progress... 52%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.072784423828125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570637\n", + "[15:41:49] Simulation (INFO) - Progress... 52%\n", + "[15:41:49] Simulation (INFO) - Progress... 52%\n", + "[15:41:49] Simulation (INFO) - Progress... 52%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.072540283203125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9866330729559818\n", + "[15:41:49] Simulation (INFO) - Progress... 52%\n", + "[15:41:49] Simulation (INFO) - Progress... 53%\n", + "[15:41:49] Simulation (INFO) - Progress... 53%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.073211669921875\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9866330729559818\n", + "[15:41:49] Simulation (INFO) - Progress... 53%\n", + "[15:41:49] Simulation (INFO) - Progress... 53%\n", + "[15:41:49] Simulation (INFO) - Progress... 53%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.073822021484375\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9866330729559818\n", + "[15:41:49] Simulation (INFO) - Progress... 53%\n", + "[15:41:49] Simulation (INFO) - Progress... 54%\n", + "[15:41:49] Simulation (INFO) - Progress... 54%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.074920654296875\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9866330729559817\n", + "[15:41:49] Simulation (INFO) - Progress... 54%\n", + "[15:41:49] Simulation (INFO) - Progress... 54%\n", + "[15:41:49] Simulation (INFO) - Progress... 54%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.074920654296875\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.985728058386732\n", + "[15:41:49] Simulation (INFO) - Progress... 54%\n", + "[15:41:49] Simulation (INFO) - Progress... 55%\n", + "[15:41:49] Simulation (INFO) - Progress... 55%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.076507568359375\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9848905743017655\n", + "[15:41:49] Simulation (INFO) - Progress... 55%\n", + "[15:41:49] Simulation (INFO) - Progress... 55%\n", + "[15:41:49] Simulation (INFO) - Progress... 55%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.0782470703125\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.984200933510651\n", + "[15:41:49] Simulation (INFO) - Progress... 55%\n", + "[15:41:49] Simulation (INFO) - Progress... 56%\n", + "[15:41:49] Simulation (INFO) - Progress... 56%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.08209228515625\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9833786711539604\n", + "[15:41:49] Simulation (INFO) - Progress... 56%\n", + "[15:41:49] Simulation (INFO) - Progress... 56%\n", + "[15:41:49] Simulation (INFO) - Progress... 56%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.08514404296875\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9829054162238393\n", + "[15:41:49] Simulation (INFO) - Progress... 56%\n", + "[15:41:49] Simulation (INFO) - Progress... 57%\n", + "[15:41:49] Simulation (INFO) - Progress... 57%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.093994140625\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9823548457232609\n", + "[15:41:49] Simulation (INFO) - Progress... 57%\n", + "[15:41:49] Simulation (INFO) - Progress... 57%\n", + "[15:41:49] Simulation (INFO) - Progress... 57%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.10003662109375\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9814914262501793\n", + "[15:41:49] Simulation (INFO) - Progress... 57%\n", + "[15:41:49] Simulation (INFO) - Progress... 58%\n", + "[15:41:49] Simulation (INFO) - Progress... 58%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.116302490234375\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9806647381641405\n", + "[15:41:49] Simulation (INFO) - Progress... 58%\n", + "[15:41:49] Simulation (INFO) - Progress... 58%\n", + "[15:41:49] Simulation (INFO) - Progress... 58%\n", + "[15:41:49] MPS (INFO) - MPS size (MiB)=0.118499755859375\n", + "[15:41:49] MPS (INFO) - MPS fidelity=0.9797474281526156\n", + "[15:41:50] Simulation (INFO) - Progress... 58%\n", + "[15:41:50] Simulation (INFO) - Progress... 59%\n", + "[15:41:50] Simulation (INFO) - Progress... 59%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.137542724609375\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9792041496059278\n", + "[15:41:50] Simulation (INFO) - Progress... 59%\n", + "[15:41:50] Simulation (INFO) - Progress... 59%\n", + "[15:41:50] Simulation (INFO) - Progress... 59%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.150360107421875\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9784485799686532\n", + "[15:41:50] Simulation (INFO) - Progress... 60%\n", + "[15:41:50] Simulation (INFO) - Progress... 60%\n", + "[15:41:50] Simulation (INFO) - Progress... 60%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.175567626953125\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.977661825354294\n", + "[15:41:50] Simulation (INFO) - Progress... 60%\n", + "[15:41:50] Simulation (INFO) - Progress... 60%\n", + "[15:41:50] Simulation (INFO) - Progress... 60%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.192779541015625\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9768742140627227\n", + "[15:41:50] Simulation (INFO) - Progress... 61%\n", + "[15:41:50] Simulation (INFO) - Progress... 61%\n", + "[15:41:50] Simulation (INFO) - Progress... 61%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.236358642578125\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9761008623122066\n", + "[15:41:50] Simulation (INFO) - Progress... 61%\n", + "[15:41:50] Simulation (INFO) - Progress... 61%\n", + "[15:41:50] Simulation (INFO) - Progress... 61%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.24725341796875\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9752497693902976\n", + "[15:41:50] Simulation (INFO) - Progress... 62%\n", + "[15:41:50] Simulation (INFO) - Progress... 62%\n", + "[15:41:50] Simulation (INFO) - Progress... 62%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.277008056640625\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208022\n", + "[15:41:50] Simulation (INFO) - Progress... 62%\n", + "[15:41:50] Simulation (INFO) - Progress... 62%\n", + "[15:41:50] Simulation (INFO) - Progress... 62%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.277008056640625\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208022\n", + "[15:41:50] Simulation (INFO) - Progress... 63%\n", + "[15:41:50] Simulation (INFO) - Progress... 63%\n", + "[15:41:50] Simulation (INFO) - Progress... 63%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208022\n", + "[15:41:50] Simulation (INFO) - Progress... 63%\n", + "[15:41:50] Simulation (INFO) - Progress... 63%\n", + "[15:41:50] Simulation (INFO) - Progress... 63%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208022\n", + "[15:41:50] Simulation (INFO) - Progress... 64%\n", + "[15:41:50] Simulation (INFO) - Progress... 64%\n", + "[15:41:50] Simulation (INFO) - Progress... 64%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208022\n", + "[15:41:50] Simulation (INFO) - Progress... 64%\n", + "[15:41:50] Simulation (INFO) - Progress... 64%\n", + "[15:41:50] Simulation (INFO) - Progress... 64%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208021\n", + "[15:41:50] Simulation (INFO) - Progress... 65%\n", + "[15:41:50] Simulation (INFO) - Progress... 65%\n", + "[15:41:50] Simulation (INFO) - Progress... 65%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208021\n", + "[15:41:50] Simulation (INFO) - Progress... 65%\n", + "[15:41:50] Simulation (INFO) - Progress... 65%\n", + "[15:41:50] Simulation (INFO) - Progress... 65%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208023\n", + "[15:41:50] Simulation (INFO) - Progress... 66%\n", + "[15:41:50] Simulation (INFO) - Progress... 66%\n", + "[15:41:50] Simulation (INFO) - Progress... 66%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208023\n", + "[15:41:50] Simulation (INFO) - Progress... 66%\n", + "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284454345703125\n", + "[15:41:50] MPS (INFO) - MPS fidelity=0.9736774416720088\n", + "[15:41:50] Simulation (INFO) - Progress... 66%\n", + "[15:41:50] Simulation (INFO) - Progress... 66%\n", + "[15:41:50] Simulation (INFO) - Progress... 67%\n", + "[15:41:50] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:50] MPS (INFO) - Fidelity before optimisation=0.9736774416720088\n", + "[15:41:50] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9822122042244481\n", + "[15:41:50] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9827583003913757\n", + "[15:41:50] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9829168903407746\n", + "[15:41:50] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.982979344248083\n", + "[15:41:51] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9830095477136648\n", + "[15:41:51] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9830260810282695\n", + "[15:41:51] MPS (INFO) - Final fidelity after optimisation=0.9830260810282695\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.284454345703125\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9825960930899819\n", + "[15:41:51] Simulation (INFO) - Progress... 67%\n", + "[15:41:51] Simulation (INFO) - Progress... 67%\n", + "[15:41:51] Simulation (INFO) - Progress... 67%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.285736083984375\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9819799784936092\n", + "[15:41:51] Simulation (INFO) - Progress... 67%\n", + "[15:41:51] Simulation (INFO) - Progress... 67%\n", + "[15:41:51] Simulation (INFO) - Progress... 68%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.288055419921875\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9811857364040928\n", + "[15:41:51] Simulation (INFO) - Progress... 68%\n", + "[15:41:51] Simulation (INFO) - Progress... 68%\n", + "[15:41:51] Simulation (INFO) - Progress... 68%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.2901611328125\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9805589329022365\n", + "[15:41:51] Simulation (INFO) - Progress... 68%\n", + "[15:41:51] Simulation (INFO) - Progress... 68%\n", + "[15:41:51] Simulation (INFO) - Progress... 69%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.29742431640625\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.979774777914149\n", + "[15:41:51] Simulation (INFO) - Progress... 69%\n", + "[15:41:51] Simulation (INFO) - Progress... 69%\n", + "[15:41:51] Simulation (INFO) - Progress... 69%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.3004150390625\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9790302714655779\n", + "[15:41:51] Simulation (INFO) - Progress... 69%\n", + "[15:41:51] Simulation (INFO) - Progress... 70%\n", + "[15:41:51] Simulation (INFO) - Progress... 70%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.3072509765625\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9781050017075105\n", + "[15:41:51] Simulation (INFO) - Progress... 70%\n", + "[15:41:51] Simulation (INFO) - Progress... 70%\n", + "[15:41:51] Simulation (INFO) - Progress... 70%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.32537841796875\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9772629361326794\n", + "[15:41:51] Simulation (INFO) - Progress... 70%\n", + "[15:41:51] Simulation (INFO) - Progress... 71%\n", + "[15:41:51] Simulation (INFO) - Progress... 71%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.349822998046875\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9763363776760228\n", + "[15:41:51] Simulation (INFO) - Progress... 71%\n", + "[15:41:51] Simulation (INFO) - Progress... 71%\n", + "[15:41:51] Simulation (INFO) - Progress... 71%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.358062744140625\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9754556940251295\n", + "[15:41:51] Simulation (INFO) - Progress... 71%\n", + "[15:41:51] Simulation (INFO) - Progress... 72%\n", + "[15:41:51] Simulation (INFO) - Progress... 72%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.365570068359375\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9745025792245486\n", + "[15:41:51] Simulation (INFO) - Progress... 72%\n", + "[15:41:51] Simulation (INFO) - Progress... 72%\n", + "[15:41:51] Simulation (INFO) - Progress... 72%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.365570068359375\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9745025792245484\n", + "[15:41:51] Simulation (INFO) - Progress... 72%\n", + "[15:41:51] Simulation (INFO) - Progress... 73%\n", + "[15:41:51] Simulation (INFO) - Progress... 73%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.365570068359375\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9745025792245484\n", + "[15:41:51] Simulation (INFO) - Progress... 73%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.365570068359375\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9745025792245482\n", + "[15:41:51] Simulation (INFO) - Progress... 73%\n", + "[15:41:51] Simulation (INFO) - Progress... 73%\n", + "[15:41:51] Simulation (INFO) - Progress... 73%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.365936279296875\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9745025792245482\n", + "[15:41:51] Simulation (INFO) - Progress... 74%\n", + "[15:41:51] Simulation (INFO) - Progress... 74%\n", + "[15:41:51] Simulation (INFO) - Progress... 74%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.366973876953125\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9739737340007235\n", + "[15:41:51] Simulation (INFO) - Progress... 74%\n", + "[15:41:51] Simulation (INFO) - Progress... 74%\n", + "[15:41:51] Simulation (INFO) - Progress... 74%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.369415283203125\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9734939221919511\n", + "[15:41:51] Simulation (INFO) - Progress... 75%\n", + "[15:41:51] Simulation (INFO) - Progress... 75%\n", + "[15:41:51] Simulation (INFO) - Progress... 75%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.374176025390625\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9728701873772134\n", + "[15:41:51] Simulation (INFO) - Progress... 75%\n", + "[15:41:51] Simulation (INFO) - Progress... 75%\n", + "[15:41:51] Simulation (INFO) - Progress... 75%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.378570556640625\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9720376362143338\n", + "[15:41:51] Simulation (INFO) - Progress... 76%\n", + "[15:41:51] Simulation (INFO) - Progress... 76%\n", + "[15:41:51] Simulation (INFO) - Progress... 76%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.383453369140625\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.971167337001675\n", + "[15:41:51] Simulation (INFO) - Progress... 76%\n", + "[15:41:51] Simulation (INFO) - Progress... 76%\n", + "[15:41:51] Simulation (INFO) - Progress... 76%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.39910888671875\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9703795628080001\n", + "[15:41:51] Simulation (INFO) - Progress... 77%\n", + "[15:41:51] Simulation (INFO) - Progress... 77%\n", + "[15:41:51] Simulation (INFO) - Progress... 77%\n", + "[15:41:51] MPS (INFO) - MPS size (MiB)=0.43072509765625\n", + "[15:41:51] MPS (INFO) - MPS fidelity=0.9695212202086415\n", + "[15:41:51] Simulation (INFO) - Progress... 77%\n", + "[15:41:51] Simulation (INFO) - Progress... 77%\n", + "[15:41:51] Simulation (INFO) - Progress... 77%\n", + "[15:41:52] MPS (INFO) - MPS size (MiB)=0.490478515625\n", + "[15:41:52] MPS (INFO) - MPS fidelity=0.9686850339371813\n", + "[15:41:52] Simulation (INFO) - Progress... 78%\n", + "[15:41:52] Simulation (INFO) - Progress... 78%\n", + "[15:41:52] Simulation (INFO) - Progress... 78%\n", + "[15:41:52] MPS (INFO) - MPS size (MiB)=0.5670166015625\n", + "[15:41:52] MPS (INFO) - MPS fidelity=0.9678550687968107\n", + "[15:41:52] Simulation (INFO) - Progress... 78%\n", + "[15:41:52] Simulation (INFO) - Progress... 78%\n", + "[15:41:52] Simulation (INFO) - Progress... 78%\n", + "[15:41:52] MPS (INFO) - MPS size (MiB)=0.61614990234375\n", + "[15:41:52] MPS (INFO) - MPS fidelity=0.9669360842897095\n", + "[15:41:52] Simulation (INFO) - Progress... 79%\n", + "[15:41:52] Simulation (INFO) - Progress... 79%\n", + "[15:41:52] Simulation (INFO) - Progress... 79%\n", + "[15:41:52] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", + "[15:41:52] MPS (INFO) - MPS fidelity=0.9660548082975919\n", + "[15:41:52] Simulation (INFO) - Progress... 79%\n", + "[15:41:52] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", + "[15:41:52] MPS (INFO) - MPS fidelity=0.9660548082975919\n", + "[15:41:52] Simulation (INFO) - Progress... 79%\n", + "[15:41:52] Simulation (INFO) - Progress... 80%\n", + "[15:41:52] Simulation (INFO) - Progress... 80%\n", + "[15:41:52] Simulation (INFO) - Progress... 80%\n", + "[15:41:52] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", + "[15:41:52] MPS (INFO) - MPS fidelity=0.9660548082975922\n", + "[15:41:52] Simulation (INFO) - Progress... 80%\n", + "[15:41:52] Simulation (INFO) - Progress... 80%\n", + "[15:41:52] Simulation (INFO) - Progress... 80%\n", + "[15:41:52] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", + "[15:41:52] MPS (INFO) - MPS fidelity=0.9660548082975922\n", + "[15:41:52] Simulation (INFO) - Progress... 81%\n", + "[15:41:52] Simulation (INFO) - Progress... 81%\n", + "[15:41:52] Simulation (INFO) - Progress... 81%\n", + "[15:41:52] Simulation (INFO) - Progress... 81%\n", + "[15:41:52] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", + "[15:41:52] MPS (INFO) - MPS fidelity=0.9660548082975922\n", + "[15:41:52] Simulation (INFO) - Progress... 81%\n", + "[15:41:52] Simulation (INFO) - Progress... 81%\n", + "[15:41:52] Simulation (INFO) - Progress... 82%\n", + "[15:41:52] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:52] MPS (INFO) - Fidelity before optimisation=0.9660548082975922\n", + "[15:41:52] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9714782872349863\n", + "[15:41:52] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9716864883910468\n", + "[15:41:52] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9717552987705841\n", + "[15:41:52] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:53] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9717903497055657\n", + "[15:41:53] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:53] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9718115474633439\n", + "[15:41:53] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:53] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9718256606609913\n", + "[15:41:53] MPS (INFO) - Final fidelity after optimisation=0.9718256606609913\n", + "[15:41:53] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", + "[15:41:53] MPS (INFO) - MPS fidelity=0.9718256606609913\n", + "[15:41:53] Simulation (INFO) - Progress... 82%\n", + "[15:41:53] Simulation (INFO) - Progress... 82%\n", + "[15:41:53] Simulation (INFO) - Progress... 82%\n", + "[15:41:53] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", + "[15:41:53] MPS (INFO) - MPS fidelity=0.9718256606609913\n", + "[15:41:53] Simulation (INFO) - Progress... 82%\n", + "[15:41:53] Simulation (INFO) - Progress... 82%\n", + "[15:41:53] Simulation (INFO) - Progress... 83%\n", + "[15:41:53] Simulation (INFO) - Progress... 83%\n", + "[15:41:53] MPS (INFO) - MPS size (MiB)=0.645721435546875\n", + "[15:41:53] MPS (INFO) - MPS fidelity=0.971304662959029\n", + "[15:41:53] Simulation (INFO) - Progress... 83%\n", + "[15:41:53] Simulation (INFO) - Progress... 83%\n", + "[15:41:53] Simulation (INFO) - Progress... 83%\n", + "[15:41:53] MPS (INFO) - MPS size (MiB)=0.645721435546875\n", + "[15:41:53] MPS (INFO) - MPS fidelity=0.971304662959029\n", + "[15:41:53] Simulation (INFO) - Progress... 83%\n", + "[15:41:53] Simulation (INFO) - Progress... 84%\n", + "[15:41:53] Simulation (INFO) - Progress... 84%\n", + "[15:41:53] MPS (INFO) - MPS size (MiB)=0.645721435546875\n", + "[15:41:53] MPS (INFO) - MPS fidelity=0.9713046629590292\n", + "[15:41:53] Simulation (INFO) - Progress... 84%\n", + "[15:41:53] Simulation (INFO) - Progress... 84%\n", + "[15:41:53] Simulation (INFO) - Progress... 84%\n", + "[15:41:53] MPS (INFO) - MPS size (MiB)=0.645721435546875\n", + "[15:41:53] MPS (INFO) - MPS fidelity=0.9713046629590292\n", + "[15:41:53] Simulation (INFO) - Progress... 84%\n", + "[15:41:53] Simulation (INFO) - Progress... 85%\n", + "[15:41:53] Simulation (INFO) - Progress... 85%\n", + "[15:41:53] Simulation (INFO) - Progress... 85%\n", + "[15:41:53] MPS (INFO) - MPS size (MiB)=0.65234375\n", + "[15:41:53] MPS (INFO) - MPS fidelity=0.9705519636179583\n", + "[15:41:53] Simulation (INFO) - Progress... 85%\n", + "[15:41:53] Simulation (INFO) - Progress... 85%\n", + "[15:41:53] Simulation (INFO) - Progress... 85%\n", + "[15:41:53] MPS (INFO) - MPS size (MiB)=0.6531982421875\n", + "[15:41:53] MPS (INFO) - MPS fidelity=0.9705519636179583\n", + "[15:41:53] Simulation (INFO) - Progress... 86%\n", + "[15:41:53] Simulation (INFO) - Progress... 86%\n", + "[15:41:53] Simulation (INFO) - Progress... 86%\n", + "[15:41:53] MPS (INFO) - MPS size (MiB)=0.6531982421875\n", + "[15:41:53] MPS (INFO) - MPS fidelity=0.9705519636179583\n", + "[15:41:53] Simulation (INFO) - Progress... 86%\n", + "[15:41:53] Simulation (INFO) - Progress... 86%\n", + "[15:41:53] Simulation (INFO) - Progress... 86%\n", + "[15:41:53] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:53] MPS (INFO) - Fidelity before optimisation=0.9705519636179583\n", + "[15:41:53] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:53] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9710393809351289\n", + "[15:41:53] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9710417093966089\n", + "[15:41:54] MPS (INFO) - Final fidelity after optimisation=0.9710417093966089\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.6531982421875\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9710417093966089\n", + "[15:41:54] Simulation (INFO) - Progress... 87%\n", + "[15:41:54] Simulation (INFO) - Progress... 87%\n", + "[15:41:54] Simulation (INFO) - Progress... 87%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.6531982421875\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9710417093966089\n", + "[15:41:54] Simulation (INFO) - Progress... 87%\n", + "[15:41:54] Simulation (INFO) - Progress... 87%\n", + "[15:41:54] Simulation (INFO) - Progress... 87%\n", + "[15:41:54] Simulation (INFO) - Progress... 88%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.663360595703125\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9703316436673765\n", + "[15:41:54] Simulation (INFO) - Progress... 88%\n", + "[15:41:54] Simulation (INFO) - Progress... 88%\n", + "[15:41:54] Simulation (INFO) - Progress... 88%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.6771240234375\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9697826685947312\n", + "[15:41:54] Simulation (INFO) - Progress... 88%\n", + "[15:41:54] Simulation (INFO) - Progress... 88%\n", + "[15:41:54] Simulation (INFO) - Progress... 89%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.6890869140625\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9688822088585105\n", + "[15:41:54] Simulation (INFO) - Progress... 89%\n", + "[15:41:54] Simulation (INFO) - Progress... 89%\n", + "[15:41:54] Simulation (INFO) - Progress... 89%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.7198486328125\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9681016107658179\n", + "[15:41:54] Simulation (INFO) - Progress... 89%\n", + "[15:41:54] Simulation (INFO) - Progress... 90%\n", + "[15:41:54] Simulation (INFO) - Progress... 90%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.732025146484375\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.967139989859211\n", + "[15:41:54] Simulation (INFO) - Progress... 90%\n", + "[15:41:54] Simulation (INFO) - Progress... 90%\n", + "[15:41:54] Simulation (INFO) - Progress... 90%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.786224365234375\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9667532538346312\n", + "[15:41:54] Simulation (INFO) - Progress... 90%\n", + "[15:41:54] Simulation (INFO) - Progress... 91%\n", + "[15:41:54] Simulation (INFO) - Progress... 91%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.805267333984375\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9657875697333652\n", + "[15:41:54] Simulation (INFO) - Progress... 91%\n", + "[15:41:54] Simulation (INFO) - Progress... 91%\n", + "[15:41:54] Simulation (INFO) - Progress... 91%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.870452880859375\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9649987228797965\n", + "[15:41:54] Simulation (INFO) - Progress... 91%\n", + "[15:41:54] Simulation (INFO) - Progress... 92%\n", + "[15:41:54] Simulation (INFO) - Progress... 92%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=0.927581787109375\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9641126521361515\n", + "[15:41:54] Simulation (INFO) - Progress... 92%\n", + "[15:41:54] Simulation (INFO) - Progress... 92%\n", + "[15:41:54] Simulation (INFO) - Progress... 92%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=1.066741943359375\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9635105846805408\n", + "[15:41:54] Simulation (INFO) - Progress... 92%\n", + "[15:41:54] Simulation (INFO) - Progress... 93%\n", + "[15:41:54] Simulation (INFO) - Progress... 93%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=1.15728759765625\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.962589075282592\n", + "[15:41:54] Simulation (INFO) - Progress... 93%\n", + "[15:41:54] Simulation (INFO) - Progress... 93%\n", + "[15:41:54] Simulation (INFO) - Progress... 93%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=1.43927001953125\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9617602212979602\n", + "[15:41:54] Simulation (INFO) - Progress... 93%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=1.54986572265625\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9608510935810075\n", + "[15:41:54] Simulation (INFO) - Progress... 94%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=1.54986572265625\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9608510935810075\n", + "[15:41:54] Simulation (INFO) - Progress... 94%\n", + "[15:41:54] Simulation (INFO) - Progress... 94%\n", + "[15:41:54] Simulation (INFO) - Progress... 94%\n", + "[15:41:54] MPS (INFO) - MPS size (MiB)=1.54986572265625\n", + "[15:41:54] MPS (INFO) - MPS fidelity=0.9600320773213169\n", + "[15:41:54] Simulation (INFO) - Progress... 94%\n", + "[15:41:54] Simulation (INFO) - Progress... 94%\n", + "[15:41:54] Simulation (INFO) - Progress... 95%\n", + "[15:41:54] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:54] MPS (INFO) - Fidelity before optimisation=0.9600320773213169\n", + "[15:41:54] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9659019975820374\n", + "[15:41:54] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9661427864728673\n", + "[15:41:54] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9662148782989015\n", + "[15:41:55] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9662495844052902\n", + "[15:41:55] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9662703863336176\n", + "[15:41:55] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9662844523829522\n", + "[15:41:55] MPS (INFO) - Final fidelity after optimisation=0.9662844523829522\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.55718994140625\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9653257816354918\n", + "[15:41:55] Simulation (INFO) - Progress... 95%\n", + "[15:41:55] Simulation (INFO) - Progress... 95%\n", + "[15:41:55] Simulation (INFO) - Progress... 95%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.58184814453125\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9644004614054085\n", + "[15:41:55] Simulation (INFO) - Progress... 95%\n", + "[15:41:55] Simulation (INFO) - Progress... 95%\n", + "[15:41:55] Simulation (INFO) - Progress... 96%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.660125732421875\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9634633842376111\n", + "[15:41:55] Simulation (INFO) - Progress... 96%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.660125732421875\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9634633842376111\n", + "[15:41:55] Simulation (INFO) - Progress... 96%\n", + "[15:41:55] Simulation (INFO) - Progress... 96%\n", + "[15:41:55] Simulation (INFO) - Progress... 96%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.660125732421875\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9634633842376111\n", + "[15:41:55] Simulation (INFO) - Progress... 96%\n", + "[15:41:55] Simulation (INFO) - Progress... 97%\n", + "[15:41:55] Simulation (INFO) - Progress... 97%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.662017822265625\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9634633842376114\n", + "[15:41:55] Simulation (INFO) - Progress... 97%\n", + "[15:41:55] Simulation (INFO) - Progress... 97%\n", + "[15:41:55] Simulation (INFO) - Progress... 97%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465783\n", + "[15:41:55] Simulation (INFO) - Progress... 97%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465783\n", + "[15:41:55] Simulation (INFO) - Progress... 98%\n", + "[15:41:55] Simulation (INFO) - Progress... 98%\n", + "[15:41:55] Simulation (INFO) - Progress... 98%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465782\n", + "[15:41:55] Simulation (INFO) - Progress... 98%\n", + "[15:41:55] Simulation (INFO) - Progress... 98%\n", + "[15:41:55] Simulation (INFO) - Progress... 98%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465782\n", + "[15:41:55] Simulation (INFO) - Progress... 99%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465782\n", + "[15:41:55] Simulation (INFO) - Progress... 99%\n", + "[15:41:55] Simulation (INFO) - Progress... 99%\n", + "[15:41:55] Simulation (INFO) - Progress... 99%\n", + "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", + "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465782\n", + "[15:41:55] Simulation (INFO) - Progress... 99%\n", + "[15:41:55] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:55] MPS (INFO) - Fidelity before optimisation=0.9625939072465782\n", + "[15:41:56] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9640884677171835\n", + "[15:41:56] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9641174266253738\n", + "[15:41:56] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9641252811032455\n", + "[15:41:56] MPS (INFO) - Final fidelity after optimisation=0.9641252811032455\n", + "[15:41:56] MPS (INFO) - Applying variational optimisation.\n", + "[15:41:56] MPS (INFO) - Fidelity before optimisation=0.9641252811032455\n", + "[15:41:56] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9641252811032449\n", + "[15:41:56] MPS (INFO) - Doing another optimisation sweep...\n", + "[15:41:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.964125281103245\n", + "[15:41:56] MPS (INFO) - Final fidelity after optimisation=0.964125281103245\n", + "[15:41:56] Simulation (INFO) - Simulation completed.\n", + "[15:41:56] Simulation (INFO) - Final MPS size=1.700042724609375 MiB\n", + "[15:41:56] Simulation (INFO) - Final MPS fidelity=0.964125281103245\n" ] } ], "source": [ "with CuTensorNetHandle() as libhandle:\n", - " simulate(libhandle, circuit, ContractionAlg.MPSxMPO, truncation_fidelity=0.999, loglevel=logging.INFO)" + " config = ConfigMPS(truncation_fidelity=0.999, loglevel=logging.INFO)\n", + " simulate(libhandle, circuit, ContractionAlg.MPSxMPO, config)" ] }, { @@ -2105,9 +2114,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "py-cuquantum-23.06.0-mypich-py3.9", "language": "python", - "name": "python3" + "name": "py-cuquantum-23.06.0-mypich-py3.9" }, "language_info": { "codemirror_mode": { From 2517ab53fbcc7c98316a48a2295860cf45f43206 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 3 Oct 2023 16:00:40 -0700 Subject: [PATCH 15/83] Warning now using warnings module --- pytket/extensions/cutensornet/mps/mps.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pytket/extensions/cutensornet/mps/mps.py b/pytket/extensions/cutensornet/mps/mps.py index 762958cc..a5f40f64 100644 --- a/pytket/extensions/cutensornet/mps/mps.py +++ b/pytket/extensions/cutensornet/mps/mps.py @@ -174,9 +174,10 @@ def __init__( self.zero = value_of_zero if value_of_zero > self._atol / 1000: - logging.warning( + warnings.warn( "Your chosen value_of_zero is relatively large. " - "Faithfulness of final fidelity estimate is not guaranteed." + "Faithfulness of final fidelity estimate is not guaranteed.", + UserWarning, ) self.k = k From a16ec7edd2268bbb9483c3052d40fd2e0bfd02f9 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 10 Oct 2023 03:20:43 -0600 Subject: [PATCH 16/83] Moved MPS code to make room for TTN. --- pytket/extensions/cutensornet/mps/__init__.py | 37 - pytket/extensions/cutensornet/mps/mps.py | 961 ------------------ pytket/extensions/cutensornet/mps/mps_gate.py | 318 ------ pytket/extensions/cutensornet/mps/mps_mpo.py | 535 ---------- .../extensions/cutensornet/mps/simulation.py | 246 ----- tests/test_mps.py | 2 +- 6 files changed, 1 insertion(+), 2098 deletions(-) delete mode 100644 pytket/extensions/cutensornet/mps/__init__.py delete mode 100644 pytket/extensions/cutensornet/mps/mps.py delete mode 100644 pytket/extensions/cutensornet/mps/mps_gate.py delete mode 100644 pytket/extensions/cutensornet/mps/mps_mpo.py delete mode 100644 pytket/extensions/cutensornet/mps/simulation.py diff --git a/pytket/extensions/cutensornet/mps/__init__.py b/pytket/extensions/cutensornet/mps/__init__.py deleted file mode 100644 index e2c3ec81..00000000 --- a/pytket/extensions/cutensornet/mps/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2019-2023 Quantinuum -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -## -# http://www.apache.org/licenses/LICENSE-2.0 -## -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Module for circuit simulation by state evolution, with states represented as -Matrix Product States (MPS). Approximate tensor network contraction is supported. -For an example of its use, see ``examples/mps_tutorial.ipynb`` in -https://github.com/CQCL/pytket-cutensornet. -""" - -from .mps import ( - CuTensorNetHandle, - DirectionMPS, - ConfigMPS, - Handle, - Tensor, - MPS, -) - -from .mps_gate import ( - MPSxGate, -) - -from .mps_mpo import ( - MPSxMPO, -) - -from .simulation import ContractionAlg, simulate, prepare_circuit diff --git a/pytket/extensions/cutensornet/mps/mps.py b/pytket/extensions/cutensornet/mps/mps.py deleted file mode 100644 index a5f40f64..00000000 --- a/pytket/extensions/cutensornet/mps/mps.py +++ /dev/null @@ -1,961 +0,0 @@ -# Copyright 2019-2023 Quantinuum -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -## -# http://www.apache.org/licenses/LICENSE-2.0 -## -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import annotations # type: ignore -import warnings -import logging -from typing import Any, Optional, Union -from enum import Enum - -from random import random # type: ignore -import numpy as np # type: ignore - -try: - import cupy as cp # type: ignore -except ImportError: - warnings.warn("local settings failed to import cupy", ImportWarning) -try: - import cuquantum as cq # type: ignore - import cuquantum.cutensornet as cutn # type: ignore - from cuquantum.cutensornet import tensor # type: ignore -except ImportError: - warnings.warn("local settings failed to import cutensornet", ImportWarning) - -from pytket.circuit import Command, Op, OpType, Qubit -from pytket.pauli import Pauli, QubitPauliString - -from pytket.extensions.cutensornet.general import set_logger - -# An alias so that `intptr_t` from CuQuantum's API (which is not available in -# base python) has some meaningful type name. -Handle = int -# An alias for the CuPy type used for tensors -try: - Tensor = cp.ndarray -except NameError: - Tensor = Any - - -class DirectionMPS(Enum): - """An enum to refer to relative directions within the MPS.""" - - LEFT = 0 - RIGHT = 1 - - -class CuTensorNetHandle: - """Initialise the cuTensorNet library with automatic workspace memory - management. - - Note: - Always use as ``with CuTensorNetHandle() as libhandle:`` so that cuTensorNet - handles are automatically destroyed at the end of execution. - - Attributes: - handle (int): The cuTensorNet library handle created by this initialisation. - device_id (int): The ID of the device (GPU) where cuTensorNet is initialised. - If not provided, defaults to ``cp.cuda.Device()``. - """ - - def __init__(self, device_id: Optional[int] = None): - self.handle = cutn.create() - self._is_destroyed = False - - # Make sure CuPy uses the specified device - cp.cuda.Device(device_id).use() - - dev = cp.cuda.Device() - self.device_id = int(dev) - - def __enter__(self) -> CuTensorNetHandle: - return self - - def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None: - cutn.destroy(self.handle) - self._is_destroyed = True - - -class ConfigMPS: - """Configuration class for simulation using MPS.""" - - def __init__( - self, - chi: Optional[int] = None, - truncation_fidelity: Optional[float] = None, - k: int = 4, - optim_delta: float = 1e-5, - float_precision: Union[np.float32, np.float64] = np.float64, # type: ignore - value_of_zero: float = 1e-16, - loglevel: int = logging.WARNING, - ): - """Instantiate a configuration object for MPS simulation. - - Note: - Providing both a custom ``chi`` and ``truncation_fidelity`` will raise an - exception. Choose one or the other (or neither, for exact simulation). - - Args: - chi: The maximum value allowed for the dimension of the virtual - bonds. Higher implies better approximation but more - computational resources. If not provided, ``chi`` will be unbounded. - truncation_fidelity: Every time a two-qubit gate is applied, the virtual - bond will be truncated to the minimum dimension that satisfies - ``||^2 >= trucantion_fidelity``, where ``|psi>`` and ``|phi>`` - are the states before and after truncation (both normalised). - If not provided, it will default to its maximum value 1. - k: If using MPSxMPO, the maximum number of layers the MPO is allowed to - have before being contracted. Increasing this might increase fidelity, - but it will also increase resource requirements exponentially. - Ignored if not using MPSxMPO. Default value is 4. - optim_delta: If using MPSxMPO, stopping criteria for the optimisation when - contracting the ``k`` layers of MPO. Stops when the increase of fidelity - between iterations is smaller than ``optim_delta``. - Ignored if not using MPSxMPO. Default value is ``1e-5``. - float_precision: The floating point precision used in tensor calculations; - choose from ``numpy`` types: ``np.float64`` or ``np.float32``. - Complex numbers are represented using two of such - ``float`` numbers. Default is ``np.float64``. - value_of_zero: Any number below this value will be considered equal to zero. - Even when no ``chi`` or ``truncation_fidelity`` is provided, singular - values below this number will be truncated. - We suggest to use a value slightly below what your chosen - ``float_precision`` can reasonably achieve. For instance, ``1e-16`` for - ``np.float64`` precision (default) and ``1e-7`` for ``np.float32``. - loglevel: Internal logger output level. Use 30 for warnings only, 20 for - verbose and 10 for debug mode. - - Raises: - ValueError: If both ``chi`` and ``truncation_fidelity`` are fixed. - ValueError: If the value of ``chi`` is set below 2. - ValueError: If the value of ``truncation_fidelity`` is not in [0,1]. - """ - if ( - chi is not None - and truncation_fidelity is not None - and truncation_fidelity != 1.0 - ): - raise ValueError("Cannot fix both chi and truncation_fidelity.") - if chi is None: - chi = 2**60 # In practice, this is like having it be unbounded - if truncation_fidelity is None: - truncation_fidelity = 1 - - if chi < 2: - raise ValueError("The max virtual bond dim (chi) must be >= 2.") - if truncation_fidelity < 0 or truncation_fidelity > 1: - raise ValueError("Provide a value of truncation_fidelity in [0,1].") - - self.chi = chi - self.truncation_fidelity = truncation_fidelity - - if float_precision is None or float_precision == np.float64: # Double precision - self._real_t = np.float64 # type: ignore - self._complex_t = np.complex128 # type: ignore - self._atol = 1e-12 - elif float_precision == np.float32: # Single precision - self._real_t = np.float32 # type: ignore - self._complex_t = np.complex64 # type: ignore - self._atol = 1e-4 - else: - allowed_precisions = [np.float64, np.float32] - raise TypeError( - f"Value of float_precision must be in {allowed_precisions}." - ) - self.zero = value_of_zero - - if value_of_zero > self._atol / 1000: - warnings.warn( - "Your chosen value_of_zero is relatively large. " - "Faithfulness of final fidelity estimate is not guaranteed.", - UserWarning, - ) - - self.k = k - self.optim_delta = 1e-5 - self.loglevel = loglevel - - def copy(self) -> ConfigMPS: - """Standard copy of the contents.""" - return ConfigMPS( - chi=self.chi, - truncation_fidelity=self.truncation_fidelity, - k=self.k, - optim_delta=self.optim_delta, - float_precision=self._real_t, # type: ignore - ) - - -class MPS: - """Represents a state as a Matrix Product State. - - Attributes: - tensors (list[Tensor]): A list of tensors in the MPS; ``tensors[0]`` is - the leftmost and ``tensors[len(self)-1]`` is the rightmost; ``tensors[i]`` - and ``tensors[i+1]`` are connected in the MPS via a bond. All of the - tensors are rank three, with the dimensions listed in ``.shape`` matching - the left, right and physical bonds, in that order. - canonical_form (dict[int, Optional[DirectionMPS]]): A dictionary mapping - positions to the canonical form direction of the corresponding tensor, - or ``None`` if it the tensor is not canonicalised. - qubit_position (dict[pytket.circuit.Qubit, int]): A dictionary mapping circuit - qubits to the position its tensor is at in the MPS. - fidelity (float): A lower bound of the fidelity, obtained by multiplying - the fidelities after each contraction. The fidelity of a contraction - corresponds to ``||^2`` where ``|psi>`` and ``|phi>`` are the - states before and after truncation (assuming both are normalised). - """ - - def __init__( - self, - libhandle: CuTensorNetHandle, - qubits: list[Qubit], - config: ConfigMPS, - ): - """Initialise an MPS on the computational state ``|0>``. - - Note: - A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` - statement. The device where the MPS is stored will match the one specified - by the library handle. - - Args: - libhandle: The cuTensorNet library handle that will be used to carry out - tensor operations on the MPS. - qubits: The list of qubits in the circuit to be simulated. - config: The object describing the configuration for simulation. - - Raises: - ValueError: If less than two qubits are provided. - """ - self._lib = libhandle - self._cfg = config - self._logger = set_logger("MPS", level=config.loglevel) - self.fidelity = 1.0 - - n_tensors = len(qubits) - if n_tensors == 0: # There's no initialisation to be done - return None - 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 = [] - 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. - - Specifically, verify that the MPS does not exceed the dimension limit ``chi`` of - the virtual bonds, that physical bonds have dimension 2, that all tensors - are rank three and that the data structure sizes are consistent. - - Returns: - False if a violation was detected or True otherwise. - """ - self._flush() - - chi_ok = all( - all(dim <= self._cfg.chi for dim in self.get_virtual_dimensions(pos)) - for pos in range(len(self)) - ) - phys_ok = all(self.get_physical_dimension(pos) == 2 for pos in range(len(self))) - shape_ok = all(len(tensor.shape) == 3 for tensor in self.tensors) - - ds_ok = set(self.canonical_form.keys()) == set(range(len(self))) - ds_ok = ds_ok and set(self.qubit_position.values()) == set(range(len(self))) - - # Debugger logging - self._logger.debug( - "Checking validity of MPS... " - f"chi_ok={chi_ok}, " - f"phys_ok={phys_ok}, " - f"shape_ok={shape_ok}, " - f"ds_ok={ds_ok}" - ) - - return chi_ok and phys_ok and shape_ok and ds_ok - - def apply_gate(self, gate: Command) -> MPS: - """Apply the gate to the MPS. - - Note: - Only one-qubit gates and two-qubit gates are supported. Two-qubit - gates must act on adjacent qubits. - - Args: - gate: The gate to be applied. - - Returns: - ``self``, to allow for method chaining. - - Raises: - RuntimeError: If the ``CuTensorNetHandle`` is out of scope. - RuntimeError: If gate acts on more than 2 qubits or acts on non-adjacent - qubits. - RuntimeError: If physical bond dimension where gate is applied is not 2. - """ - if self._lib._is_destroyed: - raise RuntimeError( - "The cuTensorNet library handle is out of scope.", - "See the documentation of update_libhandle and CuTensorNetHandle.", - ) - - positions = [self.qubit_position[q] for q in gate.qubits] - if any(self.get_physical_dimension(pos) != 2 for pos in positions): - raise RuntimeError( - "Gates can only be applied to tensors with physical" - + " bond dimension of 2." - ) - self._logger.debug(f"Applying gate {gate}") - - if len(positions) == 1: - self._apply_1q_gate(positions[0], gate.op) - # NOTE: if the tensor was in canonical form, it remains being so, - # since it is guaranteed that the gate is unitary. - - elif len(positions) == 2: - dist = positions[1] - positions[0] - # We explicitly allow both dist==1 or dist==-1 so that non-symmetric - # gates such as CX can use the same Op for the two ways it can be in. - if dist not in [1, -1]: - raise RuntimeError( - "Gates must be applied to adjacent qubits! " - + f"This is not satisfied by {gate}." - ) - self._apply_2q_gate((positions[0], positions[1]), gate.op) - # The tensors will in general no longer be in canonical form. - self.canonical_form[positions[0]] = None - self.canonical_form[positions[1]] = None - - else: - raise RuntimeError( - "Gates must act on only 1 or 2 qubits! " - + f"This is not satisfied by {gate}." - ) - - return self - - def canonicalise(self, l_pos: int, r_pos: int) -> None: - """Canonicalises the MPS object. - - Applies the necessary gauge transformations so that all MPS tensors - to the left of position ``l_pos`` are in left orthogonal form and - all MPS tensors to the right of ``r_pos`` in right orthogonal form. - - Args: - l_pos: The position of the leftmost tensor that is not to be - canonicalised. - r_pos: The position of the rightmost tensor that is not to be - canonicalised. - """ - self._logger.debug(f"Start canonicalisation... l_pos={l_pos}, r_pos={r_pos}") - - for pos in range(l_pos): - self.canonicalise_tensor(pos, form=DirectionMPS.LEFT) - for pos in reversed(range(r_pos + 1, len(self))): - self.canonicalise_tensor(pos, form=DirectionMPS.RIGHT) - - self._logger.debug(f"Finished canonicalisation.") - - def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: - """Canonicalises a tensor from an MPS object. - - Applies the necessary gauge transformations so that the tensor at - position ``pos`` in the MPS is in the orthogonal form dictated by - ``form``. - - Args: - position: The position of the tensor to be canonicalised. - form: LEFT form means that its conjugate transpose is its inverse if - connected to its left bond and physical bond. Similarly for RIGHT. - - Raises: - ValueError: If ``form`` is not a value in ``DirectionMPS``. - RuntimeError: If the ``CuTensorNetHandle`` is out of scope. - """ - if form == self.canonical_form[pos]: - # Tensor already in canonical form, nothing needs to be done - self._logger.debug(f"Position {pos} already in {form}.") - return None - - if self._lib._is_destroyed: - raise RuntimeError( - "The cuTensorNet library handle is out of scope.", - "See the documentation of update_libhandle and CuTensorNetHandle.", - ) - - self._logger.debug(f"Canonicalising {pos} to {form}.") - # Glossary of bond IDs used here: - # s -> shared virtual bond between T and Tnext - # v -> the other virtual bond of T - # V -> the other virtual bond of Tnext - # p -> physical bond of T - # P -> physical bond of Tnext - - # Gather the details from the MPS tensors at this position - T = self.tensors[pos] - - # Assign the bond IDs - if form == DirectionMPS.LEFT: - next_pos = pos + 1 - Tnext = self.tensors[next_pos] - T_bonds = "vsp" - Q_bonds = "vap" - R_bonds = "as" - Tnext_bonds = "sVP" - result_bonds = "aVP" - elif form == DirectionMPS.RIGHT: - next_pos = pos - 1 - Tnext = self.tensors[next_pos] - T_bonds = "svp" - Q_bonds = "avp" - R_bonds = "as" - Tnext_bonds = "VsP" - result_bonds = "VaP" - else: - raise ValueError("Argument form must be a value in DirectionMPS.") - - # Apply QR decomposition - self._logger.debug(f"QR decompose a {T.nbytes / 2**20} MiB tensor.") - - subscripts = T_bonds + "->" + Q_bonds + "," + R_bonds - options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - Q, R = tensor.decompose( - subscripts, T, method=tensor.QRMethod(), options=options - ) - self._logger.debug(f"QR decomposition finished.") - - # Contract R into Tnext - subscripts = R_bonds + "," + Tnext_bonds + "->" + result_bonds - result = cq.contract(subscripts, R, Tnext) - self._logger.debug(f"Contraction with {next_pos} applied.") - - # Update self.tensors - self.tensors[pos] = Q - self.canonical_form[pos] = form # type: ignore - self.tensors[next_pos] = result - self.canonical_form[next_pos] = None - - def vdot(self, other: MPS) -> complex: - """Obtain the inner product of the two MPS: ````. - - It can be used to compute the squared norm of an MPS ``mps`` as - ``mps.vdot(mps)``. The tensors within the MPS are not modified. - - Note: - The state that is conjugated is ``self``. - - Args: - other: The other MPS to compare against. - - Returns: - The resulting complex number. - - Raises: - RuntimeError: If number of tensors, dimensions or positions do not match. - RuntimeError: If there are no tensors in the MPS. - RuntimeError: If the ``CuTensorNetHandle`` is out of scope. - """ - if self._lib._is_destroyed: - raise RuntimeError( - "The cuTensorNet library handle is out of scope.", - "See the documentation of update_libhandle and CuTensorNetHandle.", - ) - - if len(self) != len(other): - raise RuntimeError("Number of tensors do not match.") - for i in range(len(self)): - if self.get_physical_dimension(i) != other.get_physical_dimension(i): - raise RuntimeError( - f"Physical bond dimension at position {i} do not match." - ) - if self.qubit_position != other.qubit_position: - raise RuntimeError( - "The qubit labels or their position on the MPS do not match." - ) - if len(self) == 0: - raise RuntimeError("There are no tensors in the MPS.") - - self._flush() - other._flush() - - # Special case if only one tensor remains - if len(self) == 1: - self._logger.debug("Applying trivial vdot on single tensor MPS.") - result = cq.contract("LRp,lrp->", self.tensors[0].conj(), other.tensors[0]) - - else: - self._logger.debug("Applying vdot between two MPS.") - - # The two MPS will be contracted from left to right, storing the - # ``partial_result`` tensor. - partial_result = cq.contract( - "LRp,lrp->Rr", self.tensors[0].conj(), other.tensors[0] - ) - # Contract all tensors in the middle - for pos in range(1, len(self) - 1): - partial_result = cq.contract( - "Ll,LRp,lrp->Rr", - partial_result, - self.tensors[pos].conj(), - other.tensors[pos], - ) - # Finally, contract the last tensor - result = cq.contract( - "Ll,LRp,lrp->", # R and r are dim 1, so they are omitted; scalar result - partial_result, - self.tensors[-1].conj(), - other.tensors[-1], - ) - - self._logger.debug(f"Result from vdot={result}") - return complex(result) - - def sample(self) -> dict[Qubit, int]: - """Returns a sample from a Z measurement applied on every qubit. - - Notes: - The MPS ``self`` is not updated. This is equivalent to applying - ``mps = self.copy()`` then ``mps.measure(mps.get_qubits())``. - - Returns: - A dictionary mapping each of the qubits in the MPS to their 0 or 1 outcome. - """ - - # TODO: Copying is not strictly necessary, but to avoid it we would need to - # modify the algorithm in `measure`. This may be done eventually if `copy` - # is shown to be a bottleneck when sampling (which is likely). - mps = self.copy() - return mps.measure(mps.get_qubits()) - - def measure(self, qubits: set[Qubit]) -> dict[Qubit, int]: - """Applies a Z measurement on ``qubits``, updates the MPS and returns outcome. - - Notes: - After applying this function, ``self`` will contain the MPS of the projected - state over the non-measured qubits. - - The resulting state has been normalised. - - Args: - qubits: The subset of qubits to be measured. - - Returns: - A dictionary mapping the given ``qubits`` to their measurement outcome, - i.e. either ``0`` or ``1``. - - Raises: - ValueError: If an element in ``qubits`` is not a qubit in the MPS. - """ - result = dict() - - # Obtain the positions that need to be measured and build the reverse dict - position_qubit_map = dict() - for q in qubits: - if q not in self.qubit_position: - raise ValueError(f"Qubit {q} is not a qubit in the MPS.") - position_qubit_map[self.qubit_position[q]] = q - positions = sorted(position_qubit_map.keys()) - self._logger.debug(f"Measuring qubits={position_qubit_map}") - - # Tensor for postselection to |0> - zero_tensor = cp.zeros(2, dtype=self._cfg._complex_t) - zero_tensor[0] = 1 - - # Measure and postselect each of the positions, one by one - while positions: - pos = positions.pop() # The rightmost position to be measured - - # Convert to canonical form with center at this position - self.canonicalise(pos, pos) - - # Glossary of bond IDs: - # l -> left virtual bond of tensor in `pos` - # r -> right virtual bond of tensor in `pos` - # p -> physical bond of tensor in `pos` - # P -> physical bond of tensor in `pos` (copy) - - # Take the tensor in this position and obtain its prob for |0>. - # Since the MPS is in canonical form, this corresponds to the probability - # if we were to take all of the other tensors into account. - prob = cq.contract( - "lrp,lrP,p,P->", # No open bonds remain; this is just a scalar - self.tensors[pos].conj(), - self.tensors[pos], - zero_tensor, - zero_tensor, - ) - - # Throw a coin to decide measurement outcome - outcome = 0 if prob > random() else 1 - result[position_qubit_map[pos]] = outcome - self._logger.debug(f"Outcome of qubit at {pos} is {outcome}.") - - # Postselect the MPS for this outcome, renormalising at the same time - postselection_tensor = cp.zeros(2, dtype=self._cfg._complex_t) - postselection_tensor[outcome] = 1 / np.sqrt( - abs(outcome - prob) - ) # Normalise - - self._postselect_qubit(position_qubit_map[pos], postselection_tensor) - - return result - - def postselect(self, qubit_outcomes: dict[Qubit, int]) -> float: - """Applies a postselection, updates the MPS and returns its probability. - - Notes: - After applying this function, ``self`` will contain the MPS of the projected - state over the non-postselected qubits. - - The resulting state has been normalised. - - Args: - qubit_outcomes: A dictionary mapping a subset of qubits in the MPS to their - desired outcome value (either ``0`` or ``1``). - - Returns: - The probability of this postselection to occur in a measurement. - - Raises: - ValueError: If a key in ``qubit_outcomes`` is not a qubit in the MPS. - ValueError: If a value in ``qubit_outcomes`` is other than ``0`` or ``1``. - ValueError: If all of the qubits in the MPS are being postselected. Instead, - you may wish to use ``get_amplitude()``. - """ - for q, v in qubit_outcomes.items(): - if q not in self.qubit_position: - raise ValueError(f"Qubit {q} is not a qubit in the MPS.") - if v not in {0, 1}: - raise ValueError(f"Outcome of {q} cannot be {v}. Choose int 0 or 1.") - - if len(qubit_outcomes) == len(self): - raise ValueError( - "Cannot postselect all qubits. You may want to use get_amplitude()." - ) - self._logger.debug(f"Postselecting qubits={qubit_outcomes}") - - # Apply a postselection for each of the qubits - for qubit, outcome in qubit_outcomes.items(): - # Create the rank-1 postselection tensor - postselection_tensor = cp.zeros(2, dtype=self._cfg._complex_t) - postselection_tensor[outcome] = 1 - # Apply postselection - self._postselect_qubit(qubit, postselection_tensor) - - # Calculate the squared norm of the postselected state; this is its probability - prob = self.vdot(self) - assert np.isclose(prob.imag, 0.0, atol=self._cfg._atol) - prob = prob.real - - # Renormalise; it suffices to update the first tensor - if len(self) > 0 and not np.isclose(prob, 0.0, atol=self._cfg._atol): - self.tensors[0] = self.tensors[0] / np.sqrt(prob) - self.canonical_form[0] = None - - self._logger.debug(f"Probability of this postselection is {prob}.") - return prob - - def _postselect_qubit(self, qubit: Qubit, postselection_tensor: cp.ndarray) -> None: - """Postselect the qubit with the given tensor.""" - - pos = self.qubit_position[qubit] - self.tensors[pos] = cq.contract( - "lrp,p->lr", - self.tensors[pos], - postselection_tensor, - ) - - # Glossary of bond IDs: - # s -> shared bond between tensor in `pos` and next - # v -> the other virtual bond of tensor in `pos` - # V -> the other virtual bond of tensor in next position - # p -> physical bond of tensor in `pos` - # P -> physical bond of tensor in next position - - if len(self) == 1: # This is the last tensor - pass - - elif pos != 0: # Contract with next tensor on the left - self.tensors[pos - 1] = cq.contract( - "sv,VsP->VvP", - self.tensors[pos], - self.tensors[pos - 1], - ) - self.canonical_form[pos - 1] = None - else: # There are no tensors on the left, contract with the one on the right - self.tensors[pos + 1] = cq.contract( - "vs,sVP->vVP", - self.tensors[pos], - self.tensors[pos + 1], - ) - self.canonical_form[pos + 1] = None - - # Shift all entries after `pos` to the left - for q, p in self.qubit_position.items(): - if pos < p: - self.qubit_position[q] = p - 1 - for p in range(pos, len(self) - 1): - self.canonical_form[p] = self.canonical_form[p + 1] - - # Remove the entry from the data structures - del self.qubit_position[qubit] - del self.canonical_form[len(self) - 1] - self.tensors.pop(pos) - - def expectation_value(self, pauli_string: QubitPauliString) -> float: - """Obtains the expectation value of the Pauli string observable. - - Args: - pauli_string: A pytket object representing a tensor product of Paulis. - - Returns: - The expectation value. - - Raises: - ValueError: If a key in ``pauli_string`` is not a qubit in the MPS. - """ - for q in pauli_string.map.keys(): - if q not in self.qubit_position: - raise ValueError(f"Qubit {q} is not a qubit in the MPS.") - - self._logger.debug(f"Calculating expectation value of {pauli_string}.") - mps_copy = self.copy() - pauli_optype = {Pauli.Z: OpType.Z, Pauli.X: OpType.X, Pauli.Y: OpType.Y} - - # Apply each of the Pauli operators to the MPS copy - for qubit, pauli in pauli_string.map.items(): - if pauli != Pauli.I: - pos = mps_copy.qubit_position[qubit] - pauli_unitary = Op.create(pauli_optype[pauli]).get_unitary() - pauli_tensor = cp.asarray( - pauli_unitary.astype(dtype=self._cfg._complex_t, copy=False), - dtype=self._cfg._complex_t, - ) - - # Contract the Pauli to the MPS tensor of the corresponding qubit - mps_copy.tensors[pos] = cq.contract( - "lrp,Pp->lrP", mps_copy.tensors[pos], pauli_tensor - ) - - # Obtain the inner product - value = self.vdot(mps_copy) - assert np.isclose(value.imag, 0.0, atol=self._cfg._atol) - - self._logger.debug(f"Expectation value is {value.real}.") - return value.real - - def get_statevector(self) -> np.ndarray: - """Returns the statevector with qubits in Increasing Lexicographic Order (ILO). - - Raises: - ValueError: If there are no qubits left in the MPS. - """ - if len(self) == 0: - raise ValueError("There are no qubits left in this MPS.") - - # If there is only one qubit left, it is trivial - if len(self) == 1: - result_tensor = self.tensors[0] - - else: - # Create the interleaved representation with all tensors - interleaved_rep = [] - for pos in range(len(self)): - interleaved_rep.append(self.tensors[pos]) - interleaved_rep.append( - ["v" + str(pos), "v" + str(pos + 1), "p" + str(pos)] - ) - - # Specify the output bond IDs in ILO order - output_bonds = [] - for q in sorted(self.qubit_position.keys()): - output_bonds.append("p" + str(self.qubit_position[q])) - interleaved_rep.append(output_bonds) - - # Contract - result_tensor = cq.contract(*interleaved_rep) - - # Convert to numpy vector and flatten - statevector: np.ndarray = cp.asnumpy(result_tensor).flatten() - return statevector - - def get_amplitude(self, state: int) -> complex: - """Returns the amplitude of the chosen computational state. - - Notes: - The result is equivalent to ``mps.get_statevector[b]``, but this method - is faster when querying a single amplitude (or just a few). - - Args: - state: The integer whose bitstring describes the computational state. - The qubits in the bitstring are in increasing lexicographic order. - - Returns: - The amplitude of the computational state in the MPS. - """ - - # Find out what the map MPS_position -> bit value is - ilo_qubits = sorted(self.qubit_position.keys()) - mps_pos_bitvalue = dict() - - for i, q in enumerate(ilo_qubits): - pos = self.qubit_position[q] - bitvalue = 1 if state & 2 ** (len(self) - i - 1) else 0 - mps_pos_bitvalue[pos] = bitvalue - - # Carry out the contraction, starting from a dummy tensor - result_tensor = cp.ones(1, dtype=self._cfg._complex_t) # rank-1, dimension 1 - - for pos in range(len(self)): - postselection_tensor = cp.zeros(2, dtype=self._cfg._complex_t) - postselection_tensor[mps_pos_bitvalue[pos]] = 1 - # Contract postselection with qubit into the result_tensor - result_tensor = cq.contract( - "l,lrp,p->r", result_tensor, self.tensors[pos], postselection_tensor - ) - - assert result_tensor.shape == (1,) - result = complex(result_tensor[0]) - self._logger.debug(f"Amplitude of state {state} is {result}.") - return result - - def get_qubits(self) -> set[Qubit]: - """Returns the set of qubits that this MPS is defined on.""" - return set(self.qubit_position.keys()) - - def get_virtual_dimensions(self, position: int) -> tuple[int, int]: - """Returns the virtual bonds dimension of the tensor ``tensors[position]``. - - Args: - position: A position in the MPS. - - Returns: - A tuple where the first element is the dimensions of the left virtual bond - and the second elements is that of the right virtual bond. - - Raises: - RuntimeError: If ``position`` is out of bounds. - """ - if position < 0 or position >= len(self): - raise RuntimeError(f"Position {position} is out of bounds.") - - virtual_dims: tuple[int, int] = self.tensors[position].shape[:2] - return virtual_dims - - def get_physical_dimension(self, position: int) -> int: - """Returns the physical bond dimension of the tensor ``tensors[position]``. - - Args: - position: A position in the MPS. - - Returns: - The dimension of the physical bond. - - Raises: - RuntimeError: If ``position`` is out of bounds. - """ - if position < 0 or position >= len(self): - raise RuntimeError(f"Position {position} is out of bounds.") - - physical_dim: int = self.tensors[position].shape[2] - return physical_dim - - def get_byte_size(self) -> int: - """ - Returns: - The number of bytes the MPS currently occupies in GPU memory. - """ - return sum(t.nbytes for t in self.tensors) - - def get_device_id(self) -> int: - """ - Returns: - The identifier of the device (GPU) where the tensors are stored. - """ - return int(self.tensors[0].device) - - def update_libhandle(self, libhandle: CuTensorNetHandle) -> None: - """Update the ``CuTensorNetHandle`` used by this ``MPS`` object. Multiple - objects may use the same handle. - - Args: - libhandle: The new cuTensorNet library handle. - - Raises: - RuntimeError: If the device (GPU) where ``libhandle`` was initialised - does not match the one where the tensors of the MPS are stored. - """ - if libhandle.device_id != self.get_device_id(): - raise RuntimeError( - "Device of libhandle is not the one where the MPS is stored.", - f"{libhandle.device_id} != {self.get_device_id()}", - ) - self._lib = libhandle - - def copy(self) -> MPS: - """ - Returns: - A deep copy of the MPS on the same device. - """ - self._flush() - - # Create a dummy object - new_mps = MPS(self._lib, qubits=[], config=self._cfg.copy()) - # Copy all data - new_mps.fidelity = self.fidelity - new_mps.tensors = [t.copy() for t in self.tensors] - new_mps.canonical_form = self.canonical_form.copy() - new_mps.qubit_position = self.qubit_position.copy() - - self._logger.debug( - "Successfully copied an MPS " - f"of size {new_mps.get_byte_size() / 2**20} MiB." - ) - return new_mps - - def __len__(self) -> int: - """ - Returns: - The number of tensors in the MPS. - """ - return len(self.tensors) - - def _apply_1q_gate(self, position: int, gate: Op) -> MPS: - raise NotImplementedError( - "MPS is a base class with no contraction algorithm implemented." - + " You must use a subclass of MPS, such as MPSxGate or MPSxMPO." - ) - - def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPS: - raise NotImplementedError( - "MPS is a base class with no contraction algorithm implemented." - + " You must use a subclass of MPS, such as MPSxGate or MPSxMPO." - ) - - def _flush(self) -> None: - # Does nothing in the general MPS case; but children classes with batched - # gate contraction will redefine this method so that the last batch of - # gates is applied. - return None diff --git a/pytket/extensions/cutensornet/mps/mps_gate.py b/pytket/extensions/cutensornet/mps/mps_gate.py deleted file mode 100644 index c7dc496e..00000000 --- a/pytket/extensions/cutensornet/mps/mps_gate.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright 2019-2023 Quantinuum -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -## -# http://www.apache.org/licenses/LICENSE-2.0 -## -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import annotations # type: ignore -import warnings -import logging - -import numpy as np # type: ignore - -try: - import cupy as cp # type: ignore -except ImportError: - 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) - -from pytket.circuit import Op -from .mps import MPS - - -class MPSxGate(MPS): - """Implements a gate-by-gate contraction algorithm to calculate the output state - of a circuit as an ``MPS``. The algorithm is described in: - https://arxiv.org/abs/2002.07730 - """ - - def _apply_1q_gate(self, position: int, gate: Op) -> MPSxGate: - """Applies the 1-qubit gate to the MPS. - - This does not increase the dimension of any bond. - - Args: - position: The position of the MPS tensor that this gate - is applied to. - gate: The gate to be applied. - - Returns: - ``self``, to allow for method chaining. - """ - - # Load the gate's unitary to the GPU memory - gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) - gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) - - # Glossary of bond IDs - # p -> physical bond of the MPS tensor - # v -> one of the virtual bonds of the MPS tensor - # V -> the other virtual bond of the MPS tensor - # o -> the output bond of the gate - - T_bonds = "vVp" - result_bonds = "vVo" - gate_bonds = "op" - - # Contract - new_tensor = cq.contract( - gate_bonds + "," + T_bonds + "->" + result_bonds, - gate_tensor, - self.tensors[position], - ) - - # Update ``self.tensors`` - self.tensors[position] = new_tensor - return self - - def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: - """Applies the 2-qubit gate to the MPS. - - If doing so increases the virtual bond dimension beyond ``chi``; - truncation is automatically applied. - The MPS is converted to canonical form before truncating. - - Args: - positions: The position of the MPS tensors that this gate - is applied to. They must be contiguous. - gate: The gate to be applied. - - Returns: - ``self``, to allow for method chaining. - """ - l_pos = min(positions) - r_pos = max(positions) - - # Figure out the new dimension of the shared virtual bond - new_dim = 2 * min( - self.get_virtual_dimensions(l_pos)[0], - self.get_virtual_dimensions(r_pos)[1], - ) - - # Canonicalisation may be required if `new_dim` is larger than `chi` - # or if set by `truncation_fidelity` - if new_dim > self._cfg.chi or self._cfg.truncation_fidelity < 1: - # If truncation required, convert to canonical form before - # contracting. Avoids the need to apply gauge transformations - # to the larger tensor resulting from the contraction. - self.canonicalise(l_pos, r_pos) - - # Since canonicalisation may change the dimension of the bonds, - # we need to recalculate the value of `new_dim` - new_dim = 2 * min( - self.get_virtual_dimensions(l_pos)[0], - self.get_virtual_dimensions(r_pos)[1], - ) - - # Load the gate's unitary to the GPU memory - gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) - gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) - - # Reshape into a rank-4 tensor - gate_tensor = cp.reshape(gate_tensor, (2, 2, 2, 2)) - - # Glossary of bond IDs - # l -> physical bond of the left tensor in the MPS - # r -> physical bond of the right tensor in the MPS - # L -> left bond of the outcome of the gate - # R -> right bond of the outcome of the gate - # a,b,c -> the virtual bonds of the tensors - - if l_pos == positions[0]: - gate_bonds = "LRlr" - else: # Implicit swap - gate_bonds = "RLrl" - - left_bonds = "abl" - right_bonds = "bcr" - result_bonds = "acLR" - - # Contract - self._logger.debug("Contracting the two-qubit gate with its site tensors...") - T = cq.contract( - gate_bonds + "," + left_bonds + "," + right_bonds + "->" + result_bonds, - gate_tensor, - self.tensors[l_pos], - self.tensors[r_pos], - ) - self._logger.debug(f"Intermediate tensor of size (MiB)={T.nbytes / 2**20}") - - # Get the template of the MPS tensors involved - L = self.tensors[l_pos] - l_shape = list(L.shape) - R = self.tensors[r_pos] - r_shape = list(R.shape) - - if self._cfg.truncation_fidelity < 1: - # Carry out SVD decomposition first with NO truncation - # to figure out where to apply the dimension cutoff. - # Then, apply S normalisation and contraction of S and L manually. - # - # TODO: As soon as cuQuantum 23.09 is released, replace this - # unintuitive code with a simple update to SVDConfig so that it - # uses REL_SUM2_CUTOFF. Then the code in the `else` block should - # be run; i.e. use standard cuTensorNet API to do the SVD - # including normalisation and contraction of S with L. - self._logger.debug( - f"Truncating to target fidelity={self._cfg.truncation_fidelity}" - ) - - options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - svd_method = tensor.SVDMethod(abs_cutoff=self._cfg.zero) - L, S, R = tensor.decompose( - "acLR->asL,scR", T, method=svd_method, options=options - ) - - # Use the fact that the entries of S are sorted in decreasing - # order and calculate the number of singular values `new_dim` to - # keep so that - # sum([s**2 for s in S']) - # truncation_fidelity <= ------------------------- - # sum([s**2 for s in S]) - # - # where S is the list of original singular values and S' is the set of - # singular values that remain after truncation (before normalisation). - denom = float(sum(cp.square(S))) # Element-wise squaring - numer = 0.0 - old_dim = new_dim - new_dim = 0 - - # Take singular values until we surpass the target fidelity - while self._cfg.truncation_fidelity > numer / denom: - numer += float(S[new_dim] ** 2) - new_dim += 1 - this_fidelity = numer / denom - - # Reshape tensors down to `new_dim` for the virtual bond - # No data is copied or moved around, we're changing the ndarray bounds - l_shape[-2] = new_dim - # pylint: disable = unexpected-keyword-arg # Disable pylint for next line - L = cp.ndarray( - l_shape, - dtype=self._cfg._complex_t, - memptr=L.data, - strides=L.strides, - ) - r_shape[0] = new_dim - # pylint: disable = unexpected-keyword-arg # Disable pylint for next line - R = cp.ndarray( - r_shape, - dtype=self._cfg._complex_t, - memptr=R.data, - strides=R.strides, - ) - # pylint: disable = unexpected-keyword-arg # Disable pylint for next line - S = cp.ndarray(new_dim, dtype=self._cfg._real_t, memptr=S.data) - - # Normalise - S *= np.sqrt(1 / this_fidelity) - - # Contract S into L - S = S.astype(dtype=self._cfg._complex_t, copy=False) - # Use some einsum index magic: since the virtual bond "s" appears in the - # list of bonds of the output, it is not summed over. - # This causes S to act as the intended diagonal matrix. - L = cq.contract("asL,s->asL", L, S) - - # We multiply the fidelity of the current step to the overall fidelity - # to keep track of a lower bound for the fidelity. - self.fidelity *= this_fidelity - - # Report to logger - self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") - self._logger.debug( - f"Reduced virtual bond dimension from {old_dim} to {new_dim}." - ) - - elif new_dim > self._cfg.chi: - # Apply SVD decomposition and truncate up to a `max_extent` (for the shared - # bond) of `self._cfg.chi`. Ask cuTensorNet to contract S directly into the - # L tensor and normalise the singular values so that the sum of its squares - # is equal to one (i.e. the MPS is a normalised state after truncation). - self._logger.debug(f"Truncating to (or below) chosen chi={self._cfg.chi}") - - options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - svd_method = tensor.SVDMethod( - abs_cutoff=self._cfg.zero, - max_extent=self._cfg.chi, - partition="U", # Contract S directly into U (named L in our case) - normalization="L2", # Sum of squares equal 1 - ) - - L, S, R, svd_info = tensor.decompose( - "acLR->asL,scR", T, method=svd_method, options=options, return_info=True - ) - assert S is None # Due to "partition" option in SVDMethod - - # discarded_weight is calculated within cuTensorNet as: - # sum([s**2 for s in S']) - # discarded_weight = 1 - ------------------------- - # sum([s**2 for s in S]) - # where S is the list of original singular values and S' is the set of - # singular values that remain after truncation (before normalisation). - # It can be shown that the fidelity ||^2 (for |phi> and |psi> - # unit vectors before and after truncation) is equal to 1 - disc_weight. - # - # We multiply the fidelity of the current step to the overall fidelity - # to keep track of a lower bound for the fidelity. - this_fidelity = 1.0 - svd_info.discarded_weight - self.fidelity *= this_fidelity - - # Report to logger - self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") - self._logger.debug( - f"Reduced virtual bond dimension from {new_dim} to {R.shape[0]}." - ) - - else: - # The user did not explicitly ask for truncation, but it is advantageous to - # remove any singular values below ``self._cfg.zero``. - self._logger.debug(f"Truncating singular values below={self._cfg.zero}.") - if self._cfg.zero > self._cfg._atol / 1000: - self._logger.info( # This was raised as a warning in ConfigMPS already - "Your chosen value_of_zero is relatively large. " - "Faithfulness of final fidelity estimate is not guaranteed." - ) - - # NOTE: There is no guarantee of canonical form in this case. This is fine - # since canonicalisation is just meant to detect the optimal singular values - # to truncate, but if we find values that are essentially zero, we are safe - # to remove them. - options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - svd_method = tensor.SVDMethod( - abs_cutoff=self._cfg.zero, - partition="U", # Contract S directly into U (named L in our case) - normalization=None, # Without canonicalisation we must not normalise - ) - L, S, R = tensor.decompose( - "acLR->asL,scR", T, method=svd_method, options=options - ) - assert S is None # Due to "partition" option in SVDMethod - - # Report to logger - self._logger.debug(f"Truncation done. Fidelity estimate unchanged.") - self._logger.debug( - f"Reduced virtual bond dimension from {new_dim} to {R.shape[0]}." - ) - - self.tensors[l_pos] = L - self.tensors[r_pos] = R - - # If requested, provide info about memory usage. - if self._logger.isEnabledFor(logging.INFO): - # If-statetement used so that we only call `get_byte_size` if needed. - self._logger.info(f"MPS size (MiB)={self.get_byte_size() / 2**20}") - self._logger.info(f"MPS fidelity={self.fidelity}") - - return self diff --git a/pytket/extensions/cutensornet/mps/mps_mpo.py b/pytket/extensions/cutensornet/mps/mps_mpo.py deleted file mode 100644 index 47274262..00000000 --- a/pytket/extensions/cutensornet/mps/mps_mpo.py +++ /dev/null @@ -1,535 +0,0 @@ -# Copyright 2019-2023 Quantinuum -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -## -# http://www.apache.org/licenses/LICENSE-2.0 -## -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from __future__ import annotations # type: ignore -import warnings - -from typing import Optional, Union - -import numpy as np # type: ignore - -try: - import cupy as cp # type: ignore -except ImportError: - 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) - -from pytket.circuit import Op, Qubit -from .mps import ( - CuTensorNetHandle, - DirectionMPS, - ConfigMPS, - Tensor, - MPS, -) -from .mps_gate import MPSxGate - - -class MPSxMPO(MPS): - """Implements a batched--gate contraction algorithm (DMRG-like) to calculate - the output state of a circuit as an ``MPS``. The algorithm is described - in: https://arxiv.org/abs/2207.05612. - """ - - def __init__( - self, - libhandle: CuTensorNetHandle, - qubits: list[Qubit], - config: ConfigMPS, - ): - """Initialise an MPS on the computational state ``|0>``. - - Note: - A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` - statement. The device where the MPS is stored will match the one specified - by the library handle. - - Args: - libhandle: The cuTensorNet library handle that will be used to carry out - tensor operations on the MPS. - qubits: The list of qubits in the circuit to be simulated. - config: The object describing the configuration for simulation. - """ - super().__init__(libhandle, qubits, config) - - # Initialise the MPO data structure. This will keep a list of the gates - # batched for application to the MPS; all of them will be applied at - # once when deemed appropriate or when calling ._flush(), removing them - # from here. The gates are kept in a list of lists. - # - # One list per MPS position, containing all the tensors of the gates - # acting on the corresponding position. These lists are originally empty. - # The last element of each list corresponds to the last gate applied. - # - # Each of the tensors will have four bonds ordered as follows: - # [input, left, right, output] - self._mpo: list[list[Tensor]] = [list() for _ in qubits] - # This ``_bond_ids`` store global bond IDs of MPO tensors, used by ``_flush()`` - self._bond_ids: list[list[tuple[int, int, int, int]]] = [list() for _ in qubits] - - # Initialise the MPS that we will use as first approximation of the - # variational algorithm. - self._aux_mps = MPSxGate(libhandle, qubits, config) - - self._mpo_bond_counter = 0 - - def update_libhandle(self, libhandle: CuTensorNetHandle) -> None: - """Set the library handle used by this ``MPS`` object. Multiple objects - may use the same library handle. - - Args: - libhandle: The new cuTensorNet library handle. - - Raises: - RuntimeError: If the device (GPU) where ``libhandle`` was initialised - does not match the one where the tensors of the MPS are stored. - """ - super().update_libhandle(libhandle) - self._aux_mps.update_libhandle(libhandle) - - def _apply_1q_gate(self, position: int, gate: Op) -> MPSxMPO: - """Applies the 1-qubit gate to the MPS. - - This does not increase the dimension of any bond. - - Args: - position: The position of the MPS tensor that this gate - is applied to. - gate: The gate to be applied. - - Returns: - ``self``, to allow for method chaining. - """ - - # Apply the gate to the MPS with eager approximation - self._aux_mps._apply_1q_gate(position, gate) - - # Load the gate's unitary to the GPU memory - gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) - gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) - - # Glossary of bond IDs - # i -> input to the MPO tensor - # o -> output of the MPO tensor - # l -> left virtual bond of the MPO tensor - # r -> right virtual bond of the MPO tensor - # g -> output bond of the gate tensor - - # Identify the tensor to contract the gate with - if self._mpo[position]: # Not empty - last_tensor = self._mpo[position][-1] - last_bonds = "ilro" - new_bonds = "ilrg" - else: # Use the MPS tensor - last_tensor = self.tensors[position] - last_bonds = "lro" - new_bonds = "lrg" - - # Contract - new_tensor = cq.contract( - "go," + last_bonds + "->" + new_bonds, - gate_tensor, - last_tensor, - ) - - # Update the tensor - if self._mpo[position]: # Not empty - self._mpo[position][-1] = new_tensor - else: # Update the MPS tensor - self.tensors[position] = new_tensor - - return self - - def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxMPO: - """Applies the 2-qubit gate to the MPS. - - If doing so increases the virtual bond dimension beyond ``chi``; - truncation is automatically applied. - The MPS is converted to canonical form before truncating. - - Args: - positions: The position of the MPS tensors that this gate - is applied to. They must be contiguous. - gate: The gate to be applied. - - Returns: - ``self``, to allow for method chaining. - """ - l_pos = min(positions) - r_pos = max(positions) - - # Check whether the MPO is large enough to flush it - if any(len(self._mpo[pos]) >= self._cfg.k for pos in [l_pos, r_pos]): - self._flush() - - # Apply the gate to the MPS with eager approximation - self._aux_mps._apply_2q_gate(positions, gate) - - # Load the gate's unitary to the GPU memory - gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) - gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) - - # Reshape into a rank-4 tensor - gate_tensor = cp.reshape(gate_tensor, (2, 2, 2, 2)) - - # Glossary of bond IDs - # l -> gate's left input bond - # r -> gate's right input bond - # L -> gate's left output bond - # R -> gate's right output bond - # s -> virtual bond after QR decomposition - - # Assign the bond IDs for the gate - if l_pos == positions[0]: - gate_bonds = "LRlr" - else: # Implicit swap - gate_bonds = "RLrl" - - # Apply a QR decomposition on the gate_tensor to shape it as an MPO - options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - L, R = tensor.decompose( - gate_bonds + "->lsL,rsR", - gate_tensor, - method=tensor.QRMethod(), - options=options, - ) - - # Add dummy bonds of dimension 1 to L and R so that they have the right shape - L = cp.reshape(L, (2, 1, 4, 2)) - R = cp.reshape(R, (2, 4, 1, 2)) - - # Store L and R - self._mpo[l_pos].append(L) - self._mpo[r_pos].append(R) - # And assign their global bonds - shared_bond_id = self._new_bond_id() - self._bond_ids[l_pos].append( - ( - self._get_physical_bond(l_pos), - self._new_bond_id(), - shared_bond_id, - self._new_bond_id(), - ) - ) - self._bond_ids[r_pos].append( - ( - self._get_physical_bond(r_pos), - shared_bond_id, - self._new_bond_id(), - self._new_bond_id(), - ) - ) - return self - - def get_physical_dimension(self, position: int) -> int: - """Returns the dimension of the physical bond at ``position``. - - Args: - position: A position in the MPS. - - Returns: - The dimension of the physical bond. - - Raises: - RuntimeError: If ``position`` is out of bounds. - """ - if position < 0 or position >= len(self): - raise RuntimeError(f"Position {position} is out of bounds.") - - # Identify the tensor last tensor in the MPO - if self._mpo[position]: # Not empty - last_tensor = self._mpo[position][-1] - else: # Use the MPS tensor - last_tensor = self.tensors[position] - - # By construction, the open bond is the last one - return int(last_tensor.shape[-1]) - - def _get_physical_bond(self, position: int) -> int: - """Returns the unique identifier of the physical bond at ``position``. - - Args - position: A position in the MPS. - - Returns: - The identifier of the physical bond. - - Raises: - RuntimeError: If ``position`` is out of bounds. - """ - if position < 0 or position >= len(self): - raise RuntimeError(f"Position {position} is out of bounds.") - - if self._bond_ids[position]: - return self._bond_ids[position][-1][-1] - else: - return self._new_bond_id() - - def _get_column_bonds(self, position: int, direction: DirectionMPS) -> list[int]: - """Returns the unique identifier of all the left (right) virtual bonds of - MPO tensors at ``position`` if ``direction`` is ``LEFT`` (``RIGHT``). - - Notes: - It does not return the corresponding bonds of the MPS tensors. - - Raises: - RuntimeError: If ``position`` is out of bounds. - ValueError: If ``direction`` is not a value in ``DirectionMPS``. - """ - if position < 0 or position >= len(self): - raise RuntimeError(f"Position {position} is out of bounds.") - - if direction == DirectionMPS.LEFT: - index = 1 # By convention, left bond at index 1 - elif direction == DirectionMPS.RIGHT: - index = 2 # By convention, right bond at index 2 - else: - raise ValueError("Argument form must be a value in DirectionMPS.") - - return [b_ids[index] for b_ids in self._bond_ids[position]] - - def _flush(self) -> None: - """Applies all batched operations within ``self._mpo`` to the MPS. - - The method applies variational optimisation of the MPS until it - converges. Based on https://arxiv.org/abs/2207.05612. - """ - self._logger.info("Applying variational optimisation.") - self._logger.info(f"Fidelity before optimisation={self._aux_mps.fidelity}") - - l_cached_tensors: list[Tensor] = [] - r_cached_tensors: list[Tensor] = [] - - def update_sweep_cache(pos: int, direction: DirectionMPS) -> None: - """Given a position in the MPS and a sweeping direction (see - ``DirectionMPS``), calculate the tensor of the partial contraction - of all MPS-MPO-vMPS* columns from ``pos`` towards ``direction``. - Update the cache accordingly. Applies canonicalisation on the vMPS - tensor before contracting. - """ - self._logger.debug("Updating the sweep cache...") - - # Canonicalise the tensor at ``pos`` - if direction == DirectionMPS.LEFT: - self._aux_mps.canonicalise_tensor(pos, form=DirectionMPS.RIGHT) - elif direction == DirectionMPS.RIGHT: - self._aux_mps.canonicalise_tensor(pos, form=DirectionMPS.LEFT) - - # Glossary of bond IDs - # p -> the physical bond of the MPS tensor - # l,r -> the virtual bonds of the MPS tensor - # L,R -> the virtual bonds of the variational MPS tensor - # P -> the physical bond of the variational MPS tensor - # MPO tensors will use ``self._bond_ids`` - - # Get the interleaved representation - interleaved_rep = [ - # The tensor of the MPS - self.tensors[pos], - ["l", "r", "p"], - # The (conjugated) tensor of the variational MPS - self._aux_mps.tensors[pos].conj(), - ["L", "R", "P" if self._mpo[pos] else "p"], - ] - for i, mpo_tensor in enumerate(self._mpo[pos]): - # The MPO tensor at this position - interleaved_rep.append(mpo_tensor) - - mpo_bonds: list[Union[int, str]] = list(self._bond_ids[pos][i]) - if i == 0: - # The input bond of the first MPO tensor must connect to the - # physical bond of the correspondong ``self.tensors`` tensor - mpo_bonds[0] = "p" - if i == len(self._mpo[pos]) - 1: - # The output bond of the last MPO tensor must connect to the - # physical bond of the corresponding ``self._aux_mps`` tensor - mpo_bonds[-1] = "P" - interleaved_rep.append(mpo_bonds) - - # Also contract the previous (cached) tensor during the sweep - if direction == DirectionMPS.LEFT: - if pos != len(self) - 1: # Otherwise, there is nothing cached yet - interleaved_rep.append(r_cached_tensors[-1]) - r_cached_bonds = self._get_column_bonds(pos + 1, DirectionMPS.LEFT) - interleaved_rep.append(["r", "R"] + r_cached_bonds) - elif direction == DirectionMPS.RIGHT: - if pos != 0: # Otherwise, there is nothing cached yet - interleaved_rep.append(l_cached_tensors[-1]) - l_cached_bonds = self._get_column_bonds(pos - 1, DirectionMPS.RIGHT) - interleaved_rep.append(["l", "L"] + l_cached_bonds) - - # Figure out the ID of the bonds of the contracted tensor - if direction == DirectionMPS.LEFT: - # Take the left bond of each of the MPO tensors - result_bonds = self._get_column_bonds(pos, DirectionMPS.LEFT) - # Take the left virtual bond of both of the MPS - interleaved_rep.append(["l", "L"] + result_bonds) - elif direction == DirectionMPS.RIGHT: - # Take the right bond of each of the MPO tensors - result_bonds = self._get_column_bonds(pos, DirectionMPS.RIGHT) - # Take the right virtual bond of both of the MPS - interleaved_rep.append(["r", "R"] + result_bonds) - - # Contract and store - T = cq.contract(*interleaved_rep) - if direction == DirectionMPS.LEFT: - r_cached_tensors.append(T) - elif direction == DirectionMPS.RIGHT: - l_cached_tensors.append(T) - - self._logger.debug("Completed update of the sweep cache.") - - def update_variational_tensor( - pos: int, left_tensor: Optional[Tensor], right_tensor: Optional[Tensor] - ) -> float: - """Update the tensor at ``pos`` of the variational MPS using ``left_tensor`` - (and ``right_tensor``) which is meant to contain the contraction of all - the left (and right) columns of the MPS-MPO-vMPS* network from ``pos``. - Contract these with the MPS-MPO column at ``pos``. - Return the current fidelity of this sweep. - """ - self._logger.debug(f"Optimising tensor at position={pos}") - - interleaved_rep = [ - # The tensor of the MPS - self.tensors[pos], - ["l", "r", "p"], - ] - result_bonds = ["l", "r", "p"] - - # The MPO tensors at position ``pos`` - for i, mpo_tensor in enumerate(self._mpo[pos]): - # The MPO tensor at this position - interleaved_rep.append(mpo_tensor) - - mpo_bonds: list[Union[int, str]] = list(self._bond_ids[pos][i]) - if i == 0: - # The input bond of the first MPO tensor must connect to the - # physical bond of the correspondong ``self.tensors`` tensor - mpo_bonds[0] = "p" - if i == len(self._mpo[pos]) - 1: - # The output bond of the last MPO tensor corresponds to the - # physical bond of the corresponding ``self._aux_mps`` tensor - mpo_bonds[-1] = "P" - result_bonds[-1] = "P" - interleaved_rep.append(mpo_bonds) - - if left_tensor is not None: - interleaved_rep.append(left_tensor) - left_tensor_bonds = self._get_column_bonds(pos - 1, DirectionMPS.RIGHT) - interleaved_rep.append(["l", "L"] + left_tensor_bonds) - result_bonds[0] = "L" - if right_tensor is not None: - interleaved_rep.append(right_tensor) - right_tensor_bonds = self._get_column_bonds(pos + 1, DirectionMPS.LEFT) - interleaved_rep.append(["r", "R"] + right_tensor_bonds) - result_bonds[1] = "R" - - # Append the bond IDs of the resulting tensor - interleaved_rep.append(result_bonds) - - # Contract and store tensor - F = cq.contract(*interleaved_rep) - - # Get the fidelity - optim_fidelity = complex(cq.contract("LRP,LRP->", F.conj(), F)) - assert np.isclose(optim_fidelity.imag, 0.0, atol=self._cfg._atol) - optim_fidelity = float(optim_fidelity.real) - - # Normalise F and update the variational MPS - self._aux_mps.tensors[pos] = F / np.sqrt(optim_fidelity) - - return optim_fidelity - - ################################## - # Variational sweeping algorithm # - ################################## - - # Begin by doing a sweep towards the left that does not update - # the variational tensors, but simply loads up the ``r_cached_tensors`` - for pos in reversed(range(1, len(self))): - update_sweep_cache(pos, direction=DirectionMPS.LEFT) - - prev_fidelity = -1.0 # Dummy value - sweep_fidelity = 0.0 # Dummy value - - # Repeat sweeps until the fidelity converges - sweep_direction = DirectionMPS.RIGHT - while not np.isclose(prev_fidelity, sweep_fidelity, atol=self._cfg.optim_delta): - self._logger.info(f"Doing another optimisation sweep...") - prev_fidelity = sweep_fidelity - - if sweep_direction == DirectionMPS.RIGHT: - sweep_fidelity = update_variational_tensor( - pos=0, left_tensor=None, right_tensor=r_cached_tensors.pop() - ) - update_sweep_cache(pos=0, direction=DirectionMPS.RIGHT) - - for pos in range(1, len(self) - 1): - sweep_fidelity = update_variational_tensor( - pos=pos, - left_tensor=l_cached_tensors[-1], - right_tensor=r_cached_tensors.pop(), - ) - update_sweep_cache(pos, direction=DirectionMPS.RIGHT) - # The last variational tensor is not updated; - # it'll be the first in the next sweep - - sweep_direction = DirectionMPS.LEFT - - elif sweep_direction == DirectionMPS.LEFT: - sweep_fidelity = update_variational_tensor( - pos=len(self) - 1, - left_tensor=l_cached_tensors.pop(), - right_tensor=None, - ) - update_sweep_cache(pos=len(self) - 1, direction=DirectionMPS.LEFT) - - for pos in reversed(range(1, len(self) - 1)): - sweep_fidelity = update_variational_tensor( - pos=pos, - left_tensor=l_cached_tensors.pop(), - right_tensor=r_cached_tensors[-1], - ) - update_sweep_cache(pos, direction=DirectionMPS.LEFT) - # The last variational tensor is not updated; - # it'll be the first in the next sweep - - sweep_direction = DirectionMPS.RIGHT - - self._logger.info( - "Optimisation sweep completed. " - f"Current fidelity={self.fidelity*sweep_fidelity}" - ) - - # Clear out the MPO - self._mpo = [list() for _ in range(len(self))] - self._bond_ids = [list() for _ in range(len(self))] - self._mpo_bond_counter = 0 - - # Update the MPS tensors - self.tensors = [t.copy() for t in self._aux_mps.tensors] - - # Update the fidelity estimate - self.fidelity *= sweep_fidelity - self._aux_mps.fidelity = self.fidelity - - self._logger.info(f"Final fidelity after optimisation={self.fidelity}") - - def _new_bond_id(self) -> int: - self._mpo_bond_counter += 1 - return self._mpo_bond_counter diff --git a/pytket/extensions/cutensornet/mps/simulation.py b/pytket/extensions/cutensornet/mps/simulation.py deleted file mode 100644 index 1d330013..00000000 --- a/pytket/extensions/cutensornet/mps/simulation.py +++ /dev/null @@ -1,246 +0,0 @@ -# Copyright 2019-2023 Quantinuum -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -## -# http://www.apache.org/licenses/LICENSE-2.0 -## -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from enum import Enum - -from random import choice # type: ignore -from collections import defaultdict # type: ignore -import numpy as np # type: ignore - -from pytket.circuit import Circuit, Command, Qubit -from pytket.transform import Transform -from pytket.architecture import Architecture -from pytket.passes import DefaultMappingPass -from pytket.predicates import CompilationUnit - -from pytket.extensions.cutensornet.general import set_logger -from .mps import CuTensorNetHandle, ConfigMPS, MPS -from .mps_gate import MPSxGate -from .mps_mpo import MPSxMPO - - -class ContractionAlg(Enum): - """An enum to refer to the MPS contraction algorithm. - - Each enum value corresponds to the class with the same name; see its docs for - information of the algorithm. - """ - - MPSxGate = 0 - MPSxMPO = 1 - - -def simulate( - libhandle: CuTensorNetHandle, - circuit: Circuit, - algorithm: ContractionAlg, - config: ConfigMPS, -) -> MPS: - """Simulate the given circuit and return the ``MPS`` representing the final state. - - Note: - A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` - statement. The device where the MPS is stored will match the one specified - by the library handle. - - The input ``circuit`` must be composed of one-qubit and two-qubit gates only. - Any gateset supported by ``pytket`` can be used. - - Two-qubit gates must act between adjacent qubits, i.e. on ``circuit.qubits[i]`` - and ``circuit.qubits[i+1]`` for any ``i``. If this is not satisfied by your - circuit, consider using ``prepare_circuit()`` on it. - - Args: - libhandle: The cuTensorNet library handle that will be used to carry out - tensor operations on the MPS. - circuit: The pytket circuit to be simulated. - algorithm: Choose between the values of the ``ContractionAlg`` enum. - config: The configuration object for simulation. - - Returns: - An instance of ``MPS`` containing (an approximation of) the final state - of the circuit. - """ - logger = set_logger("Simulation", level=config.loglevel) - - if algorithm == ContractionAlg.MPSxGate: - mps = MPSxGate( # type: ignore - libhandle, - circuit.qubits, - config, - ) - - elif algorithm == ContractionAlg.MPSxMPO: - mps = MPSxMPO( # type: ignore - libhandle, - circuit.qubits, - config, - ) - - # Sort the gates so there isn't much overhead from canonicalising back and forth. - logger.info( - "Ordering the gates in the circuit to reduce canonicalisation overhead." - ) - sorted_gates = _get_sorted_gates(circuit) - - logger.info("Running simulation...") - # Apply the gates - for i, g in enumerate(sorted_gates): - mps.apply_gate(g) - logger.info(f"Progress... {(100*i) // len(sorted_gates)}%") - - # Apply the batched operations that are left (if any) - mps._flush() - - # Apply the batched operations that are left (if any) - mps._flush() - - # Apply the circuit's phase to the leftmost tensor (any would work) - mps.tensors[0] = mps.tensors[0] * np.exp(1j * np.pi * circuit.phase) - - logger.info("Simulation completed.") - logger.info(f"Final MPS size={mps.get_byte_size() / 2**20} MiB") - logger.info(f"Final MPS fidelity={mps.fidelity}") - return mps - - -def prepare_circuit(circuit: Circuit) -> tuple[Circuit, dict[Qubit, Qubit]]: - """Prepares a circuit in a specific, ``MPS``-friendly, manner. - - Returns an equivalent circuit with the appropriate structure to be simulated by - an ``MPS`` algorithm. - - Note: - The qubits in the output circuit will be renamed. Implicit SWAPs may be added - to the circuit, meaning that the logical qubit held at the ``node[i]`` qubit - at the beginning of the circuit may differ from the one it holds at the end. - - Args: - circuit: The circuit to be simulated. - - Returns: - A tuple with an equivalent circuit with the appropriate structure and a - map of qubit names at the end of the circuit to their corresponding - original names. - """ - - # Implement it in a line architecture - cu = CompilationUnit(circuit) - architecture = Architecture([(i, i + 1) for i in range(circuit.n_qubits - 1)]) - DefaultMappingPass(architecture).apply(cu) - prep_circ = cu.circuit - Transform.DecomposeBRIDGE().apply(prep_circ) - - qubit_map: dict[Qubit, Qubit] = {} - for orig_q, arch_q in cu.final_map.items(): - assert isinstance(orig_q, Qubit) - assert isinstance(arch_q, Qubit) - qubit_map[arch_q] = orig_q - - return (prep_circ, qubit_map) - - -def _get_sorted_gates(circuit: Circuit) -> list[Command]: - """Sorts the list of gates, placing 2-qubit gates close to each other first. - - Returns an equivalent list of commands fixing the order of parallel gates so that - 2-qubit gates that are close to each other first. This reduces the overhead of - canonicalisation of the MPS, since we try to apply as many gates as we can on one - end of the MPS before we go to the other end. - - Args: - circuit: The original circuit. - - Returns: - The same gates, ordered in a beneficial way. - """ - - all_gates = circuit.get_commands() - sorted_gates = [] - # Keep track of the qubit at the center of the canonical form; start arbitrarily - current_qubit = circuit.qubits[0] - # Entries from `all_gates` that are not yet in `sorted_gates` - remaining = set(range(len(all_gates))) - - # Create the list of indices of gates acting on each qubit - gate_indices: dict[Qubit, list[int]] = defaultdict(list) - for i, g in enumerate(all_gates): - for q in g.qubits: - gate_indices[q].append(i) - # Apply all 1-qubit gates at the beginning of the circuit - for q, indices in gate_indices.items(): - while indices and len(all_gates[indices[0]].qubits) == 1: - i = indices.pop(0) - sorted_gates.append(all_gates[i]) - remaining.remove(i) - # Decide which 2-qubit gate to apply next - while remaining: - q_index = circuit.qubits.index(current_qubit) - # Find distance from q_index to first qubit with an applicable 2-qubit gate - left_distance = None - prev_q = current_qubit - for i, q in enumerate(reversed(circuit.qubits[:q_index])): - if ( - gate_indices[prev_q] - and gate_indices[q] - and gate_indices[prev_q][0] == gate_indices[q][0] - ): - left_distance = i - break - prev_q = q - right_distance = None - prev_q = current_qubit - for i, q in enumerate(circuit.qubits[q_index + 1 :]): - if ( - gate_indices[prev_q] - and gate_indices[q] - and gate_indices[prev_q][0] == gate_indices[q][0] - ): - right_distance = i - break - prev_q = q - # Choose the shortest distance - if left_distance is None and right_distance is None: - raise RuntimeError( - "Some two-qubit gate in the circuit is not acting between", - "nearest neighbour qubits. Consider using prepare_circuit().", - ) - elif left_distance is None: - assert right_distance is not None - current_qubit = circuit.qubits[q_index + right_distance] - elif right_distance is None: - current_qubit = circuit.qubits[q_index - left_distance] - elif left_distance < right_distance: - current_qubit = circuit.qubits[q_index - left_distance] - elif left_distance > right_distance: - current_qubit = circuit.qubits[q_index + right_distance] - else: - current_qubit = circuit.qubits[ - q_index + choice([-left_distance, right_distance]) - ] - # Apply the gate - i = gate_indices[current_qubit][0] - next_gate = all_gates[i] - sorted_gates.append(next_gate) - remaining.remove(i) - # Apply all 1-qubit gates after this gate - for q in next_gate.qubits: - gate_indices[q].pop(0) # Remove the 2-qubit gate `next_gate` - indices = gate_indices[q] - while indices and len(all_gates[indices[0]].qubits) == 1: - i = indices.pop(0) - sorted_gates.append(all_gates[i]) - remaining.remove(i) - - assert len(all_gates) == len(sorted_gates) - return sorted_gates diff --git a/tests/test_mps.py b/tests/test_mps.py index ab2c5b0d..57c744e0 100644 --- a/tests/test_mps.py +++ b/tests/test_mps.py @@ -8,7 +8,7 @@ from pytket.circuit import Circuit, Qubit, OpType # type: ignore from pytket.pauli import Pauli, QubitPauliString # type: ignore -from pytket.extensions.cutensornet.mps import ( +from pytket.extensions.cutensornet.states import ( CuTensorNetHandle, ConfigMPS, MPS, From 0647c392dccb7b9e48e2fc4e82e17829a27ae2ee Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 10 Oct 2023 03:43:27 -0600 Subject: [PATCH 17/83] Restoring source code in /states folder --- .../extensions/cutensornet/states/__init__.py | 39 + .../extensions/cutensornet/states/general.py | 57 ++ pytket/extensions/cutensornet/states/mps.py | 930 ++++++++++++++++++ .../extensions/cutensornet/states/mps_gate.py | 318 ++++++ .../extensions/cutensornet/states/mps_mpo.py | 535 ++++++++++ .../cutensornet/states/simulation.py | 247 +++++ 6 files changed, 2126 insertions(+) create mode 100644 pytket/extensions/cutensornet/states/__init__.py create mode 100644 pytket/extensions/cutensornet/states/general.py create mode 100644 pytket/extensions/cutensornet/states/mps.py create mode 100644 pytket/extensions/cutensornet/states/mps_gate.py create mode 100644 pytket/extensions/cutensornet/states/mps_mpo.py create mode 100644 pytket/extensions/cutensornet/states/simulation.py diff --git a/pytket/extensions/cutensornet/states/__init__.py b/pytket/extensions/cutensornet/states/__init__.py new file mode 100644 index 00000000..ce21e41e --- /dev/null +++ b/pytket/extensions/cutensornet/states/__init__.py @@ -0,0 +1,39 @@ +# Copyright 2019-2023 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +## +# http://www.apache.org/licenses/LICENSE-2.0 +## +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Module for circuit simulation by state evolution. +Approximate tensor network contraction is supported. Both MPS and TreeTN +methods are provided. +For an example of its use, see ``examples/mps_tutorial.ipynb`` in +https://github.com/CQCL/pytket-cutensornet. +""" + +from .general import CuTensorNetHandle + +from .mps import ( + DirectionMPS, + ConfigMPS, + Handle, + Tensor, + MPS, +) + +from .mps_gate import ( + MPSxGate, +) + +from .mps_mpo import ( + MPSxMPO, +) + +from .simulation import ContractionAlg, simulate, prepare_circuit diff --git a/pytket/extensions/cutensornet/states/general.py b/pytket/extensions/cutensornet/states/general.py new file mode 100644 index 00000000..fd1df311 --- /dev/null +++ b/pytket/extensions/cutensornet/states/general.py @@ -0,0 +1,57 @@ +# Copyright 2019-2023 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +## +# http://www.apache.org/licenses/LICENSE-2.0 +## +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations # type: ignore +import warnings +from typing import Any, Optional + +try: + import cupy as cp # type: ignore +except ImportError: + warnings.warn("local settings failed to import cupy", ImportWarning) +try: + import cuquantum.cutensornet as cutn # type: ignore +except ImportError: + warnings.warn("local settings failed to import cutensornet", ImportWarning) + + +class CuTensorNetHandle: + """Initialise the cuTensorNet library with automatic workspace memory + management. + + Note: + Always use as ``with CuTensorNetHandle() as libhandle:`` so that cuTensorNet + handles are automatically destroyed at the end of execution. + + Attributes: + handle (int): The cuTensorNet library handle created by this initialisation. + device_id (int): The ID of the device (GPU) where cuTensorNet is initialised. + If not provided, defaults to ``cp.cuda.Device()``. + """ + + def __init__(self, device_id: Optional[int] = None): + self.handle = cutn.create() + self._is_destroyed = False + + # Make sure CuPy uses the specified device + cp.cuda.Device(device_id).use() + + dev = cp.cuda.Device() + self.device_id = int(dev) + + def __enter__(self) -> CuTensorNetHandle: + return self + + def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None: + cutn.destroy(self.handle) + self._is_destroyed = True diff --git a/pytket/extensions/cutensornet/states/mps.py b/pytket/extensions/cutensornet/states/mps.py new file mode 100644 index 00000000..2a93b3b8 --- /dev/null +++ b/pytket/extensions/cutensornet/states/mps.py @@ -0,0 +1,930 @@ +# Copyright 2019-2023 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +## +# http://www.apache.org/licenses/LICENSE-2.0 +## +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations # type: ignore +import warnings +import logging +from typing import Any, Optional, Union +from enum import Enum + +from random import random # type: ignore +import numpy as np # type: ignore + +try: + import cupy as cp # type: ignore +except ImportError: + 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) + +from pytket.circuit import Command, Op, OpType, Qubit +from pytket.pauli import Pauli, QubitPauliString + +from pytket.extensions.cutensornet.general import set_logger + +from .general import CuTensorNetHandle + +# An alias so that `intptr_t` from CuQuantum's API (which is not available in +# base python) has some meaningful type name. +Handle = int +# An alias for the CuPy type used for tensors +try: + Tensor = cp.ndarray +except NameError: + Tensor = Any + + +class DirectionMPS(Enum): + """An enum to refer to relative directions within the MPS.""" + + LEFT = 0 + RIGHT = 1 + + +class ConfigMPS: + """Configuration class for simulation using MPS.""" + + def __init__( + self, + chi: Optional[int] = None, + truncation_fidelity: Optional[float] = None, + k: int = 4, + optim_delta: float = 1e-5, + float_precision: Union[np.float32, np.float64] = np.float64, # type: ignore + value_of_zero: float = 1e-16, + loglevel: int = logging.WARNING, + ): + """Instantiate a configuration object for MPS simulation. + + Note: + Providing both a custom ``chi`` and ``truncation_fidelity`` will raise an + exception. Choose one or the other (or neither, for exact simulation). + + Args: + chi: The maximum value allowed for the dimension of the virtual + bonds. Higher implies better approximation but more + computational resources. If not provided, ``chi`` will be unbounded. + truncation_fidelity: Every time a two-qubit gate is applied, the virtual + bond will be truncated to the minimum dimension that satisfies + ``||^2 >= trucantion_fidelity``, where ``|psi>`` and ``|phi>`` + are the states before and after truncation (both normalised). + If not provided, it will default to its maximum value 1. + k: If using MPSxMPO, the maximum number of layers the MPO is allowed to + have before being contracted. Increasing this might increase fidelity, + but it will also increase resource requirements exponentially. + Ignored if not using MPSxMPO. Default value is 4. + optim_delta: If using MPSxMPO, stopping criteria for the optimisation when + contracting the ``k`` layers of MPO. Stops when the increase of fidelity + between iterations is smaller than ``optim_delta``. + Ignored if not using MPSxMPO. Default value is ``1e-5``. + float_precision: The floating point precision used in tensor calculations; + choose from ``numpy`` types: ``np.float64`` or ``np.float32``. + Complex numbers are represented using two of such + ``float`` numbers. Default is ``np.float64``. + value_of_zero: Any number below this value will be considered equal to zero. + Even when no ``chi`` or ``truncation_fidelity`` is provided, singular + values below this number will be truncated. + We suggest to use a value slightly below what your chosen + ``float_precision`` can reasonably achieve. For instance, ``1e-16`` for + ``np.float64`` precision (default) and ``1e-7`` for ``np.float32``. + loglevel: Internal logger output level. Use 30 for warnings only, 20 for + verbose and 10 for debug mode. + + Raises: + ValueError: If both ``chi`` and ``truncation_fidelity`` are fixed. + ValueError: If the value of ``chi`` is set below 2. + ValueError: If the value of ``truncation_fidelity`` is not in [0,1]. + """ + if ( + chi is not None + and truncation_fidelity is not None + and truncation_fidelity != 1.0 + ): + raise ValueError("Cannot fix both chi and truncation_fidelity.") + if chi is None: + chi = 2**60 # In practice, this is like having it be unbounded + if truncation_fidelity is None: + truncation_fidelity = 1 + + if chi < 2: + raise ValueError("The max virtual bond dim (chi) must be >= 2.") + if truncation_fidelity < 0 or truncation_fidelity > 1: + raise ValueError("Provide a value of truncation_fidelity in [0,1].") + + self.chi = chi + self.truncation_fidelity = truncation_fidelity + + if float_precision is None or float_precision == np.float64: # Double precision + self._real_t = np.float64 # type: ignore + self._complex_t = np.complex128 # type: ignore + self._atol = 1e-12 + elif float_precision == np.float32: # Single precision + self._real_t = np.float32 # type: ignore + self._complex_t = np.complex64 # type: ignore + self._atol = 1e-4 + else: + allowed_precisions = [np.float64, np.float32] + raise TypeError( + f"Value of float_precision must be in {allowed_precisions}." + ) + self.zero = value_of_zero + + if value_of_zero > self._atol / 1000: + warnings.warn( + "Your chosen value_of_zero is relatively large. " + "Faithfulness of final fidelity estimate is not guaranteed.", + UserWarning, + ) + + self.k = k + self.optim_delta = 1e-5 + self.loglevel = loglevel + + def copy(self) -> ConfigMPS: + """Standard copy of the contents.""" + return ConfigMPS( + chi=self.chi, + truncation_fidelity=self.truncation_fidelity, + k=self.k, + optim_delta=self.optim_delta, + float_precision=self._real_t, # type: ignore + ) + + +class MPS: + """Represents a state as a Matrix Product State. + + Attributes: + tensors (list[Tensor]): A list of tensors in the MPS; ``tensors[0]`` is + the leftmost and ``tensors[len(self)-1]`` is the rightmost; ``tensors[i]`` + and ``tensors[i+1]`` are connected in the MPS via a bond. All of the + tensors are rank three, with the dimensions listed in ``.shape`` matching + the left, right and physical bonds, in that order. + canonical_form (dict[int, Optional[DirectionMPS]]): A dictionary mapping + positions to the canonical form direction of the corresponding tensor, + or ``None`` if it the tensor is not canonicalised. + qubit_position (dict[pytket.circuit.Qubit, int]): A dictionary mapping circuit + qubits to the position its tensor is at in the MPS. + fidelity (float): A lower bound of the fidelity, obtained by multiplying + the fidelities after each contraction. The fidelity of a contraction + corresponds to ``||^2`` where ``|psi>`` and ``|phi>`` are the + states before and after truncation (assuming both are normalised). + """ + + def __init__( + self, + libhandle: CuTensorNetHandle, + qubits: list[Qubit], + config: ConfigMPS, + ): + """Initialise an MPS on the computational state ``|0>``. + + Note: + A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` + statement. The device where the MPS is stored will match the one specified + by the library handle. + + Args: + libhandle: The cuTensorNet library handle that will be used to carry out + tensor operations on the MPS. + qubits: The list of qubits in the circuit to be simulated. + config: The object describing the configuration for simulation. + + Raises: + ValueError: If less than two qubits are provided. + """ + self._lib = libhandle + self._cfg = config + self._logger = set_logger("MPS", level=config.loglevel) + self.fidelity = 1.0 + + n_tensors = len(qubits) + if n_tensors == 0: # There's no initialisation to be done + return None + 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 = [] + 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. + + Specifically, verify that the MPS does not exceed the dimension limit ``chi`` of + the virtual bonds, that physical bonds have dimension 2, that all tensors + are rank three and that the data structure sizes are consistent. + + Returns: + False if a violation was detected or True otherwise. + """ + self._flush() + + chi_ok = all( + all(dim <= self._cfg.chi for dim in self.get_virtual_dimensions(pos)) + for pos in range(len(self)) + ) + phys_ok = all(self.get_physical_dimension(pos) == 2 for pos in range(len(self))) + shape_ok = all(len(tensor.shape) == 3 for tensor in self.tensors) + + ds_ok = set(self.canonical_form.keys()) == set(range(len(self))) + ds_ok = ds_ok and set(self.qubit_position.values()) == set(range(len(self))) + + # Debugger logging + self._logger.debug( + "Checking validity of MPS... " + f"chi_ok={chi_ok}, " + f"phys_ok={phys_ok}, " + f"shape_ok={shape_ok}, " + f"ds_ok={ds_ok}" + ) + + return chi_ok and phys_ok and shape_ok and ds_ok + + def apply_gate(self, gate: Command) -> MPS: + """Apply the gate to the MPS. + + Note: + Only one-qubit gates and two-qubit gates are supported. Two-qubit + gates must act on adjacent qubits. + + Args: + gate: The gate to be applied. + + Returns: + ``self``, to allow for method chaining. + + Raises: + RuntimeError: If the ``CuTensorNetHandle`` is out of scope. + RuntimeError: If gate acts on more than 2 qubits or acts on non-adjacent + qubits. + RuntimeError: If physical bond dimension where gate is applied is not 2. + """ + if self._lib._is_destroyed: + raise RuntimeError( + "The cuTensorNet library handle is out of scope.", + "See the documentation of update_libhandle and CuTensorNetHandle.", + ) + + positions = [self.qubit_position[q] for q in gate.qubits] + if any(self.get_physical_dimension(pos) != 2 for pos in positions): + raise RuntimeError( + "Gates can only be applied to tensors with physical" + + " bond dimension of 2." + ) + self._logger.debug(f"Applying gate {gate}") + + if len(positions) == 1: + self._apply_1q_gate(positions[0], gate.op) + # NOTE: if the tensor was in canonical form, it remains being so, + # since it is guaranteed that the gate is unitary. + + elif len(positions) == 2: + dist = positions[1] - positions[0] + # We explicitly allow both dist==1 or dist==-1 so that non-symmetric + # gates such as CX can use the same Op for the two ways it can be in. + if dist not in [1, -1]: + raise RuntimeError( + "Gates must be applied to adjacent qubits! " + + f"This is not satisfied by {gate}." + ) + self._apply_2q_gate((positions[0], positions[1]), gate.op) + # The tensors will in general no longer be in canonical form. + self.canonical_form[positions[0]] = None + self.canonical_form[positions[1]] = None + + else: + raise RuntimeError( + "Gates must act on only 1 or 2 qubits! " + + f"This is not satisfied by {gate}." + ) + + return self + + def canonicalise(self, l_pos: int, r_pos: int) -> None: + """Canonicalises the MPS object. + + Applies the necessary gauge transformations so that all MPS tensors + to the left of position ``l_pos`` are in left orthogonal form and + all MPS tensors to the right of ``r_pos`` in right orthogonal form. + + Args: + l_pos: The position of the leftmost tensor that is not to be + canonicalised. + r_pos: The position of the rightmost tensor that is not to be + canonicalised. + """ + self._logger.debug(f"Start canonicalisation... l_pos={l_pos}, r_pos={r_pos}") + + for pos in range(l_pos): + self.canonicalise_tensor(pos, form=DirectionMPS.LEFT) + for pos in reversed(range(r_pos + 1, len(self))): + self.canonicalise_tensor(pos, form=DirectionMPS.RIGHT) + + self._logger.debug(f"Finished canonicalisation.") + + def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: + """Canonicalises a tensor from an MPS object. + + Applies the necessary gauge transformations so that the tensor at + position ``pos`` in the MPS is in the orthogonal form dictated by + ``form``. + + Args: + position: The position of the tensor to be canonicalised. + form: LEFT form means that its conjugate transpose is its inverse if + connected to its left bond and physical bond. Similarly for RIGHT. + + Raises: + ValueError: If ``form`` is not a value in ``DirectionMPS``. + RuntimeError: If the ``CuTensorNetHandle`` is out of scope. + """ + if form == self.canonical_form[pos]: + # Tensor already in canonical form, nothing needs to be done + self._logger.debug(f"Position {pos} already in {form}.") + return None + + if self._lib._is_destroyed: + raise RuntimeError( + "The cuTensorNet library handle is out of scope.", + "See the documentation of update_libhandle and CuTensorNetHandle.", + ) + + self._logger.debug(f"Canonicalising {pos} to {form}.") + # Glossary of bond IDs used here: + # s -> shared virtual bond between T and Tnext + # v -> the other virtual bond of T + # V -> the other virtual bond of Tnext + # p -> physical bond of T + # P -> physical bond of Tnext + + # Gather the details from the MPS tensors at this position + T = self.tensors[pos] + + # Assign the bond IDs + if form == DirectionMPS.LEFT: + next_pos = pos + 1 + Tnext = self.tensors[next_pos] + T_bonds = "vsp" + Q_bonds = "vap" + R_bonds = "as" + Tnext_bonds = "sVP" + result_bonds = "aVP" + elif form == DirectionMPS.RIGHT: + next_pos = pos - 1 + Tnext = self.tensors[next_pos] + T_bonds = "svp" + Q_bonds = "avp" + R_bonds = "as" + Tnext_bonds = "VsP" + result_bonds = "VaP" + else: + raise ValueError("Argument form must be a value in DirectionMPS.") + + # Apply QR decomposition + self._logger.debug(f"QR decompose a {T.nbytes / 2**20} MiB tensor.") + + subscripts = T_bonds + "->" + Q_bonds + "," + R_bonds + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + Q, R = tensor.decompose( + subscripts, T, method=tensor.QRMethod(), options=options + ) + self._logger.debug(f"QR decomposition finished.") + + # Contract R into Tnext + subscripts = R_bonds + "," + Tnext_bonds + "->" + result_bonds + result = cq.contract(subscripts, R, Tnext) + self._logger.debug(f"Contraction with {next_pos} applied.") + + # Update self.tensors + self.tensors[pos] = Q + self.canonical_form[pos] = form # type: ignore + self.tensors[next_pos] = result + self.canonical_form[next_pos] = None + + def vdot(self, other: MPS) -> complex: + """Obtain the inner product of the two MPS: ````. + + It can be used to compute the squared norm of an MPS ``mps`` as + ``mps.vdot(mps)``. The tensors within the MPS are not modified. + + Note: + The state that is conjugated is ``self``. + + Args: + other: The other MPS to compare against. + + Returns: + The resulting complex number. + + Raises: + RuntimeError: If number of tensors, dimensions or positions do not match. + RuntimeError: If there are no tensors in the MPS. + RuntimeError: If the ``CuTensorNetHandle`` is out of scope. + """ + if self._lib._is_destroyed: + raise RuntimeError( + "The cuTensorNet library handle is out of scope.", + "See the documentation of update_libhandle and CuTensorNetHandle.", + ) + + if len(self) != len(other): + raise RuntimeError("Number of tensors do not match.") + for i in range(len(self)): + if self.get_physical_dimension(i) != other.get_physical_dimension(i): + raise RuntimeError( + f"Physical bond dimension at position {i} do not match." + ) + if self.qubit_position != other.qubit_position: + raise RuntimeError( + "The qubit labels or their position on the MPS do not match." + ) + if len(self) == 0: + raise RuntimeError("There are no tensors in the MPS.") + + self._flush() + other._flush() + + # Special case if only one tensor remains + if len(self) == 1: + self._logger.debug("Applying trivial vdot on single tensor MPS.") + result = cq.contract("LRp,lrp->", self.tensors[0].conj(), other.tensors[0]) + + else: + self._logger.debug("Applying vdot between two MPS.") + + # The two MPS will be contracted from left to right, storing the + # ``partial_result`` tensor. + partial_result = cq.contract( + "LRp,lrp->Rr", self.tensors[0].conj(), other.tensors[0] + ) + # Contract all tensors in the middle + for pos in range(1, len(self) - 1): + partial_result = cq.contract( + "Ll,LRp,lrp->Rr", + partial_result, + self.tensors[pos].conj(), + other.tensors[pos], + ) + # Finally, contract the last tensor + result = cq.contract( + "Ll,LRp,lrp->", # R and r are dim 1, so they are omitted; scalar result + partial_result, + self.tensors[-1].conj(), + other.tensors[-1], + ) + + self._logger.debug(f"Result from vdot={result}") + return complex(result) + + def sample(self) -> dict[Qubit, int]: + """Returns a sample from a Z measurement applied on every qubit. + + Notes: + The MPS ``self`` is not updated. This is equivalent to applying + ``mps = self.copy()`` then ``mps.measure(mps.get_qubits())``. + + Returns: + A dictionary mapping each of the qubits in the MPS to their 0 or 1 outcome. + """ + + # TODO: Copying is not strictly necessary, but to avoid it we would need to + # modify the algorithm in `measure`. This may be done eventually if `copy` + # is shown to be a bottleneck when sampling (which is likely). + mps = self.copy() + return mps.measure(mps.get_qubits()) + + def measure(self, qubits: set[Qubit]) -> dict[Qubit, int]: + """Applies a Z measurement on ``qubits``, updates the MPS and returns outcome. + + Notes: + After applying this function, ``self`` will contain the MPS of the projected + state over the non-measured qubits. + + The resulting state has been normalised. + + Args: + qubits: The subset of qubits to be measured. + + Returns: + A dictionary mapping the given ``qubits`` to their measurement outcome, + i.e. either ``0`` or ``1``. + + Raises: + ValueError: If an element in ``qubits`` is not a qubit in the MPS. + """ + result = dict() + + # Obtain the positions that need to be measured and build the reverse dict + position_qubit_map = dict() + for q in qubits: + if q not in self.qubit_position: + raise ValueError(f"Qubit {q} is not a qubit in the MPS.") + position_qubit_map[self.qubit_position[q]] = q + positions = sorted(position_qubit_map.keys()) + self._logger.debug(f"Measuring qubits={position_qubit_map}") + + # Tensor for postselection to |0> + zero_tensor = cp.zeros(2, dtype=self._cfg._complex_t) + zero_tensor[0] = 1 + + # Measure and postselect each of the positions, one by one + while positions: + pos = positions.pop() # The rightmost position to be measured + + # Convert to canonical form with center at this position + self.canonicalise(pos, pos) + + # Glossary of bond IDs: + # l -> left virtual bond of tensor in `pos` + # r -> right virtual bond of tensor in `pos` + # p -> physical bond of tensor in `pos` + # P -> physical bond of tensor in `pos` (copy) + + # Take the tensor in this position and obtain its prob for |0>. + # Since the MPS is in canonical form, this corresponds to the probability + # if we were to take all of the other tensors into account. + prob = cq.contract( + "lrp,lrP,p,P->", # No open bonds remain; this is just a scalar + self.tensors[pos].conj(), + self.tensors[pos], + zero_tensor, + zero_tensor, + ) + + # Throw a coin to decide measurement outcome + outcome = 0 if prob > random() else 1 + result[position_qubit_map[pos]] = outcome + self._logger.debug(f"Outcome of qubit at {pos} is {outcome}.") + + # Postselect the MPS for this outcome, renormalising at the same time + postselection_tensor = cp.zeros(2, dtype=self._cfg._complex_t) + postselection_tensor[outcome] = 1 / np.sqrt( + abs(outcome - prob) + ) # Normalise + + self._postselect_qubit(position_qubit_map[pos], postselection_tensor) + + return result + + def postselect(self, qubit_outcomes: dict[Qubit, int]) -> float: + """Applies a postselection, updates the MPS and returns its probability. + + Notes: + After applying this function, ``self`` will contain the MPS of the projected + state over the non-postselected qubits. + + The resulting state has been normalised. + + Args: + qubit_outcomes: A dictionary mapping a subset of qubits in the MPS to their + desired outcome value (either ``0`` or ``1``). + + Returns: + The probability of this postselection to occur in a measurement. + + Raises: + ValueError: If a key in ``qubit_outcomes`` is not a qubit in the MPS. + ValueError: If a value in ``qubit_outcomes`` is other than ``0`` or ``1``. + ValueError: If all of the qubits in the MPS are being postselected. Instead, + you may wish to use ``get_amplitude()``. + """ + for q, v in qubit_outcomes.items(): + if q not in self.qubit_position: + raise ValueError(f"Qubit {q} is not a qubit in the MPS.") + if v not in {0, 1}: + raise ValueError(f"Outcome of {q} cannot be {v}. Choose int 0 or 1.") + + if len(qubit_outcomes) == len(self): + raise ValueError( + "Cannot postselect all qubits. You may want to use get_amplitude()." + ) + self._logger.debug(f"Postselecting qubits={qubit_outcomes}") + + # Apply a postselection for each of the qubits + for qubit, outcome in qubit_outcomes.items(): + # Create the rank-1 postselection tensor + postselection_tensor = cp.zeros(2, dtype=self._cfg._complex_t) + postselection_tensor[outcome] = 1 + # Apply postselection + self._postselect_qubit(qubit, postselection_tensor) + + # Calculate the squared norm of the postselected state; this is its probability + prob = self.vdot(self) + assert np.isclose(prob.imag, 0.0, atol=self._cfg._atol) + prob = prob.real + + # Renormalise; it suffices to update the first tensor + if len(self) > 0 and not np.isclose(prob, 0.0, atol=self._cfg._atol): + self.tensors[0] = self.tensors[0] / np.sqrt(prob) + self.canonical_form[0] = None + + self._logger.debug(f"Probability of this postselection is {prob}.") + return prob + + def _postselect_qubit(self, qubit: Qubit, postselection_tensor: cp.ndarray) -> None: + """Postselect the qubit with the given tensor.""" + + pos = self.qubit_position[qubit] + self.tensors[pos] = cq.contract( + "lrp,p->lr", + self.tensors[pos], + postselection_tensor, + ) + + # Glossary of bond IDs: + # s -> shared bond between tensor in `pos` and next + # v -> the other virtual bond of tensor in `pos` + # V -> the other virtual bond of tensor in next position + # p -> physical bond of tensor in `pos` + # P -> physical bond of tensor in next position + + if len(self) == 1: # This is the last tensor + pass + + elif pos != 0: # Contract with next tensor on the left + self.tensors[pos - 1] = cq.contract( + "sv,VsP->VvP", + self.tensors[pos], + self.tensors[pos - 1], + ) + self.canonical_form[pos - 1] = None + else: # There are no tensors on the left, contract with the one on the right + self.tensors[pos + 1] = cq.contract( + "vs,sVP->vVP", + self.tensors[pos], + self.tensors[pos + 1], + ) + self.canonical_form[pos + 1] = None + + # Shift all entries after `pos` to the left + for q, p in self.qubit_position.items(): + if pos < p: + self.qubit_position[q] = p - 1 + for p in range(pos, len(self) - 1): + self.canonical_form[p] = self.canonical_form[p + 1] + + # Remove the entry from the data structures + del self.qubit_position[qubit] + del self.canonical_form[len(self) - 1] + self.tensors.pop(pos) + + def expectation_value(self, pauli_string: QubitPauliString) -> float: + """Obtains the expectation value of the Pauli string observable. + + Args: + pauli_string: A pytket object representing a tensor product of Paulis. + + Returns: + The expectation value. + + Raises: + ValueError: If a key in ``pauli_string`` is not a qubit in the MPS. + """ + for q in pauli_string.map.keys(): + if q not in self.qubit_position: + raise ValueError(f"Qubit {q} is not a qubit in the MPS.") + + self._logger.debug(f"Calculating expectation value of {pauli_string}.") + mps_copy = self.copy() + pauli_optype = {Pauli.Z: OpType.Z, Pauli.X: OpType.X, Pauli.Y: OpType.Y} + + # Apply each of the Pauli operators to the MPS copy + for qubit, pauli in pauli_string.map.items(): + if pauli != Pauli.I: + pos = mps_copy.qubit_position[qubit] + pauli_unitary = Op.create(pauli_optype[pauli]).get_unitary() + pauli_tensor = cp.asarray( + pauli_unitary.astype(dtype=self._cfg._complex_t, copy=False), + dtype=self._cfg._complex_t, + ) + + # Contract the Pauli to the MPS tensor of the corresponding qubit + mps_copy.tensors[pos] = cq.contract( + "lrp,Pp->lrP", mps_copy.tensors[pos], pauli_tensor + ) + + # Obtain the inner product + value = self.vdot(mps_copy) + assert np.isclose(value.imag, 0.0, atol=self._cfg._atol) + + self._logger.debug(f"Expectation value is {value.real}.") + return value.real + + def get_statevector(self) -> np.ndarray: + """Returns the statevector with qubits in Increasing Lexicographic Order (ILO). + + Raises: + ValueError: If there are no qubits left in the MPS. + """ + if len(self) == 0: + raise ValueError("There are no qubits left in this MPS.") + + # If there is only one qubit left, it is trivial + if len(self) == 1: + result_tensor = self.tensors[0] + + else: + # Create the interleaved representation with all tensors + interleaved_rep = [] + for pos in range(len(self)): + interleaved_rep.append(self.tensors[pos]) + interleaved_rep.append( + ["v" + str(pos), "v" + str(pos + 1), "p" + str(pos)] + ) + + # Specify the output bond IDs in ILO order + output_bonds = [] + for q in sorted(self.qubit_position.keys()): + output_bonds.append("p" + str(self.qubit_position[q])) + interleaved_rep.append(output_bonds) + + # Contract + result_tensor = cq.contract(*interleaved_rep) + + # Convert to numpy vector and flatten + statevector: np.ndarray = cp.asnumpy(result_tensor).flatten() + return statevector + + def get_amplitude(self, state: int) -> complex: + """Returns the amplitude of the chosen computational state. + + Notes: + The result is equivalent to ``mps.get_statevector[b]``, but this method + is faster when querying a single amplitude (or just a few). + + Args: + state: The integer whose bitstring describes the computational state. + The qubits in the bitstring are in increasing lexicographic order. + + Returns: + The amplitude of the computational state in the MPS. + """ + + # Find out what the map MPS_position -> bit value is + ilo_qubits = sorted(self.qubit_position.keys()) + mps_pos_bitvalue = dict() + + for i, q in enumerate(ilo_qubits): + pos = self.qubit_position[q] + bitvalue = 1 if state & 2 ** (len(self) - i - 1) else 0 + mps_pos_bitvalue[pos] = bitvalue + + # Carry out the contraction, starting from a dummy tensor + result_tensor = cp.ones(1, dtype=self._cfg._complex_t) # rank-1, dimension 1 + + for pos in range(len(self)): + postselection_tensor = cp.zeros(2, dtype=self._cfg._complex_t) + postselection_tensor[mps_pos_bitvalue[pos]] = 1 + # Contract postselection with qubit into the result_tensor + result_tensor = cq.contract( + "l,lrp,p->r", result_tensor, self.tensors[pos], postselection_tensor + ) + + assert result_tensor.shape == (1,) + result = complex(result_tensor[0]) + self._logger.debug(f"Amplitude of state {state} is {result}.") + return result + + def get_qubits(self) -> set[Qubit]: + """Returns the set of qubits that this MPS is defined on.""" + return set(self.qubit_position.keys()) + + def get_virtual_dimensions(self, position: int) -> tuple[int, int]: + """Returns the virtual bonds dimension of the tensor ``tensors[position]``. + + Args: + position: A position in the MPS. + + Returns: + A tuple where the first element is the dimensions of the left virtual bond + and the second elements is that of the right virtual bond. + + Raises: + RuntimeError: If ``position`` is out of bounds. + """ + if position < 0 or position >= len(self): + raise RuntimeError(f"Position {position} is out of bounds.") + + virtual_dims: tuple[int, int] = self.tensors[position].shape[:2] + return virtual_dims + + def get_physical_dimension(self, position: int) -> int: + """Returns the physical bond dimension of the tensor ``tensors[position]``. + + Args: + position: A position in the MPS. + + Returns: + The dimension of the physical bond. + + Raises: + RuntimeError: If ``position`` is out of bounds. + """ + if position < 0 or position >= len(self): + raise RuntimeError(f"Position {position} is out of bounds.") + + physical_dim: int = self.tensors[position].shape[2] + return physical_dim + + def get_byte_size(self) -> int: + """ + Returns: + The number of bytes the MPS currently occupies in GPU memory. + """ + return sum(t.nbytes for t in self.tensors) + + def get_device_id(self) -> int: + """ + Returns: + The identifier of the device (GPU) where the tensors are stored. + """ + return int(self.tensors[0].device) + + def update_libhandle(self, libhandle: CuTensorNetHandle) -> None: + """Update the ``CuTensorNetHandle`` used by this ``MPS`` object. Multiple + objects may use the same handle. + + Args: + libhandle: The new cuTensorNet library handle. + + Raises: + RuntimeError: If the device (GPU) where ``libhandle`` was initialised + does not match the one where the tensors of the MPS are stored. + """ + if libhandle.device_id != self.get_device_id(): + raise RuntimeError( + "Device of libhandle is not the one where the MPS is stored.", + f"{libhandle.device_id} != {self.get_device_id()}", + ) + self._lib = libhandle + + def copy(self) -> MPS: + """ + Returns: + A deep copy of the MPS on the same device. + """ + self._flush() + + # Create a dummy object + new_mps = MPS(self._lib, qubits=[], config=self._cfg.copy()) + # Copy all data + new_mps.fidelity = self.fidelity + new_mps.tensors = [t.copy() for t in self.tensors] + new_mps.canonical_form = self.canonical_form.copy() + new_mps.qubit_position = self.qubit_position.copy() + + self._logger.debug( + "Successfully copied an MPS " + f"of size {new_mps.get_byte_size() / 2**20} MiB." + ) + return new_mps + + def __len__(self) -> int: + """ + Returns: + The number of tensors in the MPS. + """ + return len(self.tensors) + + def _apply_1q_gate(self, position: int, gate: Op) -> MPS: + raise NotImplementedError( + "MPS is a base class with no contraction algorithm implemented." + + " You must use a subclass of MPS, such as MPSxGate or MPSxMPO." + ) + + def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPS: + raise NotImplementedError( + "MPS is a base class with no contraction algorithm implemented." + + " You must use a subclass of MPS, such as MPSxGate or MPSxMPO." + ) + + def _flush(self) -> None: + # Does nothing in the general MPS case; but children classes with batched + # gate contraction will redefine this method so that the last batch of + # gates is applied. + return None diff --git a/pytket/extensions/cutensornet/states/mps_gate.py b/pytket/extensions/cutensornet/states/mps_gate.py new file mode 100644 index 00000000..c7dc496e --- /dev/null +++ b/pytket/extensions/cutensornet/states/mps_gate.py @@ -0,0 +1,318 @@ +# Copyright 2019-2023 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +## +# http://www.apache.org/licenses/LICENSE-2.0 +## +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations # type: ignore +import warnings +import logging + +import numpy as np # type: ignore + +try: + import cupy as cp # type: ignore +except ImportError: + 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) + +from pytket.circuit import Op +from .mps import MPS + + +class MPSxGate(MPS): + """Implements a gate-by-gate contraction algorithm to calculate the output state + of a circuit as an ``MPS``. The algorithm is described in: + https://arxiv.org/abs/2002.07730 + """ + + def _apply_1q_gate(self, position: int, gate: Op) -> MPSxGate: + """Applies the 1-qubit gate to the MPS. + + This does not increase the dimension of any bond. + + Args: + position: The position of the MPS tensor that this gate + is applied to. + gate: The gate to be applied. + + Returns: + ``self``, to allow for method chaining. + """ + + # Load the gate's unitary to the GPU memory + gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) + gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) + + # Glossary of bond IDs + # p -> physical bond of the MPS tensor + # v -> one of the virtual bonds of the MPS tensor + # V -> the other virtual bond of the MPS tensor + # o -> the output bond of the gate + + T_bonds = "vVp" + result_bonds = "vVo" + gate_bonds = "op" + + # Contract + new_tensor = cq.contract( + gate_bonds + "," + T_bonds + "->" + result_bonds, + gate_tensor, + self.tensors[position], + ) + + # Update ``self.tensors`` + self.tensors[position] = new_tensor + return self + + def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: + """Applies the 2-qubit gate to the MPS. + + If doing so increases the virtual bond dimension beyond ``chi``; + truncation is automatically applied. + The MPS is converted to canonical form before truncating. + + Args: + positions: The position of the MPS tensors that this gate + is applied to. They must be contiguous. + gate: The gate to be applied. + + Returns: + ``self``, to allow for method chaining. + """ + l_pos = min(positions) + r_pos = max(positions) + + # Figure out the new dimension of the shared virtual bond + new_dim = 2 * min( + self.get_virtual_dimensions(l_pos)[0], + self.get_virtual_dimensions(r_pos)[1], + ) + + # Canonicalisation may be required if `new_dim` is larger than `chi` + # or if set by `truncation_fidelity` + if new_dim > self._cfg.chi or self._cfg.truncation_fidelity < 1: + # If truncation required, convert to canonical form before + # contracting. Avoids the need to apply gauge transformations + # to the larger tensor resulting from the contraction. + self.canonicalise(l_pos, r_pos) + + # Since canonicalisation may change the dimension of the bonds, + # we need to recalculate the value of `new_dim` + new_dim = 2 * min( + self.get_virtual_dimensions(l_pos)[0], + self.get_virtual_dimensions(r_pos)[1], + ) + + # Load the gate's unitary to the GPU memory + gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) + gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) + + # Reshape into a rank-4 tensor + gate_tensor = cp.reshape(gate_tensor, (2, 2, 2, 2)) + + # Glossary of bond IDs + # l -> physical bond of the left tensor in the MPS + # r -> physical bond of the right tensor in the MPS + # L -> left bond of the outcome of the gate + # R -> right bond of the outcome of the gate + # a,b,c -> the virtual bonds of the tensors + + if l_pos == positions[0]: + gate_bonds = "LRlr" + else: # Implicit swap + gate_bonds = "RLrl" + + left_bonds = "abl" + right_bonds = "bcr" + result_bonds = "acLR" + + # Contract + self._logger.debug("Contracting the two-qubit gate with its site tensors...") + T = cq.contract( + gate_bonds + "," + left_bonds + "," + right_bonds + "->" + result_bonds, + gate_tensor, + self.tensors[l_pos], + self.tensors[r_pos], + ) + self._logger.debug(f"Intermediate tensor of size (MiB)={T.nbytes / 2**20}") + + # Get the template of the MPS tensors involved + L = self.tensors[l_pos] + l_shape = list(L.shape) + R = self.tensors[r_pos] + r_shape = list(R.shape) + + if self._cfg.truncation_fidelity < 1: + # Carry out SVD decomposition first with NO truncation + # to figure out where to apply the dimension cutoff. + # Then, apply S normalisation and contraction of S and L manually. + # + # TODO: As soon as cuQuantum 23.09 is released, replace this + # unintuitive code with a simple update to SVDConfig so that it + # uses REL_SUM2_CUTOFF. Then the code in the `else` block should + # be run; i.e. use standard cuTensorNet API to do the SVD + # including normalisation and contraction of S with L. + self._logger.debug( + f"Truncating to target fidelity={self._cfg.truncation_fidelity}" + ) + + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + svd_method = tensor.SVDMethod(abs_cutoff=self._cfg.zero) + L, S, R = tensor.decompose( + "acLR->asL,scR", T, method=svd_method, options=options + ) + + # Use the fact that the entries of S are sorted in decreasing + # order and calculate the number of singular values `new_dim` to + # keep so that + # sum([s**2 for s in S']) + # truncation_fidelity <= ------------------------- + # sum([s**2 for s in S]) + # + # where S is the list of original singular values and S' is the set of + # singular values that remain after truncation (before normalisation). + denom = float(sum(cp.square(S))) # Element-wise squaring + numer = 0.0 + old_dim = new_dim + new_dim = 0 + + # Take singular values until we surpass the target fidelity + while self._cfg.truncation_fidelity > numer / denom: + numer += float(S[new_dim] ** 2) + new_dim += 1 + this_fidelity = numer / denom + + # Reshape tensors down to `new_dim` for the virtual bond + # No data is copied or moved around, we're changing the ndarray bounds + l_shape[-2] = new_dim + # pylint: disable = unexpected-keyword-arg # Disable pylint for next line + L = cp.ndarray( + l_shape, + dtype=self._cfg._complex_t, + memptr=L.data, + strides=L.strides, + ) + r_shape[0] = new_dim + # pylint: disable = unexpected-keyword-arg # Disable pylint for next line + R = cp.ndarray( + r_shape, + dtype=self._cfg._complex_t, + memptr=R.data, + strides=R.strides, + ) + # pylint: disable = unexpected-keyword-arg # Disable pylint for next line + S = cp.ndarray(new_dim, dtype=self._cfg._real_t, memptr=S.data) + + # Normalise + S *= np.sqrt(1 / this_fidelity) + + # Contract S into L + S = S.astype(dtype=self._cfg._complex_t, copy=False) + # Use some einsum index magic: since the virtual bond "s" appears in the + # list of bonds of the output, it is not summed over. + # This causes S to act as the intended diagonal matrix. + L = cq.contract("asL,s->asL", L, S) + + # We multiply the fidelity of the current step to the overall fidelity + # to keep track of a lower bound for the fidelity. + self.fidelity *= this_fidelity + + # Report to logger + self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") + self._logger.debug( + f"Reduced virtual bond dimension from {old_dim} to {new_dim}." + ) + + elif new_dim > self._cfg.chi: + # Apply SVD decomposition and truncate up to a `max_extent` (for the shared + # bond) of `self._cfg.chi`. Ask cuTensorNet to contract S directly into the + # L tensor and normalise the singular values so that the sum of its squares + # is equal to one (i.e. the MPS is a normalised state after truncation). + self._logger.debug(f"Truncating to (or below) chosen chi={self._cfg.chi}") + + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + svd_method = tensor.SVDMethod( + abs_cutoff=self._cfg.zero, + max_extent=self._cfg.chi, + partition="U", # Contract S directly into U (named L in our case) + normalization="L2", # Sum of squares equal 1 + ) + + L, S, R, svd_info = tensor.decompose( + "acLR->asL,scR", T, method=svd_method, options=options, return_info=True + ) + assert S is None # Due to "partition" option in SVDMethod + + # discarded_weight is calculated within cuTensorNet as: + # sum([s**2 for s in S']) + # discarded_weight = 1 - ------------------------- + # sum([s**2 for s in S]) + # where S is the list of original singular values and S' is the set of + # singular values that remain after truncation (before normalisation). + # It can be shown that the fidelity ||^2 (for |phi> and |psi> + # unit vectors before and after truncation) is equal to 1 - disc_weight. + # + # We multiply the fidelity of the current step to the overall fidelity + # to keep track of a lower bound for the fidelity. + this_fidelity = 1.0 - svd_info.discarded_weight + self.fidelity *= this_fidelity + + # Report to logger + self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") + self._logger.debug( + f"Reduced virtual bond dimension from {new_dim} to {R.shape[0]}." + ) + + else: + # The user did not explicitly ask for truncation, but it is advantageous to + # remove any singular values below ``self._cfg.zero``. + self._logger.debug(f"Truncating singular values below={self._cfg.zero}.") + if self._cfg.zero > self._cfg._atol / 1000: + self._logger.info( # This was raised as a warning in ConfigMPS already + "Your chosen value_of_zero is relatively large. " + "Faithfulness of final fidelity estimate is not guaranteed." + ) + + # NOTE: There is no guarantee of canonical form in this case. This is fine + # since canonicalisation is just meant to detect the optimal singular values + # to truncate, but if we find values that are essentially zero, we are safe + # to remove them. + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + svd_method = tensor.SVDMethod( + abs_cutoff=self._cfg.zero, + partition="U", # Contract S directly into U (named L in our case) + normalization=None, # Without canonicalisation we must not normalise + ) + L, S, R = tensor.decompose( + "acLR->asL,scR", T, method=svd_method, options=options + ) + assert S is None # Due to "partition" option in SVDMethod + + # Report to logger + self._logger.debug(f"Truncation done. Fidelity estimate unchanged.") + self._logger.debug( + f"Reduced virtual bond dimension from {new_dim} to {R.shape[0]}." + ) + + self.tensors[l_pos] = L + self.tensors[r_pos] = R + + # If requested, provide info about memory usage. + if self._logger.isEnabledFor(logging.INFO): + # If-statetement used so that we only call `get_byte_size` if needed. + self._logger.info(f"MPS size (MiB)={self.get_byte_size() / 2**20}") + self._logger.info(f"MPS fidelity={self.fidelity}") + + return self diff --git a/pytket/extensions/cutensornet/states/mps_mpo.py b/pytket/extensions/cutensornet/states/mps_mpo.py new file mode 100644 index 00000000..67cc3c93 --- /dev/null +++ b/pytket/extensions/cutensornet/states/mps_mpo.py @@ -0,0 +1,535 @@ +# Copyright 2019-2023 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +## +# http://www.apache.org/licenses/LICENSE-2.0 +## +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations # type: ignore +import warnings + +from typing import Optional, Union + +import numpy as np # type: ignore + +try: + import cupy as cp # type: ignore +except ImportError: + 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) + +from pytket.circuit import Op, Qubit +from .general import CuTensorNetHandle +from .mps import ( + DirectionMPS, + ConfigMPS, + Tensor, + MPS, +) +from .mps_gate import MPSxGate + + +class MPSxMPO(MPS): + """Implements a batched--gate contraction algorithm (DMRG-like) to calculate + the output state of a circuit as an ``MPS``. The algorithm is described + in: https://arxiv.org/abs/2207.05612. + """ + + def __init__( + self, + libhandle: CuTensorNetHandle, + qubits: list[Qubit], + config: ConfigMPS, + ): + """Initialise an MPS on the computational state ``|0>``. + + Note: + A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` + statement. The device where the MPS is stored will match the one specified + by the library handle. + + Args: + libhandle: The cuTensorNet library handle that will be used to carry out + tensor operations on the MPS. + qubits: The list of qubits in the circuit to be simulated. + config: The object describing the configuration for simulation. + """ + super().__init__(libhandle, qubits, config) + + # Initialise the MPO data structure. This will keep a list of the gates + # batched for application to the MPS; all of them will be applied at + # once when deemed appropriate or when calling ._flush(), removing them + # from here. The gates are kept in a list of lists. + # + # One list per MPS position, containing all the tensors of the gates + # acting on the corresponding position. These lists are originally empty. + # The last element of each list corresponds to the last gate applied. + # + # Each of the tensors will have four bonds ordered as follows: + # [input, left, right, output] + self._mpo: list[list[Tensor]] = [list() for _ in qubits] + # This ``_bond_ids`` store global bond IDs of MPO tensors, used by ``_flush()`` + self._bond_ids: list[list[tuple[int, int, int, int]]] = [list() for _ in qubits] + + # Initialise the MPS that we will use as first approximation of the + # variational algorithm. + self._aux_mps = MPSxGate(libhandle, qubits, config) + + self._mpo_bond_counter = 0 + + def update_libhandle(self, libhandle: CuTensorNetHandle) -> None: + """Set the library handle used by this ``MPS`` object. Multiple objects + may use the same library handle. + + Args: + libhandle: The new cuTensorNet library handle. + + Raises: + RuntimeError: If the device (GPU) where ``libhandle`` was initialised + does not match the one where the tensors of the MPS are stored. + """ + super().update_libhandle(libhandle) + self._aux_mps.update_libhandle(libhandle) + + def _apply_1q_gate(self, position: int, gate: Op) -> MPSxMPO: + """Applies the 1-qubit gate to the MPS. + + This does not increase the dimension of any bond. + + Args: + position: The position of the MPS tensor that this gate + is applied to. + gate: The gate to be applied. + + Returns: + ``self``, to allow for method chaining. + """ + + # Apply the gate to the MPS with eager approximation + self._aux_mps._apply_1q_gate(position, gate) + + # Load the gate's unitary to the GPU memory + gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) + gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) + + # Glossary of bond IDs + # i -> input to the MPO tensor + # o -> output of the MPO tensor + # l -> left virtual bond of the MPO tensor + # r -> right virtual bond of the MPO tensor + # g -> output bond of the gate tensor + + # Identify the tensor to contract the gate with + if self._mpo[position]: # Not empty + last_tensor = self._mpo[position][-1] + last_bonds = "ilro" + new_bonds = "ilrg" + else: # Use the MPS tensor + last_tensor = self.tensors[position] + last_bonds = "lro" + new_bonds = "lrg" + + # Contract + new_tensor = cq.contract( + "go," + last_bonds + "->" + new_bonds, + gate_tensor, + last_tensor, + ) + + # Update the tensor + if self._mpo[position]: # Not empty + self._mpo[position][-1] = new_tensor + else: # Update the MPS tensor + self.tensors[position] = new_tensor + + return self + + def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxMPO: + """Applies the 2-qubit gate to the MPS. + + If doing so increases the virtual bond dimension beyond ``chi``; + truncation is automatically applied. + The MPS is converted to canonical form before truncating. + + Args: + positions: The position of the MPS tensors that this gate + is applied to. They must be contiguous. + gate: The gate to be applied. + + Returns: + ``self``, to allow for method chaining. + """ + l_pos = min(positions) + r_pos = max(positions) + + # Check whether the MPO is large enough to flush it + if any(len(self._mpo[pos]) >= self._cfg.k for pos in [l_pos, r_pos]): + self._flush() + + # Apply the gate to the MPS with eager approximation + self._aux_mps._apply_2q_gate(positions, gate) + + # Load the gate's unitary to the GPU memory + gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) + gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) + + # Reshape into a rank-4 tensor + gate_tensor = cp.reshape(gate_tensor, (2, 2, 2, 2)) + + # Glossary of bond IDs + # l -> gate's left input bond + # r -> gate's right input bond + # L -> gate's left output bond + # R -> gate's right output bond + # s -> virtual bond after QR decomposition + + # Assign the bond IDs for the gate + if l_pos == positions[0]: + gate_bonds = "LRlr" + else: # Implicit swap + gate_bonds = "RLrl" + + # Apply a QR decomposition on the gate_tensor to shape it as an MPO + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + L, R = tensor.decompose( + gate_bonds + "->lsL,rsR", + gate_tensor, + method=tensor.QRMethod(), + options=options, + ) + + # Add dummy bonds of dimension 1 to L and R so that they have the right shape + L = cp.reshape(L, (2, 1, 4, 2)) + R = cp.reshape(R, (2, 4, 1, 2)) + + # Store L and R + self._mpo[l_pos].append(L) + self._mpo[r_pos].append(R) + # And assign their global bonds + shared_bond_id = self._new_bond_id() + self._bond_ids[l_pos].append( + ( + self._get_physical_bond(l_pos), + self._new_bond_id(), + shared_bond_id, + self._new_bond_id(), + ) + ) + self._bond_ids[r_pos].append( + ( + self._get_physical_bond(r_pos), + shared_bond_id, + self._new_bond_id(), + self._new_bond_id(), + ) + ) + return self + + def get_physical_dimension(self, position: int) -> int: + """Returns the dimension of the physical bond at ``position``. + + Args: + position: A position in the MPS. + + Returns: + The dimension of the physical bond. + + Raises: + RuntimeError: If ``position`` is out of bounds. + """ + if position < 0 or position >= len(self): + raise RuntimeError(f"Position {position} is out of bounds.") + + # Identify the tensor last tensor in the MPO + if self._mpo[position]: # Not empty + last_tensor = self._mpo[position][-1] + else: # Use the MPS tensor + last_tensor = self.tensors[position] + + # By construction, the open bond is the last one + return int(last_tensor.shape[-1]) + + def _get_physical_bond(self, position: int) -> int: + """Returns the unique identifier of the physical bond at ``position``. + + Args + position: A position in the MPS. + + Returns: + The identifier of the physical bond. + + Raises: + RuntimeError: If ``position`` is out of bounds. + """ + if position < 0 or position >= len(self): + raise RuntimeError(f"Position {position} is out of bounds.") + + if self._bond_ids[position]: + return self._bond_ids[position][-1][-1] + else: + return self._new_bond_id() + + def _get_column_bonds(self, position: int, direction: DirectionMPS) -> list[int]: + """Returns the unique identifier of all the left (right) virtual bonds of + MPO tensors at ``position`` if ``direction`` is ``LEFT`` (``RIGHT``). + + Notes: + It does not return the corresponding bonds of the MPS tensors. + + Raises: + RuntimeError: If ``position`` is out of bounds. + ValueError: If ``direction`` is not a value in ``DirectionMPS``. + """ + if position < 0 or position >= len(self): + raise RuntimeError(f"Position {position} is out of bounds.") + + if direction == DirectionMPS.LEFT: + index = 1 # By convention, left bond at index 1 + elif direction == DirectionMPS.RIGHT: + index = 2 # By convention, right bond at index 2 + else: + raise ValueError("Argument form must be a value in DirectionMPS.") + + return [b_ids[index] for b_ids in self._bond_ids[position]] + + def _flush(self) -> None: + """Applies all batched operations within ``self._mpo`` to the MPS. + + The method applies variational optimisation of the MPS until it + converges. Based on https://arxiv.org/abs/2207.05612. + """ + self._logger.info("Applying variational optimisation.") + self._logger.info(f"Fidelity before optimisation={self._aux_mps.fidelity}") + + l_cached_tensors: list[Tensor] = [] + r_cached_tensors: list[Tensor] = [] + + def update_sweep_cache(pos: int, direction: DirectionMPS) -> None: + """Given a position in the MPS and a sweeping direction (see + ``DirectionMPS``), calculate the tensor of the partial contraction + of all MPS-MPO-vMPS* columns from ``pos`` towards ``direction``. + Update the cache accordingly. Applies canonicalisation on the vMPS + tensor before contracting. + """ + self._logger.debug("Updating the sweep cache...") + + # Canonicalise the tensor at ``pos`` + if direction == DirectionMPS.LEFT: + self._aux_mps.canonicalise_tensor(pos, form=DirectionMPS.RIGHT) + elif direction == DirectionMPS.RIGHT: + self._aux_mps.canonicalise_tensor(pos, form=DirectionMPS.LEFT) + + # Glossary of bond IDs + # p -> the physical bond of the MPS tensor + # l,r -> the virtual bonds of the MPS tensor + # L,R -> the virtual bonds of the variational MPS tensor + # P -> the physical bond of the variational MPS tensor + # MPO tensors will use ``self._bond_ids`` + + # Get the interleaved representation + interleaved_rep = [ + # The tensor of the MPS + self.tensors[pos], + ["l", "r", "p"], + # The (conjugated) tensor of the variational MPS + self._aux_mps.tensors[pos].conj(), + ["L", "R", "P" if self._mpo[pos] else "p"], + ] + for i, mpo_tensor in enumerate(self._mpo[pos]): + # The MPO tensor at this position + interleaved_rep.append(mpo_tensor) + + mpo_bonds: list[Union[int, str]] = list(self._bond_ids[pos][i]) + if i == 0: + # The input bond of the first MPO tensor must connect to the + # physical bond of the correspondong ``self.tensors`` tensor + mpo_bonds[0] = "p" + if i == len(self._mpo[pos]) - 1: + # The output bond of the last MPO tensor must connect to the + # physical bond of the corresponding ``self._aux_mps`` tensor + mpo_bonds[-1] = "P" + interleaved_rep.append(mpo_bonds) + + # Also contract the previous (cached) tensor during the sweep + if direction == DirectionMPS.LEFT: + if pos != len(self) - 1: # Otherwise, there is nothing cached yet + interleaved_rep.append(r_cached_tensors[-1]) + r_cached_bonds = self._get_column_bonds(pos + 1, DirectionMPS.LEFT) + interleaved_rep.append(["r", "R"] + r_cached_bonds) + elif direction == DirectionMPS.RIGHT: + if pos != 0: # Otherwise, there is nothing cached yet + interleaved_rep.append(l_cached_tensors[-1]) + l_cached_bonds = self._get_column_bonds(pos - 1, DirectionMPS.RIGHT) + interleaved_rep.append(["l", "L"] + l_cached_bonds) + + # Figure out the ID of the bonds of the contracted tensor + if direction == DirectionMPS.LEFT: + # Take the left bond of each of the MPO tensors + result_bonds = self._get_column_bonds(pos, DirectionMPS.LEFT) + # Take the left virtual bond of both of the MPS + interleaved_rep.append(["l", "L"] + result_bonds) + elif direction == DirectionMPS.RIGHT: + # Take the right bond of each of the MPO tensors + result_bonds = self._get_column_bonds(pos, DirectionMPS.RIGHT) + # Take the right virtual bond of both of the MPS + interleaved_rep.append(["r", "R"] + result_bonds) + + # Contract and store + T = cq.contract(*interleaved_rep) + if direction == DirectionMPS.LEFT: + r_cached_tensors.append(T) + elif direction == DirectionMPS.RIGHT: + l_cached_tensors.append(T) + + self._logger.debug("Completed update of the sweep cache.") + + def update_variational_tensor( + pos: int, left_tensor: Optional[Tensor], right_tensor: Optional[Tensor] + ) -> float: + """Update the tensor at ``pos`` of the variational MPS using ``left_tensor`` + (and ``right_tensor``) which is meant to contain the contraction of all + the left (and right) columns of the MPS-MPO-vMPS* network from ``pos``. + Contract these with the MPS-MPO column at ``pos``. + Return the current fidelity of this sweep. + """ + self._logger.debug(f"Optimising tensor at position={pos}") + + interleaved_rep = [ + # The tensor of the MPS + self.tensors[pos], + ["l", "r", "p"], + ] + result_bonds = ["l", "r", "p"] + + # The MPO tensors at position ``pos`` + for i, mpo_tensor in enumerate(self._mpo[pos]): + # The MPO tensor at this position + interleaved_rep.append(mpo_tensor) + + mpo_bonds: list[Union[int, str]] = list(self._bond_ids[pos][i]) + if i == 0: + # The input bond of the first MPO tensor must connect to the + # physical bond of the correspondong ``self.tensors`` tensor + mpo_bonds[0] = "p" + if i == len(self._mpo[pos]) - 1: + # The output bond of the last MPO tensor corresponds to the + # physical bond of the corresponding ``self._aux_mps`` tensor + mpo_bonds[-1] = "P" + result_bonds[-1] = "P" + interleaved_rep.append(mpo_bonds) + + if left_tensor is not None: + interleaved_rep.append(left_tensor) + left_tensor_bonds = self._get_column_bonds(pos - 1, DirectionMPS.RIGHT) + interleaved_rep.append(["l", "L"] + left_tensor_bonds) + result_bonds[0] = "L" + if right_tensor is not None: + interleaved_rep.append(right_tensor) + right_tensor_bonds = self._get_column_bonds(pos + 1, DirectionMPS.LEFT) + interleaved_rep.append(["r", "R"] + right_tensor_bonds) + result_bonds[1] = "R" + + # Append the bond IDs of the resulting tensor + interleaved_rep.append(result_bonds) + + # Contract and store tensor + F = cq.contract(*interleaved_rep) + + # Get the fidelity + optim_fidelity = complex(cq.contract("LRP,LRP->", F.conj(), F)) + assert np.isclose(optim_fidelity.imag, 0.0, atol=self._cfg._atol) + optim_fidelity = float(optim_fidelity.real) + + # Normalise F and update the variational MPS + self._aux_mps.tensors[pos] = F / np.sqrt(optim_fidelity) + + return optim_fidelity + + ################################## + # Variational sweeping algorithm # + ################################## + + # Begin by doing a sweep towards the left that does not update + # the variational tensors, but simply loads up the ``r_cached_tensors`` + for pos in reversed(range(1, len(self))): + update_sweep_cache(pos, direction=DirectionMPS.LEFT) + + prev_fidelity = -1.0 # Dummy value + sweep_fidelity = 0.0 # Dummy value + + # Repeat sweeps until the fidelity converges + sweep_direction = DirectionMPS.RIGHT + while not np.isclose(prev_fidelity, sweep_fidelity, atol=self._cfg.optim_delta): + self._logger.info(f"Doing another optimisation sweep...") + prev_fidelity = sweep_fidelity + + if sweep_direction == DirectionMPS.RIGHT: + sweep_fidelity = update_variational_tensor( + pos=0, left_tensor=None, right_tensor=r_cached_tensors.pop() + ) + update_sweep_cache(pos=0, direction=DirectionMPS.RIGHT) + + for pos in range(1, len(self) - 1): + sweep_fidelity = update_variational_tensor( + pos=pos, + left_tensor=l_cached_tensors[-1], + right_tensor=r_cached_tensors.pop(), + ) + update_sweep_cache(pos, direction=DirectionMPS.RIGHT) + # The last variational tensor is not updated; + # it'll be the first in the next sweep + + sweep_direction = DirectionMPS.LEFT + + elif sweep_direction == DirectionMPS.LEFT: + sweep_fidelity = update_variational_tensor( + pos=len(self) - 1, + left_tensor=l_cached_tensors.pop(), + right_tensor=None, + ) + update_sweep_cache(pos=len(self) - 1, direction=DirectionMPS.LEFT) + + for pos in reversed(range(1, len(self) - 1)): + sweep_fidelity = update_variational_tensor( + pos=pos, + left_tensor=l_cached_tensors.pop(), + right_tensor=r_cached_tensors[-1], + ) + update_sweep_cache(pos, direction=DirectionMPS.LEFT) + # The last variational tensor is not updated; + # it'll be the first in the next sweep + + sweep_direction = DirectionMPS.RIGHT + + self._logger.info( + "Optimisation sweep completed. " + f"Current fidelity={self.fidelity*sweep_fidelity}" + ) + + # Clear out the MPO + self._mpo = [list() for _ in range(len(self))] + self._bond_ids = [list() for _ in range(len(self))] + self._mpo_bond_counter = 0 + + # Update the MPS tensors + self.tensors = [t.copy() for t in self._aux_mps.tensors] + + # Update the fidelity estimate + self.fidelity *= sweep_fidelity + self._aux_mps.fidelity = self.fidelity + + self._logger.info(f"Final fidelity after optimisation={self.fidelity}") + + def _new_bond_id(self) -> int: + self._mpo_bond_counter += 1 + return self._mpo_bond_counter diff --git a/pytket/extensions/cutensornet/states/simulation.py b/pytket/extensions/cutensornet/states/simulation.py new file mode 100644 index 00000000..47fe7558 --- /dev/null +++ b/pytket/extensions/cutensornet/states/simulation.py @@ -0,0 +1,247 @@ +# Copyright 2019-2023 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +## +# http://www.apache.org/licenses/LICENSE-2.0 +## +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from enum import Enum + +from random import choice # type: ignore +from collections import defaultdict # type: ignore +import numpy as np # type: ignore + +from pytket.circuit import Circuit, Command, Qubit +from pytket.transform import Transform +from pytket.architecture import Architecture +from pytket.passes import DefaultMappingPass +from pytket.predicates import CompilationUnit + +from pytket.extensions.cutensornet.general import set_logger +from .general import CuTensorNetHandle +from .mps import ConfigMPS, MPS +from .mps_gate import MPSxGate +from .mps_mpo import MPSxMPO + + +class ContractionAlg(Enum): + """An enum to refer to the MPS contraction algorithm. + + Each enum value corresponds to the class with the same name; see its docs for + information of the algorithm. + """ + + MPSxGate = 0 + MPSxMPO = 1 + + +def simulate( + libhandle: CuTensorNetHandle, + circuit: Circuit, + algorithm: ContractionAlg, + config: ConfigMPS, +) -> MPS: + """Simulate the given circuit and return the ``MPS`` representing the final state. + + Note: + A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` + statement. The device where the MPS is stored will match the one specified + by the library handle. + + The input ``circuit`` must be composed of one-qubit and two-qubit gates only. + Any gateset supported by ``pytket`` can be used. + + Two-qubit gates must act between adjacent qubits, i.e. on ``circuit.qubits[i]`` + and ``circuit.qubits[i+1]`` for any ``i``. If this is not satisfied by your + circuit, consider using ``prepare_circuit()`` on it. + + Args: + libhandle: The cuTensorNet library handle that will be used to carry out + tensor operations on the MPS. + circuit: The pytket circuit to be simulated. + algorithm: Choose between the values of the ``ContractionAlg`` enum. + config: The configuration object for simulation. + + Returns: + An instance of ``MPS`` containing (an approximation of) the final state + of the circuit. + """ + logger = set_logger("Simulation", level=config.loglevel) + + if algorithm == ContractionAlg.MPSxGate: + mps = MPSxGate( # type: ignore + libhandle, + circuit.qubits, + config, + ) + + elif algorithm == ContractionAlg.MPSxMPO: + mps = MPSxMPO( # type: ignore + libhandle, + circuit.qubits, + config, + ) + + # Sort the gates so there isn't much overhead from canonicalising back and forth. + logger.info( + "Ordering the gates in the circuit to reduce canonicalisation overhead." + ) + sorted_gates = _get_sorted_gates(circuit) + + logger.info("Running simulation...") + # Apply the gates + for i, g in enumerate(sorted_gates): + mps.apply_gate(g) + logger.info(f"Progress... {(100*i) // len(sorted_gates)}%") + + # Apply the batched operations that are left (if any) + mps._flush() + + # Apply the batched operations that are left (if any) + mps._flush() + + # Apply the circuit's phase to the leftmost tensor (any would work) + mps.tensors[0] = mps.tensors[0] * np.exp(1j * np.pi * circuit.phase) + + logger.info("Simulation completed.") + logger.info(f"Final MPS size={mps.get_byte_size() / 2**20} MiB") + logger.info(f"Final MPS fidelity={mps.fidelity}") + return mps + + +def prepare_circuit(circuit: Circuit) -> tuple[Circuit, dict[Qubit, Qubit]]: + """Prepares a circuit in a specific, ``MPS``-friendly, manner. + + Returns an equivalent circuit with the appropriate structure to be simulated by + an ``MPS`` algorithm. + + Note: + The qubits in the output circuit will be renamed. Implicit SWAPs may be added + to the circuit, meaning that the logical qubit held at the ``node[i]`` qubit + at the beginning of the circuit may differ from the one it holds at the end. + + Args: + circuit: The circuit to be simulated. + + Returns: + A tuple with an equivalent circuit with the appropriate structure and a + map of qubit names at the end of the circuit to their corresponding + original names. + """ + + # Implement it in a line architecture + cu = CompilationUnit(circuit) + architecture = Architecture([(i, i + 1) for i in range(circuit.n_qubits - 1)]) + DefaultMappingPass(architecture).apply(cu) + prep_circ = cu.circuit + Transform.DecomposeBRIDGE().apply(prep_circ) + + qubit_map: dict[Qubit, Qubit] = {} + for orig_q, arch_q in cu.final_map.items(): + assert isinstance(orig_q, Qubit) + assert isinstance(arch_q, Qubit) + qubit_map[arch_q] = orig_q + + return (prep_circ, qubit_map) + + +def _get_sorted_gates(circuit: Circuit) -> list[Command]: + """Sorts the list of gates, placing 2-qubit gates close to each other first. + + Returns an equivalent list of commands fixing the order of parallel gates so that + 2-qubit gates that are close to each other first. This reduces the overhead of + canonicalisation of the MPS, since we try to apply as many gates as we can on one + end of the MPS before we go to the other end. + + Args: + circuit: The original circuit. + + Returns: + The same gates, ordered in a beneficial way. + """ + + all_gates = circuit.get_commands() + sorted_gates = [] + # Keep track of the qubit at the center of the canonical form; start arbitrarily + current_qubit = circuit.qubits[0] + # Entries from `all_gates` that are not yet in `sorted_gates` + remaining = set(range(len(all_gates))) + + # Create the list of indices of gates acting on each qubit + gate_indices: dict[Qubit, list[int]] = defaultdict(list) + for i, g in enumerate(all_gates): + for q in g.qubits: + gate_indices[q].append(i) + # Apply all 1-qubit gates at the beginning of the circuit + for q, indices in gate_indices.items(): + while indices and len(all_gates[indices[0]].qubits) == 1: + i = indices.pop(0) + sorted_gates.append(all_gates[i]) + remaining.remove(i) + # Decide which 2-qubit gate to apply next + while remaining: + q_index = circuit.qubits.index(current_qubit) + # Find distance from q_index to first qubit with an applicable 2-qubit gate + left_distance = None + prev_q = current_qubit + for i, q in enumerate(reversed(circuit.qubits[:q_index])): + if ( + gate_indices[prev_q] + and gate_indices[q] + and gate_indices[prev_q][0] == gate_indices[q][0] + ): + left_distance = i + break + prev_q = q + right_distance = None + prev_q = current_qubit + for i, q in enumerate(circuit.qubits[q_index + 1 :]): + if ( + gate_indices[prev_q] + and gate_indices[q] + and gate_indices[prev_q][0] == gate_indices[q][0] + ): + right_distance = i + break + prev_q = q + # Choose the shortest distance + if left_distance is None and right_distance is None: + raise RuntimeError( + "Some two-qubit gate in the circuit is not acting between", + "nearest neighbour qubits. Consider using prepare_circuit().", + ) + elif left_distance is None: + assert right_distance is not None + current_qubit = circuit.qubits[q_index + right_distance] + elif right_distance is None: + current_qubit = circuit.qubits[q_index - left_distance] + elif left_distance < right_distance: + current_qubit = circuit.qubits[q_index - left_distance] + elif left_distance > right_distance: + current_qubit = circuit.qubits[q_index + right_distance] + else: + current_qubit = circuit.qubits[ + q_index + choice([-left_distance, right_distance]) + ] + # Apply the gate + i = gate_indices[current_qubit][0] + next_gate = all_gates[i] + sorted_gates.append(next_gate) + remaining.remove(i) + # Apply all 1-qubit gates after this gate + for q in next_gate.qubits: + gate_indices[q].pop(0) # Remove the 2-qubit gate `next_gate` + indices = gate_indices[q] + while indices and len(all_gates[indices[0]].qubits) == 1: + i = indices.pop(0) + sorted_gates.append(all_gates[i]) + remaining.remove(i) + + assert len(all_gates) == len(sorted_gates) + return sorted_gates From 17602573452fdcf30125f8ae55b9ed18f6a3710d Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 10 Oct 2023 03:49:01 -0600 Subject: [PATCH 18/83] Moved ConfigMPS to Config in general --- .../extensions/cutensornet/states/__init__.py | 3 +- .../extensions/cutensornet/states/general.py | 110 +++++++++++++++++ pytket/extensions/cutensornet/states/mps.py | 114 +----------------- .../extensions/cutensornet/states/mps_gate.py | 2 +- .../extensions/cutensornet/states/mps_mpo.py | 5 +- .../cutensornet/states/simulation.py | 6 +- tests/test_mps.py | 2 +- 7 files changed, 120 insertions(+), 122 deletions(-) diff --git a/pytket/extensions/cutensornet/states/__init__.py b/pytket/extensions/cutensornet/states/__init__.py index ce21e41e..773dd1a7 100644 --- a/pytket/extensions/cutensornet/states/__init__.py +++ b/pytket/extensions/cutensornet/states/__init__.py @@ -18,11 +18,10 @@ https://github.com/CQCL/pytket-cutensornet. """ -from .general import CuTensorNetHandle +from .general import CuTensorNetHandle, Config from .mps import ( DirectionMPS, - ConfigMPS, Handle, Tensor, MPS, diff --git a/pytket/extensions/cutensornet/states/general.py b/pytket/extensions/cutensornet/states/general.py index fd1df311..efbe8fdc 100644 --- a/pytket/extensions/cutensornet/states/general.py +++ b/pytket/extensions/cutensornet/states/general.py @@ -55,3 +55,113 @@ def __enter__(self) -> CuTensorNetHandle: def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None: cutn.destroy(self.handle) self._is_destroyed = True + + +class Config: + """Configuration class for simulation using MPS.""" + + def __init__( + self, + chi: Optional[int] = None, + truncation_fidelity: Optional[float] = None, + k: int = 4, + optim_delta: float = 1e-5, + float_precision: Union[np.float32, np.float64] = np.float64, # type: ignore + value_of_zero: float = 1e-16, + loglevel: int = logging.WARNING, + ): + """Instantiate a configuration object for MPS simulation. + + Note: + Providing both a custom ``chi`` and ``truncation_fidelity`` will raise an + exception. Choose one or the other (or neither, for exact simulation). + + Args: + chi: The maximum value allowed for the dimension of the virtual + bonds. Higher implies better approximation but more + computational resources. If not provided, ``chi`` will be unbounded. + truncation_fidelity: Every time a two-qubit gate is applied, the virtual + bond will be truncated to the minimum dimension that satisfies + ``||^2 >= trucantion_fidelity``, where ``|psi>`` and ``|phi>`` + are the states before and after truncation (both normalised). + If not provided, it will default to its maximum value 1. + k: If using MPSxMPO, the maximum number of layers the MPO is allowed to + have before being contracted. Increasing this might increase fidelity, + but it will also increase resource requirements exponentially. + Ignored if not using MPSxMPO. Default value is 4. + optim_delta: If using MPSxMPO, stopping criteria for the optimisation when + contracting the ``k`` layers of MPO. Stops when the increase of fidelity + between iterations is smaller than ``optim_delta``. + Ignored if not using MPSxMPO. Default value is ``1e-5``. + float_precision: The floating point precision used in tensor calculations; + choose from ``numpy`` types: ``np.float64`` or ``np.float32``. + Complex numbers are represented using two of such + ``float`` numbers. Default is ``np.float64``. + value_of_zero: Any number below this value will be considered equal to zero. + Even when no ``chi`` or ``truncation_fidelity`` is provided, singular + values below this number will be truncated. + We suggest to use a value slightly below what your chosen + ``float_precision`` can reasonably achieve. For instance, ``1e-16`` for + ``np.float64`` precision (default) and ``1e-7`` for ``np.float32``. + loglevel: Internal logger output level. Use 30 for warnings only, 20 for + verbose and 10 for debug mode. + + Raises: + ValueError: If both ``chi`` and ``truncation_fidelity`` are fixed. + ValueError: If the value of ``chi`` is set below 2. + ValueError: If the value of ``truncation_fidelity`` is not in [0,1]. + """ + if ( + chi is not None + and truncation_fidelity is not None + and truncation_fidelity != 1.0 + ): + raise ValueError("Cannot fix both chi and truncation_fidelity.") + if chi is None: + chi = 2**60 # In practice, this is like having it be unbounded + if truncation_fidelity is None: + truncation_fidelity = 1 + + if chi < 2: + raise ValueError("The max virtual bond dim (chi) must be >= 2.") + if truncation_fidelity < 0 or truncation_fidelity > 1: + raise ValueError("Provide a value of truncation_fidelity in [0,1].") + + self.chi = chi + self.truncation_fidelity = truncation_fidelity + + if float_precision is None or float_precision == np.float64: # Double precision + self._real_t = np.float64 # type: ignore + self._complex_t = np.complex128 # type: ignore + self._atol = 1e-12 + elif float_precision == np.float32: # Single precision + self._real_t = np.float32 # type: ignore + self._complex_t = np.complex64 # type: ignore + self._atol = 1e-4 + else: + allowed_precisions = [np.float64, np.float32] + raise TypeError( + f"Value of float_precision must be in {allowed_precisions}." + ) + self.zero = value_of_zero + + if value_of_zero > self._atol / 1000: + warnings.warn( + "Your chosen value_of_zero is relatively large. " + "Faithfulness of final fidelity estimate is not guaranteed.", + UserWarning, + ) + + self.k = k + self.optim_delta = 1e-5 + self.loglevel = loglevel + + def copy(self) -> ConfigMPS: + """Standard copy of the contents.""" + return ConfigMPS( + chi=self.chi, + truncation_fidelity=self.truncation_fidelity, + k=self.k, + optim_delta=self.optim_delta, + float_precision=self._real_t, # type: ignore + ) \ No newline at end of file diff --git a/pytket/extensions/cutensornet/states/mps.py b/pytket/extensions/cutensornet/states/mps.py index 2a93b3b8..3df2e148 100644 --- a/pytket/extensions/cutensornet/states/mps.py +++ b/pytket/extensions/cutensornet/states/mps.py @@ -35,7 +35,7 @@ from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle +from .general import CuTensorNetHandle, Config # An alias so that `intptr_t` from CuQuantum's API (which is not available in # base python) has some meaningful type name. @@ -54,116 +54,6 @@ class DirectionMPS(Enum): RIGHT = 1 -class ConfigMPS: - """Configuration class for simulation using MPS.""" - - def __init__( - self, - chi: Optional[int] = None, - truncation_fidelity: Optional[float] = None, - k: int = 4, - optim_delta: float = 1e-5, - float_precision: Union[np.float32, np.float64] = np.float64, # type: ignore - value_of_zero: float = 1e-16, - loglevel: int = logging.WARNING, - ): - """Instantiate a configuration object for MPS simulation. - - Note: - Providing both a custom ``chi`` and ``truncation_fidelity`` will raise an - exception. Choose one or the other (or neither, for exact simulation). - - Args: - chi: The maximum value allowed for the dimension of the virtual - bonds. Higher implies better approximation but more - computational resources. If not provided, ``chi`` will be unbounded. - truncation_fidelity: Every time a two-qubit gate is applied, the virtual - bond will be truncated to the minimum dimension that satisfies - ``||^2 >= trucantion_fidelity``, where ``|psi>`` and ``|phi>`` - are the states before and after truncation (both normalised). - If not provided, it will default to its maximum value 1. - k: If using MPSxMPO, the maximum number of layers the MPO is allowed to - have before being contracted. Increasing this might increase fidelity, - but it will also increase resource requirements exponentially. - Ignored if not using MPSxMPO. Default value is 4. - optim_delta: If using MPSxMPO, stopping criteria for the optimisation when - contracting the ``k`` layers of MPO. Stops when the increase of fidelity - between iterations is smaller than ``optim_delta``. - Ignored if not using MPSxMPO. Default value is ``1e-5``. - float_precision: The floating point precision used in tensor calculations; - choose from ``numpy`` types: ``np.float64`` or ``np.float32``. - Complex numbers are represented using two of such - ``float`` numbers. Default is ``np.float64``. - value_of_zero: Any number below this value will be considered equal to zero. - Even when no ``chi`` or ``truncation_fidelity`` is provided, singular - values below this number will be truncated. - We suggest to use a value slightly below what your chosen - ``float_precision`` can reasonably achieve. For instance, ``1e-16`` for - ``np.float64`` precision (default) and ``1e-7`` for ``np.float32``. - loglevel: Internal logger output level. Use 30 for warnings only, 20 for - verbose and 10 for debug mode. - - Raises: - ValueError: If both ``chi`` and ``truncation_fidelity`` are fixed. - ValueError: If the value of ``chi`` is set below 2. - ValueError: If the value of ``truncation_fidelity`` is not in [0,1]. - """ - if ( - chi is not None - and truncation_fidelity is not None - and truncation_fidelity != 1.0 - ): - raise ValueError("Cannot fix both chi and truncation_fidelity.") - if chi is None: - chi = 2**60 # In practice, this is like having it be unbounded - if truncation_fidelity is None: - truncation_fidelity = 1 - - if chi < 2: - raise ValueError("The max virtual bond dim (chi) must be >= 2.") - if truncation_fidelity < 0 or truncation_fidelity > 1: - raise ValueError("Provide a value of truncation_fidelity in [0,1].") - - self.chi = chi - self.truncation_fidelity = truncation_fidelity - - if float_precision is None or float_precision == np.float64: # Double precision - self._real_t = np.float64 # type: ignore - self._complex_t = np.complex128 # type: ignore - self._atol = 1e-12 - elif float_precision == np.float32: # Single precision - self._real_t = np.float32 # type: ignore - self._complex_t = np.complex64 # type: ignore - self._atol = 1e-4 - else: - allowed_precisions = [np.float64, np.float32] - raise TypeError( - f"Value of float_precision must be in {allowed_precisions}." - ) - self.zero = value_of_zero - - if value_of_zero > self._atol / 1000: - warnings.warn( - "Your chosen value_of_zero is relatively large. " - "Faithfulness of final fidelity estimate is not guaranteed.", - UserWarning, - ) - - self.k = k - self.optim_delta = 1e-5 - self.loglevel = loglevel - - def copy(self) -> ConfigMPS: - """Standard copy of the contents.""" - return ConfigMPS( - chi=self.chi, - truncation_fidelity=self.truncation_fidelity, - k=self.k, - optim_delta=self.optim_delta, - float_precision=self._real_t, # type: ignore - ) - - class MPS: """Represents a state as a Matrix Product State. @@ -188,7 +78,7 @@ def __init__( self, libhandle: CuTensorNetHandle, qubits: list[Qubit], - config: ConfigMPS, + config: Config, ): """Initialise an MPS on the computational state ``|0>``. diff --git a/pytket/extensions/cutensornet/states/mps_gate.py b/pytket/extensions/cutensornet/states/mps_gate.py index c7dc496e..6929ef24 100644 --- a/pytket/extensions/cutensornet/states/mps_gate.py +++ b/pytket/extensions/cutensornet/states/mps_gate.py @@ -280,7 +280,7 @@ def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> MPSxGate: # remove any singular values below ``self._cfg.zero``. self._logger.debug(f"Truncating singular values below={self._cfg.zero}.") if self._cfg.zero > self._cfg._atol / 1000: - self._logger.info( # This was raised as a warning in ConfigMPS already + self._logger.info( # This was raised as a warning in Config already "Your chosen value_of_zero is relatively large. " "Faithfulness of final fidelity estimate is not guaranteed." ) diff --git a/pytket/extensions/cutensornet/states/mps_mpo.py b/pytket/extensions/cutensornet/states/mps_mpo.py index 67cc3c93..35c8b332 100644 --- a/pytket/extensions/cutensornet/states/mps_mpo.py +++ b/pytket/extensions/cutensornet/states/mps_mpo.py @@ -29,10 +29,9 @@ warnings.warn("local settings failed to import cutensornet", ImportWarning) from pytket.circuit import Op, Qubit -from .general import CuTensorNetHandle +from .general import CuTensorNetHandle, Config from .mps import ( DirectionMPS, - ConfigMPS, Tensor, MPS, ) @@ -49,7 +48,7 @@ def __init__( self, libhandle: CuTensorNetHandle, qubits: list[Qubit], - config: ConfigMPS, + config: Config, ): """Initialise an MPS on the computational state ``|0>``. diff --git a/pytket/extensions/cutensornet/states/simulation.py b/pytket/extensions/cutensornet/states/simulation.py index 47fe7558..ab6ebd84 100644 --- a/pytket/extensions/cutensornet/states/simulation.py +++ b/pytket/extensions/cutensornet/states/simulation.py @@ -24,8 +24,8 @@ from pytket.predicates import CompilationUnit from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle -from .mps import ConfigMPS, MPS +from .general import CuTensorNetHandle, Config +from .mps import MPS from .mps_gate import MPSxGate from .mps_mpo import MPSxMPO @@ -45,7 +45,7 @@ def simulate( libhandle: CuTensorNetHandle, circuit: Circuit, algorithm: ContractionAlg, - config: ConfigMPS, + config: Config, ) -> MPS: """Simulate the given circuit and return the ``MPS`` representing the final state. diff --git a/tests/test_mps.py b/tests/test_mps.py index 57c744e0..39fab518 100644 --- a/tests/test_mps.py +++ b/tests/test_mps.py @@ -10,7 +10,7 @@ from pytket.pauli import Pauli, QubitPauliString # type: ignore from pytket.extensions.cutensornet.states import ( CuTensorNetHandle, - ConfigMPS, + Config, MPS, MPSxGate, MPSxMPO, From 014a173601ae92c223fdffb8509cfeda0176f2d6 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 10 Oct 2023 03:52:41 -0600 Subject: [PATCH 19/83] Some fixes --- .../extensions/cutensornet/states/general.py | 9 +++-- pytket/extensions/cutensornet/states/mps.py | 2 - tests/test_mps.py | 38 +++++++++---------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/pytket/extensions/cutensornet/states/general.py b/pytket/extensions/cutensornet/states/general.py index efbe8fdc..de477bc0 100644 --- a/pytket/extensions/cutensornet/states/general.py +++ b/pytket/extensions/cutensornet/states/general.py @@ -13,7 +13,10 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings -from typing import Any, Optional +import logging +from typing import Any, Optional, Union + +import numpy as np # type: ignore try: import cupy as cp # type: ignore @@ -156,9 +159,9 @@ def __init__( self.optim_delta = 1e-5 self.loglevel = loglevel - def copy(self) -> ConfigMPS: + def copy(self) -> Config: """Standard copy of the contents.""" - return ConfigMPS( + return Config( chi=self.chi, truncation_fidelity=self.truncation_fidelity, k=self.k, diff --git a/pytket/extensions/cutensornet/states/mps.py b/pytket/extensions/cutensornet/states/mps.py index 3df2e148..a0a639cb 100644 --- a/pytket/extensions/cutensornet/states/mps.py +++ b/pytket/extensions/cutensornet/states/mps.py @@ -13,8 +13,6 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings -import logging -from typing import Any, Optional, Union from enum import Enum from random import random # type: ignore diff --git a/tests/test_mps.py b/tests/test_mps.py index 39fab518..d6a2f7a4 100644 --- a/tests/test_mps.py +++ b/tests/test_mps.py @@ -26,7 +26,7 @@ def test_libhandle_manager() -> None: # Proper use of library handle with CuTensorNetHandle() as libhandle: - mps = MPS(libhandle, circ.qubits, ConfigMPS()) + mps = MPS(libhandle, circ.qubits, Config()) assert np.isclose(mps.vdot(mps), 1, atol=mps._cfg._atol) # Catch exception due to library handle out of scope @@ -38,9 +38,9 @@ def test_init() -> None: circ = Circuit(5) with CuTensorNetHandle() as libhandle: - mps_gate = MPSxGate(libhandle, circ.qubits, ConfigMPS()) + mps_gate = MPSxGate(libhandle, circ.qubits, Config()) assert mps_gate.is_valid() - mps_mpo = MPSxMPO(libhandle, circ.qubits, ConfigMPS()) + mps_mpo = MPSxMPO(libhandle, circ.qubits, Config()) assert mps_mpo.is_valid() @@ -49,7 +49,7 @@ def test_canonicalise() -> None: circ = Circuit(5) with CuTensorNetHandle() as libhandle: - mps_gate = MPSxGate(libhandle, circ.qubits, ConfigMPS()) + mps_gate = MPSxGate(libhandle, circ.qubits, Config()) # Fill up the tensors with random entries # Leftmost tensor @@ -143,7 +143,7 @@ def test_exact_circ_sim(circuit: Circuit, algorithm: ContractionAlg) -> None: state = prep_circ.get_statevector() with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, prep_circ, algorithm, ConfigMPS()) + mps = simulate(libhandle, prep_circ, algorithm, Config()) assert mps.is_valid() # Check that there was no approximation assert np.isclose(mps.fidelity, 1.0, atol=mps._cfg._atol) @@ -196,7 +196,7 @@ def test_approx_circ_sim_gate_fid(circuit: Circuit, algorithm: ContractionAlg) - prep_circ, _ = prepare_circuit(circuit) with CuTensorNetHandle() as libhandle: mps = simulate( - libhandle, prep_circ, algorithm, ConfigMPS(truncation_fidelity=0.99) + libhandle, prep_circ, algorithm, Config(truncation_fidelity=0.99) ) assert mps.is_valid() # Check that overlap is 1 @@ -236,7 +236,7 @@ def test_approx_circ_sim_gate_fid(circuit: Circuit, algorithm: ContractionAlg) - def test_approx_circ_sim_chi(circuit: Circuit, algorithm: ContractionAlg) -> None: prep_circ, _ = prepare_circuit(circuit) with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, prep_circ, algorithm, ConfigMPS(chi=4)) + mps = simulate(libhandle, prep_circ, algorithm, Config(chi=4)) assert mps.is_valid() # Check that overlap is 1 assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) @@ -275,7 +275,7 @@ def test_float_point_options( with CuTensorNetHandle() as libhandle: # Exact mps = simulate( - libhandle, prep_circ, algorithm, ConfigMPS(float_precision=fp_precision) + libhandle, prep_circ, algorithm, Config(float_precision=fp_precision) ) assert mps.is_valid() # Check that overlap is 1 @@ -286,7 +286,7 @@ def test_float_point_options( libhandle, prep_circ, algorithm, - ConfigMPS(truncation_fidelity=0.99, float_precision=fp_precision), + Config(truncation_fidelity=0.99, float_precision=fp_precision), ) assert mps.is_valid() # Check that overlap is 1 @@ -297,7 +297,7 @@ def test_float_point_options( libhandle, prep_circ, algorithm, - ConfigMPS(chi=4, float_precision=fp_precision), + Config(chi=4, float_precision=fp_precision), ) assert mps.is_valid() # Check that overlap is 1 @@ -320,7 +320,7 @@ def test_circ_approx_explicit(circuit: Circuit) -> None: libhandle, circuit, ContractionAlg.MPSxGate, - ConfigMPS(truncation_fidelity=0.99), + Config(truncation_fidelity=0.99), ) assert np.isclose(mps_gate.fidelity, 0.4, atol=1e-1) assert mps_gate.is_valid() @@ -331,7 +331,7 @@ def test_circ_approx_explicit(circuit: Circuit) -> None: libhandle, circuit, ContractionAlg.MPSxMPO, - ConfigMPS(truncation_fidelity=0.99), + Config(truncation_fidelity=0.99), ) assert np.isclose(mps_mpo.fidelity, 0.6, atol=1e-1) assert mps_mpo.is_valid() @@ -340,14 +340,14 @@ def test_circ_approx_explicit(circuit: Circuit) -> None: # Fixed virtual bond dimension # Check for MPSxGate mps_gate = simulate( - libhandle, circuit, ContractionAlg.MPSxGate, ConfigMPS(chi=8) + libhandle, circuit, ContractionAlg.MPSxGate, Config(chi=8) ) assert np.isclose(mps_gate.fidelity, 0.03, atol=1e-2) assert mps_gate.is_valid() assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=mps_gate._cfg._atol) # Check for MPSxMPO - mps_mpo = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, ConfigMPS(chi=8)) + mps_mpo = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, Config(chi=8)) assert np.isclose(mps_mpo.fidelity, 0.04, atol=1e-2) assert mps_mpo.is_valid() assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=mps_mpo._cfg._atol) @@ -385,7 +385,7 @@ def test_postselect_2q_circ(circuit: Circuit, postselect_dict: dict) -> None: sv = sv / np.sqrt(sv_prob) # Normalise with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, ConfigMPS()) + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config()) prob = mps.postselect(postselect_dict) assert np.isclose(prob, sv_prob, atol=mps._cfg._atol) assert np.allclose(mps.get_statevector(), sv, atol=mps._cfg._atol) @@ -415,7 +415,7 @@ def test_postselect_circ(circuit: Circuit, postselect_dict: dict) -> None: sv = sv / np.sqrt(sv_prob) # Normalise with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, ConfigMPS()) + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config()) prob = mps.postselect(postselect_dict) assert np.isclose(prob, sv_prob, atol=mps._cfg._atol) assert np.allclose(mps.get_statevector(), sv, atol=mps._cfg._atol) @@ -460,7 +460,7 @@ def test_expectation_value(circuit: Circuit, observable: QubitPauliString) -> No # Simulate the circuit and obtain the expectation value with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, ConfigMPS()) + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config()) assert np.isclose( mps.expectation_value(observable), expectation_value, atol=mps._cfg._atol ) @@ -490,7 +490,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, ConfigMPS()) + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config()) # Take samples measuring both qubits at once for _ in range(n_samples): @@ -517,7 +517,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, ConfigMPS()) + mps = simulate(libhandle, circuit, ContractionAlg.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} From 3b71b65a17abbadf193bd983373cb1fc701b60c5 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 10 Oct 2023 02:58:17 -0700 Subject: [PATCH 20/83] Some more fixes --- pytket/extensions/cutensornet/states/general.py | 2 +- pytket/extensions/cutensornet/states/mps.py | 4 +--- tests/test_mps.py | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pytket/extensions/cutensornet/states/general.py b/pytket/extensions/cutensornet/states/general.py index de477bc0..df88c6ec 100644 --- a/pytket/extensions/cutensornet/states/general.py +++ b/pytket/extensions/cutensornet/states/general.py @@ -167,4 +167,4 @@ def copy(self) -> Config: k=self.k, optim_delta=self.optim_delta, float_precision=self._real_t, # type: ignore - ) \ No newline at end of file + ) diff --git a/pytket/extensions/cutensornet/states/mps.py b/pytket/extensions/cutensornet/states/mps.py index a0a639cb..9052ab9c 100644 --- a/pytket/extensions/cutensornet/states/mps.py +++ b/pytket/extensions/cutensornet/states/mps.py @@ -13,6 +13,7 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings +from typing import Any from enum import Enum from random import random # type: ignore @@ -35,9 +36,6 @@ from .general import CuTensorNetHandle, Config -# An alias so that `intptr_t` from CuQuantum's API (which is not available in -# base python) has some meaningful type name. -Handle = int # An alias for the CuPy type used for tensors try: Tensor = cp.ndarray diff --git a/tests/test_mps.py b/tests/test_mps.py index d6a2f7a4..20950906 100644 --- a/tests/test_mps.py +++ b/tests/test_mps.py @@ -339,9 +339,7 @@ def test_circ_approx_explicit(circuit: Circuit) -> None: # Fixed virtual bond dimension # Check for MPSxGate - mps_gate = simulate( - libhandle, circuit, ContractionAlg.MPSxGate, Config(chi=8) - ) + mps_gate = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config(chi=8)) assert np.isclose(mps_gate.fidelity, 0.03, atol=1e-2) assert mps_gate.is_valid() assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=mps_gate._cfg._atol) From a465474f8e9d47d2fa1a6eeb62f1bcdcfe684364 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 10 Oct 2023 09:20:23 -0600 Subject: [PATCH 21/83] Moving some more things around --- pytket/extensions/cutensornet/states/__init__.py | 2 -- pytket/extensions/cutensornet/states/general.py | 7 +++++++ pytket/extensions/cutensornet/states/mps.py | 11 ++--------- pytket/extensions/cutensornet/states/mps_mpo.py | 3 +-- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/pytket/extensions/cutensornet/states/__init__.py b/pytket/extensions/cutensornet/states/__init__.py index 773dd1a7..7c7ecde7 100644 --- a/pytket/extensions/cutensornet/states/__init__.py +++ b/pytket/extensions/cutensornet/states/__init__.py @@ -22,8 +22,6 @@ from .mps import ( DirectionMPS, - Handle, - Tensor, MPS, ) diff --git a/pytket/extensions/cutensornet/states/general.py b/pytket/extensions/cutensornet/states/general.py index df88c6ec..d7462432 100644 --- a/pytket/extensions/cutensornet/states/general.py +++ b/pytket/extensions/cutensornet/states/general.py @@ -28,6 +28,13 @@ warnings.warn("local settings failed to import cutensornet", ImportWarning) +# An alias for the CuPy type used for tensors +try: + Tensor = cp.ndarray +except NameError: + Tensor = Any + + class CuTensorNetHandle: """Initialise the cuTensorNet library with automatic workspace memory management. diff --git a/pytket/extensions/cutensornet/states/mps.py b/pytket/extensions/cutensornet/states/mps.py index 9052ab9c..88cf17ef 100644 --- a/pytket/extensions/cutensornet/states/mps.py +++ b/pytket/extensions/cutensornet/states/mps.py @@ -13,7 +13,6 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings -from typing import Any from enum import Enum from random import random # type: ignore @@ -34,13 +33,7 @@ from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle, Config - -# An alias for the CuPy type used for tensors -try: - Tensor = cp.ndarray -except NameError: - Tensor = Any +from .general import CuTensorNetHandle, Tensor, Config class DirectionMPS(Enum): @@ -106,7 +99,7 @@ def __init__( self.qubit_position = {q: i for i, q in enumerate(qubits)} # Create the list of tensors - self.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> diff --git a/pytket/extensions/cutensornet/states/mps_mpo.py b/pytket/extensions/cutensornet/states/mps_mpo.py index 35c8b332..b53974c5 100644 --- a/pytket/extensions/cutensornet/states/mps_mpo.py +++ b/pytket/extensions/cutensornet/states/mps_mpo.py @@ -29,10 +29,9 @@ warnings.warn("local settings failed to import cutensornet", ImportWarning) from pytket.circuit import Op, Qubit -from .general import CuTensorNetHandle, Config +from .general import CuTensorNetHandle, Tensor, Config from .mps import ( DirectionMPS, - Tensor, MPS, ) from .mps_gate import MPSxGate From 47fd9038d7dc9063bccca53b0e2d3a40fce62529 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 10 Oct 2023 09:20:46 -0600 Subject: [PATCH 22/83] Began implementation of TTN --- pytket/extensions/cutensornet/states/ttn.py | 311 ++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 pytket/extensions/cutensornet/states/ttn.py diff --git a/pytket/extensions/cutensornet/states/ttn.py b/pytket/extensions/cutensornet/states/ttn.py new file mode 100644 index 00000000..7524959f --- /dev/null +++ b/pytket/extensions/cutensornet/states/ttn.py @@ -0,0 +1,311 @@ +# Copyright 2019-2023 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +## +# http://www.apache.org/licenses/LICENSE-2.0 +## +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations # type: ignore +import warnings +from typing import Optional, NamedTuple +from enum import IntEnum + +from random import random # type: ignore +import math # type: ignore +import numpy as np # type: ignore + +try: + import cupy as cp # type: ignore +except ImportError: + 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) + +from pytket.circuit import Command, Op, OpType, Qubit +from pytket.pauli import Pauli, QubitPauliString + +from pytket.extensions.cutensornet.general import set_logger + +from .general import CuTensorNetHandle, Tensor, Config + + +class DirTTN(IntEnum): + """An enum to refer to relative directions within the TTN.""" + + PARENT = -1 + LEFT = 0 + RIGHT = 1 + + +# An alias for the TTN path from root to a TreeNode +RootPath = tuple[DirTTN, ...] + + +class TreeNode(NamedTuple): + """Represents a single tensor in the TTN. + + The shape of the tensor agrees with the convention set in ``DirTTN``, meaning + that ``tensor.shape[DirTTN.PARENT]`` corresponds to the dimension of the bond + connecting this tree node with its parent. Notice that, since DirTTN.PARENT is + -1, this is always the last entry. + + In the case the TreeNode is a leaf, it will contain only one virtual bond + (the parent) and as many physical bonds as qubits in the group it represents. + These qubits will correspond to bonds from ``0`` to ``len(tensor.shape)``. + """ + + tensor: Tensor + is_leaf: bool = False + canonical_form: Optional[DirTTN] = None + + def copy(self) -> TreeNode: + return TreeNode( + self.tensor.copy(), + is_leaf=self.is_leaf, + canonical_form=self.canonical_form, + ) + + +class TTN: + """Represents a state as a Tree Tensor Network. + + Attributes: + nodes (dict[RootPath, TreeNode]): A dictionary providing the tree node + of the given root path in the TTN. + qubit_position (dict[pytket.circuit.Qubit, tuple[RootPath, int]]): A dictionary + mapping circuit qubits to their address in the TTN. + fidelity (float): A lower bound of the fidelity, obtained by multiplying + the fidelities after each contraction. The fidelity of a contraction + corresponds to ``||^2`` where ``|psi>`` and ``|phi>`` are the + states before and after truncation (assuming both are normalised). + """ + + def __init__( + self, + libhandle: CuTensorNetHandle, + qubit_partition: dict[int, list[Qubit]], + config: Config, + ): + """Initialise a TTN on the computational state ``|0>``. + + Note: + A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` + statement. The device where the TTN is stored will match the one specified + by the library handle. + + The current implementation requires the keys of ``qubit_partition`` to be + integers from ``0`` to ``2^l - 1`` for some ``l``. The cost of applying + gates between qubits on ``qubit_partition[i]`` and ``qubit_partition[j]`` + scales exponentially on ``|i-j|``. + + Args: + libhandle: The cuTensorNet library handle that will be used to carry out + tensor operations on the TTN. + qubits: A partition of the qubits in the circuit into disjoint groups. + config: The object describing the configuration for simulation. + + Raises: + ValueError: If the keys of ``qubit_partition`` do not range from ``0`` to + ``2^l - 1`` for some ``l``. + ValueError: If a ``Qubit`` is repeated in ``qubit_partition``. + """ + self._lib = libhandle + self._cfg = config + self._logger = set_logger("TTN", level=config.loglevel) + self.fidelity = 1.0 + self.nodes: dict[RootPath, TreeNode] = dict() + self.qubit_position: dict[Qubit, tuple[RootPath, int]] = dict() + + n_groups = len(qubit_partition) + if n_groups == 0: # There's no initialisation to be done + return None + + 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: + raise ValueError( + f"The keys of qubit_partition must range from 0 to {n_groups-1}." + ) + + # Calculate the root path of this group + for l in reversed(range(n_levels)): + path = [] + 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 in multiple entries of 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 - 1): + # 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] + ] + + def is_valid(self) -> bool: + """Verify that the TTN object is valid. + + Specifically, verify that the TTN does not exceed the dimension limit ``chi`` + specified in the ``Config`` object, that physical bonds have dimension 2, + that all tensors except the leaves are rank three and that tensors have shapes + consistent with the bond dimensions. + + Returns: + False if a violation was detected or True otherwise. + """ + chi_ok = all( + self.get_dimension(path, DirTTN.PARENT) <= self._cfg.chi + for path in self.nodes.keys() + ) + phys_ok = all( + self.nodes[path].tensor.shape[bond] == 2 + for path, bond in self.qubit_position.values() + ) + rank_ok = all( + node.is_leaf or node.tensor.shape == 3 for node in self.nodes.values() + ) + shape_ok = all( + self.get_dimension(path, DirTTN.PARENT) + == self.get_dimension(path, path[-1]) + for path in self.nodes.keys() + if path != [] + ) + + # Debugger logging + self._logger.debug( + "Checking validity of MPS... " + f"chi_ok={chi_ok}, " + f"phys_ok={phys_ok}, " + f"rank_ok={rank_ok}, " + f"shape_ok={shape_ok}" + ) + return chi_ok and phys_ok and rank_ok and shape_ok + + def get_qubits(self) -> set[Qubit]: + """Returns the set of qubits that this TTN is defined on.""" + return set(self.qubit_position.keys()) + + def get_dimension(self, path: RootPath, direction: DirTTN) -> int: + """Returns the dimension of bond ``dir`` of the node at ``path``. + + Args: + path: The path to a node in the TTN. + direction: The direction of the bond. + + Returns: + The dimension of the bond between the node and its parent. + + Raises: + ValueError: If ``path`` is not in the TTN. + """ + if path not in self.nodes: + raise ValueError(f"The path {path} is not in the TTN.") + + dim: int = self.nodes[path].tensor.shape[direction] + return dim + + def get_byte_size(self) -> int: + """ + Returns: + The number of bytes the TTN currently occupies in GPU memory. + """ + return sum(node.tensor.nbytes for node in self.nodes.values()) + + def get_device_id(self) -> int: + """ + Returns: + The identifier of the device (GPU) where the tensors are stored. + """ + return int(self.nodes[tuple()].tensor.device) + + def update_libhandle(self, libhandle: CuTensorNetHandle) -> None: + """Update the ``CuTensorNetHandle`` used by this ``TTN`` object. Multiple + objects may use the same handle. + + Args: + libhandle: The new cuTensorNet library handle. + + Raises: + RuntimeError: If the device (GPU) where ``libhandle`` was initialised + does not match the one where the tensors of the TTN are stored. + """ + if libhandle.device_id != self.get_device_id(): + raise RuntimeError( + "Device of libhandle is not the one where the TTN is stored.", + f"{libhandle.device_id} != {self.get_device_id()}", + ) + self._lib = libhandle + + def copy(self) -> TTN: + """ + Returns: + A deep copy of the TTN on the same device. + """ + + # Create a dummy object + new_ttn = TTN(self._lib, qubit_partition=dict(), config=self._cfg.copy()) + # Copy all data + new_ttn.fidelity = self.fidelity + new_ttn.nodes = {path: node.copy() for path, node in self.nodes.items()} + new_ttn.qubit_position = self.qubit_position.copy() + + self._logger.debug( + "Successfully copied a TTN " + f"of size {new_ttn.get_byte_size() / 2**20} MiB." + ) + return new_ttn + + def _apply_1q_gate(self, position: int, gate: Op) -> TTN: + raise NotImplementedError( + "TTN is a base class with no contraction algorithm implemented." + + " You must use a subclass of TTN, such as TTNxGate." + ) + + def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> TTN: + raise NotImplementedError( + "TTN is a base class with no contraction algorithm implemented." + + " You must use a subclass of TTN, such as TTNxGate." + ) From b86a54573b688db29bc4a0b51a3b4a91615b0739 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 11 Oct 2023 16:36:37 +0100 Subject: [PATCH 23/83] Added single-qubit gate support and some basic tests. --- pytket/extensions/cutensornet/states/ttn.py | 70 ++++++++++++--- .../extensions/cutensornet/states/ttn_gate.py | 88 ++++++++++++++++++ tests/conftest.py | 28 ++++-- tests/test_ttn.py | 90 +++++++++++++++++++ 4 files changed, 259 insertions(+), 17 deletions(-) create mode 100644 pytket/extensions/cutensornet/states/ttn_gate.py create mode 100644 tests/test_ttn.py diff --git a/pytket/extensions/cutensornet/states/ttn.py b/pytket/extensions/cutensornet/states/ttn.py index 7524959f..d21cb69f 100644 --- a/pytket/extensions/cutensornet/states/ttn.py +++ b/pytket/extensions/cutensornet/states/ttn.py @@ -13,7 +13,7 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings -from typing import Optional, NamedTuple +from typing import Optional from enum import IntEnum from random import random # type: ignore @@ -50,7 +50,7 @@ class DirTTN(IntEnum): RootPath = tuple[DirTTN, ...] -class TreeNode(NamedTuple): +class TreeNode: """Represents a single tensor in the TTN. The shape of the tensor agrees with the convention set in ``DirTTN``, meaning @@ -60,19 +60,21 @@ class TreeNode(NamedTuple): In the case the TreeNode is a leaf, it will contain only one virtual bond (the parent) and as many physical bonds as qubits in the group it represents. - These qubits will correspond to bonds from ``0`` to ``len(tensor.shape)``. + These qubits will correspond to bonds from ``0`` to ``len(tensor.shape)-2``. """ - tensor: Tensor - is_leaf: bool = False - canonical_form: Optional[DirTTN] = None + def __init__(self, tensor: Tensor, is_leaf: bool = False): + self.tensor = tensor + self.is_leaf = is_leaf + self.canonical_form: Optional[DirTTN] = None def copy(self) -> TreeNode: - return TreeNode( + new_ttn = TreeNode( self.tensor.copy(), is_leaf=self.is_leaf, - canonical_form=self.canonical_form, ) + new_ttn.canonical_form = self.canonical_form + return new_ttn class TTN: @@ -117,6 +119,8 @@ def __init__( ValueError: If the keys of ``qubit_partition`` do not range from ``0`` to ``2^l - 1`` for some ``l``. ValueError: If a ``Qubit`` is repeated in ``qubit_partition``. + NotImplementedError: If the value of ``truncation_fidelity`` in ``config`` + is smaller than one. """ self._lib = libhandle self._cfg = config @@ -125,6 +129,11 @@ def __init__( self.nodes: dict[RootPath, TreeNode] = dict() self.qubit_position: dict[Qubit, tuple[RootPath, int]] = dict() + if self._cfg.truncation_fidelity < 1: + raise NotImplementedError( + "Truncation fidelity mode not currently implemented on TTN." + ) + n_groups = len(qubit_partition) if n_groups == 0: # There's no initialisation to be done return None @@ -216,7 +225,7 @@ def is_valid(self) -> bool: # Debugger logging self._logger.debug( - "Checking validity of MPS... " + "Checking validity of TTN... " f"chi_ok={chi_ok}, " f"phys_ok={phys_ok}, " f"rank_ok={rank_ok}, " @@ -224,6 +233,45 @@ def is_valid(self) -> bool: ) return chi_ok and phys_ok and rank_ok and shape_ok + def apply_gate(self, gate: Command) -> TTN: + """Apply the gate to the TTN. + + Note: + Only single-qubit gates and two-qubit gates are supported. + + Args: + gate: The gate to be applied. + + Returns: + ``self``, to allow for method chaining. + + Raises: + RuntimeError: If the ``CuTensorNetHandle`` is out of scope. + RuntimeError: If gate acts on more than 2 qubits. + """ + if self._lib._is_destroyed: + raise RuntimeError( + "The cuTensorNet library handle is out of scope.", + "See the documentation of update_libhandle and CuTensorNetHandle.", + ) + + self._logger.debug(f"Applying gate {gate}") + + if len(gate.qubits) == 1: + self._apply_1q_gate(gate.qubits[0], gate.op) + + elif len(gate.qubits) == 2: + self._apply_2q_gate(gate.qubits[0], gate.qubits[1], gate.op) + + else: + # NOTE: This could be supported if gate acts on same group of qubits + raise RuntimeError( + "Gates must act on only 1 or 2 qubits! " + + f"This is not satisfied by {gate}." + ) + + return self + def get_qubits(self) -> set[Qubit]: """Returns the set of qubits that this TTN is defined on.""" return set(self.qubit_position.keys()) @@ -298,13 +346,13 @@ def copy(self) -> TTN: ) return new_ttn - def _apply_1q_gate(self, position: int, gate: Op) -> TTN: + def _apply_1q_gate(self, qubit: Qubit, gate: Op) -> TTN: raise NotImplementedError( "TTN is a base class with no contraction algorithm implemented." + " You must use a subclass of TTN, such as TTNxGate." ) - def _apply_2q_gate(self, positions: tuple[int, int], gate: Op) -> TTN: + def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTN: raise NotImplementedError( "TTN is a base class with no contraction algorithm implemented." + " You must use a subclass of TTN, such as TTNxGate." diff --git a/pytket/extensions/cutensornet/states/ttn_gate.py b/pytket/extensions/cutensornet/states/ttn_gate.py new file mode 100644 index 00000000..354bac36 --- /dev/null +++ b/pytket/extensions/cutensornet/states/ttn_gate.py @@ -0,0 +1,88 @@ +# Copyright 2019-2023 Quantinuum +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +## +# http://www.apache.org/licenses/LICENSE-2.0 +## +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations # type: ignore +import warnings +import logging + +import numpy as np # type: ignore + +try: + import cupy as cp # type: ignore +except ImportError: + 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) + +from pytket.circuit import Op, Qubit +from .ttn import TTN, RootPath + + +class TTNxGate(TTN): + """Implements a gate-by-gate contraction algorithm to calculate the output state + of a circuit as a ``TTN``. + """ + + def _apply_1q_gate(self, qubit: Qubit, gate: Op) -> TTNxGate: + """Applies the 1-qubit gate to the TTN. + + This does not increase the dimension of any bond. + + Args: + qubit: The qubit that this gate is applied to. + gate: The gate to be applied. + + Returns: + ``self``, to allow for method chaining. + """ + + # Load the gate's unitary to the GPU memory + gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) + gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) + + path, target = self.qubit_position[qubit] + tensor = self.nodes[path].tensor + n_qbonds = len(tensor.shape) - 1 # Total number of physical bonds in this node + + # Glossary of bond IDs + # qX -> where X is the X-th physical bond (qubit) in the TTN node + # p -> the parent bond of the TTN node + # i -> the input bond of the gate + # o -> the output bond of the gate + + node_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] + result_bonds = node_bonds.copy() + node_bonds[target] = "i" # Target bond must match with the gate input bond + result_bonds[target] = "o" # After contraction it matches the output bond + + interleaved_rep = [ + # The tensor of the TTN + tensor, + node_bonds, + # The tensor of the gate + gate_tensor, + ["o", "i"], + # The bonds of the resulting tensor + result_bonds, + ] + + # Contract + new_tensor = cq.contract(*interleaved_rep) + + # Update ``self.nodes`` + # NOTE: Canonicalisation of the node does not change + self.nodes[path].tensor = new_tensor + return self diff --git a/tests/conftest.py b/tests/conftest.py index 32ae62c2..36c0672a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,6 +50,18 @@ def quantum_volume_circuit(n_qubits: int) -> Circuit: return c +@pytest.fixture +def q5_empty() -> Circuit: + circuit = Circuit(5) + return circuit + + +@pytest.fixture +def q8_empty() -> Circuit: + circuit = Circuit(8) + return circuit + + @pytest.fixture def q2_x0() -> Circuit: circuit = Circuit(2) @@ -173,12 +185,6 @@ def q4_multicontrols() -> Circuit: return circ -@pytest.fixture -def q5_empty() -> Circuit: - circuit = Circuit(5) - return circuit - - @pytest.fixture def q5_h0s1rz2ry3tk4tk13() -> Circuit: circuit = Circuit(5) @@ -191,6 +197,16 @@ def q5_h0s1rz2ry3tk4tk13() -> Circuit: return circuit +@pytest.fixture +def q8_x0h2v5z6() -> Circuit: + circuit = Circuit(8) + circuit.X(0) + circuit.H(2) + circuit.V(5) + circuit.Z(6) + return circuit + + @pytest.fixture def q5_line_circ_30_layers() -> Circuit: np.random.seed(1) diff --git a/tests/test_ttn.py b/tests/test_ttn.py new file mode 100644 index 00000000..e4d2b60c --- /dev/null +++ b/tests/test_ttn.py @@ -0,0 +1,90 @@ +from typing import Any +import random # type: ignore +import pytest + +import cuquantum as cq # type: ignore +import cupy as cp # type: ignore +import numpy as np # type: ignore + +from pytket.circuit import Circuit, Qubit, OpType # type: ignore +from pytket.pauli import Pauli, QubitPauliString # type: ignore +from pytket.extensions.cutensornet.states import ( + CuTensorNetHandle, + Config, + TTN, + TTNxGate, +) +from pytket.extensions.cutensornet.utils import circuit_statevector_postselect + + +def test_libhandle_manager() -> None: + circ = Circuit(5) + + # Proper use of library handle + with CuTensorNetHandle() as libhandle: + ttn = TTN(libhandle, circ.qubits, Config()) + assert np.isclose(ttn.vdot(ttn), 1, atol=ttn._cfg._atol) + + # Catch exception due to library handle out of scope + with pytest.raises(RuntimeError): + ttn.vdot(ttn) + + +def test_init() -> None: + circ = Circuit(5) + + with CuTensorNetHandle() as libhandle: + ttn_gate = TTNxGate(libhandle, circ.qubits, Config()) + assert ttn_gate.is_valid() + +@pytest.mark.parametrize( + "circuit", + [ + # pytest.lazy_fixture("q5_empty"), # type: ignore + pytest.lazy_fixture("q8_empty"), # type: ignore + pytest.lazy_fixture("q2_x0"), # type: ignore + pytest.lazy_fixture("q2_x1"), # type: ignore + pytest.lazy_fixture("q2_v0"), # type: ignore + pytest.lazy_fixture("q8_x0h2v5z6"), # type: ignore + # pytest.lazy_fixture("q2_x0cx01"), # type: ignore + # pytest.lazy_fixture("q2_x1cx10x1"), # type: ignore + # pytest.lazy_fixture("q2_x0cx01cx10"), # type: ignore + # pytest.lazy_fixture("q2_v0cx01cx10"), # type: ignore + # pytest.lazy_fixture("q2_hadamard_test"), # type: ignore + # pytest.lazy_fixture("q2_lcu1"), # type: ignore + # pytest.lazy_fixture("q2_lcu2"), # type: ignore + # pytest.lazy_fixture("q2_lcu3"), # type: ignore + # pytest.lazy_fixture("q3_v0cx02"), # type: ignore + # pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore + # pytest.lazy_fixture("q4_lcu1"), # TTN doesn't support n-qubit gates with n>2 + # pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore + # pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore + # pytest.lazy_fixture("q6_qvol"), # type: ignore + ], +) +def test_exact_circ_sim(circuit: Circuit) -> None: + n_qubits = len(circuit.qubits) + qubit_partition = {i: q for i, q in enumerate(circuit.qubits)} + state = circuit.get_statevector() + + with CuTensorNetHandle() as libhandle: + ttn = TTNxGate(libhandle, qubit_partition, Config()) + for g in circuit.get_commands(): + ttn.apply_gate(g) + + assert ttn.is_valid() + # Check that there was no approximation + assert np.isclose(ttn.fidelity, 1.0, atol=ttn._cfg._atol) + # Check that overlap is 1 + assert np.isclose(ttn.vdot(ttn), 1.0, atol=ttn._cfg._atol) + + # Check that all of the amplitudes are correct + for b in range(2**n_qubits): + assert np.isclose( + ttn.get_amplitude(b), + state[b], + atol=ttn._cfg._atol, + ) + + # Check that the statevector is correct + assert np.allclose(ttn.get_statevector(), state, atol=ttn._cfg._atol) \ No newline at end of file From 68401ce811516feb0eb14d0d157e45e68187f586 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Wed, 11 Oct 2023 10:21:40 -0700 Subject: [PATCH 24/83] Fixed some bugs --- .../extensions/cutensornet/states/__init__.py | 19 ++++++------------- pytket/extensions/cutensornet/states/ttn.py | 18 ++++++++++-------- tests/test_ttn.py | 15 +++++++++------ 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/pytket/extensions/cutensornet/states/__init__.py b/pytket/extensions/cutensornet/states/__init__.py index 7c7ecde7..6bcd2765 100644 --- a/pytket/extensions/cutensornet/states/__init__.py +++ b/pytket/extensions/cutensornet/states/__init__.py @@ -19,18 +19,11 @@ """ from .general import CuTensorNetHandle, Config +from .simulation import ContractionAlg, simulate, prepare_circuit -from .mps import ( - DirectionMPS, - MPS, -) - -from .mps_gate import ( - MPSxGate, -) - -from .mps_mpo import ( - MPSxMPO, -) +from .mps import DirectionMPS, MPS +from .mps_gate import MPSxGate +from .mps_mpo import MPSxMPO -from .simulation import ContractionAlg, simulate, prepare_circuit +from .ttn import TTN, DirTTN +from .ttn_gate import TTNxGate diff --git a/pytket/extensions/cutensornet/states/ttn.py b/pytket/extensions/cutensornet/states/ttn.py index d21cb69f..93af30be 100644 --- a/pytket/extensions/cutensornet/states/ttn.py +++ b/pytket/extensions/cutensornet/states/ttn.py @@ -69,12 +69,12 @@ def __init__(self, tensor: Tensor, is_leaf: bool = False): self.canonical_form: Optional[DirTTN] = None def copy(self) -> TreeNode: - new_ttn = TreeNode( + new_node = TreeNode( self.tensor.copy(), is_leaf=self.is_leaf, ) - new_ttn.canonical_form = self.canonical_form - return new_ttn + new_node.canonical_form = self.canonical_form + return new_node class TTN: @@ -152,8 +152,8 @@ def __init__( ) # Calculate the root path of this group + path = [] for l in reversed(range(n_levels)): - path = [] if k < 2**l: path.append(DirTTN.LEFT) else: @@ -182,7 +182,7 @@ def __init__( # Create the internal TreeNodes paths: list[list[DirTTN]] = [[]] - for _ in range(n_levels - 1): + 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) @@ -193,6 +193,8 @@ def __init__( 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. @@ -214,13 +216,13 @@ def is_valid(self) -> bool: for path, bond in self.qubit_position.values() ) rank_ok = all( - node.is_leaf or node.tensor.shape == 3 for node in self.nodes.values() + node.is_leaf or len(node.tensor.shape) == 3 for node in self.nodes.values() ) shape_ok = all( self.get_dimension(path, DirTTN.PARENT) - == self.get_dimension(path, path[-1]) + == self.get_dimension(path[:-1], path[-1]) for path in self.nodes.keys() - if path != [] + if len(path) != 0 ) # Debugger logging diff --git a/tests/test_ttn.py b/tests/test_ttn.py index e4d2b60c..fb6d2622 100644 --- a/tests/test_ttn.py +++ b/tests/test_ttn.py @@ -18,11 +18,12 @@ def test_libhandle_manager() -> None: - circ = Circuit(5) + circ = Circuit(4) + qubit_partition = {i: [q] for i, q in enumerate(circ.qubits)} # Proper use of library handle with CuTensorNetHandle() as libhandle: - ttn = TTN(libhandle, circ.qubits, Config()) + ttn = TTN(libhandle, qubit_partition, Config()) assert np.isclose(ttn.vdot(ttn), 1, atol=ttn._cfg._atol) # Catch exception due to library handle out of scope @@ -31,12 +32,14 @@ def test_libhandle_manager() -> None: def test_init() -> None: - circ = Circuit(5) + circ = Circuit(4) + qubit_partition = {i: [q] for i, q in enumerate(circ.qubits)} with CuTensorNetHandle() as libhandle: - ttn_gate = TTNxGate(libhandle, circ.qubits, Config()) + ttn_gate = TTNxGate(libhandle, qubit_partition, Config()) assert ttn_gate.is_valid() + @pytest.mark.parametrize( "circuit", [ @@ -64,7 +67,7 @@ def test_init() -> None: ) def test_exact_circ_sim(circuit: Circuit) -> None: n_qubits = len(circuit.qubits) - qubit_partition = {i: q for i, q in enumerate(circuit.qubits)} + qubit_partition = {i: [q] for i, q in enumerate(circuit.qubits)} state = circuit.get_statevector() with CuTensorNetHandle() as libhandle: @@ -87,4 +90,4 @@ def test_exact_circ_sim(circuit: Circuit) -> None: ) # Check that the statevector is correct - assert np.allclose(ttn.get_statevector(), state, atol=ttn._cfg._atol) \ No newline at end of file + assert np.allclose(ttn.get_statevector(), state, atol=ttn._cfg._atol) From de3dccb4e574e05f45b24c9f4f54c3623e59803b Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 12 Oct 2023 13:10:51 +0100 Subject: [PATCH 25/83] Added vdot to TTN --- pytket/extensions/cutensornet/states/mps.py | 2 +- pytket/extensions/cutensornet/states/ttn.py | 94 +++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/pytket/extensions/cutensornet/states/mps.py b/pytket/extensions/cutensornet/states/mps.py index 88cf17ef..4fd06773 100644 --- a/pytket/extensions/cutensornet/states/mps.py +++ b/pytket/extensions/cutensornet/states/mps.py @@ -315,7 +315,7 @@ def vdot(self, other: MPS) -> complex: The state that is conjugated is ``self``. Args: - other: The other MPS to compare against. + other: The other MPS. Returns: The resulting complex number. diff --git a/pytket/extensions/cutensornet/states/ttn.py b/pytket/extensions/cutensornet/states/ttn.py index 93af30be..e1f81dc9 100644 --- a/pytket/extensions/cutensornet/states/ttn.py +++ b/pytket/extensions/cutensornet/states/ttn.py @@ -224,6 +224,7 @@ def is_valid(self) -> bool: for path in self.nodes.keys() if len(path) != 0 ) + shape_ok = shape_ok and self.get_dimension((), DirTTN.PARENT) == 1 # Debugger logging self._logger.debug( @@ -274,6 +275,99 @@ def apply_gate(self, gate: Command) -> TTN: return self + def vdot(self, other: TTN) -> complex: + """Obtain the inner product of the two TTN: ````. + + It can be used to compute the squared norm of a TTN ``ttn`` as + ``ttn.vdot(ttn)``. The tensors within the TTN are not modified. + + Note: + The state that is conjugated is ``self``. + + Args: + other: The other TTN. + + Returns: + The resulting complex number. + + Raises: + RuntimeError: If the two TTNs do not have the same qubits. + RuntimeError: If the ``CuTensorNetHandle`` is out of scope. + """ + if self._lib._is_destroyed: + raise RuntimeError( + "The cuTensorNet library handle is out of scope.", + "See the documentation of update_libhandle and CuTensorNetHandle.", + ) + + if len(self.qubit_position) != len(other.qubit_position): + raise RuntimeError("Number of qubits do not match.") + if self.get_qubits != other.get_qubits: + raise RuntimeError( + "The sets of qubits are not the same." + "\n\tself has {self.get_qubits()}" + "\n\tother has {other.get_qubits()}" + ) + if len(self.qubit_position) == 0: + raise RuntimeError("There are no qubits in the TTN.") + + self._logger.debug("Applying vdot between two TTNs.") + + # We convert both TTNs to their interleaved representation and + # contract them using cuQuantum. A single sample is enough for + # contraction path optimisation, since there is little to optimise. + ttn1 = self.get_interleaved_representation(conj=True) + ttn2 = other.get_interleaved_representation(conj=False) + interleaved_rep = tt1 + tt2 + [[]] # Discards dim=1 bonds with [] + result = cq.contract( + *interleaved_rep, + options={"handle": self._lib.handle, "device_id": self._lib.device_id}, + optimize={"samples": 1} + ) + + self._logger.debug(f"Result from vdot={result}") + return complex(result) + + def get_interleaved_representation(self, conj: bool = False) -> list[Union[Tensor, str]]: + """Returns the interleaved representation of the TTN used by cuQuantum. + + Args: + conj: If True, all tensors are conjugated and bonds IDs are prefixed + with * (except physical bonds). Defaults to False. + """ + self._logger.debug("Creating interleaved representation...") + + # Auxiliar dictionary of physical bonds to qubit IDs + qubit_id = {location: str(qubit) for qubit, location in self.qubit_position.items()} + + interleaved_rep = [] + for path, node in self.nodes.items(): + + # Append the tensor + if conj: + interleaved_rep.append(node.tensor.conj()) + else: + interleaved_rep.append(node.tensor) + + # Create the ID for the parent bond + parentID = "".join(str(int(d)) for d in path) + if conj: + parentID = "*" + parentID + + # Append the bonds + if node.isleaf: + bonds = [] + for b in range(len(node.tensor.shape) - 1): + bonds.append(qubit_id[(path,b)]) + bonds.append(parentID) + else: + bonds = [parentID+"0", parentID+"1", parentID] + + interleaved_rep.append(bonds) + self._logger.debug(f"Bond IDs: {bonds}") + + return interleaved_rep + def get_qubits(self) -> set[Qubit]: """Returns the set of qubits that this TTN is defined on.""" return set(self.qubit_position.keys()) From c00f975ff164af90128ae024dc78a8c9f9253392 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 12 Oct 2023 13:14:36 +0100 Subject: [PATCH 26/83] Some bugs fixed --- pytket/extensions/cutensornet/states/ttn.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/pytket/extensions/cutensornet/states/ttn.py b/pytket/extensions/cutensornet/states/ttn.py index e1f81dc9..d8097c93 100644 --- a/pytket/extensions/cutensornet/states/ttn.py +++ b/pytket/extensions/cutensornet/states/ttn.py @@ -13,7 +13,7 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings -from typing import Optional +from typing import Optional, Union from enum import IntEnum from random import random # type: ignore @@ -318,17 +318,19 @@ def vdot(self, other: TTN) -> complex: # contraction path optimisation, since there is little to optimise. ttn1 = self.get_interleaved_representation(conj=True) ttn2 = other.get_interleaved_representation(conj=False) - interleaved_rep = tt1 + tt2 + [[]] # Discards dim=1 bonds with [] + interleaved_rep = ttn1 + ttn2 + [[]] # Discards dim=1 bonds with [] result = cq.contract( *interleaved_rep, options={"handle": self._lib.handle, "device_id": self._lib.device_id}, - optimize={"samples": 1} + optimize={"samples": 1}, ) self._logger.debug(f"Result from vdot={result}") return complex(result) - def get_interleaved_representation(self, conj: bool = False) -> list[Union[Tensor, str]]: + def get_interleaved_representation( + self, conj: bool = False + ) -> list[Union[Tensor, str]]: """Returns the interleaved representation of the TTN used by cuQuantum. Args: @@ -338,11 +340,12 @@ def get_interleaved_representation(self, conj: bool = False) -> list[Union[Tenso self._logger.debug("Creating interleaved representation...") # Auxiliar dictionary of physical bonds to qubit IDs - qubit_id = {location: str(qubit) for qubit, location in self.qubit_position.items()} + qubit_id = { + location: str(qubit) for qubit, location in self.qubit_position.items() + } interleaved_rep = [] for path, node in self.nodes.items(): - # Append the tensor if conj: interleaved_rep.append(node.tensor.conj()) @@ -355,13 +358,13 @@ def get_interleaved_representation(self, conj: bool = False) -> list[Union[Tenso parentID = "*" + parentID # Append the bonds - if node.isleaf: + if node.is_leaf: bonds = [] for b in range(len(node.tensor.shape) - 1): - bonds.append(qubit_id[(path,b)]) + bonds.append(qubit_id[(path, b)]) bonds.append(parentID) else: - bonds = [parentID+"0", parentID+"1", parentID] + bonds = [parentID + "0", parentID + "1", parentID] interleaved_rep.append(bonds) self._logger.debug(f"Bond IDs: {bonds}") From eeab27c3454cce55e932b25a3da9e6f8af95b2ec Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 12 Oct 2023 14:11:13 +0100 Subject: [PATCH 27/83] Implemented get_amplitude and get_statevector. Current tests passing. --- pytket/extensions/cutensornet/states/ttn.py | 72 +++++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/pytket/extensions/cutensornet/states/ttn.py b/pytket/extensions/cutensornet/states/ttn.py index d8097c93..ad693e86 100644 --- a/pytket/extensions/cutensornet/states/ttn.py +++ b/pytket/extensions/cutensornet/states/ttn.py @@ -302,7 +302,7 @@ def vdot(self, other: TTN) -> complex: if len(self.qubit_position) != len(other.qubit_position): raise RuntimeError("Number of qubits do not match.") - if self.get_qubits != other.get_qubits: + if self.get_qubits() != other.get_qubits(): raise RuntimeError( "The sets of qubits are not the same." "\n\tself has {self.get_qubits()}" @@ -328,6 +328,72 @@ def vdot(self, other: TTN) -> complex: self._logger.debug(f"Result from vdot={result}") return complex(result) + def get_statevector(self) -> np.ndarray: + """Returns the statevector with qubits in Increasing Lexicographic Order (ILO).""" + + # Create the interleaved representation with all tensors + interleaved_rep = self.get_interleaved_representation() + + # Specify the output bond IDs in ILO order + output_bonds = [] + for q in sorted(self.get_qubits()): + output_bonds.append(str(q)) + interleaved_rep.append(output_bonds) + + # Contract + result_tensor = cq.contract( + *interleaved_rep, + options={"handle": self._lib.handle, "device_id": self._lib.device_id}, + optimize={"samples": 1}, # There is little to no optimisation to be done + ) + + # Convert to numpy vector and flatten + statevector: np.ndarray = cp.asnumpy(result_tensor).flatten() + return statevector + + def get_amplitude(self, state: int) -> complex: + """Returns the amplitude of the chosen computational state. + + Notes: + The result is equivalent to ``self.get_statevector[b]``, but this method + is faster when querying a single amplitude. + + Args: + state: The integer whose bitstring describes the computational state. + The qubits in the bitstring are in increasing lexicographic order. + + Returns: + The amplitude of the computational state in the TTN. + """ + + interleaved_rep = self.get_interleaved_representation() + ilo_qubits = sorted(self.get_qubits()) + + for i, q in enumerate(ilo_qubits): + # Create the tensors for each qubit in ``state`` + bitvalue = 1 if state & 2 ** (len(ilo_qubits) - i - 1) else 0 + tensor = cp.zeros(shape=(2,), dtype=self._cfg._complex_t) + tensor[bitvalue] = 1 + # Append it to the interleaved representation + interleaved_rep.append(tensor) + interleaved_rep.append([str(q)]) # The bond + # Ignore the dim=1 tensors in the output + interleaved_rep.append([]) + + # Contract + result = cq.contract( + *interleaved_rep, + options={"handle": self._lib.handle, "device_id": self._lib.device_id}, + optimize={"samples": 1}, # There is little to no optimisation to be done + ) + + self._logger.debug(f"Amplitude of state {state} is {result}.") + return complex(result) + + def get_qubits(self) -> set[Qubit]: + """Returns the set of qubits that this TTN is defined on.""" + return set(self.qubit_position.keys()) + def get_interleaved_representation( self, conj: bool = False ) -> list[Union[Tensor, str]]: @@ -371,10 +437,6 @@ def get_interleaved_representation( return interleaved_rep - def get_qubits(self) -> set[Qubit]: - """Returns the set of qubits that this TTN is defined on.""" - return set(self.qubit_position.keys()) - def get_dimension(self, path: RootPath, direction: DirTTN) -> int: """Returns the dimension of bond ``dir`` of the node at ``path``. From fd79ef90e1fed02e91cd4149a11ecc2f58d25820 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 12 Oct 2023 20:24:53 +0100 Subject: [PATCH 28/83] Implemented canonicalisation --- pytket/extensions/cutensornet/states/ttn.py | 185 ++++++++++++++++++ .../extensions/cutensornet/states/ttn_gate.py | 6 +- 2 files changed, 188 insertions(+), 3 deletions(-) diff --git a/pytket/extensions/cutensornet/states/ttn.py b/pytket/extensions/cutensornet/states/ttn.py index ad693e86..4159031b 100644 --- a/pytket/extensions/cutensornet/states/ttn.py +++ b/pytket/extensions/cutensornet/states/ttn.py @@ -119,6 +119,7 @@ def __init__( ValueError: If the keys of ``qubit_partition`` do not range from ``0`` to ``2^l - 1`` for some ``l``. ValueError: If a ``Qubit`` is repeated in ``qubit_partition``. + ValueError: If there is only one entry in ``qubit_partition``. NotImplementedError: If the value of ``truncation_fidelity`` in ``config`` is smaller than one. """ @@ -137,6 +138,11 @@ def __init__( n_groups = len(qubit_partition) if n_groups == 0: # There's no initialisation to be done return None + if 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: @@ -275,6 +281,185 @@ def apply_gate(self, gate: Command) -> TTN: return self + def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor + """Canonicalise the TTN so that all tensors are isometries from ``center``. + + Args: + center: Identifies the bond that is to become the center of the canonical + form. If it is a ``RootPath`` it refers to the parent bond of + ``self.nodes[center]``. If it is a ``Qubit`` it refers to its physical + bond. + + Returns: + The tensor created at ``center`` when all other bonds are canonicalised. + Applying SVD to this tensor yields the global SVD of the TTN. + """ + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + + if isinstance(center, Qubit): + target_path = self.qubit_position[center][0] + else: + target_path = center + + # Separate nodes to be canonicalised towards children from those towards parent + towards_child = [] + towards_parent = [] + for path in self.nodes.keys(): + # Nodes towards children are closer to the root and coincide in the path + if ( + len(path) < len(target_path) and + all(path[l] == target_path[l] for l in len(path)) + ): + towards_child.append(path) + # If the center is a physical bond (qubit), its node is skipped + if path == target_path and isinstance(center, Qubit): + continue + # All other nodes are canonicalised towards their parent + else: + towards_parent.append(path) + # Sanity checks + assert len(towards_child) != 0 + assert len(towards_parent) != 0 + + # Glossary of bond IDs + # chr(x) -> bond of the x-th qubit in the node (if it is a leaf) + # l -> left child bond of the TTN node + # r -> right child bond of the TTN node + # p -> parent bond of the TTN node + # s -> bond between Q and R after decomposition + + # Canonicalise all towards parent, starting from the furthest away from the root + for path in sorted(towards_parent, key=len, reverse=True): + # If already in desired canonical form, do nothing + if self.nodes[path].canonical_form == DirTTN.PARENT: + continue + + # Otherwise, apply QR decomposition + if self.nodes[path].is_leaf: + n_qbonds = len(self.nodes[path].tensor.shape) - 1 # Num of qubit bonds + q_bonds = "".join(chr(x) for x in range(n_qbonds)) + node_bonds = q_bonds + "p" + Q_bonds = q_bonds + "s" + else: + node_bonds = "lrp" + Q_bonds = "lrs" + R_bonds = "sp" + + Q, R = tensor.decompose( + node_bonds + "->" + Q_bonds + "," + R_bonds, + self.nodes[path].tensor, + method=tensor.QRMethod(), + options=options, + ) + + # Update the tensor + self.nodes[path].tensor = Q + self.nodes[path].canonical_form = TreeDir.PARENT + + # Contract R with the parent node + if path[-1] == TreeDir.LEFT: + R_bonds = "sl" + else: + R_bonds = "sr" + parent_node = self.nodes[path[:-1]] + + parent_node.tensor = cq.contract( + R_bonds + ",lrp->srp", + R, parent_node.tensor, + options=options, + optimize={"samples": 1}, + ) + # The canonical form of the parent node is lost + parent_node.canonical_form = None + + # Canonicalise the rest of the nodes, from the root up to the center + for path in sorted(towards_child, key=len): + # Identify the direction of the canonicalisation + target_direction = target_path[len(path)] + # Sanity checks + assert not self.nodes[path].is_leaf + assert target_direction != TreeDir.PARENT + + # If already in the desired canonical form, do nothing + if self.nodes[path].canonical_form == target_direction: + continue + + # Otherwise, apply QR decomposition + if target_direction == TreeDir.LEFT: + Q_bonds = "srp" + R_bonds = "ls" + else + Q_bonds = "lsp" + R_bonds = "rs" + node_bonds = "lrp" + + 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 + child_path = path+[target_direction] + if child_path != target_path: + child_node = self.nodes[child_path] + + # Contract R with the child node + child_node.tensor = cq.contract( + "lrp,ps->srp", + child_node.tensor, R, + options=options, + optimize={"samples": 1}, + ) + + # The canonical form of the child node is lost + child_node.canonical_form = None + # Update the tensor + self.nodes[path].tensor = Q + self.nodes[path].canonical_form = target_direction + + # If ``center`` is not a physical bond, we are done canonicalising and R is + # the tensor to return. Otherwise, we need to do a final contraction and QR + # decomposition on the leaf node corresponding to ``target_path``. + if isinstance(center, Qubit): + leaf_node = self.nodes[target_path] + + n_qbonds = len(leaf_node.tensor.shape) - 1 # Number of qubit bonds + q_bonds = "".join(chr(x) for x in range(n_qbonds)) + node_bonds = q_bonds + "p" + new_bonds = q_bonds + "s" + R_bonds = "ps" + + # Contract R with the leaf node + leaf_node.tensor = cq.contract( + node_bonds + "," + R_bonds + "->" + new_bonds, + leaf_node.tensor, R, + options=options, + optimize={"samples": 1}, + ) + + # The canonical form of the leaf node is lost + leaf_node.canonical_form = None + + # Finally, apply QR decomposition on the leaf_node to obtain the R + # tensor to be returned + target_bond = self.qubit_position[center][1] + Q_bonds = node_bonds[:target_bond] + "s" + node_bonds[target_bond+1:] + R_bonds = chr(target_bond) + "s" + + 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. + + return R + + def vdot(self, other: TTN) -> complex: """Obtain the inner product of the two TTN: ````. diff --git a/pytket/extensions/cutensornet/states/ttn_gate.py b/pytket/extensions/cutensornet/states/ttn_gate.py index 354bac36..9ebab406 100644 --- a/pytket/extensions/cutensornet/states/ttn_gate.py +++ b/pytket/extensions/cutensornet/states/ttn_gate.py @@ -54,8 +54,8 @@ def _apply_1q_gate(self, qubit: Qubit, gate: Op) -> TTNxGate: gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) path, target = self.qubit_position[qubit] - tensor = self.nodes[path].tensor - n_qbonds = len(tensor.shape) - 1 # Total number of physical bonds in this node + node_tensor = self.nodes[path].tensor + n_qbonds = len(node_tensor.shape) - 1 # Total number of physical bonds in this node # Glossary of bond IDs # qX -> where X is the X-th physical bond (qubit) in the TTN node @@ -70,7 +70,7 @@ def _apply_1q_gate(self, qubit: Qubit, gate: Op) -> TTNxGate: interleaved_rep = [ # The tensor of the TTN - tensor, + node_tensor, node_bonds, # The tensor of the gate gate_tensor, From ff1dbf9aa9d2a7ba6ebed1f266ecac0dfa37a060 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 12 Oct 2023 20:28:34 +0100 Subject: [PATCH 29/83] Renamed module states to tnstate --- pytket/extensions/cutensornet/{states => tnstate}/__init__.py | 0 pytket/extensions/cutensornet/{states => tnstate}/general.py | 0 pytket/extensions/cutensornet/{states => tnstate}/mps.py | 0 pytket/extensions/cutensornet/{states => tnstate}/mps_gate.py | 0 pytket/extensions/cutensornet/{states => tnstate}/mps_mpo.py | 0 pytket/extensions/cutensornet/{states => tnstate}/simulation.py | 0 pytket/extensions/cutensornet/{states => tnstate}/ttn.py | 0 pytket/extensions/cutensornet/{states => tnstate}/ttn_gate.py | 0 tests/test_mps.py | 2 +- tests/test_ttn.py | 2 +- 10 files changed, 2 insertions(+), 2 deletions(-) rename pytket/extensions/cutensornet/{states => tnstate}/__init__.py (100%) rename pytket/extensions/cutensornet/{states => tnstate}/general.py (100%) rename pytket/extensions/cutensornet/{states => tnstate}/mps.py (100%) rename pytket/extensions/cutensornet/{states => tnstate}/mps_gate.py (100%) rename pytket/extensions/cutensornet/{states => tnstate}/mps_mpo.py (100%) rename pytket/extensions/cutensornet/{states => tnstate}/simulation.py (100%) rename pytket/extensions/cutensornet/{states => tnstate}/ttn.py (100%) rename pytket/extensions/cutensornet/{states => tnstate}/ttn_gate.py (100%) diff --git a/pytket/extensions/cutensornet/states/__init__.py b/pytket/extensions/cutensornet/tnstate/__init__.py similarity index 100% rename from pytket/extensions/cutensornet/states/__init__.py rename to pytket/extensions/cutensornet/tnstate/__init__.py diff --git a/pytket/extensions/cutensornet/states/general.py b/pytket/extensions/cutensornet/tnstate/general.py similarity index 100% rename from pytket/extensions/cutensornet/states/general.py rename to pytket/extensions/cutensornet/tnstate/general.py diff --git a/pytket/extensions/cutensornet/states/mps.py b/pytket/extensions/cutensornet/tnstate/mps.py similarity index 100% rename from pytket/extensions/cutensornet/states/mps.py rename to pytket/extensions/cutensornet/tnstate/mps.py diff --git a/pytket/extensions/cutensornet/states/mps_gate.py b/pytket/extensions/cutensornet/tnstate/mps_gate.py similarity index 100% rename from pytket/extensions/cutensornet/states/mps_gate.py rename to pytket/extensions/cutensornet/tnstate/mps_gate.py diff --git a/pytket/extensions/cutensornet/states/mps_mpo.py b/pytket/extensions/cutensornet/tnstate/mps_mpo.py similarity index 100% rename from pytket/extensions/cutensornet/states/mps_mpo.py rename to pytket/extensions/cutensornet/tnstate/mps_mpo.py diff --git a/pytket/extensions/cutensornet/states/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py similarity index 100% rename from pytket/extensions/cutensornet/states/simulation.py rename to pytket/extensions/cutensornet/tnstate/simulation.py diff --git a/pytket/extensions/cutensornet/states/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py similarity index 100% rename from pytket/extensions/cutensornet/states/ttn.py rename to pytket/extensions/cutensornet/tnstate/ttn.py diff --git a/pytket/extensions/cutensornet/states/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py similarity index 100% rename from pytket/extensions/cutensornet/states/ttn_gate.py rename to pytket/extensions/cutensornet/tnstate/ttn_gate.py diff --git a/tests/test_mps.py b/tests/test_mps.py index 20950906..2826fa8d 100644 --- a/tests/test_mps.py +++ b/tests/test_mps.py @@ -8,7 +8,7 @@ from pytket.circuit import Circuit, Qubit, OpType # type: ignore from pytket.pauli import Pauli, QubitPauliString # type: ignore -from pytket.extensions.cutensornet.states import ( +from pytket.extensions.cutensornet.tnstate import ( CuTensorNetHandle, Config, MPS, diff --git a/tests/test_ttn.py b/tests/test_ttn.py index fb6d2622..8e4bea27 100644 --- a/tests/test_ttn.py +++ b/tests/test_ttn.py @@ -8,7 +8,7 @@ from pytket.circuit import Circuit, Qubit, OpType # type: ignore from pytket.pauli import Pauli, QubitPauliString # type: ignore -from pytket.extensions.cutensornet.states import ( +from pytket.extensions.cutensornet.tnstate import ( CuTensorNetHandle, Config, TTN, From ba6c4e809a53b8266338285131769d88e57c8bcb Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Fri, 13 Oct 2023 09:05:32 -0700 Subject: [PATCH 30/83] Some basic fixes --- pytket/extensions/cutensornet/tnstate/ttn.py | 35 ++++++++++--------- .../cutensornet/tnstate/ttn_gate.py | 4 ++- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index 4159031b..065e3410 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -281,7 +281,7 @@ def apply_gate(self, gate: Command) -> TTN: return self - def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor + def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: """Canonicalise the TTN so that all tensors are isometries from ``center``. Args: @@ -306,9 +306,8 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor towards_parent = [] for path in self.nodes.keys(): # Nodes towards children are closer to the root and coincide in the path - if ( - len(path) < len(target_path) and - all(path[l] == target_path[l] for l in len(path)) + if len(path) < len(target_path) and all( + path[l] == target_path[l] for l in range(len(path)) ): towards_child.append(path) # If the center is a physical bond (qubit), its node is skipped @@ -354,10 +353,10 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor # Update the tensor self.nodes[path].tensor = Q - self.nodes[path].canonical_form = TreeDir.PARENT + self.nodes[path].canonical_form = DirTTN.PARENT # Contract R with the parent node - if path[-1] == TreeDir.LEFT: + if path[-1] == DirTTN.LEFT: R_bonds = "sl" else: R_bonds = "sr" @@ -365,7 +364,8 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor parent_node.tensor = cq.contract( R_bonds + ",lrp->srp", - R, parent_node.tensor, + R, + parent_node.tensor, options=options, optimize={"samples": 1}, ) @@ -378,17 +378,17 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor target_direction = target_path[len(path)] # Sanity checks assert not self.nodes[path].is_leaf - assert target_direction != TreeDir.PARENT + assert target_direction != DirTTN.PARENT # If already in the desired canonical form, do nothing if self.nodes[path].canonical_form == target_direction: continue # Otherwise, apply QR decomposition - if target_direction == TreeDir.LEFT: + if target_direction == DirTTN.LEFT: Q_bonds = "srp" R_bonds = "ls" - else + else: Q_bonds = "lsp" R_bonds = "rs" node_bonds = "lrp" @@ -401,14 +401,15 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor ) # If the child bond is not the center yet, contract R with child node - child_path = path+[target_direction] + child_path = tuple(list(path) + [target_direction]) if child_path != target_path: child_node = self.nodes[child_path] # Contract R with the child node child_node.tensor = cq.contract( "lrp,ps->srp", - child_node.tensor, R, + child_node.tensor, + R, options=options, optimize={"samples": 1}, ) @@ -434,7 +435,8 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor # Contract R with the leaf node leaf_node.tensor = cq.contract( node_bonds + "," + R_bonds + "->" + new_bonds, - leaf_node.tensor, R, + leaf_node.tensor, + R, options=options, optimize={"samples": 1}, ) @@ -445,7 +447,7 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor # Finally, apply QR decomposition on the leaf_node to obtain the R # tensor to be returned target_bond = self.qubit_position[center][1] - Q_bonds = node_bonds[:target_bond] + "s" + node_bonds[target_bond+1:] + Q_bonds = node_bonds[:target_bond] + "s" + node_bonds[target_bond + 1 :] R_bonds = chr(target_bond) + "s" Q, R = tensor.decompose( @@ -459,7 +461,6 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor return R - def vdot(self, other: TTN) -> complex: """Obtain the inner product of the two TTN: ````. @@ -514,7 +515,9 @@ def vdot(self, other: TTN) -> complex: return complex(result) def get_statevector(self) -> np.ndarray: - """Returns the statevector with qubits in Increasing Lexicographic Order (ILO).""" + """Returns the statevector represented by the TTN, with qubits ordered + in Increasing Lexicographic Order (ILO). + """ # Create the interleaved representation with all tensors interleaved_rep = self.get_interleaved_representation() diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 9ebab406..517e19b5 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -55,7 +55,9 @@ def _apply_1q_gate(self, qubit: Qubit, gate: Op) -> TTNxGate: path, target = self.qubit_position[qubit] node_tensor = self.nodes[path].tensor - n_qbonds = len(node_tensor.shape) - 1 # Total number of physical bonds in this node + n_qbonds = ( + len(node_tensor.shape) - 1 + ) # Total number of physical bonds in this node # Glossary of bond IDs # qX -> where X is the X-th physical bond (qubit) in the TTN node From 45288980b10c05b3b898b3b0f255d14362e0707b Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 17 Oct 2023 09:35:15 -0700 Subject: [PATCH 31/83] In the process of debugging canonicalisation --- .../extensions/cutensornet/tnstate/general.py | 3 + pytket/extensions/cutensornet/tnstate/ttn.py | 33 ++++++- tests/test_ttn.py | 92 ++++++++++++++++++- 3 files changed, 122 insertions(+), 6 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index d7462432..bd74e0e2 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -174,4 +174,7 @@ def copy(self) -> Config: k=self.k, optim_delta=self.optim_delta, float_precision=self._real_t, # type: ignore + value_of_zero=self.zero, + loglevel=self.loglevel, + ) diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index 065e3410..f868343f 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -296,6 +296,8 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: """ options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + self._logger.debug(f"Canonicalising to {str(center)}") + if isinstance(center, Qubit): target_path = self.qubit_position[center][0] else: @@ -311,7 +313,7 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: ): towards_child.append(path) # If the center is a physical bond (qubit), its node is skipped - if path == target_path and isinstance(center, Qubit): + elif path == target_path and isinstance(center, Qubit): continue # All other nodes are canonicalised towards their parent else: @@ -327,10 +329,13 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: # p -> parent bond of the TTN node # s -> bond between Q and R after decomposition - # Canonicalise all towards parent, starting from the furthest away from the root + # Canonicalise nodes towards parent, start from the furthest away from root for path in sorted(towards_parent, key=len, reverse=True): + self._logger.debug(f"Canonicalising node at {path} towards parent.") + # If already in desired canonical form, do nothing if self.nodes[path].canonical_form == DirTTN.PARENT: + self._logger.debug("Skipping, already in canonical form.") continue # Otherwise, apply QR decomposition @@ -358,12 +363,15 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: # Contract R with the parent node if path[-1] == DirTTN.LEFT: R_bonds = "sl" + result_bonds = "srp" else: R_bonds = "sr" - parent_node = self.nodes[path[:-1]] + result_bonds = "lsp" + node_bonds = "lrp" + parent_node = self.nodes[path[:-1]] parent_node.tensor = cq.contract( - R_bonds + ",lrp->srp", + R_bonds + "," + node_bonds + "->" + result_bonds, R, parent_node.tensor, options=options, @@ -372,6 +380,8 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: # The canonical form of the parent node is lost parent_node.canonical_form = None + self._logger.debug(f"Node canonicalised. Shape: {Q.shape}") + # Canonicalise the rest of the nodes, from the root up to the center for path in sorted(towards_child, key=len): # Identify the direction of the canonicalisation @@ -380,8 +390,13 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: assert not self.nodes[path].is_leaf assert target_direction != DirTTN.PARENT + self._logger.debug( + f"Canonicalising node at {path} towards {str(target_direction)}." + ) + # If already in the desired canonical form, do nothing if self.nodes[path].canonical_form == target_direction: + self._logger.debug("Skipping, already in canonical form.") continue # Otherwise, apply QR decomposition @@ -420,12 +435,17 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: self.nodes[path].tensor = Q self.nodes[path].canonical_form = target_direction + self._logger.debug(f"Node canonicalised. Shape: {Q.shape}") + # If ``center`` is not a physical bond, we are done canonicalising and R is # the tensor to return. Otherwise, we need to do a final contraction and QR # decomposition on the leaf node corresponding to ``target_path``. if isinstance(center, Qubit): - leaf_node = self.nodes[target_path] + self._logger.debug( + f"Applying QR decomposition on leaf node at {target_path}." + ) + leaf_node = self.nodes[target_path] n_qbonds = len(leaf_node.tensor.shape) - 1 # Number of qubit bonds q_bonds = "".join(chr(x) for x in range(n_qbonds)) node_bonds = q_bonds + "p" @@ -459,6 +479,9 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: # 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. + self._logger.debug( + f"Finished canonicalisation. Returning R tensor of shape {R.shape}" + ) return R def vdot(self, other: TTN) -> complex: diff --git a/tests/test_ttn.py b/tests/test_ttn.py index 8e4bea27..4e0bb2fb 100644 --- a/tests/test_ttn.py +++ b/tests/test_ttn.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Union import random # type: ignore import pytest @@ -13,7 +13,9 @@ Config, TTN, TTNxGate, + DirTTN, ) +from pytket.extensions.cutensornet.tnstate.ttn import RootPath from pytket.extensions.cutensornet.utils import circuit_statevector_postselect @@ -40,6 +42,94 @@ def test_init() -> None: assert ttn_gate.is_valid() +@pytest.mark.parametrize( + "center", + [ + (DirTTN.RIGHT,), + (DirTTN.LEFT, DirTTN.RIGHT), + (DirTTN.LEFT, DirTTN.RIGHT, DirTTN.RIGHT), + Qubit("q", [2]), + ], +) +def test_canonicalise(center: Union[RootPath, Qubit]) -> None: + cp.random.seed(1) + n_levels = 3 + n_qubits = 2**n_levels + max_dim = 2 ** (n_qubits // 2) + + circ = Circuit(n_qubits) + qubit_partition = {i: [q] for i, q in enumerate(circ.qubits)} + + with CuTensorNetHandle() as libhandle: + ttn = TTNxGate(libhandle, qubit_partition, Config()) + + # Fill up the tensors with random entries + for path, node in ttn.nodes.items(): + if node.is_leaf: + T = cp.empty(shape=(2, max_dim), dtype=ttn._cfg._complex_t) + for i0 in range(T.shape[0]): + for i1 in range(T.shape[1]): + T[i0][i1] = cp.random.rand() + 1j * cp.random.rand() + else: + shape = (max_dim, max_dim, max_dim if len(path) != 0 else 1) + T = cp.empty(shape=shape, dtype=ttn._cfg._complex_t) + for i0 in range(shape[0]): + for i1 in range(shape[1]): + for i2 in range(shape[2]): + T[i0][i1][i2] = cp.random.rand() + 1j * cp.random.rand() + node.tensor = T + + assert ttn.is_valid() + + # Calculate the norm of the TTN + norm_sq = ttn.vdot(ttn) + + # Keep a copy of the non-canonicalised TTN + ttn_copy = ttn.copy() + + # Canonicalise at target path + R = ttn.canonicalise(center) + + # Check that canonicalisation did not change the vector + overlap = ttn.vdot(ttn_copy) + assert np.isclose(overlap, norm_sq, atol=ttn._cfg._atol) + + # Check that the tensor R returned agrees with the norm + overlap_R = cq.contract("ud,ud->", R, R.conj()) + assert np.isclose(overlap_R, norm_sq, atol=ttn._cfg._atol) + + # Check that the corresponding tensors are in orthogonal form + for path, node in ttn.nodes.items(): + # If it's the node just below the center of canonicalisation, it + # cannot be in orthogonal form + if isinstance(center, Qubit) and path == ttn.qubit_position[center][0]: + assert node.canonical_form is None + continue + elif path == center[:-1]: # type: ignore + assert node.canonical_form is None + continue + # Otherwise, it should be in orthogonal form + assert node.canonical_form is not None + + T = node.tensor + + if node.is_leaf: + assert node.canonical_form == DirTTN.PARENT + result = cq.contract("qp,qP->pP", T, T.conj()) + + elif node.canonical_form == DirTTN.PARENT: + result = cq.contract("lrp,lrP->pP", T, T.conj()) + + elif node.canonical_form == DirTTN.LEFT: + result = cq.contract("lrp,Lrp->lL", T, T.conj()) + + elif node.canonical_form == DirTTN.RIGHT: + result = cq.contract("lrp,lRp->rR", T, T.conj()) + + # Check that the result is the identity + assert cp.allclose(result, cp.eye(result.shape[0])) + + @pytest.mark.parametrize( "circuit", [ From 2fbc559d805031ac70216ab34698dfc804a47f37 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 18 Oct 2023 13:36:04 +0100 Subject: [PATCH 32/83] Canonicalisation debugged --- pytket/extensions/cutensornet/tnstate/ttn.py | 7 ++++++- tests/test_ttn.py | 19 +++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index f868343f..800f79dc 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -422,7 +422,7 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: # Contract R with the child node child_node.tensor = cq.contract( - "lrp,ps->srp", + "lrp,ps->lrs", child_node.tensor, R, options=options, @@ -463,6 +463,11 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: # The canonical form of the leaf node is lost leaf_node.canonical_form = None + # Update the parent tensor + parent_path = target_path[:-1] + self.nodes[parent_path].tensor = Q + self.nodes[parent_path].canonical_form = target_path[-1] + self._logger.debug(f"Node canonicalised. Shape: {Q.shape}") # Finally, apply QR decomposition on the leaf_node to obtain the R # tensor to be returned diff --git a/tests/test_ttn.py b/tests/test_ttn.py index 4e0bb2fb..b67e6db9 100644 --- a/tests/test_ttn.py +++ b/tests/test_ttn.py @@ -89,25 +89,28 @@ def test_canonicalise(center: Union[RootPath, Qubit]) -> None: # Canonicalise at target path R = ttn.canonicalise(center) + assert ttn.is_valid() # Check that canonicalisation did not change the vector overlap = ttn.vdot(ttn_copy) - assert np.isclose(overlap, norm_sq, atol=ttn._cfg._atol) + assert np.isclose(overlap / norm_sq, 1.0, atol=ttn._cfg._atol) # Check that the tensor R returned agrees with the norm overlap_R = cq.contract("ud,ud->", R, R.conj()) - assert np.isclose(overlap_R, norm_sq, atol=ttn._cfg._atol) + assert np.isclose(overlap_R / norm_sq, 1.0, atol=ttn._cfg._atol) # Check that the corresponding tensors are in orthogonal form for path, node in ttn.nodes.items(): # If it's the node just below the center of canonicalisation, it # cannot be in orthogonal form - if isinstance(center, Qubit) and path == ttn.qubit_position[center][0]: - assert node.canonical_form is None - continue - elif path == center[:-1]: # type: ignore - assert node.canonical_form is None - continue + if isinstance(center, Qubit): + if path == ttn.qubit_position[center][0]: + assert node.canonical_form is None + continue + else: + if path == center[:-1]: + assert node.canonical_form is None + continue # Otherwise, it should be in orthogonal form assert node.canonical_form is not None From 13fffe3fdfe242421847e2f6decc4d08fb6f8e97 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 18 Oct 2023 18:32:10 +0100 Subject: [PATCH 33/83] In the process of implementing _apply_2q_gate --- .../extensions/cutensornet/tnstate/general.py | 1 - .../cutensornet/tnstate/ttn_gate.py | 145 ++++++++++++++++++ 2 files changed, 145 insertions(+), 1 deletion(-) diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index bd74e0e2..ecbe3b17 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -176,5 +176,4 @@ def copy(self) -> Config: float_precision=self._real_t, # type: ignore value_of_zero=self.zero, loglevel=self.loglevel, - ) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 517e19b5..55a4927f 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -28,6 +28,7 @@ warnings.warn("local settings failed to import cutensornet", ImportWarning) from pytket.circuit import Op, Qubit +from .general import Tensor from .ttn import TTN, RootPath @@ -88,3 +89,147 @@ def _apply_1q_gate(self, qubit: Qubit, gate: Op) -> TTNxGate: # NOTE: Canonicalisation of the node does not change self.nodes[path].tensor = new_tensor return self + + def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: + """Applies the 2-qubit gate to the TTN. + + Truncation is automatically applied according to the paremeters + in the ``Config`` object passed to this ``TTN``. + The TTN is converted to canonical form before truncating. + + Args: + q0: The 0-th qubit the gate acts upon. + q1: The 1-st qubit the gate acts upon. + gate: The gate to be applied. + + Returns: + ``self``, to allow for method chaining. + """ + + # Load the gate's unitary to the GPU memory + gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) + gate_tensor = cp.asarray(gate_unitary, dtype=self._cfg._complex_t) + # Reshape into a rank-4 tensor + gate_tensor = cp.reshape(gate_tensor, (2, 2, 2, 2)) + + (path_q0, bond_q0) = self.qubit_position[q0] + (path_q1, bond_q1) = self.qubit_position[q1] + + # If the two qubits are in the same leaf node, contract the gate with it. + # There is no truncation needed. + if path_q0 == path_q1: + n_qbonds = ( + len(node_tensor.shape) - 1 + ) # Total number of physical bonds in this node + + # Glossary of bond IDs + # qX -> where X is the X-th physical bond (qubit) in the TTN node + # p -> the parent bond of the TTN node + # i0 -> the input bond of the gate on q0 + # o0 -> the output bond of the gate on q0 + # i1 -> the input bond of the gate on q1 + # o1 -> the output bond of the gate on q1 + gate_bond = ["o0", "o1", "i0", "i1"] + + aux_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] + node_bonds = aux_bonds.copy() + node_bonds[bond_q0] = "i0" + node_bonds[bond_q1] = "i1" + result_bonds = aux_bonds.copy() + result_bonds[bond_q0] = "o0" + result_bonds[bond_q1] = "o1" + + self.nodes[path_q0].tensor = cq.contract( + self.nodes[path_q0].tensor, + node_bonds, + gate_tensor, + gate_bond, + result_bonds, + ) + + return self + + # Otherwise, we must include the gate in the common ancestor tensor and + # rewire the inputs and outputs. First, identify common path and direction + common_dirs = [] + for d0, d1 in zip(path_q0, path_q1): + if d0 == d1: + common_dirs.append(d0) + else: + break + common_path = tuple(common_dirs) + (qL, qR) = (q0, q1) if path_q0[len(common_path)] == DirTTN.LEFT else (q1, q0) + + # Glossary of bond IDs + # l -> the input bond of the gate on qL + # r -> the input bond of the gate on qR + # L -> the output bond of the gate on qL + # R -> the output bond of the gate on qR + # c -> a child bond of the TTN node + # C -> another child bond of the TTN node + # p -> the parent bond of the TTN node + # f -> the output bond of a funnel + # F -> the output bond of another funnel + if dir_q0 == DirTTN.LEFT: + gate_bonds = "LRlr" + else: # Implicit swap + gate_bonds = "RLrl" + + # Update the common ancestor tensor with the gate and funnels + left_funnel = self._create_funnel_tensor(common_path, DirTTN.LEFT) + right_funnel = self._create_funnel_tensor(common_path, DirTTN.RIGHT) + + self.nodes[common_path].tensor = cq.contract( + "cCp,fclL,FCrR," + gate_bonds + "->fFp", + self.nodes[common_path].tensor, + left_funnel, + right_funnel, + gate_tensor, + ) + self.nodes[common_path].canonical_form = None + + # For each bond along the path, add two identity dim 2 wires + mid_paths = [ + (path_qX[:i], path_qX[i]) + for path_qX in [path_q0, path_q1] + for i in range(len(common_path) + 1, len(path_qX)) + ] + + for path, child_dir in mid_paths: + child_funnel = self._create_funnel_tensor(path, child_dir) + parent_funnel = self._create_funnel_tensor(path, DirTTN.PARENT) + + if child_dir == DirTTN.LEFT: + node_bonds = "cCp" + result_bonds = "fCF" + else: + node_bonds = "Ccp" + result_bonds = "CfF" + + self.nodes[path].tensor = cq.contract( + "fcab,Fpab," + node_bonds + "->" + result_bonds + child_funnel, + parent_funnel, + self.nodes[path].tensor, + ) + self.nodes[path].canonical_form = None + + # Update the leaf tensors containing qL and qR + # TODO! + + # Truncate to chi (if needed) on all bonds along the path from qL to qR + # TODO! + + def _create_funnel_tensor(self, path: RootPath, direction: DirTTN) -> Tensor: + """Creates a funnel tensor for the given bond. + + A funnel tensor is a reshape of an identity, merging three bonds to one. + A funnel tensor has four bonds. It satisfies ``funnel.shape[0] == 4*dim`` + where ``dim`` is the dimension of the bond of ``path`` and ``direction``. + Hence, the first bond of the funnel is the "merged" bond. The other three + satisfy ``funnel.shape[1] == dim`` (this the original bond) and + ``funnel.shape[x] == 2`` for ``x`` 2 and 3. + """ + dim = self.get_dimension(path, direction) + identity = cp.eye(4*dim) + return cp.reshape(identity, (4*dim, dim, 2, 2)) \ No newline at end of file From 1e907dae7a202128bf6c4f5ed59d9ebb314ccae4 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 19 Oct 2023 11:50:15 +0100 Subject: [PATCH 34/83] Exact 2-qubit gate application implemented. Not debugged yet. --- .../cutensornet/tnstate/ttn_gate.py | 49 ++++++++++++++++--- tests/test_ttn.py | 37 ++++++++------ 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 55a4927f..598cdc17 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -29,7 +29,7 @@ from pytket.circuit import Op, Qubit from .general import Tensor -from .ttn import TTN, RootPath +from .ttn import TTN, DirTTN, RootPath class TTNxGate(TTN): @@ -119,7 +119,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # There is no truncation needed. if path_q0 == path_q1: n_qbonds = ( - len(node_tensor.shape) - 1 + len(self.nodes[path_q0].tensor.shape) - 1 ) # Total number of physical bonds in this node # Glossary of bond IDs @@ -147,6 +147,10 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: result_bonds, ) + self._logger.debug( + "The qubits the gate acts on are on the same group. " + "Gate trivially applied, no dimensions changed." + ) return self # Otherwise, we must include the gate in the common ancestor tensor and @@ -170,7 +174,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # p -> the parent bond of the TTN node # f -> the output bond of a funnel # F -> the output bond of another funnel - if dir_q0 == DirTTN.LEFT: + if qL == q0: gate_bonds = "LRlr" else: # Implicit swap gate_bonds = "RLrl" @@ -179,6 +183,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: left_funnel = self._create_funnel_tensor(common_path, DirTTN.LEFT) right_funnel = self._create_funnel_tensor(common_path, DirTTN.RIGHT) + self._logger.debug(f"Including gate in tensor at {common_path}.") self.nodes[common_path].tensor = cq.contract( "cCp,fclL,FCrR," + gate_bonds + "->fFp", self.nodes[common_path].tensor, @@ -187,6 +192,9 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: gate_tensor, ) self.nodes[common_path].canonical_form = None + self._logger.debug( + f"New tensor size (MiB)={self.nodes[common_path].tensor.nbytes / 2**20}" + ) # For each bond along the path, add two identity dim 2 wires mid_paths = [ @@ -206,16 +214,43 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: node_bonds = "Ccp" result_bonds = "CfF" + self._logger.debug(f"Adding funnels at {path}.") self.nodes[path].tensor = cq.contract( - "fcab,Fpab," + node_bonds + "->" + result_bonds + "fcab,Fpab," + node_bonds + "->" + result_bonds, child_funnel, parent_funnel, self.nodes[path].tensor, ) self.nodes[path].canonical_form = None + self._logger.debug( + f"New tensor size (MiB)={self.nodes[path].tensor.nbytes / 2**20}" + ) # Update the leaf tensors containing qL and qR - # TODO! + for path, bond in [self.qubit_position[q] for q in [qL, qR]]: + leaf_node = self.nodes[path] + n_qbonds = ( + len(leaf_node.tensor.shape) - 1 + ) # Total number of physical bonds in this node + + aux_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] + node_bonds = aux_bonds.copy() + node_bonds[bond] = "a" + result_bonds = aux_bonds.copy() + result_bonds[bond] = "b" + + self._logger.debug(f"Adding funnels at leaf node at {path}.") + leaf_node.tensor = cq.contract( + leaf_node.tensor, + node_bonds, + self._create_funnel_tensor(path, DirTTN.PARENT), + ["f", "p", "a", "b"], + result_bonds, + ) + leaf_node.canonical_form = None + self._logger.debug( + f"New tensor size (MiB)={leaf_node.tensor.nbytes / 2**20}" + ) # Truncate to chi (if needed) on all bonds along the path from qL to qR # TODO! @@ -231,5 +266,5 @@ def _create_funnel_tensor(self, path: RootPath, direction: DirTTN) -> Tensor: ``funnel.shape[x] == 2`` for ``x`` 2 and 3. """ dim = self.get_dimension(path, direction) - identity = cp.eye(4*dim) - return cp.reshape(identity, (4*dim, dim, 2, 2)) \ No newline at end of file + identity = cp.eye(4 * dim, dtype=self._cfg._complex_t) + return cp.reshape(identity, (4 * dim, dim, 2, 2)) diff --git a/tests/test_ttn.py b/tests/test_ttn.py index b67e6db9..f549b6c5 100644 --- a/tests/test_ttn.py +++ b/tests/test_ttn.py @@ -1,5 +1,6 @@ from typing import Any, Union import random # type: ignore +import math import pytest import cuquantum as cq # type: ignore @@ -55,7 +56,7 @@ def test_canonicalise(center: Union[RootPath, Qubit]) -> None: cp.random.seed(1) n_levels = 3 n_qubits = 2**n_levels - max_dim = 2 ** (n_qubits // 2) + max_dim = 8 circ = Circuit(n_qubits) qubit_partition = {i: [q] for i, q in enumerate(circ.qubits)} @@ -136,31 +137,35 @@ def test_canonicalise(center: Union[RootPath, Qubit]) -> None: @pytest.mark.parametrize( "circuit", [ - # pytest.lazy_fixture("q5_empty"), # type: ignore + pytest.lazy_fixture("q5_empty"), # type: ignore pytest.lazy_fixture("q8_empty"), # type: ignore pytest.lazy_fixture("q2_x0"), # type: ignore pytest.lazy_fixture("q2_x1"), # type: ignore pytest.lazy_fixture("q2_v0"), # type: ignore pytest.lazy_fixture("q8_x0h2v5z6"), # type: ignore - # pytest.lazy_fixture("q2_x0cx01"), # type: ignore - # pytest.lazy_fixture("q2_x1cx10x1"), # type: ignore - # pytest.lazy_fixture("q2_x0cx01cx10"), # type: ignore - # pytest.lazy_fixture("q2_v0cx01cx10"), # type: ignore - # pytest.lazy_fixture("q2_hadamard_test"), # type: ignore - # pytest.lazy_fixture("q2_lcu1"), # type: ignore - # pytest.lazy_fixture("q2_lcu2"), # type: ignore - # pytest.lazy_fixture("q2_lcu3"), # type: ignore - # pytest.lazy_fixture("q3_v0cx02"), # type: ignore - # pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore + pytest.lazy_fixture("q2_x0cx01"), # type: ignore + pytest.lazy_fixture("q2_x1cx10x1"), # type: ignore + pytest.lazy_fixture("q2_x0cx01cx10"), # type: ignore + pytest.lazy_fixture("q2_v0cx01cx10"), # type: ignore + pytest.lazy_fixture("q2_hadamard_test"), # type: ignore + pytest.lazy_fixture("q2_lcu1"), # type: ignore + pytest.lazy_fixture("q2_lcu2"), # type: ignore + pytest.lazy_fixture("q2_lcu3"), # type: ignore + pytest.lazy_fixture("q3_v0cx02"), # type: ignore + pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore # pytest.lazy_fixture("q4_lcu1"), # TTN doesn't support n-qubit gates with n>2 - # pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore - # pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore - # pytest.lazy_fixture("q6_qvol"), # type: ignore + pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore + pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore + pytest.lazy_fixture("q6_qvol"), # type: ignore ], ) def test_exact_circ_sim(circuit: Circuit) -> None: n_qubits = len(circuit.qubits) - qubit_partition = {i: [q] for i, q in enumerate(circuit.qubits)} + n_groups = 2**math.floor(math.log2(n_qubits)) + qubit_partition: dict[int, list[Qubit]] = {i: [] for i in range(n_groups)} + for i, q in enumerate(circuit.qubits): + qubit_partition[i % n_groups].append(q) + state = circuit.get_statevector() with CuTensorNetHandle() as libhandle: From cfb15a649cf6923f483f0a2e502e21fc9462a171 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Thu, 19 Oct 2023 05:29:02 -0700 Subject: [PATCH 35/83] Exact apply 2q gate implemented and debugged --- .../cutensornet/tnstate/ttn_gate.py | 20 +++++++++++-------- tests/test_ttn.py | 5 ++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 598cdc17..aadfb944 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -193,7 +193,8 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: ) self.nodes[common_path].canonical_form = None self._logger.debug( - f"New tensor size (MiB)={self.nodes[common_path].tensor.nbytes / 2**20}" + f"New tensor of shape {self.nodes[common_path].tensor.shape} and " + f"size (MiB)={self.nodes[common_path].tensor.nbytes / 2**20}" ) # For each bond along the path, add two identity dim 2 wires @@ -208,22 +209,21 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: parent_funnel = self._create_funnel_tensor(path, DirTTN.PARENT) if child_dir == DirTTN.LEFT: - node_bonds = "cCp" - result_bonds = "fCF" + indices = "fcab,Fpab,cCp->fCF" else: - node_bonds = "Ccp" - result_bonds = "CfF" + indices = "fcab,Fpab,Ccp->CfF" self._logger.debug(f"Adding funnels at {path}.") self.nodes[path].tensor = cq.contract( - "fcab,Fpab," + node_bonds + "->" + result_bonds, + indices, child_funnel, parent_funnel, self.nodes[path].tensor, ) self.nodes[path].canonical_form = None self._logger.debug( - f"New tensor size (MiB)={self.nodes[path].tensor.nbytes / 2**20}" + f"New tensor of shape {self.nodes[path].tensor.shape} and " + f"size (MiB)={self.nodes[path].tensor.nbytes / 2**20}" ) # Update the leaf tensors containing qL and qR @@ -238,6 +238,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: node_bonds[bond] = "a" result_bonds = aux_bonds.copy() result_bonds[bond] = "b" + result_bonds[-1] = "f" self._logger.debug(f"Adding funnels at leaf node at {path}.") leaf_node.tensor = cq.contract( @@ -249,12 +250,15 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: ) leaf_node.canonical_form = None self._logger.debug( - f"New tensor size (MiB)={leaf_node.tensor.nbytes / 2**20}" + f"New tensor of shape {leaf_node.tensor.shape} and " + f"size (MiB)={leaf_node.tensor.nbytes / 2**20}" ) # Truncate to chi (if needed) on all bonds along the path from qL to qR # TODO! + return self + def _create_funnel_tensor(self, path: RootPath, direction: DirTTN) -> Tensor: """Creates a funnel tensor for the given bond. diff --git a/tests/test_ttn.py b/tests/test_ttn.py index f549b6c5..d60ba24f 100644 --- a/tests/test_ttn.py +++ b/tests/test_ttn.py @@ -161,7 +161,7 @@ def test_canonicalise(center: Union[RootPath, Qubit]) -> None: ) def test_exact_circ_sim(circuit: Circuit) -> None: n_qubits = len(circuit.qubits) - n_groups = 2**math.floor(math.log2(n_qubits)) + n_groups = 2 ** math.floor(math.log2(n_qubits)) qubit_partition: dict[int, list[Qubit]] = {i: [] for i in range(n_groups)} for i, q in enumerate(circuit.qubits): qubit_partition[i % n_groups].append(q) @@ -172,6 +172,9 @@ def test_exact_circ_sim(circuit: Circuit) -> None: ttn = TTNxGate(libhandle, qubit_partition, Config()) for g in circuit.get_commands(): ttn.apply_gate(g) + ttn.canonicalise((DirTTN.LEFT,)) # TODO: Remove. Just a hack for now + # TODO: Remove. Just a hack for now. Move to simulate + ttn.nodes[()].tensor *= np.exp(1j * np.pi * circuit.phase) assert ttn.is_valid() # Check that there was no approximation From e1ca88224fe81d5ef684733626412f07eab9d7d3 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 19 Oct 2023 16:51:57 +0100 Subject: [PATCH 36/83] Added truncation by chi and value_of_zero --- pytket/extensions/cutensornet/tnstate/ttn.py | 23 ++++- .../cutensornet/tnstate/ttn_gate.py | 94 ++++++++++++++++++- tests/test_ttn.py | 1 - 3 files changed, 113 insertions(+), 5 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index 800f79dc..674956b0 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -281,7 +281,9 @@ def apply_gate(self, gate: Command) -> TTN: return self - def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: + def canonicalise( + self, center: Union[RootPath, Qubit], unsafe: bool = False + ) -> Tensor: """Canonicalise the TTN so that all tensors are isometries from ``center``. Args: @@ -289,10 +291,17 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: form. If it is a ``RootPath`` it refers to the parent bond of ``self.nodes[center]``. If it is a ``Qubit`` it refers to its physical bond. + unsafe: If True, the final state will be different than the starting one. + Specifically, the information in the returned bond tensor at ``center`` + is removed from the TTN. It is expected that the caller will reintroduce + the bond tensor after some processing (e.g. after SVD truncation). Returns: - The tensor created at ``center`` when all other bonds are canonicalised. + The bond tensor created at ``center`` when canonicalisation is complete. Applying SVD to this tensor yields the global SVD of the TTN. + + Raises: + ValueError: If the ``center`` is ``tuple()``. """ options = {"handle": self._lib.handle, "device_id": self._lib.device_id} @@ -300,6 +309,9 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: if isinstance(center, Qubit): target_path = self.qubit_position[center][0] + assert not unsafe # Unsafe disallowed when ``center`` is a qubit + elif center == (): + raise ValueError("There is no bond at path ().") else: target_path = center @@ -484,6 +496,13 @@ def canonicalise(self, center: Union[RootPath, Qubit]) -> Tensor: # 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. + # Otherwise, if ``unsafe`` is enabled, update the last tensor to Q + elif unsafe: + self.nodes[target_path[:-1]].tensor = Q + self.nodes[target_path[:-1]].canonical_form = target_path[-1] + + self._logger.debug(f"Node canonicalised (unsafe!). Shape: {Q.shape}") + self._logger.debug( f"Finished canonicalisation. Returning R tensor of shape {R.shape}" ) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index aadfb944..527a4dc7 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -254,8 +254,98 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: f"size (MiB)={leaf_node.tensor.nbytes / 2**20}" ) - # Truncate to chi (if needed) on all bonds along the path from qL to qR - # TODO! + # Truncate (if needed) bonds along the path from q0 to q1 + trunc_paths = [ # From q0 to the common ancestor + path_q0[:i] for i in reversed(range(len(common_path) + 1, len(path_q0) + 1)) + ] + trunc_paths += [ # From the common ancestor to q1 + path_q1[:i] for i in range(len(common_path) + 1, len(path_q1) + 1) + ] + + towards_root = True + for path in trunc_paths: + # Canonicalise to this bond (unsafely, so we must reintroduce bond_tensor) + bond_tensor = self.canonicalise(path, unsafe=True) + + # Apply SVD decomposition on bond_tensor and truncate up to + # `self._cfg.chi`. Ask cuTensorNet to contract S directly into U/V and + # normalise the singular values so that the sum of its squares is equal + # to one (i.e. the TTN is a normalised state after truncation). + self._logger.debug(f"Truncating to (or below) chosen chi={self._cfg.chi}") + + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + svd_method = tensor.SVDMethod( + abs_cutoff=self._cfg.zero, + max_extent=self._cfg.chi, + partition="V" if towards_root else "U", + normalization="L2", # Sum of squares equal 1 + ) + + U, S, V, svd_info = tensor.decompose( + "cp->cs,sp", + bond_tensor, + method=svd_method, + options=options, + return_info=True, + ) + assert S is None # Due to "partition" option in SVDMethod + + # discarded_weight is calculated within cuTensorNet as: + # sum([s**2 for s in S']) + # discarded_weight = 1 - ------------------------- + # sum([s**2 for s in S]) + # where S is the list of original singular values and S' is the set of + # singular values that remain after truncation (before normalisation). + # It can be shown that the fidelity ||^2 (for |phi> and |psi> + # unit vectors before and after truncation) is equal to 1 - disc_weight. + # + # We multiply the fidelity of the current step to the overall fidelity + # to keep track of a lower bound for the fidelity. + this_fidelity = 1.0 - svd_info.discarded_weight + self.fidelity *= this_fidelity + + # Contract V to the parent node of the bond + direction = path[-1] + if direction == DirTTN.LEFT: + indices = "lrp,sl->srp" + else: + indices = "lrp,sr->lsp" + self.nodes[path[:-1]].tensor = cq.contract( + indices, + self.nodes[path[:-1]].tensor, + V, + ) + + # Contract U to the child node of the bond + if self.nodes[path].is_leaf: + node_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] + else: + node_bonds = ["l", "r", "p"] + result_bonds = node_bonds.copy() + result_bonds[-1] = "s" + + self.nodes[path].tensor = cq.contract( + self.nodes[path].tensor, + node_bonds, + U, + ["p", "s"], + result_bonds, + ) + # With these two contractions, bond_tensor has been reintroduced, as + # required when calling ``canonicalise(.., unsafe=True)`` + + # The next node in the path towards qR loses its canonical form, since + # S was contracted to it (either via U or V) + if towards_root: + self.nodes[path[:-1]].canonical_form = None + else: + self.nodes[path].canonical_form = None + + # Report to logger + self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") + self._logger.debug( + f"Reduced virtual bond dimension from {bond_tensor.shape[0]} to {V.shape[0]}." + ) return self diff --git a/tests/test_ttn.py b/tests/test_ttn.py index d60ba24f..0f9b8c23 100644 --- a/tests/test_ttn.py +++ b/tests/test_ttn.py @@ -172,7 +172,6 @@ def test_exact_circ_sim(circuit: Circuit) -> None: ttn = TTNxGate(libhandle, qubit_partition, Config()) for g in circuit.get_commands(): ttn.apply_gate(g) - ttn.canonicalise((DirTTN.LEFT,)) # TODO: Remove. Just a hack for now # TODO: Remove. Just a hack for now. Move to simulate ttn.nodes[()].tensor *= np.exp(1j * np.pi * circuit.phase) From 19fea048e8ce172def1f4c065a402af7a228a7f0 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Thu, 19 Oct 2023 09:49:52 -0700 Subject: [PATCH 37/83] Small changes to debugging messages --- pytket/extensions/cutensornet/tnstate/ttn_gate.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 527a4dc7..45201ad7 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -271,7 +271,9 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # `self._cfg.chi`. Ask cuTensorNet to contract S directly into U/V and # normalise the singular values so that the sum of its squares is equal # to one (i.e. the TTN is a normalised state after truncation). - self._logger.debug(f"Truncating to (or below) chosen chi={self._cfg.chi}") + self._logger.debug( + f"Truncating at {path} to (or below) chosen chi={self._cfg.chi}" + ) options = {"handle": self._lib.handle, "device_id": self._lib.device_id} svd_method = tensor.SVDMethod( @@ -344,7 +346,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # Report to logger self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") self._logger.debug( - f"Reduced virtual bond dimension from {bond_tensor.shape[0]} to {V.shape[0]}." + f"Reduced bond dimension from {bond_tensor.shape[0]} to {V.shape[0]}." ) return self From d38451e75e9076178138871c40801cbe4587a994 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Fri, 20 Oct 2023 13:26:13 +0100 Subject: [PATCH 38/83] Approximate sim under chi implemented and tested. --- .../extensions/cutensornet/tnstate/general.py | 27 ++++++++++++ pytket/extensions/cutensornet/tnstate/ttn.py | 22 ++++------ .../cutensornet/tnstate/ttn_gate.py | 15 ++++--- tests/test_ttn.py | 41 +++++++++++++++++++ 4 files changed, 83 insertions(+), 22 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index ecbe3b17..cefc1a7b 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -24,6 +24,7 @@ warnings.warn("local settings failed to import cupy", ImportWarning) try: import cuquantum.cutensornet as cutn # type: ignore + from cuquantum.cutensornet import tensor # type: ignore except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) @@ -177,3 +178,29 @@ def copy(self) -> Config: value_of_zero=self.zero, loglevel=self.loglevel, ) + + +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/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index 674956b0..bab132ad 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -35,7 +35,7 @@ from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle, Tensor, Config +from .general import CuTensorNetHandle, Tensor, Config, _safe_qr class DirTTN(IntEnum): @@ -303,9 +303,8 @@ def canonicalise( Raises: ValueError: If the ``center`` is ``tuple()``. """ - options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - self._logger.debug(f"Canonicalising to {str(center)}") + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} if isinstance(center, Qubit): target_path = self.qubit_position[center][0] @@ -361,11 +360,10 @@ def canonicalise( Q_bonds = "lrs" R_bonds = "sp" - Q, R = tensor.decompose( + Q, R = _safe_qr( + self._lib, node_bonds + "->" + Q_bonds + "," + R_bonds, self.nodes[path].tensor, - method=tensor.QRMethod(), - options=options, ) # Update the tensor @@ -420,11 +418,10 @@ def canonicalise( R_bonds = "rs" node_bonds = "lrp" - Q, R = tensor.decompose( + Q, R = _safe_qr( + self._lib, 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 @@ -487,11 +484,8 @@ def canonicalise( Q_bonds = node_bonds[:target_bond] + "s" + node_bonds[target_bond + 1 :] R_bonds = chr(target_bond) + "s" - Q, R = tensor.decompose( - node_bonds + "->" + Q_bonds + "," + R_bonds, - leaf_node.tensor, - method=tensor.QRMethod(), - options=options, + Q, R = _safe_qr( + self._lib, node_bonds + "->" + Q_bonds + "," + R_bonds, leaf_node.tensor ) # 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/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 45201ad7..ed9e0d81 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -71,19 +71,14 @@ def _apply_1q_gate(self, qubit: Qubit, gate: Op) -> TTNxGate: node_bonds[target] = "i" # Target bond must match with the gate input bond result_bonds[target] = "o" # After contraction it matches the output bond - interleaved_rep = [ - # The tensor of the TTN + # Contract + new_tensor = cq.contract( node_tensor, node_bonds, - # The tensor of the gate gate_tensor, ["o", "i"], - # The bonds of the resulting tensor result_bonds, - ] - - # Contract - new_tensor = cq.contract(*interleaved_rep) + ) # Update ``self.nodes`` # NOTE: Canonicalisation of the node does not change @@ -319,7 +314,11 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: ) # Contract U to the child node of the bond + if self.nodes[path].is_leaf: + n_qbonds = ( + len(self.nodes[path].tensor.shape) - 1 + ) # Total number of physical bonds in this node node_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] else: node_bonds = ["l", "r", "p"] diff --git a/tests/test_ttn.py b/tests/test_ttn.py index 0f9b8c23..96e05c14 100644 --- a/tests/test_ttn.py +++ b/tests/test_ttn.py @@ -191,3 +191,44 @@ def test_exact_circ_sim(circuit: Circuit) -> None: # Check that the statevector is correct assert np.allclose(ttn.get_statevector(), state, atol=ttn._cfg._atol) + + +@pytest.mark.parametrize( + "circuit", + [ + pytest.lazy_fixture("q5_empty"), # type: ignore + pytest.lazy_fixture("q8_empty"), # type: ignore + pytest.lazy_fixture("q2_x0"), # type: ignore + pytest.lazy_fixture("q2_x1"), # type: ignore + pytest.lazy_fixture("q2_v0"), # type: ignore + pytest.lazy_fixture("q8_x0h2v5z6"), # type: ignore + pytest.lazy_fixture("q2_x0cx01"), # type: ignore + pytest.lazy_fixture("q2_x1cx10x1"), # type: ignore + pytest.lazy_fixture("q2_x0cx01cx10"), # type: ignore + pytest.lazy_fixture("q2_v0cx01cx10"), # type: ignore + pytest.lazy_fixture("q2_hadamard_test"), # type: ignore + pytest.lazy_fixture("q2_lcu1"), # type: ignore + pytest.lazy_fixture("q2_lcu2"), # type: ignore + pytest.lazy_fixture("q2_lcu3"), # type: ignore + pytest.lazy_fixture("q3_v0cx02"), # type: ignore + pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore + # pytest.lazy_fixture("q4_lcu1"), # TTN doesn't support n-qubit gates with n>2 + pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore + pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore + pytest.lazy_fixture("q6_qvol"), # type: ignore + ], +) +def test_approx_circ_sim_chi(circuit: Circuit) -> None: + n_qubits = len(circuit.qubits) + n_groups = 2 ** math.floor(math.log2(n_qubits)) + qubit_partition: dict[int, list[Qubit]] = {i: [] for i in range(n_groups)} + for i, q in enumerate(circuit.qubits): + qubit_partition[i % n_groups].append(q) + + with CuTensorNetHandle() as libhandle: + ttn = TTNxGate(libhandle, qubit_partition, Config(chi=4)) + for g in circuit.get_commands(): + ttn.apply_gate(g) + assert ttn.is_valid() + # Check that overlap is 1 + assert np.isclose(ttn.vdot(ttn), 1.0, atol=ttn._cfg._atol) From f839f155bb57ba5e5181761935918f05fdb0cce3 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Fri, 20 Oct 2023 13:57:10 +0100 Subject: [PATCH 39/83] Added some more computationally intensive tests --- tests/conftest.py | 12 ++++++++++++ tests/test_ttn.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 36c0672a..4f7bcc28 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -223,3 +223,15 @@ def q20_line_circ_20_layers() -> Circuit: def q6_qvol() -> Circuit: np.random.seed(1) return quantum_volume_circuit(n_qubits=6) + + +@pytest.fixture +def q8_qvol() -> Circuit: + np.random.seed(1) + return quantum_volume_circuit(n_qubits=8) + + +@pytest.fixture +def q15_qvol() -> Circuit: + np.random.seed(1) + return quantum_volume_circuit(n_qubits=15) diff --git a/tests/test_ttn.py b/tests/test_ttn.py index 96e05c14..f7482361 100644 --- a/tests/test_ttn.py +++ b/tests/test_ttn.py @@ -157,6 +157,7 @@ def test_canonicalise(center: Union[RootPath, Qubit]) -> None: pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore pytest.lazy_fixture("q6_qvol"), # type: ignore + pytest.lazy_fixture("q8_qvol"), # type: ignore ], ) def test_exact_circ_sim(circuit: Circuit) -> None: @@ -216,6 +217,7 @@ def test_exact_circ_sim(circuit: Circuit) -> None: pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore pytest.lazy_fixture("q6_qvol"), # type: ignore + pytest.lazy_fixture("q8_qvol"), # type: ignore ], ) def test_approx_circ_sim_chi(circuit: Circuit) -> None: @@ -232,3 +234,29 @@ def test_approx_circ_sim_chi(circuit: Circuit) -> None: assert ttn.is_valid() # Check that overlap is 1 assert np.isclose(ttn.vdot(ttn), 1.0, atol=ttn._cfg._atol) + + +@pytest.mark.parametrize( + "circuit", + [ + pytest.lazy_fixture("q15_qvol"), # type: ignore + ], +) +def test_circ_approx_explicit(circuit: Circuit) -> None: + random.seed(1) + + n_qubits = len(circuit.qubits) + n_groups = 2 ** math.floor(math.log2(n_qubits)) + qubit_partition: dict[int, list[Qubit]] = {i: [] for i in range(n_groups)} + for i, q in enumerate(circuit.qubits): + qubit_partition[i % n_groups].append(q) + + with CuTensorNetHandle() as libhandle: + # Fixed virtual bond dimension + # Check for TTNxGate + ttn_gate = TTNxGate(libhandle, qubit_partition, Config(chi=120)) + for g in circuit.get_commands(): + ttn_gate.apply_gate(g) + assert np.isclose(ttn_gate.fidelity, 0.80, atol=1e-2) + assert ttn_gate.is_valid() + assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=ttn_gate._cfg._atol) From c2cd8ce178f4a7d0979fc960b9679f9f6bda90a9 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Sun, 22 Oct 2023 17:26:37 +0100 Subject: [PATCH 40/83] Unified TTN and MPS via TNState abstract class --- .../cutensornet/tnstate/__init__.py | 4 +- .../extensions/cutensornet/tnstate/general.py | 225 +++++++++++++- pytket/extensions/cutensornet/tnstate/mps.py | 22 +- .../cutensornet/tnstate/simulation.py | 82 +++-- pytket/extensions/cutensornet/tnstate/ttn.py | 103 ++++++- tests/{test_mps.py => test_tnstate.py} | 282 +++++++++++++----- tests/test_ttn.py | 262 ---------------- 7 files changed, 606 insertions(+), 374 deletions(-) rename tests/{test_mps.py => test_tnstate.py} (63%) delete mode 100644 tests/test_ttn.py diff --git a/pytket/extensions/cutensornet/tnstate/__init__.py b/pytket/extensions/cutensornet/tnstate/__init__.py index 6bcd2765..46428237 100644 --- a/pytket/extensions/cutensornet/tnstate/__init__.py +++ b/pytket/extensions/cutensornet/tnstate/__init__.py @@ -18,8 +18,8 @@ https://github.com/CQCL/pytket-cutensornet. """ -from .general import CuTensorNetHandle, Config -from .simulation import ContractionAlg, simulate, prepare_circuit +from .general import CuTensorNetHandle, Config, TNState +from .simulation import ContractionAlg, simulate, prepare_circuit_mps from .mps import DirectionMPS, MPS from .mps_gate import MPSxGate diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index cefc1a7b..847abc13 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -12,12 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations # type: ignore +from abc import ABC, abstractmethod import warnings import logging from typing import Any, Optional, Union import numpy as np # type: ignore +from pytket.circuit import Command, Op, OpType, Qubit +from pytket.pauli import Pauli, QubitPauliString + try: import cupy as cp # type: ignore except ImportError: @@ -69,7 +73,7 @@ def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None: class Config: - """Configuration class for simulation using MPS.""" + """Configuration class for simulation using ``TNState``.""" def __init__( self, @@ -81,7 +85,7 @@ def __init__( value_of_zero: float = 1e-16, loglevel: int = logging.WARNING, ): - """Instantiate a configuration object for MPS simulation. + """Instantiate a configuration object for ``TNState`` simulation. Note: Providing both a custom ``chi`` and ``truncation_fidelity`` will raise an @@ -179,6 +183,223 @@ def copy(self) -> Config: loglevel=self.loglevel, ) + def _flush(self) -> None: + # Does nothing in the general case; but children classes with batched + # gate contraction will redefine this method so that the last batch of + # gates is applied. + return None + + +class TNState(ABC): + """Abstract class to refer to a Tensor Network state. + + This class does not implement any of the methods it offers. You will need + to use an instance of a child class to call them. + """ + + @abstractmethod + def is_valid(self) -> bool: + """Verify that the tensor network state is valid. + + Returns: + False if a violation was detected or True otherwise. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def apply_gate(self, gate: Command) -> TNState: + """Applies the gate to the TNState. + + Args: + gate: The gate to be applied. + + Returns: + ``self``, to allow for method chaining. + + Raises: + RuntimeError: If the ``CuTensorNetHandle`` is out of scope. + RuntimeError: If gate is not supported. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def apply_scalar(self, scalar: complex) -> TNState: + """Multiplies the state by a complex number. + + Args: + scalar: The complex number to be multiplied. + + Returns: + ``self``, to allow for method chaining. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def vdot(self, other: TNState) -> complex: + """Obtain the inner product of the two states: ````. + + It can be used to compute the squared norm of a state ``tnstate`` as + ``tnstate.vdot(tnstate)``. The tensors within the state are not modified. + + Note: + The state that is conjugated is ``self``. + + Args: + other: The other ``TNState``. + + Returns: + The resulting complex number. + + Raises: + RuntimeError: If the two states do not have the same qubits. + RuntimeError: If the ``CuTensorNetHandle`` is out of scope. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def sample(self) -> dict[Qubit, int]: + """Returns a sample from a Z measurement applied on every qubit. + + Notes: + The contents of ``self`` are not updated. This is equivalent to applying + ``tnstate = self.copy()`` then ``tnstate.measure(tnstate.get_qubits())``. + + Returns: + A dictionary mapping each qubit in the state to its 0 or 1 outcome. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def measure(self, qubits: set[Qubit]) -> dict[Qubit, int]: + """Applies a Z measurement on ``qubits``, updates the state and returns outcome. + + Notes: + After applying this function, ``self`` will contain the projected + state over the non-measured qubits. + + The resulting state has been normalised. + + Args: + qubits: The subset of qubits to be measured. + + Returns: + A dictionary mapping the given ``qubits`` to their measurement outcome, + i.e. either ``0`` or ``1``. + + Raises: + ValueError: If an element in ``qubits`` is not a qubit in the state. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def postselect(self, qubit_outcomes: dict[Qubit, int]) -> float: + """Applies a postselection, updates the states and returns its probability. + + Notes: + After applying this function, ``self`` will contain the projected + state over the non-postselected qubits. + + The resulting state has been normalised. + + Args: + qubit_outcomes: A dictionary mapping a subset of qubits to their + desired outcome value (either ``0`` or ``1``). + + Returns: + The probability of this postselection to occur in a measurement. + + Raises: + ValueError: If a key in ``qubit_outcomes`` is not a qubit in the state. + ValueError: If a value in ``qubit_outcomes`` is other than ``0`` or ``1``. + ValueError: If all of the qubits in the state are being postselected. Instead, + you may wish to use ``get_amplitude()``. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def expectation_value(self, pauli_string: QubitPauliString) -> float: + """Obtains the expectation value of the Pauli string observable. + + Args: + pauli_string: A pytket object representing a tensor product of Paulis. + + Returns: + The expectation value. + + Raises: + ValueError: If a key in ``pauli_string`` is not a qubit in the state. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def get_fidelity(self) -> float: + """Returns the current fidelity of the state.""" + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def get_statevector(self) -> np.ndarray: + """Returns the statevector with qubits in Increasing Lexicographic Order (ILO). + + Raises: + ValueError: If there are no qubits left in the state. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def get_amplitude(self, state: int) -> complex: + """Returns the amplitude of the chosen computational state. + + Notes: + The result is equivalent to ``tnstate.get_statevector[b]``, but this method + is faster when querying a single amplitude (or just a few). + + Args: + state: The integer whose bitstring describes the computational state. + The qubits in the bitstring are in increasing lexicographic order. + + Returns: + The amplitude of the computational state in ``self``. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def get_qubits(self) -> set[Qubit]: + """Returns the set of qubits that ``self`` is defined on.""" + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def get_byte_size(self) -> int: + """Returns the number of bytes ``self`` currently occupies in GPU memory.""" + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def get_device_id(self) -> int: + """Returns the identifier of the device (GPU) where the tensors are stored.""" + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def update_libhandle(self, libhandle: CuTensorNetHandle) -> None: + """Update the ``CuTensorNetHandle`` used by ``self``. Multiple + objects may use the same handle. + + Args: + libhandle: The new cuTensorNet library handle. + + Raises: + RuntimeError: If the device (GPU) where ``libhandle`` was initialised + does not match the one where the tensors of ``self`` are stored. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @abstractmethod + def copy(self) -> TNState: + """Returns a deep copy of ``self`` on the same device.""" + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + @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 diff --git a/pytket/extensions/cutensornet/tnstate/mps.py b/pytket/extensions/cutensornet/tnstate/mps.py index 4fd06773..1a888b59 100644 --- a/pytket/extensions/cutensornet/tnstate/mps.py +++ b/pytket/extensions/cutensornet/tnstate/mps.py @@ -33,7 +33,7 @@ from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle, Tensor, Config +from .general import CuTensorNetHandle, Config, TNState, Tensor class DirectionMPS(Enum): @@ -43,7 +43,7 @@ class DirectionMPS(Enum): RIGHT = 1 -class MPS: +class MPS(TNState): """Represents a state as a Matrix Product State. Attributes: @@ -204,6 +204,18 @@ def apply_gate(self, gate: Command) -> MPS: return self + def apply_scalar(self, scalar: complex) -> TNState: + """Multiplies the state by a complex number. + + Args: + scalar: The complex number to be multiplied. + + Returns: + ``self``, to allow for method chaining. + """ + self.tensors[0] *= scalar + return self + def canonicalise(self, l_pos: int, r_pos: int) -> None: """Canonicalises the MPS object. @@ -305,7 +317,7 @@ def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: self.tensors[next_pos] = result self.canonical_form[next_pos] = None - def vdot(self, other: MPS) -> complex: + def vdot(self, other: MPS) -> complex: # type: ignore """Obtain the inner product of the two MPS: ````. It can be used to compute the squared norm of an MPS ``mps`` as @@ -614,6 +626,10 @@ def expectation_value(self, pauli_string: QubitPauliString) -> float: self._logger.debug(f"Expectation value is {value.real}.") return value.real + def get_fidelity(self) -> float: + """Returns the current fidelity of the state.""" + return self.fidelity + def get_statevector(self) -> np.ndarray: """Returns the statevector with qubits in Increasing Lexicographic Order (ILO). diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py index ab6ebd84..ea231e1b 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/tnstate/simulation.py @@ -13,6 +13,7 @@ # limitations under the License. from enum import Enum +import math # type: ignore from random import choice # type: ignore from collections import defaultdict # type: ignore import numpy as np # type: ignore @@ -24,21 +25,22 @@ from pytket.predicates import CompilationUnit from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle, Config -from .mps import MPS +from .general import CuTensorNetHandle, Config, TNState from .mps_gate import MPSxGate from .mps_mpo import MPSxMPO +from .ttn_gate import TTNxGate class ContractionAlg(Enum): - """An enum to refer to the MPS contraction algorithm. + """An enum to refer to the TNState contraction algorithm. Each enum value corresponds to the class with the same name; see its docs for - information of the algorithm. + information about the algorithm. """ MPSxGate = 0 MPSxMPO = 1 + TTNxGate = 2 def simulate( @@ -46,8 +48,8 @@ def simulate( circuit: Circuit, algorithm: ContractionAlg, config: Config, -) -> MPS: - """Simulate the given circuit and return the ``MPS`` representing the final state. +) -> TNState: + """Simulates the circuit and returns the ``TNState`` representing the final state. Note: A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` @@ -57,65 +59,65 @@ def simulate( The input ``circuit`` must be composed of one-qubit and two-qubit gates only. Any gateset supported by ``pytket`` can be used. - Two-qubit gates must act between adjacent qubits, i.e. on ``circuit.qubits[i]`` - and ``circuit.qubits[i+1]`` for any ``i``. If this is not satisfied by your - circuit, consider using ``prepare_circuit()`` on it. - Args: libhandle: The cuTensorNet library handle that will be used to carry out - tensor operations on the MPS. + tensor operations. circuit: The pytket circuit to be simulated. algorithm: Choose between the values of the ``ContractionAlg`` enum. config: The configuration object for simulation. Returns: - An instance of ``MPS`` containing (an approximation of) the final state - of the circuit. + An instance of ``TNState`` containing (an approximation of) the final state + of the circuit. The instance be of the class matching ``algorithm``. """ logger = set_logger("Simulation", level=config.loglevel) + logger.info( + "Ordering the gates in the circuit to reduce canonicalisation overhead." + ) if algorithm == ContractionAlg.MPSxGate: - mps = MPSxGate( # type: ignore + tnstate = MPSxGate( # type: ignore libhandle, circuit.qubits, config, ) + sorted_gates = _get_sorted_gates(circuit) elif algorithm == ContractionAlg.MPSxMPO: - mps = MPSxMPO( # type: ignore + tnstate = MPSxMPO( # type: ignore libhandle, circuit.qubits, config, ) + sorted_gates = _get_sorted_gates(circuit) - # Sort the gates so there isn't much overhead from canonicalising back and forth. - logger.info( - "Ordering the gates in the circuit to reduce canonicalisation overhead." - ) - sorted_gates = _get_sorted_gates(circuit) + elif algorithm == ContractionAlg.TTNxGate: + tnstate = TTNxGate( # type: ignore + libhandle, + _get_qubit_partition(circuit), + config, + ) + sorted_gates = circuit.get_commands() # TODO: change! logger.info("Running simulation...") # Apply the gates for i, g in enumerate(sorted_gates): - mps.apply_gate(g) + tnstate.apply_gate(g) logger.info(f"Progress... {(100*i) // len(sorted_gates)}%") # Apply the batched operations that are left (if any) - mps._flush() - - # Apply the batched operations that are left (if any) - mps._flush() + tnstate._flush() - # Apply the circuit's phase to the leftmost tensor (any would work) - mps.tensors[0] = mps.tensors[0] * np.exp(1j * np.pi * circuit.phase) + # Apply the circuit's phase to the state + tnstate.apply_scalar(np.exp(1j * np.pi * circuit.phase)) logger.info("Simulation completed.") - logger.info(f"Final MPS size={mps.get_byte_size() / 2**20} MiB") - logger.info(f"Final MPS fidelity={mps.fidelity}") - return mps + logger.info(f"Final TNState size={tnstate.get_byte_size() / 2**20} MiB") + logger.info(f"Final TNState fidelity={tnstate.fidelity}") + return tnstate -def prepare_circuit(circuit: Circuit) -> tuple[Circuit, dict[Qubit, Qubit]]: +def prepare_circuit_mps(circuit: Circuit) -> tuple[Circuit, dict[Qubit, Qubit]]: """Prepares a circuit in a specific, ``MPS``-friendly, manner. Returns an equivalent circuit with the appropriate structure to be simulated by @@ -151,6 +153,22 @@ def prepare_circuit(circuit: Circuit) -> tuple[Circuit, dict[Qubit, Qubit]]: return (prep_circ, qubit_map) +def _get_qubit_partition(circuit: Circuit) -> dict[int, list[Qubit]]: + """Returns a qubit partition for a TTN. + + Proceeds by recursive bisection of the qubit connectivity graph, so that + qubits that interact with each other less are connected by a common ancestor + closer to the root. + """ + # TODO: This current one is a naive approach, not using bisections. REPLACE! + n_qubits = len(circuit.qubits) + n_groups = 2 ** math.floor(math.log2(n_qubits)) + qubit_partition: dict[int, list[Qubit]] = {i: [] for i in range(n_groups)} + for i, q in enumerate(circuit.qubits): + qubit_partition[i % n_groups].append(q) + return qubit_partition + + def _get_sorted_gates(circuit: Circuit) -> list[Command]: """Sorts the list of gates, placing 2-qubit gates close to each other first. @@ -214,7 +232,7 @@ def _get_sorted_gates(circuit: Circuit) -> list[Command]: if left_distance is None and right_distance is None: raise RuntimeError( "Some two-qubit gate in the circuit is not acting between", - "nearest neighbour qubits. Consider using prepare_circuit().", + "nearest neighbour qubits. Consider using prepare_circuit_mps().", ) elif left_distance is None: assert right_distance is not None diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index bab132ad..f020fa19 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -35,7 +35,7 @@ from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle, Tensor, Config, _safe_qr +from .general import CuTensorNetHandle, Config, TNState, Tensor, _safe_qr class DirTTN(IntEnum): @@ -77,7 +77,7 @@ def copy(self) -> TreeNode: return new_node -class TTN: +class TTN(TNState): """Represents a state as a Tree Tensor Network. Attributes: @@ -281,6 +281,18 @@ def apply_gate(self, gate: Command) -> TTN: return self + def apply_scalar(self, scalar: complex) -> TNState: + """Multiplies the state by a complex number. + + Args: + scalar: The complex number to be multiplied. + + Returns: + ``self``, to allow for method chaining. + """ + self.nodes[()].tensor *= scalar + return self + def canonicalise( self, center: Union[RootPath, Qubit], unsafe: bool = False ) -> Tensor: @@ -502,7 +514,7 @@ def canonicalise( ) return R - def vdot(self, other: TTN) -> complex: + def vdot(self, other: TTN) -> complex: # type: ignore """Obtain the inner product of the two TTN: ````. It can be used to compute the squared norm of a TTN ``ttn`` as @@ -555,10 +567,89 @@ def vdot(self, other: TTN) -> complex: self._logger.debug(f"Result from vdot={result}") return complex(result) + def sample(self) -> dict[Qubit, int]: + """Returns a sample from a Z measurement applied on every qubit. + + Notes: + The contents of ``self`` are not updated. This is equivalent to applying + ``tnstate = self.copy()`` then ``tnstate.measure(tnstate.get_qubits())``. + + Returns: + A dictionary mapping each qubit in the state to its 0 or 1 outcome. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + def measure(self, qubits: set[Qubit]) -> dict[Qubit, int]: + """Applies a Z measurement on ``qubits``, updates the state and returns outcome. + + Notes: + After applying this function, ``self`` will contain the projected + state over the non-measured qubits. + + The resulting state has been normalised. + + Args: + qubits: The subset of qubits to be measured. + + Returns: + A dictionary mapping the given ``qubits`` to their measurement outcome, + i.e. either ``0`` or ``1``. + + Raises: + ValueError: If an element in ``qubits`` is not a qubit in the state. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + def postselect(self, qubit_outcomes: dict[Qubit, int]) -> float: + """Applies a postselection, updates the states and returns its probability. + + Notes: + After applying this function, ``self`` will contain the projected + state over the non-postselected qubits. + + The resulting state has been normalised. + + Args: + qubit_outcomes: A dictionary mapping a subset of qubits to their + desired outcome value (either ``0`` or ``1``). + + Returns: + The probability of this postselection to occur in a measurement. + + Raises: + ValueError: If a key in ``qubit_outcomes`` is not a qubit in the state. + ValueError: If a value in ``qubit_outcomes`` is other than ``0`` or ``1``. + ValueError: If all of the qubits in the state are being postselected. Instead, + you may wish to use ``get_amplitude()``. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + def expectation_value(self, pauli_string: QubitPauliString) -> float: + """Obtains the expectation value of the Pauli string observable. + + Args: + pauli_string: A pytket object representing a tensor product of Paulis. + + Returns: + The expectation value. + + Raises: + ValueError: If a key in ``pauli_string`` is not a qubit in the state. + """ + raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") + + def get_fidelity(self) -> float: + """Returns the current fidelity of the state.""" + return self.fidelity + def get_statevector(self) -> np.ndarray: """Returns the statevector represented by the TTN, with qubits ordered in Increasing Lexicographic Order (ILO). + Raises: + ValueError: If there are no qubits left in the TTN. """ + if len(self.get_qubits()) == 0: + raise ValueError("There are no qubits left in this TTN.") # Create the interleaved representation with all tensors interleaved_rep = self.get_interleaved_representation() @@ -747,3 +838,9 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTN: "TTN is a base class with no contraction algorithm implemented." + " You must use a subclass of TTN, such as TTNxGate." ) + + def _flush(self) -> None: + # Does nothing in the general MPS case; but children classes with batched + # gate contraction will redefine this method so that the last batch of + # gates is applied. + return None diff --git a/tests/test_mps.py b/tests/test_tnstate.py similarity index 63% rename from tests/test_mps.py rename to tests/test_tnstate.py index 2826fa8d..a386de5e 100644 --- a/tests/test_mps.py +++ b/tests/test_tnstate.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Union import random # type: ignore import pytest @@ -14,10 +14,13 @@ MPS, MPSxGate, MPSxMPO, + TTNxGate, + DirTTN, simulate, - prepare_circuit, + prepare_circuit_mps, ContractionAlg, ) +from pytket.extensions.cutensornet.tnstate.ttn import RootPath from pytket.extensions.cutensornet.utils import circuit_statevector_postselect @@ -26,8 +29,9 @@ def test_libhandle_manager() -> None: # Proper use of library handle with CuTensorNetHandle() as libhandle: - mps = MPS(libhandle, circ.qubits, Config()) - assert np.isclose(mps.vdot(mps), 1, atol=mps._cfg._atol) + cfg = Config() + mps = MPS(libhandle, circ.qubits, cfg) + assert np.isclose(mps.vdot(mps), 1, atol=cfg._atol) # Catch exception due to library handle out of scope with pytest.raises(RuntimeError): @@ -35,25 +39,29 @@ def test_libhandle_manager() -> None: def test_init() -> None: - circ = Circuit(5) + circ = Circuit(8) + qubit_partition = {i: [q] for i, q in enumerate(circ.qubits)} with CuTensorNetHandle() as libhandle: mps_gate = MPSxGate(libhandle, circ.qubits, Config()) assert mps_gate.is_valid() mps_mpo = MPSxMPO(libhandle, circ.qubits, Config()) assert mps_mpo.is_valid() + ttn_gate = TTNxGate(libhandle, qubit_partition, Config()) + assert ttn_gate.is_valid() -def test_canonicalise() -> None: +def test_canonicalise_mps() -> None: cp.random.seed(1) circ = Circuit(5) with CuTensorNetHandle() as libhandle: - mps_gate = MPSxGate(libhandle, circ.qubits, Config()) + cfg = Config() + mps_gate = MPSxGate(libhandle, circ.qubits, cfg) # Fill up the tensors with random entries # Leftmost tensor - T_d = cp.empty(shape=(1, 4, 2), dtype=mps_gate._cfg._complex_t) + T_d = cp.empty(shape=(1, 4, 2), dtype=cfg._complex_t) for i1 in range(T_d.shape[1]): for i2 in range(T_d.shape[2]): T_d[0][i1][i2] = cp.random.rand() + 1j * cp.random.rand() @@ -61,7 +69,7 @@ def test_canonicalise() -> None: # Middle tensors for pos in range(1, len(mps_gate) - 1): - T_d = cp.empty(shape=(4, 4, 2), dtype=mps_gate._cfg._complex_t) + T_d = cp.empty(shape=(4, 4, 2), dtype=cfg._complex_t) for i0 in range(T_d.shape[0]): for i1 in range(T_d.shape[1]): for i2 in range(T_d.shape[2]): @@ -69,7 +77,7 @@ def test_canonicalise() -> None: mps_gate.tensors[pos] = T_d # Rightmost tensor - T_d = cp.empty(shape=(4, 1, 2), dtype=mps_gate._cfg._complex_t) + T_d = cp.empty(shape=(4, 1, 2), dtype=cfg._complex_t) for i0 in range(T_d.shape[0]): for i2 in range(T_d.shape[2]): T_d[i0][0][i2] = cp.random.rand() + 1j * cp.random.rand() @@ -89,7 +97,7 @@ def test_canonicalise() -> None: # Check that canonicalisation did not change the vector overlap = mps_gate.vdot(mps_copy) - assert np.isclose(overlap, norm_sq, atol=mps_gate._cfg._atol) + assert np.isclose(overlap, norm_sq, atol=cfg._atol) # Check that the corresponding tensors are in orthogonal form for pos in range(len(mps_gate)): @@ -107,13 +115,106 @@ def test_canonicalise() -> None: assert cp.allclose(result, cp.eye(result.shape[0])) +@pytest.mark.parametrize( + "center", + [ + (DirTTN.RIGHT,), + (DirTTN.LEFT, DirTTN.RIGHT), + (DirTTN.LEFT, DirTTN.RIGHT, DirTTN.RIGHT), + Qubit("q", [2]), + ], +) +def test_canonicalise_ttn(center: Union[RootPath, Qubit]) -> None: + cp.random.seed(1) + n_levels = 3 + n_qubits = 2**n_levels + max_dim = 8 + + circ = Circuit(n_qubits) + qubit_partition = {i: [q] for i, q in enumerate(circ.qubits)} + + with CuTensorNetHandle() as libhandle: + ttn = TTNxGate(libhandle, qubit_partition, Config()) + + # Fill up the tensors with random entries + for path, node in ttn.nodes.items(): + if node.is_leaf: + T = cp.empty(shape=(2, max_dim), dtype=ttn._cfg._complex_t) + for i0 in range(T.shape[0]): + for i1 in range(T.shape[1]): + T[i0][i1] = cp.random.rand() + 1j * cp.random.rand() + else: + shape = (max_dim, max_dim, max_dim if len(path) != 0 else 1) + T = cp.empty(shape=shape, dtype=ttn._cfg._complex_t) + for i0 in range(shape[0]): + for i1 in range(shape[1]): + for i2 in range(shape[2]): + T[i0][i1][i2] = cp.random.rand() + 1j * cp.random.rand() + node.tensor = T + + assert ttn.is_valid() + + # Calculate the norm of the TTN + norm_sq = ttn.vdot(ttn) + + # Keep a copy of the non-canonicalised TTN + ttn_copy = ttn.copy() + + # Canonicalise at target path + R = ttn.canonicalise(center) + assert ttn.is_valid() + + # Check that canonicalisation did not change the vector + overlap = ttn.vdot(ttn_copy) + assert np.isclose(overlap / norm_sq, 1.0, atol=ttn._cfg._atol) + + # Check that the tensor R returned agrees with the norm + overlap_R = cq.contract("ud,ud->", R, R.conj()) + assert np.isclose(overlap_R / norm_sq, 1.0, atol=ttn._cfg._atol) + + # Check that the corresponding tensors are in orthogonal form + for path, node in ttn.nodes.items(): + # If it's the node just below the center of canonicalisation, it + # cannot be in orthogonal form + if isinstance(center, Qubit): + if path == ttn.qubit_position[center][0]: + assert node.canonical_form is None + continue + else: + if path == center[:-1]: + assert node.canonical_form is None + continue + # Otherwise, it should be in orthogonal form + assert node.canonical_form is not None + + T = node.tensor + + if node.is_leaf: + assert node.canonical_form == DirTTN.PARENT + result = cq.contract("qp,qP->pP", T, T.conj()) + + elif node.canonical_form == DirTTN.PARENT: + result = cq.contract("lrp,lrP->pP", T, T.conj()) + + elif node.canonical_form == DirTTN.LEFT: + result = cq.contract("lrp,Lrp->lL", T, T.conj()) + + elif node.canonical_form == DirTTN.RIGHT: + result = cq.contract("lrp,lRp->rR", T, T.conj()) + + # Check that the result is the identity + assert cp.allclose(result, cp.eye(result.shape[0])) + + @pytest.mark.parametrize( "circuit", [ pytest.lazy_fixture("q5_empty"), # type: ignore + pytest.lazy_fixture("q8_empty"), # type: ignore pytest.lazy_fixture("q2_x0"), # type: ignore pytest.lazy_fixture("q2_x1"), # type: ignore pytest.lazy_fixture("q2_v0"), # type: ignore + pytest.lazy_fixture("q8_x0h2v5z6"), # type: ignore pytest.lazy_fixture("q2_x0cx01"), # type: ignore pytest.lazy_fixture("q2_x1cx10x1"), # type: ignore pytest.lazy_fixture("q2_x0cx01cx10"), # type: ignore @@ -124,10 +225,10 @@ def test_canonicalise() -> None: pytest.lazy_fixture("q2_lcu3"), # type: ignore pytest.lazy_fixture("q3_v0cx02"), # type: ignore pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore - # pytest.lazy_fixture("q4_lcu1"), # MPS doesn't support n-qubit gates with n>2 pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore pytest.lazy_fixture("q6_qvol"), # type: ignore + pytest.lazy_fixture("q8_qvol"), # type: ignore ], ) @pytest.mark.parametrize( @@ -135,40 +236,46 @@ def test_canonicalise() -> None: [ ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO, + ContractionAlg.TTNxGate, ], ) def test_exact_circ_sim(circuit: Circuit, algorithm: ContractionAlg) -> None: - prep_circ, _ = prepare_circuit(circuit) + if algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: + circuit, _ = prepare_circuit_mps(circuit) + n_qubits = len(circuit.qubits) - state = prep_circ.get_statevector() + state = circuit.get_statevector() with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, prep_circ, algorithm, Config()) - assert mps.is_valid() + cfg = Config() + tnstate = simulate(libhandle, circuit, algorithm, cfg) + assert tnstate.is_valid() # Check that there was no approximation - assert np.isclose(mps.fidelity, 1.0, atol=mps._cfg._atol) + assert np.isclose(tnstate.get_fidelity(), 1.0, atol=cfg._atol) # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) + assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) # Check that all of the amplitudes are correct for b in range(2**n_qubits): assert np.isclose( - mps.get_amplitude(b), + tnstate.get_amplitude(b), state[b], - atol=mps._cfg._atol, + atol=cfg._atol, ) # Check that the statevector is correct - assert np.allclose(mps.get_statevector(), state, atol=mps._cfg._atol) + assert np.allclose(tnstate.get_statevector(), state, atol=cfg._atol) @pytest.mark.parametrize( "circuit", [ pytest.lazy_fixture("q5_empty"), # type: ignore + pytest.lazy_fixture("q8_empty"), # type: ignore pytest.lazy_fixture("q2_x0"), # type: ignore pytest.lazy_fixture("q2_x1"), # type: ignore pytest.lazy_fixture("q2_v0"), # type: ignore + pytest.lazy_fixture("q8_x0h2v5z6"), # type: ignore pytest.lazy_fixture("q2_x0cx01"), # type: ignore pytest.lazy_fixture("q2_x1cx10x1"), # type: ignore pytest.lazy_fixture("q2_x0cx01cx10"), # type: ignore @@ -179,10 +286,10 @@ def test_exact_circ_sim(circuit: Circuit, algorithm: ContractionAlg) -> None: pytest.lazy_fixture("q2_lcu3"), # type: ignore pytest.lazy_fixture("q3_v0cx02"), # type: ignore pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore - # pytest.lazy_fixture("q4_lcu1"), # MPS doesn't support n-qubit gates with n>2 pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore pytest.lazy_fixture("q6_qvol"), # type: ignore + pytest.lazy_fixture("q8_qvol"), # type: ignore ], ) @pytest.mark.parametrize( @@ -193,23 +300,26 @@ def test_exact_circ_sim(circuit: Circuit, algorithm: ContractionAlg) -> None: ], ) def test_approx_circ_sim_gate_fid(circuit: Circuit, algorithm: ContractionAlg) -> None: - prep_circ, _ = prepare_circuit(circuit) + if algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: + circuit, _ = prepare_circuit_mps(circuit) + with CuTensorNetHandle() as libhandle: - mps = simulate( - libhandle, prep_circ, algorithm, Config(truncation_fidelity=0.99) - ) - assert mps.is_valid() + cfg = Config(truncation_fidelity=0.99) + tnstate = simulate(libhandle, circuit, algorithm, cfg) + assert tnstate.is_valid() # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) + assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) @pytest.mark.parametrize( "circuit", [ pytest.lazy_fixture("q5_empty"), # type: ignore + pytest.lazy_fixture("q8_empty"), # type: ignore pytest.lazy_fixture("q2_x0"), # type: ignore pytest.lazy_fixture("q2_x1"), # type: ignore pytest.lazy_fixture("q2_v0"), # type: ignore + pytest.lazy_fixture("q8_x0h2v5z6"), # type: ignore pytest.lazy_fixture("q2_x0cx01"), # type: ignore pytest.lazy_fixture("q2_x1cx10x1"), # type: ignore pytest.lazy_fixture("q2_x0cx01cx10"), # type: ignore @@ -220,10 +330,10 @@ def test_approx_circ_sim_gate_fid(circuit: Circuit, algorithm: ContractionAlg) - pytest.lazy_fixture("q2_lcu3"), # type: ignore pytest.lazy_fixture("q3_v0cx02"), # type: ignore pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore - # pytest.lazy_fixture("q4_lcu1"), # MPS doesn't support n-qubit gates with n>2 pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore pytest.lazy_fixture("q6_qvol"), # type: ignore + pytest.lazy_fixture("q8_qvol"), # type: ignore ], ) @pytest.mark.parametrize( @@ -231,15 +341,19 @@ def test_approx_circ_sim_gate_fid(circuit: Circuit, algorithm: ContractionAlg) - [ ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO, + ContractionAlg.TTNxGate, ], ) def test_approx_circ_sim_chi(circuit: Circuit, algorithm: ContractionAlg) -> None: - prep_circ, _ = prepare_circuit(circuit) + if algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: + circuit, _ = prepare_circuit_mps(circuit) + with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, prep_circ, algorithm, Config(chi=4)) - assert mps.is_valid() + cfg = Config(chi=4) + tnstate = simulate(libhandle, circuit, algorithm, cfg) + assert tnstate.is_valid() # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) + assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) @pytest.mark.parametrize( @@ -270,38 +384,40 @@ def test_approx_circ_sim_chi(circuit: Circuit, algorithm: ContractionAlg) -> Non def test_float_point_options( circuit: Circuit, algorithm: ContractionAlg, fp_precision: Any ) -> None: - prep_circ, _ = prepare_circuit(circuit) + if algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: + circuit, _ = prepare_circuit_mps(circuit) with CuTensorNetHandle() as libhandle: # Exact - mps = simulate( - libhandle, prep_circ, algorithm, Config(float_precision=fp_precision) - ) - assert mps.is_valid() + cfg = Config(float_precision=fp_precision) + tnstate = simulate(libhandle, circuit, algorithm, cfg) + assert tnstate.is_valid() # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) + assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) # Approximate, bound truncation fidelity - mps = simulate( + cfg = Config(truncation_fidelity=0.99, float_precision=fp_precision) + tnstate = simulate( libhandle, - prep_circ, + circuit, algorithm, - Config(truncation_fidelity=0.99, float_precision=fp_precision), + cfg, ) - assert mps.is_valid() + assert tnstate.is_valid() # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) + assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) # Approximate, bound chi - mps = simulate( + cfg = Config(chi=4, float_precision=fp_precision) + tnstate = simulate( libhandle, - prep_circ, + circuit, algorithm, - Config(chi=4, float_precision=fp_precision), + cfg, ) - assert mps.is_valid() + assert tnstate.is_valid() # Check that overlap is 1 - assert np.isclose(mps.vdot(mps), 1.0, atol=mps._cfg._atol) + assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) @pytest.mark.parametrize( @@ -310,45 +426,68 @@ def test_float_point_options( pytest.lazy_fixture("q20_line_circ_20_layers"), # type: ignore ], ) -def test_circ_approx_explicit(circuit: Circuit) -> None: +def test_circ_approx_explicit_mps(circuit: Circuit) -> None: random.seed(1) with CuTensorNetHandle() as libhandle: # Finite gate fidelity # Check for MPSxGate + cfg = Config(truncation_fidelity=0.99) mps_gate = simulate( libhandle, circuit, ContractionAlg.MPSxGate, - Config(truncation_fidelity=0.99), + cfg, ) - assert np.isclose(mps_gate.fidelity, 0.4, atol=1e-1) + assert np.isclose(mps_gate.get_fidelity(), 0.4, atol=1e-1) assert mps_gate.is_valid() - assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=mps_gate._cfg._atol) + assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=cfg._atol) # Check for MPSxMPO mps_mpo = simulate( libhandle, circuit, ContractionAlg.MPSxMPO, - Config(truncation_fidelity=0.99), + cfg, ) - assert np.isclose(mps_mpo.fidelity, 0.6, atol=1e-1) + assert np.isclose(mps_mpo.get_fidelity(), 0.6, atol=1e-1) assert mps_mpo.is_valid() - assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=mps_mpo._cfg._atol) + assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=cfg._atol) # Fixed virtual bond dimension # Check for MPSxGate - mps_gate = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config(chi=8)) - assert np.isclose(mps_gate.fidelity, 0.03, atol=1e-2) + cfg = Config(chi=8) + mps_gate = simulate(libhandle, circuit, ContractionAlg.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=mps_gate._cfg._atol) + assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=cfg._atol) # Check for MPSxMPO - mps_mpo = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, Config(chi=8)) - assert np.isclose(mps_mpo.fidelity, 0.04, atol=1e-2) + mps_mpo = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, cfg) + assert np.isclose(mps_mpo.get_fidelity(), 0.04, atol=1e-2) assert mps_mpo.is_valid() - assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=mps_mpo._cfg._atol) + assert np.isclose(mps_mpo.vdot(mps_mpo), 1.0, atol=cfg._atol) + + +@pytest.mark.parametrize( + "circuit", + [ + pytest.lazy_fixture("q15_qvol"), # type: ignore + ], +) +def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: + random.seed(1) + + with CuTensorNetHandle() as libhandle: + # Fixed virtual bond dimension + # Check for TTNxGate + cfg = Config(chi=120) + ttn_gate = simulate(libhandle, circuit, ContractionAlg.TTNxGate, cfg) + for g in circuit.get_commands(): + ttn_gate.apply_gate(g) + assert np.isclose(ttn_gate.get_fidelity(), 0.51, atol=1e-2) + assert ttn_gate.is_valid() + assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=cfg._atol) @pytest.mark.parametrize( @@ -383,10 +522,11 @@ def test_postselect_2q_circ(circuit: Circuit, postselect_dict: dict) -> None: sv = sv / np.sqrt(sv_prob) # Normalise with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config()) + cfg = Config() + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, cfg) prob = mps.postselect(postselect_dict) - assert np.isclose(prob, sv_prob, atol=mps._cfg._atol) - assert np.allclose(mps.get_statevector(), sv, atol=mps._cfg._atol) + assert np.isclose(prob, sv_prob, atol=cfg._atol) + assert np.allclose(mps.get_statevector(), sv, atol=cfg._atol) @pytest.mark.parametrize( @@ -413,10 +553,11 @@ def test_postselect_circ(circuit: Circuit, postselect_dict: dict) -> None: sv = sv / np.sqrt(sv_prob) # Normalise with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config()) + cfg = Config() + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, cfg) prob = mps.postselect(postselect_dict) - assert np.isclose(prob, sv_prob, atol=mps._cfg._atol) - assert np.allclose(mps.get_statevector(), sv, atol=mps._cfg._atol) + assert np.isclose(prob, sv_prob, atol=cfg._atol) + assert np.allclose(mps.get_statevector(), sv, atol=cfg._atol) @pytest.mark.parametrize( @@ -458,9 +599,10 @@ def test_expectation_value(circuit: Circuit, observable: QubitPauliString) -> No # Simulate the circuit and obtain the expectation value with CuTensorNetHandle() as libhandle: - mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, Config()) + cfg = Config() + mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, cfg) assert np.isclose( - mps.expectation_value(observable), expectation_value, atol=mps._cfg._atol + mps.expectation_value(observable), expectation_value, atol=cfg._atol ) diff --git a/tests/test_ttn.py b/tests/test_ttn.py deleted file mode 100644 index f7482361..00000000 --- a/tests/test_ttn.py +++ /dev/null @@ -1,262 +0,0 @@ -from typing import Any, Union -import random # type: ignore -import math -import pytest - -import cuquantum as cq # type: ignore -import cupy as cp # type: ignore -import numpy as np # type: ignore - -from pytket.circuit import Circuit, Qubit, OpType # type: ignore -from pytket.pauli import Pauli, QubitPauliString # type: ignore -from pytket.extensions.cutensornet.tnstate import ( - CuTensorNetHandle, - Config, - TTN, - TTNxGate, - DirTTN, -) -from pytket.extensions.cutensornet.tnstate.ttn import RootPath -from pytket.extensions.cutensornet.utils import circuit_statevector_postselect - - -def test_libhandle_manager() -> None: - circ = Circuit(4) - qubit_partition = {i: [q] for i, q in enumerate(circ.qubits)} - - # Proper use of library handle - with CuTensorNetHandle() as libhandle: - ttn = TTN(libhandle, qubit_partition, Config()) - assert np.isclose(ttn.vdot(ttn), 1, atol=ttn._cfg._atol) - - # Catch exception due to library handle out of scope - with pytest.raises(RuntimeError): - ttn.vdot(ttn) - - -def test_init() -> None: - circ = Circuit(4) - qubit_partition = {i: [q] for i, q in enumerate(circ.qubits)} - - with CuTensorNetHandle() as libhandle: - ttn_gate = TTNxGate(libhandle, qubit_partition, Config()) - assert ttn_gate.is_valid() - - -@pytest.mark.parametrize( - "center", - [ - (DirTTN.RIGHT,), - (DirTTN.LEFT, DirTTN.RIGHT), - (DirTTN.LEFT, DirTTN.RIGHT, DirTTN.RIGHT), - Qubit("q", [2]), - ], -) -def test_canonicalise(center: Union[RootPath, Qubit]) -> None: - cp.random.seed(1) - n_levels = 3 - n_qubits = 2**n_levels - max_dim = 8 - - circ = Circuit(n_qubits) - qubit_partition = {i: [q] for i, q in enumerate(circ.qubits)} - - with CuTensorNetHandle() as libhandle: - ttn = TTNxGate(libhandle, qubit_partition, Config()) - - # Fill up the tensors with random entries - for path, node in ttn.nodes.items(): - if node.is_leaf: - T = cp.empty(shape=(2, max_dim), dtype=ttn._cfg._complex_t) - for i0 in range(T.shape[0]): - for i1 in range(T.shape[1]): - T[i0][i1] = cp.random.rand() + 1j * cp.random.rand() - else: - shape = (max_dim, max_dim, max_dim if len(path) != 0 else 1) - T = cp.empty(shape=shape, dtype=ttn._cfg._complex_t) - for i0 in range(shape[0]): - for i1 in range(shape[1]): - for i2 in range(shape[2]): - T[i0][i1][i2] = cp.random.rand() + 1j * cp.random.rand() - node.tensor = T - - assert ttn.is_valid() - - # Calculate the norm of the TTN - norm_sq = ttn.vdot(ttn) - - # Keep a copy of the non-canonicalised TTN - ttn_copy = ttn.copy() - - # Canonicalise at target path - R = ttn.canonicalise(center) - assert ttn.is_valid() - - # Check that canonicalisation did not change the vector - overlap = ttn.vdot(ttn_copy) - assert np.isclose(overlap / norm_sq, 1.0, atol=ttn._cfg._atol) - - # Check that the tensor R returned agrees with the norm - overlap_R = cq.contract("ud,ud->", R, R.conj()) - assert np.isclose(overlap_R / norm_sq, 1.0, atol=ttn._cfg._atol) - - # Check that the corresponding tensors are in orthogonal form - for path, node in ttn.nodes.items(): - # If it's the node just below the center of canonicalisation, it - # cannot be in orthogonal form - if isinstance(center, Qubit): - if path == ttn.qubit_position[center][0]: - assert node.canonical_form is None - continue - else: - if path == center[:-1]: - assert node.canonical_form is None - continue - # Otherwise, it should be in orthogonal form - assert node.canonical_form is not None - - T = node.tensor - - if node.is_leaf: - assert node.canonical_form == DirTTN.PARENT - result = cq.contract("qp,qP->pP", T, T.conj()) - - elif node.canonical_form == DirTTN.PARENT: - result = cq.contract("lrp,lrP->pP", T, T.conj()) - - elif node.canonical_form == DirTTN.LEFT: - result = cq.contract("lrp,Lrp->lL", T, T.conj()) - - elif node.canonical_form == DirTTN.RIGHT: - result = cq.contract("lrp,lRp->rR", T, T.conj()) - - # Check that the result is the identity - assert cp.allclose(result, cp.eye(result.shape[0])) - - -@pytest.mark.parametrize( - "circuit", - [ - pytest.lazy_fixture("q5_empty"), # type: ignore - pytest.lazy_fixture("q8_empty"), # type: ignore - pytest.lazy_fixture("q2_x0"), # type: ignore - pytest.lazy_fixture("q2_x1"), # type: ignore - pytest.lazy_fixture("q2_v0"), # type: ignore - pytest.lazy_fixture("q8_x0h2v5z6"), # type: ignore - pytest.lazy_fixture("q2_x0cx01"), # type: ignore - pytest.lazy_fixture("q2_x1cx10x1"), # type: ignore - pytest.lazy_fixture("q2_x0cx01cx10"), # type: ignore - pytest.lazy_fixture("q2_v0cx01cx10"), # type: ignore - pytest.lazy_fixture("q2_hadamard_test"), # type: ignore - pytest.lazy_fixture("q2_lcu1"), # type: ignore - pytest.lazy_fixture("q2_lcu2"), # type: ignore - pytest.lazy_fixture("q2_lcu3"), # type: ignore - pytest.lazy_fixture("q3_v0cx02"), # type: ignore - pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore - # pytest.lazy_fixture("q4_lcu1"), # TTN doesn't support n-qubit gates with n>2 - pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore - pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore - pytest.lazy_fixture("q6_qvol"), # type: ignore - pytest.lazy_fixture("q8_qvol"), # type: ignore - ], -) -def test_exact_circ_sim(circuit: Circuit) -> None: - n_qubits = len(circuit.qubits) - n_groups = 2 ** math.floor(math.log2(n_qubits)) - qubit_partition: dict[int, list[Qubit]] = {i: [] for i in range(n_groups)} - for i, q in enumerate(circuit.qubits): - qubit_partition[i % n_groups].append(q) - - state = circuit.get_statevector() - - with CuTensorNetHandle() as libhandle: - ttn = TTNxGate(libhandle, qubit_partition, Config()) - for g in circuit.get_commands(): - ttn.apply_gate(g) - # TODO: Remove. Just a hack for now. Move to simulate - ttn.nodes[()].tensor *= np.exp(1j * np.pi * circuit.phase) - - assert ttn.is_valid() - # Check that there was no approximation - assert np.isclose(ttn.fidelity, 1.0, atol=ttn._cfg._atol) - # Check that overlap is 1 - assert np.isclose(ttn.vdot(ttn), 1.0, atol=ttn._cfg._atol) - - # Check that all of the amplitudes are correct - for b in range(2**n_qubits): - assert np.isclose( - ttn.get_amplitude(b), - state[b], - atol=ttn._cfg._atol, - ) - - # Check that the statevector is correct - assert np.allclose(ttn.get_statevector(), state, atol=ttn._cfg._atol) - - -@pytest.mark.parametrize( - "circuit", - [ - pytest.lazy_fixture("q5_empty"), # type: ignore - pytest.lazy_fixture("q8_empty"), # type: ignore - pytest.lazy_fixture("q2_x0"), # type: ignore - pytest.lazy_fixture("q2_x1"), # type: ignore - pytest.lazy_fixture("q2_v0"), # type: ignore - pytest.lazy_fixture("q8_x0h2v5z6"), # type: ignore - pytest.lazy_fixture("q2_x0cx01"), # type: ignore - pytest.lazy_fixture("q2_x1cx10x1"), # type: ignore - pytest.lazy_fixture("q2_x0cx01cx10"), # type: ignore - pytest.lazy_fixture("q2_v0cx01cx10"), # type: ignore - pytest.lazy_fixture("q2_hadamard_test"), # type: ignore - pytest.lazy_fixture("q2_lcu1"), # type: ignore - pytest.lazy_fixture("q2_lcu2"), # type: ignore - pytest.lazy_fixture("q2_lcu3"), # type: ignore - pytest.lazy_fixture("q3_v0cx02"), # type: ignore - pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore - # pytest.lazy_fixture("q4_lcu1"), # TTN doesn't support n-qubit gates with n>2 - pytest.lazy_fixture("q5_h0s1rz2ry3tk4tk13"), # type: ignore - pytest.lazy_fixture("q5_line_circ_30_layers"), # type: ignore - pytest.lazy_fixture("q6_qvol"), # type: ignore - pytest.lazy_fixture("q8_qvol"), # type: ignore - ], -) -def test_approx_circ_sim_chi(circuit: Circuit) -> None: - n_qubits = len(circuit.qubits) - n_groups = 2 ** math.floor(math.log2(n_qubits)) - qubit_partition: dict[int, list[Qubit]] = {i: [] for i in range(n_groups)} - for i, q in enumerate(circuit.qubits): - qubit_partition[i % n_groups].append(q) - - with CuTensorNetHandle() as libhandle: - ttn = TTNxGate(libhandle, qubit_partition, Config(chi=4)) - for g in circuit.get_commands(): - ttn.apply_gate(g) - assert ttn.is_valid() - # Check that overlap is 1 - assert np.isclose(ttn.vdot(ttn), 1.0, atol=ttn._cfg._atol) - - -@pytest.mark.parametrize( - "circuit", - [ - pytest.lazy_fixture("q15_qvol"), # type: ignore - ], -) -def test_circ_approx_explicit(circuit: Circuit) -> None: - random.seed(1) - - n_qubits = len(circuit.qubits) - n_groups = 2 ** math.floor(math.log2(n_qubits)) - qubit_partition: dict[int, list[Qubit]] = {i: [] for i in range(n_groups)} - for i, q in enumerate(circuit.qubits): - qubit_partition[i % n_groups].append(q) - - with CuTensorNetHandle() as libhandle: - # Fixed virtual bond dimension - # Check for TTNxGate - ttn_gate = TTNxGate(libhandle, qubit_partition, Config(chi=120)) - for g in circuit.get_commands(): - ttn_gate.apply_gate(g) - assert np.isclose(ttn_gate.fidelity, 0.80, atol=1e-2) - assert ttn_gate.is_valid() - assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=ttn_gate._cfg._atol) From 7ded61154af1b55d3ee9e05330e787fd85d9ff0a Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 1 Nov 2023 12:16:29 +0000 Subject: [PATCH 41/83] _get_qubit_partition now does recursive balanced bisections. --- .../extensions/cutensornet/tnstate/general.py | 11 ++- .../cutensornet/tnstate/simulation.py | 70 ++++++++++++++++--- .../cutensornet/tnstate/ttn_gate.py | 1 - tests/test_tnstate.py | 2 +- 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index 847abc13..1b528753 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -83,6 +83,7 @@ def __init__( optim_delta: float = 1e-5, float_precision: Union[np.float32, np.float64] = np.float64, # type: ignore value_of_zero: float = 1e-16, + leaf_size: int = 10, loglevel: int = logging.WARNING, ): """Instantiate a configuration object for ``TNState`` simulation. @@ -118,6 +119,8 @@ def __init__( We suggest to use a value slightly below what your chosen ``float_precision`` can reasonably achieve. For instance, ``1e-16`` for ``np.float64`` precision (default) and ``1e-7`` for ``np.float32``. + leaf_size: Maximum number of qubits in a leaf node when using ``TTN``. + Default is 10. loglevel: Internal logger output level. Use 30 for warnings only, 20 for verbose and 10 for debug mode. @@ -170,6 +173,7 @@ def __init__( self.k = k self.optim_delta = 1e-5 self.loglevel = loglevel + self.leaf_size = leaf_size def copy(self) -> Config: """Standard copy of the contents.""" @@ -181,14 +185,9 @@ def copy(self) -> Config: float_precision=self._real_t, # type: ignore value_of_zero=self.zero, loglevel=self.loglevel, + leaf_size=self.leaf_size, ) - def _flush(self) -> None: - # Does nothing in the general case; but children classes with batched - # gate contraction will redefine this method so that the last batch of - # gates is applied. - return None - class TNState(ABC): """Abstract class to refer to a Tensor Network state. diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py index ea231e1b..fde39940 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/tnstate/simulation.py @@ -18,6 +18,8 @@ from collections import defaultdict # type: ignore import numpy as np # type: ignore +from networkx import Graph, community # type: ignore + from pytket.circuit import Circuit, Command, Qubit from pytket.transform import Transform from pytket.architecture import Architecture @@ -94,7 +96,7 @@ def simulate( elif algorithm == ContractionAlg.TTNxGate: tnstate = TTNxGate( # type: ignore libhandle, - _get_qubit_partition(circuit), + _get_qubit_partition(circuit, config.leaf_size), config, ) sorted_gates = circuit.get_commands() # TODO: change! @@ -153,19 +155,71 @@ def prepare_circuit_mps(circuit: Circuit) -> tuple[Circuit, dict[Qubit, Qubit]]: return (prep_circ, qubit_map) -def _get_qubit_partition(circuit: Circuit) -> dict[int, list[Qubit]]: +def _get_qubit_partition( + circuit: Circuit, max_q_per_leaf: int +) -> dict[int, list[Qubit]]: """Returns a qubit partition for a TTN. Proceeds by recursive bisection of the qubit connectivity graph, so that qubits that interact with each other less are connected by a common ancestor closer to the root. + + Args: + circuit: The circuit to be simulated. + max_q_per_leaf: The maximum allowed number of qubits per node leaf + + Returns: + A dictionary describing the partition in the format expected by TTN. + + Raises: + RuntimeError: If gate acts on more than 2 qubits. """ - # TODO: This current one is a naive approach, not using bisections. REPLACE! - n_qubits = len(circuit.qubits) - n_groups = 2 ** math.floor(math.log2(n_qubits)) - qubit_partition: dict[int, list[Qubit]] = {i: [] for i in range(n_groups)} - for i, q in enumerate(circuit.qubits): - qubit_partition[i % n_groups].append(q) + + # Scan the circuit and generate the edges of the connectivity graph + edge_weights: dict[tuple[Qubit, Qubit], int] = dict() + for cmd in circuit.get_commands(): + if cmd.op.is_gate(): + if cmd.op.n_qubits == 2: + edge = (min(cmd.qubits), max(cmd.qubits)) + + if edge in edge_weights: + edge_weights[edge] += 1 + else: + edge_weights[edge] = 1 + + elif cmd.op.n_qubits > 2: + raise RuntimeError( + "Gates must act on only 1 or 2 qubits! " + + f"This is not satisfied by {cmd}." + ) + + # Create the connectivity graph in NetworkX + connectivity_graph = Graph() + connectivity_graph.add_nodes_from(circuit.qubits) + for (u, v), weight in edge_weights.items(): + connectivity_graph.add_edge(u, v, weight=weight) + + # Apply balanced bisections until each qubit group is small enough + partition = {0: set(circuit.qubits)} + stop_bisec = False # Do at least one bisection (TTN reqs >1 leaf nodes) + + while not stop_bisec: + old_partition = partition.copy() + for key, group in old_partition.items(): + # Apply the balanced bisection on this group + (groupA, groupB) = community.kernighan_lin_bisection( + connectivity_graph.subgraph(group), + max_iter=2 * len(group), # Iteractions scaling with number of nodes + weight="weight", + ) + # Groups A and B are on the same subtree (key separated by +1) + partition[2 * key] = groupA + partition[2 * key + 1] = groupB + + # Stop if all groups have less than ``max_q_per_leaf`` qubits in them + stop_bisec = all(len(group) <= max_q_per_leaf for group in partition.values()) + + qubit_partition = {key: list(leaf_qubits) for key, leaf_qubits in partition.items()} return qubit_partition diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index ed9e0d81..056a5f92 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -314,7 +314,6 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: ) # Contract U to the child node of the bond - if self.nodes[path].is_leaf: n_qbonds = ( len(self.nodes[path].tensor.shape) - 1 diff --git a/tests/test_tnstate.py b/tests/test_tnstate.py index a386de5e..4a714db7 100644 --- a/tests/test_tnstate.py +++ b/tests/test_tnstate.py @@ -485,7 +485,7 @@ def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: ttn_gate = simulate(libhandle, circuit, ContractionAlg.TTNxGate, cfg) for g in circuit.get_commands(): ttn_gate.apply_gate(g) - assert np.isclose(ttn_gate.get_fidelity(), 0.51, atol=1e-2) + assert np.isclose(ttn_gate.get_fidelity(), 0.62, atol=1e-2) assert ttn_gate.is_valid() assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=cfg._atol) From 4161474da3c72b06baa5de66e10205ffad420369 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 1 Nov 2023 14:31:07 +0000 Subject: [PATCH 42/83] Updated cq.contract providing handles and contraction path when known --- pytket/extensions/cutensornet/tnstate/ttn.py | 8 ++++---- pytket/extensions/cutensornet/tnstate/ttn_gate.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index f020fa19..bb319bbe 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -397,7 +397,7 @@ def canonicalise( R, parent_node.tensor, options=options, - optimize={"samples": 1}, + optimize={"path": [(0, 1)]}, ) # The canonical form of the parent node is lost parent_node.canonical_form = None @@ -447,7 +447,7 @@ def canonicalise( child_node.tensor, R, options=options, - optimize={"samples": 1}, + optimize={"path": [(0, 1)]}, ) # The canonical form of the child node is lost @@ -479,7 +479,7 @@ def canonicalise( leaf_node.tensor, R, options=options, - optimize={"samples": 1}, + optimize={"path": [(0, 1)]}, ) # The canonical form of the leaf node is lost @@ -561,7 +561,7 @@ def vdot(self, other: TTN) -> complex: # type: ignore result = cq.contract( *interleaved_rep, options={"handle": self._lib.handle, "device_id": self._lib.device_id}, - optimize={"samples": 1}, + optimize={"samples": 1}, # There is little to no optimisation to be done ) self._logger.debug(f"Result from vdot={result}") diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 056a5f92..fef3c8f4 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -78,6 +78,8 @@ def _apply_1q_gate(self, qubit: Qubit, gate: Op) -> TTNxGate: gate_tensor, ["o", "i"], result_bonds, + options={"handle": self._lib.handle, "device_id": self._lib.device_id}, + optimize={"path": [(0, 1)]}, ) # Update ``self.nodes`` @@ -100,6 +102,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: Returns: ``self``, to allow for method chaining. """ + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} # Load the gate's unitary to the GPU memory gate_unitary = gate.get_unitary().astype(dtype=self._cfg._complex_t, copy=False) @@ -140,6 +143,8 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: gate_tensor, gate_bond, result_bonds, + options=options, + optimize={"path": [(0, 1)]}, ) self._logger.debug( @@ -185,6 +190,8 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: left_funnel, right_funnel, gate_tensor, + options=options, + optimize={"path": [(0, 1), (1, 2), (0, 1)]}, ) self.nodes[common_path].canonical_form = None self._logger.debug( @@ -214,6 +221,8 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: child_funnel, parent_funnel, self.nodes[path].tensor, + options=options, + optimize={"path": [(1, 2), (0, 1)]}, ) self.nodes[path].canonical_form = None self._logger.debug( @@ -242,6 +251,8 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: self._create_funnel_tensor(path, DirTTN.PARENT), ["f", "p", "a", "b"], result_bonds, + options=options, + optimize={"path": [(0, 1)]}, ) leaf_node.canonical_form = None self._logger.debug( @@ -311,6 +322,8 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: indices, self.nodes[path[:-1]].tensor, V, + options=options, + optimize={"path": [(0, 1)]}, ) # Contract U to the child node of the bond @@ -330,6 +343,8 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: U, ["p", "s"], result_bonds, + options=options, + optimize={"path": [(0, 1)]}, ) # With these two contractions, bond_tensor has been reintroduced, as # required when calling ``canonicalise(.., unsafe=True)`` From bc9298a0048e923b1b6278473187a98b9e5fc3d7 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 1 Nov 2023 14:33:35 +0000 Subject: [PATCH 43/83] Changed default leaf size --- pytket/extensions/cutensornet/tnstate/general.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index 1b528753..5b02e757 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -83,7 +83,7 @@ def __init__( optim_delta: float = 1e-5, float_precision: Union[np.float32, np.float64] = np.float64, # type: ignore value_of_zero: float = 1e-16, - leaf_size: int = 10, + leaf_size: int = 8, loglevel: int = logging.WARNING, ): """Instantiate a configuration object for ``TNState`` simulation. @@ -120,7 +120,7 @@ def __init__( ``float_precision`` can reasonably achieve. For instance, ``1e-16`` for ``np.float64`` precision (default) and ``1e-7`` for ``np.float32``. leaf_size: Maximum number of qubits in a leaf node when using ``TTN``. - Default is 10. + Default is 8. loglevel: Internal logger output level. Use 30 for warnings only, 20 for verbose and 10 for debug mode. From b295937a102a0fddc54ecda4fcba09d17e8acacc Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 1 Nov 2023 14:50:35 +0000 Subject: [PATCH 44/83] Changed the leaf_size parameter of the explicit_ttn test --- tests/test_tnstate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_tnstate.py b/tests/test_tnstate.py index 4a714db7..4316d7fb 100644 --- a/tests/test_tnstate.py +++ b/tests/test_tnstate.py @@ -481,7 +481,7 @@ def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: with CuTensorNetHandle() as libhandle: # Fixed virtual bond dimension # Check for TTNxGate - cfg = Config(chi=120) + cfg = Config(chi=120, leaf_size=3) ttn_gate = simulate(libhandle, circuit, ContractionAlg.TTNxGate, cfg) for g in circuit.get_commands(): ttn_gate.apply_gate(g) From ca2fab8b81166778d1d9b47be74eaa42d708b76c Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Wed, 1 Nov 2023 08:03:05 -0700 Subject: [PATCH 45/83] Removing unused imports and pleasing linter. --- pytket/extensions/cutensornet/tnstate/general.py | 8 ++++---- pytket/extensions/cutensornet/tnstate/simulation.py | 1 - pytket/extensions/cutensornet/tnstate/ttn.py | 10 ++++------ pytket/extensions/cutensornet/tnstate/ttn_gate.py | 3 --- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index 5b02e757..51fba143 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -19,8 +19,8 @@ import numpy as np # type: ignore -from pytket.circuit import Command, Op, OpType, Qubit -from pytket.pauli import Pauli, QubitPauliString +from pytket.circuit import Command, Qubit +from pytket.pauli import QubitPauliString try: import cupy as cp # type: ignore @@ -310,8 +310,8 @@ def postselect(self, qubit_outcomes: dict[Qubit, int]) -> float: Raises: ValueError: If a key in ``qubit_outcomes`` is not a qubit in the state. ValueError: If a value in ``qubit_outcomes`` is other than ``0`` or ``1``. - ValueError: If all of the qubits in the state are being postselected. Instead, - you may wish to use ``get_amplitude()``. + ValueError: If all of the qubits in the state are being postselected. + Instead, you may wish to use ``get_amplitude()``. """ raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py index fde39940..aba8d2bd 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/tnstate/simulation.py @@ -13,7 +13,6 @@ # limitations under the License. from enum import Enum -import math # type: ignore from random import choice # type: ignore from collections import defaultdict # type: ignore import numpy as np # type: ignore diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index bb319bbe..3dd89eee 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -16,7 +16,6 @@ from typing import Optional, Union from enum import IntEnum -from random import random # type: ignore import math # type: ignore import numpy as np # type: ignore @@ -26,12 +25,11 @@ 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) -from pytket.circuit import Command, Op, OpType, Qubit -from pytket.pauli import Pauli, QubitPauliString +from pytket.circuit import Command, Op, Qubit +from pytket.pauli import QubitPauliString from pytket.extensions.cutensornet.general import set_logger @@ -619,8 +617,8 @@ def postselect(self, qubit_outcomes: dict[Qubit, int]) -> float: Raises: ValueError: If a key in ``qubit_outcomes`` is not a qubit in the state. ValueError: If a value in ``qubit_outcomes`` is other than ``0`` or ``1``. - ValueError: If all of the qubits in the state are being postselected. Instead, - you may wish to use ``get_amplitude()``. + ValueError: If all of the qubits in the state are being postselected. + Instead, you may wish to use ``get_amplitude()``. """ raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index fef3c8f4..c16a3bb6 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -13,9 +13,6 @@ # limitations under the License. from __future__ import annotations # type: ignore import warnings -import logging - -import numpy as np # type: ignore try: import cupy as cp # type: ignore From 5f5453a0fbfff43adae1f1619588ce3262615623 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 1 Nov 2023 16:52:01 +0000 Subject: [PATCH 46/83] Changed docstrings of Config --- .../extensions/cutensornet/tnstate/general.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index 51fba143..bcacb9bf 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -79,11 +79,11 @@ def __init__( self, chi: Optional[int] = None, truncation_fidelity: Optional[float] = None, - k: int = 4, - optim_delta: float = 1e-5, float_precision: Union[np.float32, np.float64] = np.float64, # type: ignore value_of_zero: float = 1e-16, leaf_size: int = 8, + k: int = 4, + optim_delta: float = 1e-5, loglevel: int = logging.WARNING, ): """Instantiate a configuration object for ``TNState`` simulation. @@ -101,14 +101,6 @@ def __init__( ``||^2 >= trucantion_fidelity``, where ``|psi>`` and ``|phi>`` are the states before and after truncation (both normalised). If not provided, it will default to its maximum value 1. - k: If using MPSxMPO, the maximum number of layers the MPO is allowed to - have before being contracted. Increasing this might increase fidelity, - but it will also increase resource requirements exponentially. - Ignored if not using MPSxMPO. Default value is 4. - optim_delta: If using MPSxMPO, stopping criteria for the optimisation when - contracting the ``k`` layers of MPO. Stops when the increase of fidelity - between iterations is smaller than ``optim_delta``. - Ignored if not using MPSxMPO. Default value is ``1e-5``. float_precision: The floating point precision used in tensor calculations; choose from ``numpy`` types: ``np.float64`` or ``np.float32``. Complex numbers are represented using two of such @@ -119,8 +111,16 @@ def __init__( We suggest to use a value slightly below what your chosen ``float_precision`` can reasonably achieve. For instance, ``1e-16`` for ``np.float64`` precision (default) and ``1e-7`` for ``np.float32``. - leaf_size: Maximum number of qubits in a leaf node when using ``TTN``. - Default is 8. + leaf_size: For ``TTN`` simulation only. Sets the maximum number of + qubits in a leaf node when using ``TTN``. Default is 8. + k: For ``MPSxMPO`` simulation only. Sets the maximum number of layers + the MPO is allowed to have before being contracted. Increasing this + might increase fidelity, but it will also increase resource requirements + exponentially. Default value is 4. + optim_delta: For ``MPSxMPO`` simulation only. Sets the stopping criteria for + the optimisation when contracting the ``k`` layers of MPO. Stops when + the increase of fidelity between iterations is smaller than this value. + Default value is ``1e-5``. loglevel: Internal logger output level. Use 30 for warnings only, 20 for verbose and 10 for debug mode. @@ -170,22 +170,22 @@ def __init__( UserWarning, ) + self.leaf_size = leaf_size self.k = k self.optim_delta = 1e-5 self.loglevel = loglevel - self.leaf_size = leaf_size def copy(self) -> Config: """Standard copy of the contents.""" return Config( chi=self.chi, truncation_fidelity=self.truncation_fidelity, - k=self.k, - optim_delta=self.optim_delta, float_precision=self._real_t, # type: ignore value_of_zero=self.zero, - loglevel=self.loglevel, leaf_size=self.leaf_size, + k=self.k, + optim_delta=self.optim_delta, + loglevel=self.loglevel, ) From 8f551bd5f442a59db6cffef0f71d2099e2f3fe32 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 1 Nov 2023 17:57:09 +0000 Subject: [PATCH 47/83] Updated docs --- docs/api.rst | 2 +- docs/modules/mps.rst | 63 ----------------- docs/modules/tnstate.rst | 61 +++++++++++++++++ .../cutensornet/tnstate/__init__.py | 4 +- .../extensions/cutensornet/tnstate/general.py | 5 +- pytket/extensions/cutensornet/tnstate/mps.py | 18 ++--- .../extensions/cutensornet/tnstate/mps_mpo.py | 68 +++++++++---------- .../cutensornet/tnstate/simulation.py | 12 ++-- 8 files changed, 114 insertions(+), 119 deletions(-) delete mode 100644 docs/modules/mps.rst create mode 100644 docs/modules/tnstate.rst diff --git a/docs/api.rst b/docs/api.rst index 89fa9d19..fa3f4aab 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -3,4 +3,4 @@ API documentation .. toctree:: modules/fullTN.rst - modules/mps.rst + modules/tnstate.rst diff --git a/docs/modules/mps.rst b/docs/modules/mps.rst deleted file mode 100644 index d6fff984..00000000 --- a/docs/modules/mps.rst +++ /dev/null @@ -1,63 +0,0 @@ -Matrix Product State (MPS) -========================== - -.. automodule:: pytket.extensions.cutensornet.mps - - -Simulation -~~~~~~~~~~ - -.. autofunction:: pytket.extensions.cutensornet.mps.simulate - -.. autoenum:: pytket.extensions.cutensornet.mps.ContractionAlg() - :members: - -.. autoclass:: pytket.extensions.cutensornet.mps.ConfigMPS() - - .. automethod:: __init__ - -.. autoclass:: pytket.extensions.cutensornet.mps.CuTensorNetHandle - - -Classes -~~~~~~~ - -.. autoclass:: pytket.extensions.cutensornet.mps.MPS() - - .. automethod:: __init__ - .. automethod:: apply_gate - .. automethod:: vdot - .. automethod:: canonicalise - .. automethod:: sample - .. automethod:: measure - .. automethod:: postselect - .. automethod:: expectation_value - .. automethod:: get_statevector - .. automethod:: get_amplitude - .. automethod:: get_qubits - .. automethod:: get_virtual_dimensions - .. automethod:: get_physical_dimension - .. automethod:: get_device_id - .. automethod:: is_valid - .. automethod:: update_libhandle - .. automethod:: copy - .. automethod:: __len__ - -.. autoclass:: pytket.extensions.cutensornet.mps.MPSxGate() - :show-inheritance: - - .. automethod:: __init__ - -.. autoclass:: pytket.extensions.cutensornet.mps.MPSxMPO() - :show-inheritance: - - .. automethod:: __init__ - - -Miscellaneous -~~~~~~~~~~~~~ - -.. autoenum:: pytket.extensions.cutensornet.mps.DirectionMPS() - :members: - -.. autofunction:: pytket.extensions.cutensornet.mps.prepare_circuit diff --git a/docs/modules/tnstate.rst b/docs/modules/tnstate.rst new file mode 100644 index 00000000..8b4ac456 --- /dev/null +++ b/docs/modules/tnstate.rst @@ -0,0 +1,61 @@ +TN state evolution +================== + +.. automodule:: pytket.extensions.cutensornet.tnstate + + +Simulation +~~~~~~~~~~ + +.. autofunction:: pytket.extensions.cutensornet.tnstate.simulate + +.. autoenum:: pytket.extensions.cutensornet.tnstate.ContractionAlg() + :members: + +.. autoclass:: pytket.extensions.cutensornet.tnstate.Config() + + .. automethod:: __init__ + +.. autoclass:: pytket.extensions.cutensornet.tnstate.CuTensorNetHandle + + +Classes +~~~~~~~ + +.. autoclass:: pytket.extensions.cutensornet.tnstate.TNState() + + .. automethod:: __init__ + .. automethod:: is_valid + .. automethod:: apply_gate + .. automethod:: apply_scalar + .. automethod:: vdot + .. automethod:: sample + .. automethod:: measure + .. automethod:: postselect + .. automethod:: expectation_value + .. automethod:: get_fidelity + .. automethod:: get_statevector + .. automethod:: get_amplitude + .. automethod:: get_qubits + .. automethod:: get_byte_size + .. automethod:: get_device_id + .. automethod:: update_libhandle + .. automethod:: copy + +.. autoclass:: pytket.extensions.cutensornet.tnstate.TTNxGate() + + .. automethod:: __init__ + +.. autoclass:: pytket.extensions.cutensornet.tnstate.MPSxGate() + + .. automethod:: __init__ + +.. autoclass:: pytket.extensions.cutensornet.tnstate.MPSxMPO() + + .. automethod:: __init__ + + +Miscellaneous +~~~~~~~~~~~~~ + +.. autofunction:: pytket.extensions.cutensornet.tnstate.prepare_circuit_mps diff --git a/pytket/extensions/cutensornet/tnstate/__init__.py b/pytket/extensions/cutensornet/tnstate/__init__.py index 46428237..d85258c3 100644 --- a/pytket/extensions/cutensornet/tnstate/__init__.py +++ b/pytket/extensions/cutensornet/tnstate/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Module for circuit simulation by state evolution. -Approximate tensor network contraction is supported. Both MPS and TreeTN +Approximate tensor network contraction is supported. Both ``MPS`` and ``TTN`` methods are provided. For an example of its use, see ``examples/mps_tutorial.ipynb`` in https://github.com/CQCL/pytket-cutensornet. @@ -21,7 +21,7 @@ from .general import CuTensorNetHandle, Config, TNState from .simulation import ContractionAlg, simulate, prepare_circuit_mps -from .mps import DirectionMPS, MPS +from .mps import DirMPS, MPS from .mps_gate import MPSxGate from .mps_mpo import MPSxMPO diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index bcacb9bf..87ba22de 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -190,10 +190,7 @@ def copy(self) -> Config: class TNState(ABC): - """Abstract class to refer to a Tensor Network state. - - This class does not implement any of the methods it offers. You will need - to use an instance of a child class to call them. + """Class representing a Tensor Network state. """ @abstractmethod diff --git a/pytket/extensions/cutensornet/tnstate/mps.py b/pytket/extensions/cutensornet/tnstate/mps.py index 7a385fab..5ba48b0c 100644 --- a/pytket/extensions/cutensornet/tnstate/mps.py +++ b/pytket/extensions/cutensornet/tnstate/mps.py @@ -37,7 +37,7 @@ from .general import CuTensorNetHandle, Config, TNState, Tensor -class DirectionMPS(Enum): +class DirMPS(Enum): """An enum to refer to relative directions within the MPS.""" LEFT = 0 @@ -53,7 +53,7 @@ class MPS(TNState): and ``tensors[i+1]`` are connected in the MPS via a bond. All of the tensors are rank three, with the dimensions listed in ``.shape`` matching the left, right and physical bonds, in that order. - canonical_form (dict[int, Optional[DirectionMPS]]): A dictionary mapping + canonical_form (dict[int, Optional[DirMPS]]): A dictionary mapping positions to the canonical form direction of the corresponding tensor, or ``None`` if it the tensor is not canonicalised. qubit_position (dict[pytket.circuit.Qubit, int]): A dictionary mapping circuit @@ -233,13 +233,13 @@ def canonicalise(self, l_pos: int, r_pos: int) -> None: self._logger.debug(f"Start canonicalisation... l_pos={l_pos}, r_pos={r_pos}") for pos in range(l_pos): - self.canonicalise_tensor(pos, form=DirectionMPS.LEFT) + self.canonicalise_tensor(pos, form=DirMPS.LEFT) for pos in reversed(range(r_pos + 1, len(self))): - self.canonicalise_tensor(pos, form=DirectionMPS.RIGHT) + self.canonicalise_tensor(pos, form=DirMPS.RIGHT) self._logger.debug(f"Finished canonicalisation.") - def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: + def canonicalise_tensor(self, pos: int, form: DirMPS) -> None: """Canonicalises a tensor from an MPS object. Applies the necessary gauge transformations so that the tensor at @@ -252,7 +252,7 @@ def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: connected to its left bond and physical bond. Similarly for RIGHT. Raises: - ValueError: If ``form`` is not a value in ``DirectionMPS``. + ValueError: If ``form`` is not a value in ``DirMPS``. RuntimeError: If the ``CuTensorNetHandle`` is out of scope. """ if form == self.canonical_form[pos]: @@ -278,7 +278,7 @@ def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: T = self.tensors[pos] # Assign the bond IDs - if form == DirectionMPS.LEFT: + if form == DirMPS.LEFT: next_pos = pos + 1 Tnext = self.tensors[next_pos] T_bonds = "vsp" @@ -286,7 +286,7 @@ def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: R_bonds = "as" Tnext_bonds = "sVP" result_bonds = "aVP" - elif form == DirectionMPS.RIGHT: + elif form == DirMPS.RIGHT: next_pos = pos - 1 Tnext = self.tensors[next_pos] T_bonds = "svp" @@ -295,7 +295,7 @@ def canonicalise_tensor(self, pos: int, form: DirectionMPS) -> None: Tnext_bonds = "VsP" result_bonds = "VaP" else: - raise ValueError("Argument form must be a value in DirectionMPS.") + raise ValueError("Argument form must be a value in DirMPS.") # Apply QR decomposition self._logger.debug(f"QR decompose a {T.nbytes / 2**20} MiB tensor.") diff --git a/pytket/extensions/cutensornet/tnstate/mps_mpo.py b/pytket/extensions/cutensornet/tnstate/mps_mpo.py index 7f4112c5..c6253974 100644 --- a/pytket/extensions/cutensornet/tnstate/mps_mpo.py +++ b/pytket/extensions/cutensornet/tnstate/mps_mpo.py @@ -31,7 +31,7 @@ from pytket.circuit import Op, Qubit from .general import CuTensorNetHandle, Tensor, Config from .mps import ( - DirectionMPS, + DirMPS, MPS, ) from .mps_gate import MPSxGate @@ -279,7 +279,7 @@ def _get_physical_bond(self, position: int) -> int: else: return self._new_bond_id() - def _get_column_bonds(self, position: int, direction: DirectionMPS) -> list[int]: + def _get_column_bonds(self, position: int, direction: DirMPS) -> list[int]: """Returns the unique identifier of all the left (right) virtual bonds of MPO tensors at ``position`` if ``direction`` is ``LEFT`` (``RIGHT``). @@ -288,17 +288,17 @@ def _get_column_bonds(self, position: int, direction: DirectionMPS) -> list[int] Raises: RuntimeError: If ``position`` is out of bounds. - ValueError: If ``direction`` is not a value in ``DirectionMPS``. + ValueError: If ``direction`` is not a value in ``DirMPS``. """ if position < 0 or position >= len(self): raise RuntimeError(f"Position {position} is out of bounds.") - if direction == DirectionMPS.LEFT: + if direction == DirMPS.LEFT: index = 1 # By convention, left bond at index 1 - elif direction == DirectionMPS.RIGHT: + elif direction == DirMPS.RIGHT: index = 2 # By convention, right bond at index 2 else: - raise ValueError("Argument form must be a value in DirectionMPS.") + raise ValueError("Argument form must be a value in DirMPS.") return [b_ids[index] for b_ids in self._bond_ids[position]] @@ -314,9 +314,9 @@ def _flush(self) -> None: l_cached_tensors: list[Tensor] = [] r_cached_tensors: list[Tensor] = [] - def update_sweep_cache(pos: int, direction: DirectionMPS) -> None: + def update_sweep_cache(pos: int, direction: DirMPS) -> None: """Given a position in the MPS and a sweeping direction (see - ``DirectionMPS``), calculate the tensor of the partial contraction + ``DirMPS``), calculate the tensor of the partial contraction of all MPS-MPO-vMPS* columns from ``pos`` towards ``direction``. Update the cache accordingly. Applies canonicalisation on the vMPS tensor before contracting. @@ -324,10 +324,10 @@ def update_sweep_cache(pos: int, direction: DirectionMPS) -> None: self._logger.debug("Updating the sweep cache...") # Canonicalise the tensor at ``pos`` - if direction == DirectionMPS.LEFT: - self._aux_mps.canonicalise_tensor(pos, form=DirectionMPS.RIGHT) - elif direction == DirectionMPS.RIGHT: - self._aux_mps.canonicalise_tensor(pos, form=DirectionMPS.LEFT) + if direction == DirMPS.LEFT: + self._aux_mps.canonicalise_tensor(pos, form=DirMPS.RIGHT) + elif direction == DirMPS.RIGHT: + self._aux_mps.canonicalise_tensor(pos, form=DirMPS.LEFT) # Glossary of bond IDs # p -> the physical bond of the MPS tensor @@ -361,26 +361,26 @@ def update_sweep_cache(pos: int, direction: DirectionMPS) -> None: interleaved_rep.append(mpo_bonds) # Also contract the previous (cached) tensor during the sweep - if direction == DirectionMPS.LEFT: + if direction == DirMPS.LEFT: if pos != len(self) - 1: # Otherwise, there is nothing cached yet interleaved_rep.append(r_cached_tensors[-1]) - r_cached_bonds = self._get_column_bonds(pos + 1, DirectionMPS.LEFT) + r_cached_bonds = self._get_column_bonds(pos + 1, DirMPS.LEFT) interleaved_rep.append(["r", "R"] + r_cached_bonds) - elif direction == DirectionMPS.RIGHT: + elif direction == DirMPS.RIGHT: if pos != 0: # Otherwise, there is nothing cached yet interleaved_rep.append(l_cached_tensors[-1]) - l_cached_bonds = self._get_column_bonds(pos - 1, DirectionMPS.RIGHT) + l_cached_bonds = self._get_column_bonds(pos - 1, DirMPS.RIGHT) interleaved_rep.append(["l", "L"] + l_cached_bonds) # Figure out the ID of the bonds of the contracted tensor - if direction == DirectionMPS.LEFT: + if direction == DirMPS.LEFT: # Take the left bond of each of the MPO tensors - result_bonds = self._get_column_bonds(pos, DirectionMPS.LEFT) + result_bonds = self._get_column_bonds(pos, DirMPS.LEFT) # Take the left virtual bond of both of the MPS interleaved_rep.append(["l", "L"] + result_bonds) - elif direction == DirectionMPS.RIGHT: + elif direction == DirMPS.RIGHT: # Take the right bond of each of the MPO tensors - result_bonds = self._get_column_bonds(pos, DirectionMPS.RIGHT) + result_bonds = self._get_column_bonds(pos, DirMPS.RIGHT) # Take the right virtual bond of both of the MPS interleaved_rep.append(["r", "R"] + result_bonds) @@ -390,9 +390,9 @@ def update_sweep_cache(pos: int, direction: DirectionMPS) -> None: options={"handle": self._lib.handle, "device_id": self._lib.device_id}, optimize={"samples": 1}, ) - if direction == DirectionMPS.LEFT: + if direction == DirMPS.LEFT: r_cached_tensors.append(T) - elif direction == DirectionMPS.RIGHT: + elif direction == DirMPS.RIGHT: l_cached_tensors.append(T) self._logger.debug("Completed update of the sweep cache.") @@ -434,12 +434,12 @@ def update_variational_tensor( if left_tensor is not None: interleaved_rep.append(left_tensor) - left_tensor_bonds = self._get_column_bonds(pos - 1, DirectionMPS.RIGHT) + left_tensor_bonds = self._get_column_bonds(pos - 1, DirMPS.RIGHT) interleaved_rep.append(["l", "L"] + left_tensor_bonds) result_bonds[0] = "L" if right_tensor is not None: interleaved_rep.append(right_tensor) - right_tensor_bonds = self._get_column_bonds(pos + 1, DirectionMPS.LEFT) + right_tensor_bonds = self._get_column_bonds(pos + 1, DirMPS.LEFT) interleaved_rep.append(["r", "R"] + right_tensor_bonds) result_bonds[1] = "R" @@ -481,22 +481,22 @@ def update_variational_tensor( # Begin by doing a sweep towards the left that does not update # the variational tensors, but simply loads up the ``r_cached_tensors`` for pos in reversed(range(1, len(self))): - update_sweep_cache(pos, direction=DirectionMPS.LEFT) + update_sweep_cache(pos, direction=DirMPS.LEFT) prev_fidelity = -1.0 # Dummy value sweep_fidelity = 0.0 # Dummy value # Repeat sweeps until the fidelity converges - sweep_direction = DirectionMPS.RIGHT + sweep_direction = DirMPS.RIGHT while not np.isclose(prev_fidelity, sweep_fidelity, atol=self._cfg.optim_delta): self._logger.info(f"Doing another optimisation sweep...") prev_fidelity = sweep_fidelity - if sweep_direction == DirectionMPS.RIGHT: + if sweep_direction == DirMPS.RIGHT: sweep_fidelity = update_variational_tensor( pos=0, left_tensor=None, right_tensor=r_cached_tensors.pop() ) - update_sweep_cache(pos=0, direction=DirectionMPS.RIGHT) + update_sweep_cache(pos=0, direction=DirMPS.RIGHT) for pos in range(1, len(self) - 1): sweep_fidelity = update_variational_tensor( @@ -504,19 +504,19 @@ def update_variational_tensor( left_tensor=l_cached_tensors[-1], right_tensor=r_cached_tensors.pop(), ) - update_sweep_cache(pos, direction=DirectionMPS.RIGHT) + update_sweep_cache(pos, direction=DirMPS.RIGHT) # The last variational tensor is not updated; # it'll be the first in the next sweep - sweep_direction = DirectionMPS.LEFT + sweep_direction = DirMPS.LEFT - elif sweep_direction == DirectionMPS.LEFT: + elif sweep_direction == DirMPS.LEFT: sweep_fidelity = update_variational_tensor( pos=len(self) - 1, left_tensor=l_cached_tensors.pop(), right_tensor=None, ) - update_sweep_cache(pos=len(self) - 1, direction=DirectionMPS.LEFT) + update_sweep_cache(pos=len(self) - 1, direction=DirMPS.LEFT) for pos in reversed(range(1, len(self) - 1)): sweep_fidelity = update_variational_tensor( @@ -524,11 +524,11 @@ def update_variational_tensor( left_tensor=l_cached_tensors.pop(), right_tensor=r_cached_tensors[-1], ) - update_sweep_cache(pos, direction=DirectionMPS.LEFT) + update_sweep_cache(pos, direction=DirMPS.LEFT) # The last variational tensor is not updated; # it'll be the first in the next sweep - sweep_direction = DirectionMPS.RIGHT + sweep_direction = DirMPS.RIGHT self._logger.info( "Optimisation sweep completed. " diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py index aba8d2bd..e8cff13b 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/tnstate/simulation.py @@ -39,9 +39,9 @@ class ContractionAlg(Enum): information about the algorithm. """ - MPSxGate = 0 - MPSxMPO = 1 - TTNxGate = 2 + TTNxGate = 0 + MPSxGate = 1 + MPSxMPO = 2 def simulate( @@ -54,8 +54,8 @@ def simulate( Note: A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` - statement. The device where the MPS is stored will match the one specified - by the library handle. + statement. The device where the ``TNState`` is stored will match the one + specified by the library handle. The input ``circuit`` must be composed of one-qubit and two-qubit gates only. Any gateset supported by ``pytket`` can be used. @@ -119,7 +119,7 @@ def simulate( def prepare_circuit_mps(circuit: Circuit) -> tuple[Circuit, dict[Qubit, Qubit]]: - """Prepares a circuit in a specific, ``MPS``-friendly, manner. + """Transpiles the circuit for it to be ``MPS``-friendly. Returns an equivalent circuit with the appropriate structure to be simulated by an ``MPS`` algorithm. From df9db66165254fe232ba75bcde71e60d888c916a Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 2 Nov 2023 13:06:24 +0000 Subject: [PATCH 48/83] Updated the _get_sorted_gates algorithm so that it's compatible with TTN distance metric --- .../extensions/cutensornet/tnstate/general.py | 3 +- .../cutensornet/tnstate/simulation.py | 151 ++++++++++-------- pytket/extensions/cutensornet/tnstate/ttn.py | 9 +- 3 files changed, 92 insertions(+), 71 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index 87ba22de..c35c5b21 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -190,8 +190,7 @@ def copy(self) -> Config: class TNState(ABC): - """Class representing a Tensor Network state. - """ + """Class representing a Tensor Network state.""" @abstractmethod def is_valid(self) -> bool: diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py index e8cff13b..ebbf6e21 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/tnstate/simulation.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from typing import Optional from enum import Enum from random import choice # type: ignore @@ -82,7 +83,7 @@ def simulate( circuit.qubits, config, ) - sorted_gates = _get_sorted_gates(circuit) + sorted_gates = _get_sorted_gates(circuit, algorithm) elif algorithm == ContractionAlg.MPSxMPO: tnstate = MPSxMPO( # type: ignore @@ -90,15 +91,16 @@ def simulate( circuit.qubits, config, ) - sorted_gates = _get_sorted_gates(circuit) + sorted_gates = _get_sorted_gates(circuit, algorithm) elif algorithm == ContractionAlg.TTNxGate: + qubit_partition = _get_qubit_partition(circuit, config.leaf_size) tnstate = TTNxGate( # type: ignore libhandle, - _get_qubit_partition(circuit, config.leaf_size), + qubit_partition, config, ) - sorted_gates = circuit.get_commands() # TODO: change! + sorted_gates = _get_sorted_gates(circuit, algorithm, qubit_partition) logger.info("Running simulation...") # Apply the gates @@ -222,92 +224,111 @@ def _get_qubit_partition( return qubit_partition -def _get_sorted_gates(circuit: Circuit) -> list[Command]: - """Sorts the list of gates, placing 2-qubit gates close to each other first. +def _get_sorted_gates( + circuit: Circuit, + algorithm: ContractionAlg, + qubit_partition: Optional[dict[int, list[Qubit]]] = None, +) -> list[Command]: + """Sorts the list of gates so that there's less canonicalisation during simulation. Returns an equivalent list of commands fixing the order of parallel gates so that - 2-qubit gates that are close to each other first. This reduces the overhead of - canonicalisation of the MPS, since we try to apply as many gates as we can on one - end of the MPS before we go to the other end. + 2-qubit gates that are close together are applied one after the other. This reduces + the overhead of canonicalisation during simulation. Args: circuit: The original circuit. + algorithm: The simulation algorithm that will be used on this circuit. + qubit_partition: For TTN simulation algorithms only. A partition of the + qubits in the circuit into disjoint groups, describing the hierarchical + structure of the TTN. Returns: - The same gates, ordered in a beneficial way. + The same gates, ordered in a beneficial way for the given algorithm. """ - all_gates = circuit.get_commands() sorted_gates = [] - # Keep track of the qubit at the center of the canonical form; start arbitrarily - current_qubit = circuit.qubits[0] # Entries from `all_gates` that are not yet in `sorted_gates` remaining = set(range(len(all_gates))) + # Do some precomputation depending on the algorithm + if algorithm in [ContractionAlg.TTNxGate]: + if qubit_partition is None: + raise RuntimeError("You must provide a qubit partition!") + + leaf_of_qubit: dict[Qubit, int] = dict() + for leaf, qubits in qubit_partition.items(): + for q in qubits: + leaf_of_qubit[q] = leaf + + elif algorithm in [ContractionAlg.MPSxGate, ContractionAlg.MPSxMPO]: + idx_of_qubit = {q: i for i, q in enumerate(circuit.qubits)} + + else: + raise RuntimeError(f"Sorting gates for {algorithm} not supported.") + # Create the list of indices of gates acting on each qubit gate_indices: dict[Qubit, list[int]] = defaultdict(list) for i, g in enumerate(all_gates): for q in g.qubits: gate_indices[q].append(i) - # Apply all 1-qubit gates at the beginning of the circuit + # Schedule all 1-qubit gates at the beginning of the circuit for q, indices in gate_indices.items(): while indices and len(all_gates[indices[0]].qubits) == 1: i = indices.pop(0) sorted_gates.append(all_gates[i]) remaining.remove(i) + # Decide which 2-qubit gate to apply next + last_qubits = [circuit.qubits[0], circuit.qubits[0]] # Arbitrary choice at start while remaining: - q_index = circuit.qubits.index(current_qubit) - # Find distance from q_index to first qubit with an applicable 2-qubit gate - left_distance = None - prev_q = current_qubit - for i, q in enumerate(reversed(circuit.qubits[:q_index])): - if ( - gate_indices[prev_q] - and gate_indices[q] - and gate_indices[prev_q][0] == gate_indices[q][0] - ): - left_distance = i - break - prev_q = q - right_distance = None - prev_q = current_qubit - for i, q in enumerate(circuit.qubits[q_index + 1 :]): - if ( - gate_indices[prev_q] - and gate_indices[q] - and gate_indices[prev_q][0] == gate_indices[q][0] - ): - right_distance = i - break - prev_q = q - # Choose the shortest distance - if left_distance is None and right_distance is None: - raise RuntimeError( - "Some two-qubit gate in the circuit is not acting between", - "nearest neighbour qubits. Consider using prepare_circuit_mps().", - ) - elif left_distance is None: - assert right_distance is not None - current_qubit = circuit.qubits[q_index + right_distance] - elif right_distance is None: - current_qubit = circuit.qubits[q_index - left_distance] - elif left_distance < right_distance: - current_qubit = circuit.qubits[q_index - left_distance] - elif left_distance > right_distance: - current_qubit = circuit.qubits[q_index + right_distance] - else: - current_qubit = circuit.qubits[ - q_index + choice([-left_distance, right_distance]) - ] - # Apply the gate - i = gate_indices[current_qubit][0] - next_gate = all_gates[i] - sorted_gates.append(next_gate) - remaining.remove(i) - # Apply all 1-qubit gates after this gate - for q in next_gate.qubits: - gate_indices[q].pop(0) # Remove the 2-qubit gate `next_gate` + # Gather all gates that have nothing in front of them at one of its qubits + reachable_gates = [gates[0] for gates in gate_indices.values() if gates] + # Among them, find those that are available in both qubits + available_gates: list[int] = [] + for gate_idx in reachable_gates: + gate_qubits = all_gates[gate_idx].qubits + assert len(gate_qubits) == 2 # Sanity check: all of them are 2-qubit gates + # If the first gate in both qubits coincides, then this gate is available + if gate_indices[gate_qubits[0]][0] == gate_indices[gate_qubits[1]][0]: + assert gate_indices[gate_qubits[0]][0] == gate_idx + available_gates.append(gate_idx) + # Sanity check: there is at least one available 2-qubit gate + assert available_gates + + # Find distance from last_qubits to current applicable 2-qubit gates + gate_distance: dict[int, int] = dict() + for gate_idx in available_gates: + gate_qubits = all_gates[gate_idx].qubits + + # Criterion for distance depends on the simulation algorithm + if algorithm in [ContractionAlg.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]: + 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]]), + abs(idx_of_qubit[last_qubits[1]] - idx_of_qubit[gate_qubits[0]]), + abs(idx_of_qubit[last_qubits[1]] - idx_of_qubit[gate_qubits[1]]), + ) + else: + raise RuntimeError(f"Sorting gates for {algorithm} not supported.") + + # Choose the gate with shortest distance + chosen_gate_idx = min(gate_distance, key=gate_distance.get) # type: ignore + chosen_gate = all_gates[chosen_gate_idx] + + # Schedule the gate + last_qubits = chosen_gate.qubits + sorted_gates.append(chosen_gate) + remaining.remove(chosen_gate_idx) + # Schedule all 1-qubit gates after this gate + for q in last_qubits: + gate_indices[q].pop(0) # Remove the 2-qubit `chosen_gate` indices = gate_indices[q] while indices and len(all_gates[indices[0]].qubits) == 1: i = indices.pop(0) diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index 3dd89eee..8a17f441 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -103,14 +103,15 @@ def __init__( by the library handle. The current implementation requires the keys of ``qubit_partition`` to be - integers from ``0`` to ``2^l - 1`` for some ``l``. The cost of applying - gates between qubits on ``qubit_partition[i]`` and ``qubit_partition[j]`` - scales exponentially on ``|i-j|``. + integers from ``0`` to ``2^l - 1`` for some ``l``. Args: libhandle: The cuTensorNet library handle that will be used to carry out tensor operations on the TTN. - qubits: A partition of the qubits in the circuit into disjoint groups. + 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``. config: The object describing the configuration for simulation. Raises: From 405f951625dca8a44994174cbfc1db1e8d0c1241 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Thu, 2 Nov 2023 06:17:38 -0700 Subject: [PATCH 49/83] Fidelity slightly improved in one of the tests --- tests/test_tnstate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_tnstate.py b/tests/test_tnstate.py index 4316d7fb..6f85a473 100644 --- a/tests/test_tnstate.py +++ b/tests/test_tnstate.py @@ -464,7 +464,7 @@ def test_circ_approx_explicit_mps(circuit: Circuit) -> None: # Check for MPSxMPO mps_mpo = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, cfg) - assert np.isclose(mps_mpo.get_fidelity(), 0.04, atol=1e-2) + 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) From 5d4edf2e7b57359022d4220561b6618c690b205a Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 7 Nov 2023 12:18:12 +0000 Subject: [PATCH 50/83] 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} From 9d0a2845dd03cef360fd9254bf59e2957d9fdfa5 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 7 Nov 2023 04:23:33 -0800 Subject: [PATCH 51/83] Removed unused imports --- pytket/extensions/cutensornet/tnstate/general.py | 1 - pytket/extensions/cutensornet/tnstate/simulation.py | 1 - 2 files changed, 2 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index 398852b5..2c2608e4 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -28,7 +28,6 @@ warnings.warn("local settings failed to import cupy", ImportWarning) try: import cuquantum.cutensornet as cutn # type: ignore - from cuquantum.cutensornet import tensor # type: ignore except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py index 66853e39..671399ee 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/tnstate/simulation.py @@ -14,7 +14,6 @@ from typing import Optional from enum import Enum -from random import choice # type: ignore from collections import defaultdict # type: ignore import numpy as np # type: ignore From 9308bfe2a17d93b8cfa57252e82f602b1919f291 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 14 Nov 2023 10:54:11 +0000 Subject: [PATCH 52/83] Changes suggested by Iakov --- pytket/extensions/cutensornet/tnstate/mps_mpo.py | 4 ++-- pytket/extensions/cutensornet/tnstate/ttn.py | 8 ++++---- pytket/extensions/cutensornet/tnstate/ttn_gate.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/mps_mpo.py b/pytket/extensions/cutensornet/tnstate/mps_mpo.py index c6253974..915db486 100644 --- a/pytket/extensions/cutensornet/tnstate/mps_mpo.py +++ b/pytket/extensions/cutensornet/tnstate/mps_mpo.py @@ -388,7 +388,7 @@ def update_sweep_cache(pos: int, direction: DirMPS) -> None: T = cq.contract( *interleaved_rep, options={"handle": self._lib.handle, "device_id": self._lib.device_id}, - optimize={"samples": 1}, + optimize={"samples": 0}, ) if direction == DirMPS.LEFT: r_cached_tensors.append(T) @@ -450,7 +450,7 @@ def update_variational_tensor( F = cq.contract( *interleaved_rep, options={"handle": self._lib.handle, "device_id": self._lib.device_id}, - optimize={"samples": 1}, + optimize={"samples": 0}, ) # Get the fidelity diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index d56dd4d5..efc142ce 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -569,7 +569,7 @@ def vdot(self, other: TTN) -> complex: # type: ignore result = cq.contract( *interleaved_rep, options={"handle": self._lib.handle, "device_id": self._lib.device_id}, - optimize={"samples": 1}, # There is little to no optimisation to be done + optimize={"samples": 0}, # There is little to no optimisation to be done ) self._logger.debug(f"Result from vdot={result}") @@ -672,7 +672,7 @@ def get_statevector(self) -> np.ndarray: result_tensor = cq.contract( *interleaved_rep, options={"handle": self._lib.handle, "device_id": self._lib.device_id}, - optimize={"samples": 1}, # There is little to no optimisation to be done + optimize={"samples": 0}, # There is little to no optimisation to be done ) # Convert to numpy vector and flatten @@ -712,7 +712,7 @@ def get_amplitude(self, state: int) -> complex: result = cq.contract( *interleaved_rep, options={"handle": self._lib.handle, "device_id": self._lib.device_id}, - optimize={"samples": 1}, # There is little to no optimisation to be done + optimize={"samples": 0}, # There is little to no optimisation to be done ) self._logger.debug(f"Amplitude of state {state} is {result}.") @@ -773,7 +773,7 @@ def get_dimension(self, path: RootPath, direction: DirTTN) -> int: direction: The direction of the bond. Returns: - The dimension of the bond between the node and its parent. + The dimension of the specified bond. Raises: ValueError: If ``path`` is not in the TTN. diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index c16a3bb6..cd226c06 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -87,7 +87,7 @@ def _apply_1q_gate(self, qubit: Qubit, gate: Op) -> TTNxGate: def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: """Applies the 2-qubit gate to the TTN. - Truncation is automatically applied according to the paremeters + Truncation is automatically applied according to the parameters in the ``Config`` object passed to this ``TTN``. The TTN is converted to canonical form before truncating. @@ -130,7 +130,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: node_bonds = aux_bonds.copy() node_bonds[bond_q0] = "i0" node_bonds[bond_q1] = "i1" - result_bonds = aux_bonds.copy() + result_bonds = aux_bonds result_bonds[bond_q0] = "o0" result_bonds[bond_q1] = "o1" @@ -237,7 +237,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: aux_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] node_bonds = aux_bonds.copy() node_bonds[bond] = "a" - result_bonds = aux_bonds.copy() + result_bonds = aux_bonds result_bonds[bond] = "b" result_bonds[-1] = "f" From 193aed9e6cb86df66d2e3a573d24be847b1db445 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 15 Nov 2023 13:08:44 +0000 Subject: [PATCH 53/83] Jupyter notebook updated --- examples/mps_tutorial.ipynb | 2248 +++++++++++++++++------------------ 1 file changed, 1117 insertions(+), 1131 deletions(-) diff --git a/examples/mps_tutorial.ipynb b/examples/mps_tutorial.ipynb index 237504e7..c996eb5f 100644 --- a/examples/mps_tutorial.ipynb +++ b/examples/mps_tutorial.ipynb @@ -13,12 +13,12 @@ "from pytket import Circuit\n", "from pytket.circuit.display import render_circuit_jupyter\n", "\n", - "from pytket.extensions.cutensornet.mps import (\n", + "from pytket.extensions.cutensornet.tnstate import (\n", " CuTensorNetHandle,\n", - " ConfigMPS,\n", - " ContractionAlg,\n", + " Config,\n", + " SimulationAlgorithm,\n", " simulate, \n", - " prepare_circuit\n", + " prepare_circuit_mps\n", ")" ] }, @@ -48,7 +48,7 @@ "\n", "In essence, whenever we want to apply a gate to certain qubit we will connect a tensor (matrix) representing the gate to the corresponding physical bond and *contract* the network back to an MPS form (tensor contraction is a generalisation of matrix multiplication to multidimensional arrays). Whenever a two-qubit gate is applied, the entanglement information after contraction will be kept in the degrees of freedom of the virtual bonds. As such, the dimension of the virtual bonds will generally increase exponentially as we apply entangling gates, leading to large memory footprints of the tensors and, consequently, long runtime for tensor contraction. We provide functionalities to limit the growth of the dimension of the virtual bonds, keeping resource consumption in check. Read the *Approximate simulation* section on this notebook to learn more.\n", "\n", - "**NOTE**: MPS methods can only be applied to circuits that only contain gates that act between nearest-neighbours in a line. If your circuit does not satisfy this constraint, you can use the `prepare_circuit` function (see the *Preparing the circuit* section); this will add multiple `SWAP` gates to the circuit that *need* to be simulated explicitly within the MPS, increasing the resources required considerably. In the future, we will support other tensor network state approaches that do not suffer so drastically from this restrictive connectivity.\n", + "**NOTE**: MPS methods can only be applied to circuits that only contain gates that act between nearest-neighbours in a line. If your circuit does not satisfy this constraint, you can use the `prepare_circuit_mps` function (see the *Preparing the circuit* section); this will add multiple `SWAP` gates to the circuit that *need* to be simulated explicitly within the MPS, increasing the resources required considerably. In the future, we will support other tensor network state approaches that do not suffer so drastically from this restrictive connectivity.\n", "\n", "**References**: To read more about MPS we recommend the following papers.\n", "* For an introduction to MPS and its canonical form: https://arxiv.org/abs/1901.05824.\n", @@ -98,7 +98,7 @@ "\n", "\n", "\n", - " <div id="circuit-display-vue-container-6f3d27c2-9192-4cb0-a7ab-af175d1b97d3" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-9a33e5e8-cd1f-4fc5-b70a-72d2ba2cbc6e" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["q", [0]], ["q", [1]]], "op": {"type": "CZ"}}, {"args": [["q", [2]]], "op": {"type": "H"}}, {"args": [["q", [3]], ["q", [4]]], "op": {"type": "CX"}}, {"args": [["q", [0]]], "op": {"params": ["0.2"], "type": "Ry"}}, {"args": [["q", [2]], ["q", [1]]], "op": {"params": ["0.3", "0.5", "0.7"], "type": "TK2"}}, {"args": [["q", [4]], ["q", [3]]], "op": {"params": ["0.1"], "type": "ZZPhase"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["q", [0]], ["q", [0]]], [["q", [1]], ["q", [1]]], [["q", [2]], ["q", [2]]], [["q", [3]], ["q", [3]]], [["q", [4]], ["q", [4]]]], "phase": "0.0", "qubits": [["q", [0]], ["q", [1]], ["q", [2]], ["q", [3]], ["q", [4]]]}</div>\n", " </div>\n", @@ -108,7 +108,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "6f3d27c2-9192-4cb0-a7ab-af175d1b97d3";\n", + " const circuitRendererUid = "9a33e5e8-cd1f-4fc5-b70a-72d2ba2cbc6e";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -184,7 +184,7 @@ "outputs": [], "source": [ "with CuTensorNetHandle() as libhandle:\n", - " my_mps = simulate(libhandle, my_circ, ContractionAlg.MPSxGate, ConfigMPS())" + " my_mps = simulate(libhandle, my_circ, SimulationAlgorithm.MPSxGate, Config())" ] }, { @@ -217,7 +217,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "(0.03968884089773739+0.05462700305610267j)\n" + "(0.03968884089773739+0.054627003056102665j)\n" ] } ], @@ -286,7 +286,7 @@ "outputs": [ { "data": { - "image/png": "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wYIFuuOOO1RcXKzjx4+7nZU5cuSIIiMjK5yfv7+//P39a7tsAADgBUw/I3O+0tJSFRUVqWfPnvL19dX69etdw/bs2aPMzEzFx8ebWCEAAPAWpp6RmT17tgYNGqRWrVrpxIkTeuutt5SSkqJPP/1Udrtd99xzj2bOnKlmzZopJCRE999/v+Lj4yt9xxIAAKjfTA0yubm5Gjt2rLKzs2W329WtWzd9+umnuummmyRJ8+fPV6NGjTRixAgVFRVp4MCBeuWVV8wsGQAAeBGve45MTeM5MgAAWI/lniMDAABQVQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWR4FmaysLB08eND1fdOmTZo+fboWL15cY4UBAABcjEdB5q677tKGDRskSTk5Obrpppu0adMmPfzww3riiSdqtEAAAICKeBRkduzYoV69ekmS/vnPf6pLly765ptv9OabbyopKakm6wMAAKiQR0Hm9OnT8vf3lyR9/vnn+v3vfy9J6tixo7Kzs2uuOgAAgAvwKMhcfvnleu211/TVV19p3bp1SkxMlCQdPnxY4eHhNVogAABARTwKMs8++6wWLVqkfv36aeTIkerevbsk6aOPPnJdcgIAAKhtNsMwDE8mLCkpUV5ensLCwlxt+/fvV9OmTdWyZcsaK7C68vLyZLfb5XA4FBISYnY5AACgEir7++3xc2QMw1B6eroWLVqkEydOSJL8/PzUtGlTT2cJAABQJY09mejAgQNKTExUZmamioqKdNNNNyk4OFjPPvusioqK9Nprr9V0nQAAAGV4dEZm2rRpuuqqq3Ts2DEFBAS42ocNG6b169fXWHEAAAAX4tEZma+++krffPON/Pz83Nrj4uJ06NChGikMAADgYjw6I1NaWqqSkpIy7QcPHlRwcHC1iwIAAKgMj4LMgAED9OKLL7q+22w25efna86cORo8eHBN1QYAAHBBHt1+ffDgQQ0cOFCGYWjv3r266qqrtHfvXjVv3lxffvklt18DAIBqqezvt8fPkfntt9+0cuVKbd++Xfn5+erRo4dGjRrl1vnXGxBkAACwnsr+fnvU2VeSGjdurNGjR3s6OQAAQLVVOsh89NFHlZ7pmZdIAgAA1KZKB5lbb721UuPZbLZy72gCAACoaZUOMqWlpbVZBwAAQJV5/K4lAAAAs3kcZNavX69bbrlFl156qS699FLdcsst+vzzz2uyNgAAgAvyKMi88sorSkxMVHBwsKZNm6Zp06YpJCREgwcP1ssvv1zTNQIAAJTLo+fIxMTEaNasWZo6dapb+8svv6y//vWvXvW+JZ4jAwCA9VT299ujMzLHjx9XYmJimfYBAwbI4XB4MksAAIAq8yjI/P73v1dycnKZ9g8//FC33HJLtYsCAACoDI+e7Nu5c2c9/fTTSklJUXx8vCTp22+/1ddff60HH3xQCxcudI37wAMP1EylAAAA5/Goj0ybNm0qN3ObTf/973+rXFRNoo8MAADWU6vvWsrIyPC4MAAAgJrCA/EAAIBleXRGxjAMvffee9qwYYNyc3PLvL5g1apVNVIcAADAhXgUZKZPn65FixbphhtuUEREhGw2W03XBQAAcFEeBZn//d//1apVqzR48OBqLXzevHlatWqVfvzxRwUEBOh3v/udnn32WXXo0ME1TmFhoR588EGtXLlSRUVFGjhwoF555RVFRERUa9kAAMD6POojY7fb1bZt22ovPDU1VVOmTNG3336rdevW6fTp0xowYIAKCgpc48yYMUOrV6/Wu+++q9TUVB0+fFjDhw+v9rIBAID1eXT79RtvvKG1a9fq9ddfV0BAQI0V8/PPP6tly5ZKTU1V37595XA41KJFC7311lv6wx/+IEn68ccf1alTJ6WlpalPnz4XnSe3XwMAYD21evv17bffrrffflstW7ZUXFycfH193YZv3brVk9m6Xm/QrFkzSVJ6erpOnz6thIQE1zgdO3ZUq1atKgwyRUVFKioqcn3Py8vzqBYAAOD9PAoy48aNU3p6ukaPHl1jnX1LS0s1ffp0XXPNNerSpYskKScnR35+fgoNDXUbNyIiQjk5OeXOZ968eZo7d2616wEAAN7PoyCzZs0affrpp7r22mtrrJApU6Zox44d+ve//12t+cye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lRr3fC+88ILRvn17o7i4uNzhrVu3NubPn3/R+dx8883Ggw8+6Pp+/fXXG9OmTXMb58knnzQGDBjg1paVlWVIMvbs2WOkp6cbkoz9+/dXqnYANYMzMgAu6IYbbtC2bdu0bds2bdq0SQMHDtSgQYN04MAB1zjvvPOOrrnmGkVGRiooKEiPPPKIMjMzXcNnzpypP/7xj0pISNAzzzyjffv2Vbi8qox722236dSpU2rbtq0mTpyo5ORk/fbbbxdcn5KSEj355JPq2rWrmjVrpqCgIH366adu9Zbn+++/14YNGxQUFOT6dOzYUZK0b98+de/eXf3791fXrl112223acmSJTp27NgF5wmg+ggyAC4oMDBQ7dq1U7t27XT11Vdr6dKlKigo0JIlSyRJaWlpGjVqlAYPHqyPP/5Y3333nR5++GEVFxe75vH4449r586duvnmm/XFF1+oc+fOSk5OLnd5VRk3NjZWe/bs0SuvvKKAgADdd9996tu3r1v/nPM9//zzWrBggR566CFt2LBB27Zt08CBA93qLU9+fr6GDBniCnVnPnv37lXfvn3l4+OjdevW6ZNPPlHnzp310ksvqUOHDsrIyLjYJgZQDQQZAFVis9nUqFEjnTp1SpL0zTffqHXr1nr44Yd11VVX6bLLLnM7W3NG+/btNWPGDH322WcaPny4li1bVuEyqjJuQECAhgwZooULFyolJUVpaWn64YcfJEl+fn4qKSlxG//rr7/W0KFDNXr0aHXv3l1t27Ytc/t3edP16NFDO3fuVFxcnCvYnfkEBga6ts0111yjuXPn6rvvvpOfn1+FIQxAzSDIALigoqIi5eTkKCcnR7t379b999/vOjshSZdddpkyMzO1cuVK7du3TwsXLnT78T516pSmTp2qlJQUHThwQF9//bU2b96sTp06lVlWVcaVnHc8/eMf/9COHTv03//+VytWrFBAQIBat24tyfkcmS+//FKHDh1y3V102WWXad26dfrmm2+0e/du/b//9/905MgRt/nGxcVp48aN2r9/v3755ReVlpZqypQpOnr0qEaOHKnNmzdr3759+vTTTzVhwgSVlJRo48aN+utf/6otW7YoMzNTq1at0s8//1xh7QBqiNmddAB4r3HjxhmSXJ/g4GDj6quvNt577z238f785z8b4eHhRlBQkHHHHXcY8+fPd3XgLSoqMu68804jNjbW8PPzM6Kjo42pU6cap06dMgzDvbPvxcY9X3JystG7d28jJCTECAwMNPr06WN8/vnnruFpaWlGt27dDH9/f+PMP3e//vqrMXToUCMoKMho2bKl8cgjjxhjx441hg4d6ppuz549Rp8+fYyAgABDkpGRkWEYhmH85z//MYYNG2aEhoYaAQEBRseOHY3p06cbpaWlxq5du4yBAwcaLVq0MPz9/Y327dsbL730Ug3sBQAXYjMM7g0EAADWxKUlAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWQQZAABgWf8frP1e4F+3sjkAAAAASUVORK5CYII=", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjMAAAGwCAYAAABcnuQpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA7WklEQVR4nO3deXQUVf7+8aeJSWdvIJqNRAibiggKCMKI4CghoIgyPxcUBHFXxIAzMAzyBRwFZQ6CiDoiI+jgiHMU3GVglEVlByPrQdQAAROjEDshkADJ/f2RSUOTQJLOUl3J+3VOH+lbS39uVUE/Vt2qdhhjjAAAAGyqkdUFAAAAVAdhBgAA2BphBgAA2BphBgAA2BphBgAA2BphBgAA2BphBgAA2Np5VhdQ24qLi/XTTz8pIiJCDofD6nIAAEAlGGOUl5en+Ph4NWp07nMv9T7M/PTTT0pMTLS6DAAA4IOMjAwlJCScc556H2YiIiIklWyMyMhIi6sBAACVkZubq8TERM/3+LnU+zBTemkpMjKSMAMAgM1UZogIA4ABAICtEWYAAICtEWYAAICt1fsxMwAA/1ZUVKQTJ05YXQbqWGBgoAICAmpkXYQZAIAljDHKysrSb7/9ZnUpsEjjxo0VGxtb7efAEWYAAJYoDTLR0dEKDQ3lwaYNiDFGR48eVXZ2tiQpLi6uWusjzAAA6lxRUZEnyERFRVldDiwQEhIiScrOzlZ0dHS1LjkxABgAUOdKx8iEhoZaXAmsVLr/qztmijADALAMl5Yatpra/4QZNGwFbsl9sPxp7oMl0wEAfo0wg4arwC0t/IO0oL/kPuA9zX2gpH3hHwg0AODnCDNouAqPSPm/SDl7pQU3nAo07gMl73P2lkwvPGJllQBsZO/evXI4HEpLS7O6lAaFMIOGy9VMGv6J1KTFqUCzf/2pINOkRcl0VzNr6wRwVkXFRmt/OKQP0g5q7Q+HVFRsau2zHA7HOV/Dhw+vtc/GuXFrNho2V0JJYCkNMK8nl7R7gkyCldUBOIel2zM15aOdynQXeNriXMGaNKCdUtpX77kl5cnMzPT8+Z133tH//d//affu3Z62kJAQ5eTk1PjnVkZRUZEcDocaNWqY5ygaZq+B07kSpFvmerfdMpcgA/ixpdsz9fDCLV5BRpKy3AV6eOEWLd2eeZYlfRcbG+t5uVwuORyOMm2lfvzxR1177bUKDQ1Vx44dtXbtWq91rVmzRtdcc41CQkKUmJioUaNGKT8/3zM9JydHd999t5o0aaLQ0FD169dPe/bs8UxfsGCBGjdurI8//ljt2rWT0+nUl19+qcDAQGVlZXl91hNPPKFrrrmmxreHPyHMAO4D0pIHvNuWPFB2UDAAv1BUbDTlo50q74JSaduUj3bW6iWnikyYMEF//OMflZaWprZt22rw4ME6efKkJGnbtm3q27evBg0apK1bt+qdd97RV199pZEjR3qWHz58uDZt2qQPP/xQa9eulTFG/fv393oey9GjRzVt2jTNmzdPO3bsUJcuXdSyZUv985//9Mxz8uRJLVy4UPfcc0/ddd4ChBk0bKcP9m3SQhqxzHsMDYEG8Dsb0g+XOSNzOiMp012gDemH666oM/zxj3/UDTfcoLZt22rKlCnat2+fvv/+e0nS3/72N915551KTU1VmzZt1KNHD82ePVtvvvmmCgoKtGfPHn344YeaN2+eevbsqY4dO+qtt97SwYMH9f7773s+48SJE3r55ZfVo0cPXXTRRQoLC9O9996r+fPne+b55JNPdPToUd122211vQnqFGEGDZf7YNnBvhd2Kzso+GzPoQFgiey8swcZX+arDR06dPD8ufR3h0p/h2jz5s1asGCBwsPDPa++ffuquLhY6enp2rVrl8477zx169bNs46oqChddNFF2rVrl6ctKCjI63OkkjM633//vdatWydJev3113XbbbcpLCys1vrqDxgAjIbLGS6FXVDy59MH+54+KDjsgpL5APiN6IjgGp2vNgQGBnr+XPqU2+LiYs9/H3zwQY0aNarMchdeeKG+++67ctdpjPF6Ym5ISEiZJ+hGR0drwIABmj9/vlq2bKlPP/1UK1eurG53/B5hBg1XsEsa8l7Jc2TOvP3alSAN/7QkyAS7yl8egCW6JjVVnCtYWe6CcsfNOCTFuoLVNalpXZdWKZ06ddKOHTvUunXrcqe3a9dOJ0+e1Pr169WjRw9J0qFDh/Tdd9/pkksuqXD99913n+644w4lJCSoVatW+t3vflej9fsjLjOhYQt2nf05Mq5mBBnADwU0cmjSgHaSSoLL6UrfTxrQTgGN/PN3n8aNG6e1a9fq0UcfVVpammeMzGOPPSZJatOmjQYOHKj7779fX331lb799lsNGTJEzZo108CBAytcf9++feVyufT000/X+4G/pQgzAADbSWkfp1eGdFKsy/tSUqwrWK8M6VQrz5mpKR06dNCqVau0Z88e9ezZU1dccYUmTpzoGVsjSfPnz1fnzp114403qnv37jLG6NNPP/W6fHU2jRo10vDhw1VUVKS77767NrviNxzGGOvuXasDubm5crlccrvdioyMtLocAICkgoICpaenKykpScHBvo9tKSo22pB+WNl5BYqOKLm05K9nZOrS/fffr59//lkffvih1aWc07mOg6p8fzNmBgBgWwGNHOreKsrqMvyG2+3Wxo0b9dZbb+mDDz6wupw6Q5gBAKCeGDhwoDZs2KAHH3xQffr0sbqcOkOYAQCgnmgIt2GXhwHAAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADUE8OHD9fNN99sdRl1jjADAEAVDB8+XA6Ho8wrJSXF6tL0wgsvaMGCBVaXIank18Lff//9OvksnjMDALCfAnf5v3gvSe6Dtf6L9ykpKZo/f75Xm9PprLXPq0hRUZEcDodcrob547icmQEA2EuBW1r4B2lBf8l9wHua+0BJ+8I/lMxXS5xOp2JjY71eTZo00cqVKxUUFKQvv/zSM++MGTN0/vnnKzMzU5LUu3dvjRw5UiNHjlTjxo0VFRWlJ598Uqf/VOLx48c1duxYNWvWTGFhYerWrZvXA/EWLFigxo0b6+OPP1a7du3kdDq1b9++MpeZevfurccee0ypqalq0qSJYmJiNHfuXOXn5+uee+5RRESEWrVqpc8++8yrfzt37lT//v0VHh6umJgYDR06VL/++qvXekeNGqWxY8eqadOmio2N1eTJkz3TW7RoIUm65ZZb5HA4PO9rC2EGAGAvhUek/F+knL3SghtOBRr3gZL3OXtLphceqfPSevfurdTUVA0dOlRut1vffvutJkyYoNdee83rV7HfeOMNnXfeeVq/fr1mz56tmTNnat68eZ7p99xzj77++mstWrRIW7du1a233qqUlBTt2bPHM8/Ro0c1bdo0zZs3Tzt27FB0dHS5Nb3xxhs6//zztWHDBj322GN6+OGHdeutt6pHjx7asmWL+vbtq6FDh+ro0aOSpMzMTPXq1UuXX365Nm3apKVLl+rnn3/WbbfdVma9YWFhWr9+vaZPn66nnnpKy5cvlyRt3LhRUsmvf2dmZnre1xpTz7ndbiPJuN1uq0sBAPzPsWPHzM6dO82xY8d8W8FvGcbM6mDMpMiS/+5b5/3+t4yaLfg0w4YNMwEBASYsLMzr9dRTTxljjCksLDRXXHGFue2228yll15q7rvvPq/le/XqZS655BJTXFzsaRs3bpy55JJLjDHGfP/998bhcJiDBw96LXfdddeZ8ePHG2OMmT9/vpFk0tLSytQ2cOBAr8+6+uqrPe9PnjxpwsLCzNChQz1tmZmZRpJZu3atMcaYiRMnmuTkZK/1ZmRkGElm9+7d5a7XGGOuvPJKM27cOM97SWbJkiVn2YolznUcVOX7mzEzAAD7cSVIwz85dSbm9eSS9iYtStpdCbX68ddee61eeeUVr7amTZtKkoKCgrRw4UJ16NBBzZs316xZs8osf9VVV8nhcHjed+/eXTNmzFBRUZG2bNkiY4zatm3rtUxhYaGiok79QnhQUJA6dOhQYa2nzxMQEKCoqChddtllnraYmBhJUnZ2tiRp8+bNWrFihcLDw8us64cffvDUdeZnx8XFedZR1wgzAAB7ciVIt8w9FWSkkve1HGQkKSwsTK1btz7r9DVr1kiSDh8+rMOHDyssLKzS6y4uLlZAQIA2b96sgIAAr2mnB4yQkBCvQHQ2gYGBXu8dDodXW+k6iouLPf8dMGCAnnvuuTLrOv1SWXnrLV1HXSPMAADsyX1AWvKAd9uSB+rkzMy5/PDDDxo9erRee+01/fvf/9bdd9+tzz//XI0anRqmum7dOq9l1q1bpzZt2iggIEBXXHGFioqKlJ2drZ49e9Z1+erUqZPee+89tWjRQued53tMCAwMVFFRUQ1WdnYMAAYA2M/pg32btJBGLCv575mDgmtJYWGhsrKyvF6//vqrioqKNHToUCUnJ+uee+7R/PnztX37ds2YMcNr+YyMDI0ZM0a7d+/W22+/rRdffFGPP/64JKlt27a66667dPfdd2vx4sVKT0/Xxo0b9dxzz+nTTz+t1X5J0qOPPqrDhw9r8ODB2rBhg3788UctW7ZMI0aMqFI4adGihT7//HNlZWUpJyenFismzAAA7MZ90DvIDP9EurBbyX+9As3BWith6dKliouL83pdffXVeuaZZ7R3717NnTtXkhQbG6t58+bpySefVFpammf5u+++W8eOHVPXrl316KOP6rHHHtMDD5w6yzR//nzdfffdeuKJJ3TRRRfppptu0vr165WYmFhrfSoVHx+vr7/+WkVFRerbt6/at2+vxx9/XC6Xy+vsUkVmzJih5cuXKzExUVdccUUtViw5/jfiuN7Kzc2Vy+WS2+1WZGSk1eUAACQVFBQoPT1dSUlJCg4OruLC/3vOTP4vZS8plZ6xCbtAGvJerT44z1e9e/fW5ZdfXu7A4IbmXMdBVb6/GTMDALCXYFdJUCnvCcCuBGn4p7X+BGD4F8IMAMB+gl1nDyvl/cQB6jXCDAAAdej0nyVAzWAAMAAAsDXCDADAMvX8HhRUoKb2P2EGAFDnSp8eW/rjhmiYSvf/mU8TrirGzAAA6lxAQIAaN27s+S2f0NDQSj2aH/WDMUZHjx5Vdna2GjduXOZnG6qKMAMAsERsbKwkWfbjhLBe48aNPcdBdRBmAACWcDgciouLU3R0tE6cOGF1OahjgYGB1T4jU4owU1UFbqnwiIoi4rUh/bCy8woUHRGsrklNFZD3Ew9qsqmiYlN2fzbilDdQFwICAmrsSw0NE2GmKv73CO2jOVkafGKivs099VPsHSOP6O3Avyq0SazfPkIb5Vu6PVNTPtqpTHeBpy3OFaxJA9oppX3cOZYEAPgD7maqisIjOpqTpdD8DM0ueFJxOiRJitMhzS54UqH5GTqak1XyiG3YwtLtmXp44RavICNJWe4CPbxwi5Zuz7SoMgBAZRFmqqAoIl6DT0zUvuJoNW+UrUVBf1Unx3daFPRXNW+UrX3F0Rp8YqKKIuKtLhWVUFRsNOWjnSrvKQelbVM+2qmiYp6DAQD+jDBTBRvSD+vb3HDdcfxUoFnsnOwJMnccL7n0tCH9sNWlohI2pB8uc0bmdEZSpruA/QkAfo4wUwXZeSVffJmK0ugTj3hNG33iEWUqyms++LfK7if2JwD4N8JMFURHBEsqGSMzM/Blr2kzA1/2jKEpnQ/+rbL7if0JAP6NMFMFXZOaqmPkEa8xMoMKJ3uNoekYeURdk5paXSoqoWtSU8W5gnW2G7AdKrmrif0JAP7Nb8LMtGnT5HA4lJqa6mkzxmjy5MmKj49XSEiIevfurR07dlhWY0DeT3o78K9eY2S2mLZeY2jeDvxryfNm4PcCGjk0aUA7SSoTaErfTxrQjufNAICf84sws3HjRs2dO1cdOnTwap8+fbqef/55zZkzRxs3blRsbKz69OmjvLw8awp1hiu0SayOhiVqVPDTnjEymYrSqOCndTQsseQ5M87wClYEf5HSPk6vDOmkWJf3paRYV7BeGdKJ58wAgA04jMW/v37kyBF16tRJL7/8sp5++mldfvnlmjVrlowxio+PV2pqqsaNGydJKiwsVExMjJ577jk9+OCDlVp/bm6uXC6X3G63IiMjq18wTwCul3gCMAD4l6p8f1v+BOBHH31UN9xwg66//no9/fTTnvb09HRlZWUpOTnZ0+Z0OtWrVy+tWbPmrGGmsLBQhYWFnve5ubk1W3CwSwp2KUBS91ZR3tNczWr2s1BnAho5yu5PAIAtWBpmFi1apC1btmjjxo1lpmVlZUmSYmJivNpjYmK0b9++s65z2rRpmjJlSs0WCgAA/JZlY2YyMjL0+OOPa+HChQoOPvutrw6H96l+Y0yZttONHz9ebrfb88rIyKixmgEAgP+x7MzM5s2blZ2drc6dO3vaioqKtHr1as2ZM0e7d++WVHKGJi7u1CDM7OzsMmdrTud0OuV0OmuvcAAA4FcsOzNz3XXXadu2bUpLS/O8unTporvuuktpaWlq2bKlYmNjtXz5cs8yx48f16pVq9SjRw+rygYAAH7GsjMzERERat++vVdbWFiYoqKiPO2pqamaOnWq2rRpozZt2mjq1KkKDQ3VnXfeaUXJAADAD1l+N9O5jB07VseOHdMjjzyinJwcdevWTcuWLVNERITVpQEAAD9h+XNmaluNP2cGAADUuqp8f/vFE4ABAAB8RZgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2RpgBAAC2ZmmYeeWVV9ShQwdFRkYqMjJS3bt312effeaZbozR5MmTFR8fr5CQEPXu3Vs7duywsGIAAOBvLA0zCQkJevbZZ7Vp0yZt2rRJv//97zVw4EBPYJk+fbqef/55zZkzRxs3blRsbKz69OmjvLw8K8sGAAB+xGGMMVYXcbqmTZvqb3/7m0aMGKH4+HilpqZq3LhxkqTCwkLFxMToueee04MPPlju8oWFhSosLPS8z83NVWJiotxutyIjI+ukDwAAoHpyc3Plcrkq9f3tN2NmioqKtGjRIuXn56t79+5KT09XVlaWkpOTPfM4nU716tVLa9asOet6pk2bJpfL5XklJibWRfkAAMAiloeZbdu2KTw8XE6nUw899JCWLFmidu3aKSsrS5IUExPjNX9MTIxnWnnGjx8vt9vteWVkZNRq/QAAwFrnWV3ARRddpLS0NP3222967733NGzYMK1atcoz3eFweM1vjCnTdjqn0ymn01lr9QIAAP9i+ZmZoKAgtW7dWl26dNG0adPUsWNHvfDCC4qNjZWkMmdhsrOzy5ytAQAADZflYeZMxhgVFhYqKSlJsbGxWr58uWfa8ePHtWrVKvXo0cPCCgEAgD+x9DLTX/7yF/Xr10+JiYnKy8vTokWLtHLlSi1dulQOh0OpqamaOnWq2rRpozZt2mjq1KkKDQ3VnXfeaWXZAADAj1gaZn7++WcNHTpUmZmZcrlc6tChg5YuXao+ffpIksaOHatjx47pkUceUU5Ojrp166Zly5YpIiLCyrIBAIAf8bvnzNS0qtynDgAA/IMtnzMDAADgC8IMAACwNcIMAACwNcIMAACwNcIMAACwNcIMAACwNcIMAACwNcIMAACwNcIMAACwNcIMAACwNcIMAACwNcIMAACwNZ/CTEZGhg4cOOB5v2HDBqWmpmru3Lk1VhgAAEBl+BRm7rzzTq1YsUKSlJWVpT59+mjDhg36y1/+oqeeeqpGCwQAADgXn8LM9u3b1bVrV0nSv//9b7Vv315r1qzRv/71Ly1YsKAm6wMAADgnn8LMiRMn5HQ6JUn//e9/ddNNN0mSLr74YmVmZtZcdQAAABXwKcxceuml+vvf/64vv/xSy5cvV0pKiiTpp59+UlRUVI0WCAAAcC4+hZnnnntOr776qnr37q3BgwerY8eOkqQPP/zQc/kJAACgLjiMMcaXBYuKipSbm6smTZp42vbu3avQ0FBFR0fXWIHVlZubK5fLJbfbrcjISKvLAQAAlVCV72+fnzNjjNHmzZv16quvKi8vT5IUFBSk0NBQX1cJAABQZef5stC+ffuUkpKi/fv3q7CwUH369FFERISmT5+ugoIC/f3vf6/pOgEAAMrl05mZxx9/XF26dFFOTo5CQkI87bfccos+//zzGisOAACgIj6dmfnqq6/09ddfKygoyKu9efPmOnjwYI0UBgAAUBk+nZkpLi5WUVFRmfYDBw4oIiKi2kUBAABUlk9hpk+fPpo1a5bnvcPh0JEjRzRp0iT179+/pmoDAACokE+3Zv/000+69tprFRAQoD179qhLly7as2ePzj//fK1evZpbswEAQLVU5fvbpzEz8fHxSktL09tvv60tW7aouLhY9957r+666y6vAcEAAAC1zeeH5tkFZ2YAALCfWjkz8+GHH1a6gNIfngQAAKhtlQ4zN998c6Xmczgc5d7pBAAAUBsqHWaKi4trsw4AAACf+PzbTAAAAP7A5zDz+eef68Ybb1SrVq3UunVr3Xjjjfrvf/9bk7UBAABUyKcwM2fOHKWkpCgiIkKPP/64Ro0apcjISPXv319z5syp6RoBAADOyqdbs5s1a6bx48dr5MiRXu0vvfSSnnnmGf300081VmB1cWs2AAD2U5Xvb5/OzOTm5iolJaVMe3JysnJzc31ZJQAAgE98CjM33XSTlixZUqb9gw8+0IABA6pdFAAAQGX59HMGl1xyiZ555hmtXLlS3bt3lyStW7dOX3/9tZ544gnNnj3bM++oUaNqplIAAIBy+DRmJikpqXIrdzj0448/VrmomsSYGQAA7KfWf2gyPT3dp8IAAABqGg/NAwAAtubTmRljjN59912tWLFC2dnZZX7qYPHixTVSHAAAQEV8CjOPP/645s6dq2uvvVYxMTFyOBw1XRcAAECl+BRmFi5cqMWLF6t///41XQ8AAECV+DRmxuVyqWXLljVdCwAAQJX5FGYmT56sKVOm6NixYzVdDwAAQJX4dJnp1ltv1dtvv63o6Gi1aNFCgYGBXtO3bNlSI8UBAABUxKcwM3z4cG3evFlDhgxhADAAALCUT2Hmk08+0X/+8x9dffXVNV0PAABAlfg0ZiYxMZGfBgAAAH7BpzAzY8YMjR07Vnv37q3hcgAAAKrGp8tMQ4YM0dGjR9WqVSuFhoaWGQB8+PDhGikOAACgIj6FmVmzZtVwGQAAAL7xKcwMGzaspusAAADwiU9h5nTHjh3TiRMnvNoYHAwAAOqKTwOA8/PzNXLkSEVHRys8PFxNmjTxegEAANQVn8LM2LFj9cUXX+jll1+W0+nUvHnzNGXKFMXHx+vNN9+s6RoBAADOyqfLTB999JHefPNN9e7dWyNGjFDPnj3VunVrNW/eXG+99Zbuuuuumq4TAACgXD6dmTl8+LCSkpIklYyPKb0V++qrr9bq1atrrjoAAIAK+BRmWrZs6XlgXrt27fTvf/9bUskZm8aNG9dUbQAAABXyKczcc889+vbbbyVJ48eP94ydGT16tP70pz9Vej3Tpk3TlVdeqYiICEVHR+vmm2/W7t27veYxxmjy5MmKj49XSEiIevfurR07dvhSNgAAqIccxhhT3ZXs379fmzZtUqtWrdSxY8dKL5eSkqI77rhDV155pU6ePKkJEyZo27Zt2rlzp8LCwiRJzz33nJ555hktWLBAbdu21dNPP63Vq1dr9+7dioiIqPAzcnNz5XK55Ha7uWUcAACbqMr3d5XCzPr163X48GH169fP0/bmm29q0qRJys/P180336wXX3xRTqfTp8J/+eUXRUdHa9WqVbrmmmtkjFF8fLxSU1M1btw4SVJhYaFiYmL03HPP6cEHH6xwnYQZAADspyrf31W6zDR58mRt3brV837btm269957df3112v8+PH66KOPNG3aNN+qluR2uyVJTZs2lSSlp6crKytLycnJnnmcTqd69eqlNWvWlLuOwsJC5ebmer0AAED9VaUwk5aWpuuuu87zftGiRerWrZtee+01jR49WrNnz/YMBq4qY4zGjBmjq6++Wu3bt5ckZWVlSZJiYmK85o2JifFMO9O0adPkcrk8r8TERJ/qAQAA9lClMJOTk+MVLFatWqWUlBTP+yuvvFIZGRk+FTJy5Eht3bpVb7/9dplpDofD670xpkxbqfHjx8vtdntevtYDAADsoUphJiYmRunp6ZKk48ePa8uWLerevbtnel5engIDA6tcxGOPPaYPP/xQK1asUEJCgqc9NjZWksqchcnOzi5ztqaU0+lUZGSk1wsAANRfVQozKSkp+vOf/6wvv/xS48ePV2hoqHr27OmZvnXrVrVq1arS6zPGaOTIkVq8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uuuu02WXXaZbb71Vr732mnJycs65TgA1gzAD4JzCwsLUunVrtW7dWl27dtU//vEP5efn67XXXpMkrVu3TnfccYf69eunjz/+WN98840mTJig48ePe9YxefJk7dixQzfccIO++OILtWvXTkuWLCn386oyb2Jionbv3q2XXnpJISEheuSRR3TNNdd4jdc504wZMzRz5kyNHTtWX3zxhdLS0tS3b1+vestTXFysAQMGeIJd6WvPnj265pprFBAQoOXLl+uzzz5Tu3bt9OKLL+qiiy5Senp6RZsYQDURZgBUicPhUKNGjXTs2DFJ0tdff63mzZtrwoQJ6tKli9q0aeN11qZU27ZtNXr0aC1btkyDBg3S/Pnzz/oZVZk3JCREN910k2bPnq2VK1dq7dq12rZtmyQpKChIRUVFXvN/+eWXGjhwoIYMGaKOHTuqZcuW2rNnj9c85S3XqVMn7dixQy1atPCEu9JXWFiYZ9v87ne/05QpU/TNN98oKCjorEEMQM0hzAA4p8LCQmVlZSkrK0u7du3SY489piNHjmjAgAGSpNatW2v//v1atGiRfvjhB82ePdvrC/zYsWMaOXKkVq5cqX379unrr7/Wxo0bdckll5T5rKrMK5XcCfWPf/xD27dv148//qh//vOfCgkJUfPmzSWVPGdm9erVOnjwoOeuo9atW2v58uVas2aNdu3apQcffFBZWVle623RooXWr1+vvXv36tdff1VxcbEeffRRHT58WIMHD9aGDRv0448/atmyZRoxYoSKioq0fv16TZ06VZs2bdL+/fu1ePFi/fLLL2etHUANsnrQDgD/NWzYMCPJ84qIiDBXXnmleffdd73m+9Of/mSioqJMeHi4uf32283MmTM9g3oLCwvNHXfcYRITE01QUJCJj483I0eONMeOHTPGeA8ArmjeMy1ZssR069bNREZGmrCwMHPVVVeZ//73v57pa9euNR06dDBOp9OU/nN36NAhM3DgQBMeHm6io6PNk08+ae6++24zcOBAz3K7d+82V111lQkJCTGSTHp6ujHGmO+++87ccsstpnHjxiYkJMRcfPHFJjU11RQXF5udO3eavn37mgsuuMA4nU7Ttm1b8+KLL9bAXgBQEYcx3DcIAADsi8tMAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1v4/nwmLo90jSN4AAAAASUVORK5CYII=", "text/plain": [ "
" ] @@ -395,7 +395,7 @@ "\n", "# Simulate them\n", "with CuTensorNetHandle() as libhandle:\n", - " other_mps = simulate(libhandle, other_circ, ContractionAlg.MPSxGate, ConfigMPS())" + " other_mps = simulate(libhandle, other_circ, SimulationAlgorithm.MPSxGate, Config())" ] }, { @@ -474,7 +474,7 @@ "\n", "\n", "\n", - " <div id="circuit-display-vue-container-04ed427a-5ec8-4e3d-9fbf-e11d67078a06" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-90af8c21-b20e-49c8-aac0-c6459bf1d27c" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["q", [1]]], "op": {"type": "H"}}, {"args": [["q", [2]], ["q", [3]]], "op": {"params": ["0.3"], "type": "ZZPhase"}}, {"args": [["q", [4]]], "op": {"params": ["0.8"], "type": "Ry"}}, {"args": [["q", [0]], ["q", [1]]], "op": {"type": "CX"}}, {"args": [["q", [3]], ["q", [4]]], "op": {"type": "CZ"}}, {"args": [["q", [1]], ["q", [2]]], "op": {"params": ["0.7"], "type": "XXPhase"}}, {"args": [["q", [1]], ["q", [4]]], "op": {"params": ["0.1", "0.2", "0.4"], "type": "TK2"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["q", [0]], ["q", [0]]], [["q", [1]], ["q", [1]]], [["q", [2]], ["q", [2]]], [["q", [3]], ["q", [3]]], [["q", [4]], ["q", [4]]]], "phase": "0.0", "qubits": [["q", [0]], ["q", [1]], ["q", [2]], ["q", [3]], ["q", [4]]]}</div>\n", " </div>\n", @@ -484,7 +484,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "04ed427a-5ec8-4e3d-9fbf-e11d67078a06";\n", + " const circuitRendererUid = "90af8c21-b20e-49c8-aac0-c6459bf1d27c";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -551,14 +551,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "('Some two-qubit gate in the circuit is not acting between', 'nearest neighbour qubits. Consider using prepare_circuit().')\n" + "Gates must be applied to adjacent qubits! This is not satisfied by TK2(0.1, 0.2, 0.4) q[1], q[4];.\n" ] } ], "source": [ "with CuTensorNetHandle() as libhandle:\n", " try:\n", - " simulate(libhandle, bad_circ, ContractionAlg.MPSxGate, ConfigMPS())\n", + " simulate(libhandle, bad_circ, SimulationAlgorithm.MPSxGate, Config())\n", " except RuntimeError as e:\n", " print(e)" ] @@ -568,7 +568,7 @@ "id": "ba933c45-63a8-46ae-a578-47d6f59076e4", "metadata": {}, "source": [ - "As suggested by the error message, we can call `prepare_circuit` to use `pytket` routing capabilities to guarantee that the circuit can be run using our MPS approaches." + "We can call `prepare_circuit_mps` to use `pytket` routing capabilities to guarantee that the circuit can be run using our MPS approaches." ] }, { @@ -602,7 +602,7 @@ "\n", "\n", "\n", - " <div id="circuit-display-vue-container-08a5a05d-8989-43a0-a5a9-8c83ea08f5a6" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-c1a0f1fd-abca-4258-bd95-dede43c8c2fa" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["node", [1]]], "op": {"params": ["0.8"], "type": "Ry"}}, {"args": [["node", [3]]], "op": {"type": "H"}}, {"args": [["node", [0]], ["node", [1]]], "op": {"type": "SWAP"}}, {"args": [["node", [4]], ["node", [3]]], "op": {"type": "CX"}}, {"args": [["node", [2]], ["node", [1]]], "op": {"params": ["0.3"], "type": "ZZPhase"}}, {"args": [["node", [1]], ["node", [0]]], "op": {"type": "CZ"}}, {"args": [["node", [3]], ["node", [2]]], "op": {"params": ["0.7"], "type": "XXPhase"}}, {"args": [["node", [3]], ["node", [2]]], "op": {"type": "SWAP"}}, {"args": [["node", [2]], ["node", [1]]], "op": {"type": "SWAP"}}, {"args": [["node", [1]], ["node", [0]]], "op": {"params": ["0.1", "0.2", "0.4"], "type": "TK2"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["node", [0]], ["node", [0]]], [["node", [1]], ["node", [1]]], [["node", [2]], ["node", [2]]], [["node", [3]], ["node", [3]]], [["node", [4]], ["node", [4]]]], "phase": "0.0", "qubits": [["node", [0]], ["node", [1]], ["node", [2]], ["node", [3]], ["node", [4]]]}</div>\n", " </div>\n", @@ -612,7 +612,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "08a5a05d-8989-43a0-a5a9-8c83ea08f5a6";\n", + " const circuitRendererUid = "c1a0f1fd-abca-4258-bd95-dede43c8c2fa";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -664,7 +664,7 @@ } ], "source": [ - "prep_circ, qubit_map = prepare_circuit(bad_circ)\n", + "prep_circ, qubit_map = prepare_circuit_mps(bad_circ)\n", "render_circuit_jupyter(prep_circ)\n", "# Print the correspondence between qubit names in `prep_circuit` and the original qubits from `circuit` at the output\n", "print(qubit_map)" @@ -695,7 +695,7 @@ ], "source": [ "with CuTensorNetHandle() as libhandle:\n", - " prep_mps = simulate(libhandle, prep_circ, ContractionAlg.MPSxGate, ConfigMPS())\n", + " prep_mps = simulate(libhandle, prep_circ, SimulationAlgorithm.MPSxGate, Config())\n", " print(\"Did simulation succeed?\")\n", " print(prep_mps.is_valid())" ] @@ -712,7 +712,7 @@ "* Bound the maximum value of the virtual bond dimension `chi`. If a bond dimension would increase past that point, we *truncate* (i.e. discard) the degrees of freedom that contribute the least to the state description. We can keep track of a lower bound of the error that this truncation causes.\n", "* Provide a value for acceptable two-qubit gate fidelity `truncation_fidelity`. After each two-qubit gate we truncate the dimension of virtual bonds as much as we can while guaranteeing the target gate fidelity. The more fidelity you require, the longer it will take to simulate. **Note**: this is *not* the final fidelity of the output state, but the fidelity per gate.\n", "\n", - "Values for `chi` and `truncation_fidelity` can be set via `ConfigMPS`. To showcase approximate simulation, let's define a circuit where exact MPS contraction starts struggling." + "Values for `chi` and `truncation_fidelity` can be set via `Config`. To showcase approximate simulation, let's define a circuit where exact MPS contraction starts struggling." ] }, { @@ -775,18 +775,18 @@ "output_type": "stream", "text": [ "Time taken by approximate contraction with bound chi:\n", - "1.89 seconds\n", + "2.36 seconds\n", "\n", "Lower bound of the fidelity:\n", - "0.3742\n" + "0.4226\n" ] } ], "source": [ "start = time()\n", "with CuTensorNetHandle() as libhandle:\n", - " config = ConfigMPS(chi=16)\n", - " bound_chi_mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, config)\n", + " config = Config(chi=16)\n", + " bound_chi_mps = simulate(libhandle, circuit, SimulationAlgorithm.MPSxGate, config)\n", "end = time()\n", "print(\"Time taken by approximate contraction with bound chi:\")\n", "print(f\"{round(end-start,2)} seconds\")\n", @@ -813,18 +813,18 @@ "output_type": "stream", "text": [ "Time taken by approximate contraction with fixed truncation fidelity:\n", - "2.89 seconds\n", + "4.39 seconds\n", "\n", "Lower bound of the fidelity:\n", - "0.9298\n" + "0.9311\n" ] } ], "source": [ "start = time()\n", "with CuTensorNetHandle() as libhandle:\n", - " config = ConfigMPS(truncation_fidelity=0.999)\n", - " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, config)\n", + " config = Config(truncation_fidelity=0.999)\n", + " fixed_fidelity_mps = simulate(libhandle, circuit, SimulationAlgorithm.MPSxGate, config)\n", "end = time()\n", "print(\"Time taken by approximate contraction with fixed truncation fidelity:\")\n", "print(f\"{round(end-start,2)} seconds\")\n", @@ -869,16 +869,16 @@ "output_type": "stream", "text": [ "MPSxGate\n", - "\tTime taken: 1.89 seconds\n", - "\tLower bound of the fidelity: 0.3712\n" + "\tTime taken: 1.72 seconds\n", + "\tLower bound of the fidelity: 0.4226\n" ] } ], "source": [ "start = time()\n", "with CuTensorNetHandle() as libhandle:\n", - " config = ConfigMPS(chi=16)\n", - " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxGate, config)\n", + " config = Config(chi=16)\n", + " fixed_fidelity_mps = simulate(libhandle, circuit, SimulationAlgorithm.MPSxGate, config)\n", "end = time()\n", "print(\"MPSxGate\")\n", "print(f\"\\tTime taken: {round(end-start,2)} seconds\")\n", @@ -896,16 +896,16 @@ "output_type": "stream", "text": [ "MPSxMPO, default parameters\n", - "\tTime taken: 27.17 seconds\n", - "\tLower bound of the fidelity: 0.3956\n" + "\tTime taken: 13.55 seconds\n", + "\tLower bound of the fidelity: 0.4532\n" ] } ], "source": [ "start = time()\n", "with CuTensorNetHandle() as libhandle:\n", - " config = ConfigMPS(chi=16)\n", - " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, config)\n", + " config = Config(chi=16)\n", + " fixed_fidelity_mps = simulate(libhandle, circuit, SimulationAlgorithm.MPSxMPO, config)\n", "end = time()\n", "print(\"MPSxMPO, default parameters\")\n", "print(f\"\\tTime taken: {round(end-start,2)} seconds\")\n", @@ -923,16 +923,16 @@ "output_type": "stream", "text": [ "MPSxMPO, custom parameters\n", - "\tTime taken: 26.99 seconds\n", - "\tLower bound of the fidelity: 0.4209\n" + "\tTime taken: 19.74 seconds\n", + "\tLower bound of the fidelity: 0.4737\n" ] } ], "source": [ "start = time()\n", "with CuTensorNetHandle() as libhandle:\n", - " config = ConfigMPS(k=8, optim_delta=1e-15, chi=16)\n", - " fixed_fidelity_mps = simulate(libhandle, circuit, ContractionAlg.MPSxMPO, config)\n", + " config = Config(k=8, optim_delta=1e-15, chi=16)\n", + " fixed_fidelity_mps = simulate(libhandle, circuit, SimulationAlgorithm.MPSxMPO, config)\n", "end = time()\n", "print(\"MPSxMPO, custom parameters\")\n", "print(f\"\\tTime taken: {round(end-start,2)} seconds\")\n", @@ -960,7 +960,7 @@ "id": "7607b5bd-f332-4d97-963b-2a163d3fb194", "metadata": {}, "source": [ - "You can request a verbose log to be produced during simulation, by assigning the `loglevel` argument when creating a `ConfigMPS` instance. Currently, two log levels are supported (other than default, which is silent): \n", + "You can request a verbose log to be produced during simulation, by assigning the `loglevel` argument when creating a `Config` instance. Currently, two log levels are supported (other than default, which is silent): \n", "- `logging.INFO` will print information about progress percent, memory currently occupied by the MPS and current fidelity. Additionally, some high level information of the current stage of the simulation is provided, such as when `MPSxMPO` is applying optimisation sweeps.\n", "- `logging.DEBUG` provides all of the messages from the loglevel above plus detailed information of the current operation being carried out and the values of important variables.\n", "\n", @@ -976,7 +976,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 21, @@ -1010,1097 +1010,1083 @@ "name": "stderr", "output_type": "stream", "text": [ - "[15:41:45] Simulation (INFO) - Ordering the gates in the circuit to reduce canonicalisation overhead.\n", - "[15:41:45] Simulation (INFO) - Running simulation...\n", - "[15:41:45] Simulation (INFO) - Progress... 0%\n", - "[15:41:45] Simulation (INFO) - Progress... 0%\n", - "[15:41:45] Simulation (INFO) - Progress... 0%\n", - "[15:41:45] Simulation (INFO) - Progress... 0%\n", - "[15:41:45] Simulation (INFO) - Progress... 0%\n", - "[15:41:45] Simulation (INFO) - Progress... 0%\n", - "[15:41:45] Simulation (INFO) - Progress... 1%\n", - "[15:41:45] Simulation (INFO) - Progress... 1%\n", - "[15:41:45] Simulation (INFO) - Progress... 1%\n", - "[15:41:45] Simulation (INFO) - Progress... 1%\n", - "[15:41:45] Simulation (INFO) - Progress... 1%\n", - "[15:41:45] Simulation (INFO) - Progress... 1%\n", - "[15:41:45] Simulation (INFO) - Progress... 2%\n", - "[15:41:45] Simulation (INFO) - Progress... 2%\n", - "[15:41:45] Simulation (INFO) - Progress... 2%\n", - "[15:41:45] Simulation (INFO) - Progress... 2%\n", - "[15:41:45] Simulation (INFO) - Progress... 2%\n", - "[15:41:45] Simulation (INFO) - Progress... 2%\n", - "[15:41:45] Simulation (INFO) - Progress... 3%\n", - "[15:41:45] Simulation (INFO) - Progress... 3%\n", - "[15:41:45] MPS (INFO) - MPS size (MiB)=0.00067138671875\n", - "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", - "[15:41:45] Simulation (INFO) - Progress... 3%\n", - "[15:41:45] Simulation (INFO) - Progress... 3%\n", - "[15:41:45] Simulation (INFO) - Progress... 3%\n", - "[15:41:45] Simulation (INFO) - Progress... 3%\n", - "[15:41:45] MPS (INFO) - MPS size (MiB)=0.000732421875\n", - "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", - "[15:41:45] Simulation (INFO) - Progress... 4%\n", - "[15:41:45] Simulation (INFO) - Progress... 4%\n", - "[15:41:45] Simulation (INFO) - Progress... 4%\n", - "[15:41:45] MPS (INFO) - MPS size (MiB)=0.0008544921875\n", - "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", - "[15:41:45] Simulation (INFO) - Progress... 4%\n", - "[15:41:45] Simulation (INFO) - Progress... 4%\n", - "[15:41:45] Simulation (INFO) - Progress... 4%\n", - "[15:41:45] MPS (INFO) - MPS size (MiB)=0.0008544921875\n", - "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", - "[15:41:45] Simulation (INFO) - Progress... 5%\n", - "[15:41:45] Simulation (INFO) - Progress... 5%\n", - "[15:41:45] Simulation (INFO) - Progress... 5%\n", - "[15:41:45] Simulation (INFO) - Progress... 5%\n", - "[15:41:45] MPS (INFO) - MPS size (MiB)=0.00091552734375\n", - "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", - "[15:41:45] Simulation (INFO) - Progress... 5%\n", - "[15:41:45] Simulation (INFO) - Progress... 5%\n", - "[15:41:45] Simulation (INFO) - Progress... 6%\n", - "[15:41:45] MPS (INFO) - MPS size (MiB)=0.00103759765625\n", - "[15:41:45] MPS (INFO) - MPS fidelity=1.0\n", - "[15:41:45] Simulation (INFO) - Progress... 6%\n", - "[15:41:45] Simulation (INFO) - Progress... 6%\n", - "[15:41:45] Simulation (INFO) - Progress... 6%\n", - "[15:41:45] MPS (INFO) - MPS size (MiB)=0.00128173828125\n", - "[15:41:45] MPS (INFO) - MPS fidelity=1.0000000000000002\n", - "[15:41:45] Simulation (INFO) - Progress... 6%\n", - "[15:41:45] Simulation (INFO) - Progress... 6%\n", - "[15:41:45] Simulation (INFO) - Progress... 7%\n", - "[15:41:45] MPS (INFO) - MPS size (MiB)=0.00164794921875\n", - "[15:41:45] MPS (INFO) - MPS fidelity=1.0000000000000002\n", - "[15:41:45] Simulation (INFO) - Progress... 7%\n", - "[15:41:45] Simulation (INFO) - Progress... 7%\n", - "[15:41:45] Simulation (INFO) - Progress... 7%\n", - "[15:41:45] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:45] MPS (INFO) - Fidelity before optimisation=1.0000000000000002\n", - "[15:41:45] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:45] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.999999999999996\n", - "[15:41:45] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:46] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9999999999999964\n", - "[15:41:46] MPS (INFO) - Final fidelity after optimisation=0.9999999999999964\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00164794921875\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9999999999999964\n", - "[15:41:46] Simulation (INFO) - Progress... 7%\n", - "[15:41:46] Simulation (INFO) - Progress... 7%\n", - "[15:41:46] Simulation (INFO) - Progress... 8%\n", - "[15:41:46] Simulation (INFO) - Progress... 8%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.001708984375\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9999999999999964\n", - "[15:41:46] Simulation (INFO) - Progress... 8%\n", - "[15:41:46] Simulation (INFO) - Progress... 8%\n", - "[15:41:46] Simulation (INFO) - Progress... 8%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0018310546875\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9999999999999964\n", - "[15:41:46] Simulation (INFO) - Progress... 8%\n", - "[15:41:46] Simulation (INFO) - Progress... 9%\n", - "[15:41:46] Simulation (INFO) - Progress... 9%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0020751953125\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9999999999999962\n", - "[15:41:46] Simulation (INFO) - Progress... 9%\n", - "[15:41:46] Simulation (INFO) - Progress... 9%\n", - "[15:41:46] Simulation (INFO) - Progress... 9%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0025634765625\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9999999999999962\n", - "[15:41:46] Simulation (INFO) - Progress... 10%\n", - "[15:41:46] Simulation (INFO) - Progress... 10%\n", - "[15:41:46] Simulation (INFO) - Progress... 10%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0030517578125\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", - "[15:41:46] Simulation (INFO) - Progress... 10%\n", - "[15:41:46] Simulation (INFO) - Progress... 10%\n", - "[15:41:46] Simulation (INFO) - Progress... 10%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0030517578125\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", - "[15:41:46] Simulation (INFO) - Progress... 11%\n", - "[15:41:46] Simulation (INFO) - Progress... 11%\n", - "[15:41:46] Simulation (INFO) - Progress... 11%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0030517578125\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", - "[15:41:46] Simulation (INFO) - Progress... 11%\n", - "[15:41:46] Simulation (INFO) - Progress... 11%\n", - "[15:41:46] Simulation (INFO) - Progress... 11%\n", - "[15:41:46] Simulation (INFO) - Progress... 12%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00311279296875\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", - "[15:41:46] Simulation (INFO) - Progress... 12%\n", - "[15:41:46] Simulation (INFO) - Progress... 12%\n", - "[15:41:46] Simulation (INFO) - Progress... 12%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00323486328125\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", - "[15:41:46] Simulation (INFO) - Progress... 12%\n", - "[15:41:46] Simulation (INFO) - Progress... 12%\n", - "[15:41:46] Simulation (INFO) - Progress... 13%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00347900390625\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071941\n", - "[15:41:46] Simulation (INFO) - Progress... 13%\n", - "[15:41:46] Simulation (INFO) - Progress... 13%\n", - "[15:41:46] Simulation (INFO) - Progress... 13%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00396728515625\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071943\n", - "[15:41:46] Simulation (INFO) - Progress... 13%\n", - "[15:41:46] Simulation (INFO) - Progress... 13%\n", - "[15:41:46] Simulation (INFO) - Progress... 14%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00494384765625\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9997200110071943\n", - "[15:41:46] Simulation (INFO) - Progress... 14%\n", - "[15:41:46] Simulation (INFO) - Progress... 14%\n", - "[15:41:46] Simulation (INFO) - Progress... 14%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.0062255859375\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9992820662866865\n", - "[15:41:46] Simulation (INFO) - Progress... 14%\n", - "[15:41:46] Simulation (INFO) - Progress... 14%\n", - "[15:41:46] Simulation (INFO) - Progress... 15%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006561279296875\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9986887177546038\n", - "[15:41:46] Simulation (INFO) - Progress... 15%\n", - "[15:41:46] Simulation (INFO) - Progress... 15%\n", - "[15:41:46] Simulation (INFO) - Progress... 15%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006561279296875\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9986887177546038\n", - "[15:41:46] Simulation (INFO) - Progress... 15%\n", - "[15:41:46] Simulation (INFO) - Progress... 15%\n", - "[15:41:46] Simulation (INFO) - Progress... 16%\n", - "[15:41:46] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:46] MPS (INFO) - Fidelity before optimisation=0.9986887177546038\n", - "[15:41:46] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:46] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9992190736072919\n", - "[15:41:46] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:46] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9992335371417129\n", - "[15:41:46] MPS (INFO) - Final fidelity after optimisation=0.9992335371417129\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006561279296875\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9992335371417129\n", - "[15:41:46] Simulation (INFO) - Progress... 16%\n", - "[15:41:46] Simulation (INFO) - Progress... 16%\n", - "[15:41:46] Simulation (INFO) - Progress... 16%\n", - "[15:41:46] Simulation (INFO) - Progress... 16%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006622314453125\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9992335371417131\n", - "[15:41:46] Simulation (INFO) - Progress... 16%\n", - "[15:41:46] Simulation (INFO) - Progress... 17%\n", - "[15:41:46] Simulation (INFO) - Progress... 17%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006744384765625\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9992335371417131\n", - "[15:41:46] Simulation (INFO) - Progress... 17%\n", - "[15:41:46] Simulation (INFO) - Progress... 17%\n", - "[15:41:46] Simulation (INFO) - Progress... 17%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.006988525390625\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9992335371417131\n", - "[15:41:46] Simulation (INFO) - Progress... 17%\n", - "[15:41:46] Simulation (INFO) - Progress... 18%\n", - "[15:41:46] Simulation (INFO) - Progress... 18%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.007476806640625\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9992335371417131\n", - "[15:41:46] Simulation (INFO) - Progress... 18%\n", - "[15:41:46] Simulation (INFO) - Progress... 18%\n", - "[15:41:46] Simulation (INFO) - Progress... 18%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.007476806640625\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9982345023466558\n", - "[15:41:46] Simulation (INFO) - Progress... 18%\n", - "[15:41:46] Simulation (INFO) - Progress... 19%\n", - "[15:41:46] Simulation (INFO) - Progress... 19%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.008209228515625\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9975515249441151\n", - "[15:41:46] Simulation (INFO) - Progress... 19%\n", - "[15:41:46] Simulation (INFO) - Progress... 19%\n", - "[15:41:46] Simulation (INFO) - Progress... 19%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00860595703125\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9967787323351995\n", - "[15:41:46] Simulation (INFO) - Progress... 20%\n", - "[15:41:46] Simulation (INFO) - Progress... 20%\n", - "[15:41:46] Simulation (INFO) - Progress... 20%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.00958251953125\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.996089833981607\n", - "[15:41:46] Simulation (INFO) - Progress... 20%\n", - "[15:41:46] Simulation (INFO) - Progress... 20%\n", - "[15:41:46] Simulation (INFO) - Progress... 20%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.996089833981607\n", - "[15:41:46] Simulation (INFO) - Progress... 21%\n", - "[15:41:46] Simulation (INFO) - Progress... 21%\n", - "[15:41:46] Simulation (INFO) - Progress... 21%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9960898339816068\n", - "[15:41:46] Simulation (INFO) - Progress... 21%\n", - "[15:41:46] Simulation (INFO) - Progress... 21%\n", - "[15:41:46] Simulation (INFO) - Progress... 21%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9960898339816068\n", - "[15:41:46] Simulation (INFO) - Progress... 22%\n", - "[15:41:46] Simulation (INFO) - Progress... 22%\n", - "[15:41:46] Simulation (INFO) - Progress... 22%\n", - "[15:41:46] Simulation (INFO) - Progress... 22%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9955765571586642\n", - "[15:41:46] Simulation (INFO) - Progress... 22%\n", - "[15:41:46] Simulation (INFO) - Progress... 22%\n", - "[15:41:46] Simulation (INFO) - Progress... 23%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.01007080078125\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9955765571586642\n", - "[15:41:46] Simulation (INFO) - Progress... 23%\n", - "[15:41:46] Simulation (INFO) - Progress... 23%\n", - "[15:41:46] Simulation (INFO) - Progress... 23%\n", - "[15:41:46] MPS (INFO) - MPS size (MiB)=0.01031494140625\n", - "[15:41:46] MPS (INFO) - MPS fidelity=0.9955765571586642\n", - "[15:41:46] Simulation (INFO) - Progress... 23%\n", - "[15:41:46] Simulation (INFO) - Progress... 23%\n", - "[15:41:46] Simulation (INFO) - Progress... 24%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01080322265625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9955765571586642\n", - "[15:41:47] Simulation (INFO) - Progress... 24%\n", - "[15:41:47] Simulation (INFO) - Progress... 24%\n", - "[15:41:47] Simulation (INFO) - Progress... 24%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01153564453125\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9951866821980899\n", - "[15:41:47] Simulation (INFO) - Progress... 24%\n", - "[15:41:47] Simulation (INFO) - Progress... 24%\n", - "[15:41:47] Simulation (INFO) - Progress... 25%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.012542724609375\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9945684970193788\n", - "[15:41:47] Simulation (INFO) - Progress... 25%\n", - "[15:41:47] Simulation (INFO) - Progress... 25%\n", - "[15:41:47] Simulation (INFO) - Progress... 25%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.013336181640625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9937800765622566\n", - "[15:41:47] Simulation (INFO) - Progress... 25%\n", - "[15:41:47] Simulation (INFO) - Progress... 25%\n", - "[15:41:47] Simulation (INFO) - Progress... 26%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.015167236328125\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9933657156418472\n", - "[15:41:47] Simulation (INFO) - Progress... 26%\n", - "[15:41:47] Simulation (INFO) - Progress... 26%\n", - "[15:41:47] Simulation (INFO) - Progress... 26%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.016326904296875\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9926168168757035\n", - "[15:41:47] Simulation (INFO) - Progress... 26%\n", - "[15:41:47] Simulation (INFO) - Progress... 26%\n", - "[15:41:47] Simulation (INFO) - Progress... 27%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01806640625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9919907474019105\n", - "[15:41:47] Simulation (INFO) - Progress... 27%\n", - "[15:41:47] Simulation (INFO) - Progress... 27%\n", - "[15:41:47] Simulation (INFO) - Progress... 27%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01806640625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9919907474019105\n", - "[15:41:47] Simulation (INFO) - Progress... 27%\n", - "[15:41:47] Simulation (INFO) - Progress... 27%\n", - "[15:41:47] Simulation (INFO) - Progress... 28%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01806640625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9919907474019105\n", - "[15:41:47] Simulation (INFO) - Progress... 28%\n", - "[15:41:47] Simulation (INFO) - Progress... 28%\n", - "[15:41:47] Simulation (INFO) - Progress... 28%\n", - "[15:41:47] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:47] MPS (INFO) - Fidelity before optimisation=0.9919907474019105\n", - "[15:41:47] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:47] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9944986651228879\n", - "[15:41:47] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:47] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9945622863823858\n", - "[15:41:47] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:47] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9945744642325651\n", - "[15:41:47] MPS (INFO) - Final fidelity after optimisation=0.9945744642325651\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01806640625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9945744642325651\n", - "[15:41:47] Simulation (INFO) - Progress... 28%\n", - "[15:41:47] Simulation (INFO) - Progress... 28%\n", - "[15:41:47] Simulation (INFO) - Progress... 29%\n", - "[15:41:47] Simulation (INFO) - Progress... 29%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.01812744140625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9945744642325651\n", - "[15:41:47] Simulation (INFO) - Progress... 29%\n", - "[15:41:47] Simulation (INFO) - Progress... 29%\n", - "[15:41:47] Simulation (INFO) - Progress... 29%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.018218994140625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9945744642325651\n", - "[15:41:47] Simulation (INFO) - Progress... 30%\n", - "[15:41:47] Simulation (INFO) - Progress... 30%\n", - "[15:41:47] Simulation (INFO) - Progress... 30%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.018341064453125\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9945744642325651\n", - "[15:41:47] Simulation (INFO) - Progress... 30%\n", - "[15:41:47] Simulation (INFO) - Progress... 30%\n", - "[15:41:47] Simulation (INFO) - Progress... 30%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.018707275390625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9945744642325651\n", - "[15:41:47] Simulation (INFO) - Progress... 31%\n", - "[15:41:47] Simulation (INFO) - Progress... 31%\n", - "[15:41:47] Simulation (INFO) - Progress... 31%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.019439697265625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9939252776724739\n", - "[15:41:47] Simulation (INFO) - Progress... 31%\n", - "[15:41:47] Simulation (INFO) - Progress... 31%\n", - "[15:41:47] Simulation (INFO) - Progress... 31%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.021148681640625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9939252776724739\n", - "[15:41:47] Simulation (INFO) - Progress... 32%\n", - "[15:41:47] Simulation (INFO) - Progress... 32%\n", - "[15:41:47] Simulation (INFO) - Progress... 32%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.02252197265625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9936102094018504\n", - "[15:41:47] Simulation (INFO) - Progress... 32%\n", - "[15:41:47] Simulation (INFO) - Progress... 32%\n", - "[15:41:47] Simulation (INFO) - Progress... 32%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.02447509765625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9932716193018882\n", - "[15:41:47] Simulation (INFO) - Progress... 33%\n", - "[15:41:47] Simulation (INFO) - Progress... 33%\n", - "[15:41:47] Simulation (INFO) - Progress... 33%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.027679443359375\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9926992945331796\n", - "[15:41:47] Simulation (INFO) - Progress... 33%\n", - "[15:41:47] Simulation (INFO) - Progress... 33%\n", - "[15:41:47] Simulation (INFO) - Progress... 33%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.031036376953125\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9917770056878091\n", - "[15:41:47] Simulation (INFO) - Progress... 34%\n", - "[15:41:47] Simulation (INFO) - Progress... 34%\n", - "[15:41:47] Simulation (INFO) - Progress... 34%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.034332275390625\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9910186053590768\n", - "[15:41:47] Simulation (INFO) - Progress... 34%\n", - "[15:41:47] Simulation (INFO) - Progress... 34%\n", - "[15:41:47] Simulation (INFO) - Progress... 34%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.035736083984375\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9903708169455654\n", - "[15:41:47] Simulation (INFO) - Progress... 35%\n", - "[15:41:47] Simulation (INFO) - Progress... 35%\n", - "[15:41:47] Simulation (INFO) - Progress... 35%\n", - "[15:41:47] MPS (INFO) - MPS size (MiB)=0.035736083984375\n", - "[15:41:47] MPS (INFO) - MPS fidelity=0.9903708169455654\n", - "[15:41:47] Simulation (INFO) - Progress... 35%\n", - "[15:41:47] Simulation (INFO) - Progress... 35%\n", - "[15:41:47] Simulation (INFO) - Progress... 35%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.035736083984375\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9903708169455654\n", - "[15:41:48] Simulation (INFO) - Progress... 36%\n", - "[15:41:48] Simulation (INFO) - Progress... 36%\n", - "[15:41:48] Simulation (INFO) - Progress... 36%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.035736083984375\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9903708169455652\n", - "[15:41:48] Simulation (INFO) - Progress... 36%\n", - "[15:41:48] Simulation (INFO) - Progress... 36%\n", - "[15:41:48] Simulation (INFO) - Progress... 36%\n", - "[15:41:48] Simulation (INFO) - Progress... 37%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.035797119140625\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9903708169455652\n", - "[15:41:48] Simulation (INFO) - Progress... 37%\n", - "[15:41:48] Simulation (INFO) - Progress... 37%\n", - "[15:41:48] Simulation (INFO) - Progress... 37%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.035919189453125\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9903708169455652\n", - "[15:41:48] Simulation (INFO) - Progress... 37%\n", - "[15:41:48] Simulation (INFO) - Progress... 37%\n", - "[15:41:48] Simulation (INFO) - Progress... 38%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.035919189453125\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9903532709139039\n", - "[15:41:48] Simulation (INFO) - Progress... 38%\n", - "[15:41:48] Simulation (INFO) - Progress... 38%\n", - "[15:41:48] Simulation (INFO) - Progress... 38%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.036163330078125\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9903532709139038\n", - "[15:41:48] Simulation (INFO) - Progress... 38%\n", - "[15:41:48] Simulation (INFO) - Progress... 38%\n", - "[15:41:48] Simulation (INFO) - Progress... 39%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.036651611328125\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9903532709139038\n", - "[15:41:48] Simulation (INFO) - Progress... 39%\n", - "[15:41:48] Simulation (INFO) - Progress... 39%\n", - "[15:41:48] Simulation (INFO) - Progress... 39%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.03765869140625\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9899182044513357\n", - "[15:41:48] Simulation (INFO) - Progress... 39%\n", - "[15:41:48] Simulation (INFO) - Progress... 40%\n", - "[15:41:48] Simulation (INFO) - Progress... 40%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.038116455078125\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9892830604520672\n", - "[15:41:48] Simulation (INFO) - Progress... 40%\n", - "[15:41:48] Simulation (INFO) - Progress... 40%\n", - "[15:41:48] Simulation (INFO) - Progress... 40%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.040313720703125\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9886878009898008\n", - "[15:41:48] Simulation (INFO) - Progress... 40%\n", - "[15:41:48] Simulation (INFO) - Progress... 41%\n", - "[15:41:48] Simulation (INFO) - Progress... 41%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.043121337890625\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9877332864162025\n", - "[15:41:48] Simulation (INFO) - Progress... 41%\n", - "[15:41:48] Simulation (INFO) - Progress... 41%\n", - "[15:41:48] Simulation (INFO) - Progress... 41%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.047698974609375\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9870492609117136\n", - "[15:41:48] Simulation (INFO) - Progress... 41%\n", - "[15:41:48] Simulation (INFO) - Progress... 42%\n", - "[15:41:48] Simulation (INFO) - Progress... 42%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.052581787109375\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9864155906572986\n", - "[15:41:48] Simulation (INFO) - Progress... 42%\n", - "[15:41:48] Simulation (INFO) - Progress... 42%\n", - "[15:41:48] Simulation (INFO) - Progress... 42%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.060150146484375\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.985553172457638\n", - "[15:41:48] Simulation (INFO) - Progress... 42%\n", - "[15:41:48] Simulation (INFO) - Progress... 43%\n", - "[15:41:48] Simulation (INFO) - Progress... 43%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.066925048828125\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9848524485301354\n", - "[15:41:48] Simulation (INFO) - Progress... 43%\n", - "[15:41:48] Simulation (INFO) - Progress... 43%\n", - "[15:41:48] Simulation (INFO) - Progress... 43%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.068695068359375\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9848524485301354\n", - "[15:41:48] Simulation (INFO) - Progress... 43%\n", - "[15:41:48] Simulation (INFO) - Progress... 44%\n", - "[15:41:48] Simulation (INFO) - Progress... 44%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.068695068359375\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9848524485301353\n", - "[15:41:48] Simulation (INFO) - Progress... 44%\n", - "[15:41:48] Simulation (INFO) - Progress... 44%\n", - "[15:41:48] Simulation (INFO) - Progress... 44%\n", - "[15:41:48] MPS (INFO) - MPS size (MiB)=0.068695068359375\n", - "[15:41:48] MPS (INFO) - MPS fidelity=0.9848524485301353\n", - "[15:41:48] Simulation (INFO) - Progress... 44%\n", - "[15:41:48] Simulation (INFO) - Progress... 45%\n", - "[15:41:48] Simulation (INFO) - Progress... 45%\n", - "[15:41:48] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:48] MPS (INFO) - Fidelity before optimisation=0.9848524485301353\n", - "[15:41:48] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:48] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9873602516768857\n", - "[15:41:48] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:48] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9874400577886869\n", - "[15:41:48] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:48] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9874665951742544\n", - "[15:41:48] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:49] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.987479443857063\n", - "[15:41:49] MPS (INFO) - Final fidelity after optimisation=0.987479443857063\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.068695068359375\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", - "[15:41:49] Simulation (INFO) - Progress... 45%\n", - "[15:41:49] Simulation (INFO) - Progress... 45%\n", - "[15:41:49] Simulation (INFO) - Progress... 45%\n", - "[15:41:49] Simulation (INFO) - Progress... 45%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.068756103515625\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", - "[15:41:49] Simulation (INFO) - Progress... 46%\n", - "[15:41:49] Simulation (INFO) - Progress... 46%\n", - "[15:41:49] Simulation (INFO) - Progress... 46%\n", - "[15:41:49] Simulation (INFO) - Progress... 46%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.068878173828125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", - "[15:41:49] Simulation (INFO) - Progress... 46%\n", - "[15:41:49] Simulation (INFO) - Progress... 46%\n", - "[15:41:49] Simulation (INFO) - Progress... 47%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.068878173828125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", - "[15:41:49] Simulation (INFO) - Progress... 47%\n", - "[15:41:49] Simulation (INFO) - Progress... 47%\n", - "[15:41:49] Simulation (INFO) - Progress... 47%\n", - "[15:41:49] Simulation (INFO) - Progress... 47%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.069122314453125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", - "[15:41:49] Simulation (INFO) - Progress... 47%\n", - "[15:41:49] Simulation (INFO) - Progress... 48%\n", - "[15:41:49] Simulation (INFO) - Progress... 48%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.069488525390625\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.987479443857063\n", - "[15:41:49] Simulation (INFO) - Progress... 48%\n", - "[15:41:49] Simulation (INFO) - Progress... 48%\n", - "[15:41:49] Simulation (INFO) - Progress... 48%\n", - "[15:41:49] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:49] MPS (INFO) - Fidelity before optimisation=0.987479443857063\n", - "[15:41:49] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:49] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9874794438570587\n", - "[15:41:49] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:49] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9874794438570632\n", - "[15:41:49] MPS (INFO) - Final fidelity after optimisation=0.9874794438570632\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.069488525390625\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570632\n", - "[15:41:49] Simulation (INFO) - Progress... 48%\n", - "[15:41:49] Simulation (INFO) - Progress... 49%\n", - "[15:41:49] Simulation (INFO) - Progress... 49%\n", - "[15:41:49] Simulation (INFO) - Progress... 49%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.069854736328125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570632\n", - "[15:41:49] Simulation (INFO) - Progress... 49%\n", - "[15:41:49] Simulation (INFO) - Progress... 49%\n", - "[15:41:49] Simulation (INFO) - Progress... 50%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.070831298828125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570634\n", - "[15:41:49] Simulation (INFO) - Progress... 50%\n", - "[15:41:49] Simulation (INFO) - Progress... 50%\n", - "[15:41:49] Simulation (INFO) - Progress... 50%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.070831298828125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570634\n", - "[15:41:49] Simulation (INFO) - Progress... 50%\n", - "[15:41:49] Simulation (INFO) - Progress... 50%\n", - "[15:41:49] Simulation (INFO) - Progress... 51%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.070831298828125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570637\n", - "[15:41:49] Simulation (INFO) - Progress... 51%\n", - "[15:41:49] Simulation (INFO) - Progress... 51%\n", - "[15:41:49] Simulation (INFO) - Progress... 51%\n", - "[15:41:49] Simulation (INFO) - Progress... 51%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.071319580078125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570637\n", - "[15:41:49] Simulation (INFO) - Progress... 51%\n", - "[15:41:49] Simulation (INFO) - Progress... 52%\n", - "[15:41:49] Simulation (INFO) - Progress... 52%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.072784423828125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9874794438570637\n", - "[15:41:49] Simulation (INFO) - Progress... 52%\n", - "[15:41:49] Simulation (INFO) - Progress... 52%\n", - "[15:41:49] Simulation (INFO) - Progress... 52%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.072540283203125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9866330729559818\n", - "[15:41:49] Simulation (INFO) - Progress... 52%\n", - "[15:41:49] Simulation (INFO) - Progress... 53%\n", - "[15:41:49] Simulation (INFO) - Progress... 53%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.073211669921875\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9866330729559818\n", - "[15:41:49] Simulation (INFO) - Progress... 53%\n", - "[15:41:49] Simulation (INFO) - Progress... 53%\n", - "[15:41:49] Simulation (INFO) - Progress... 53%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.073822021484375\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9866330729559818\n", - "[15:41:49] Simulation (INFO) - Progress... 53%\n", - "[15:41:49] Simulation (INFO) - Progress... 54%\n", - "[15:41:49] Simulation (INFO) - Progress... 54%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.074920654296875\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9866330729559817\n", - "[15:41:49] Simulation (INFO) - Progress... 54%\n", - "[15:41:49] Simulation (INFO) - Progress... 54%\n", - "[15:41:49] Simulation (INFO) - Progress... 54%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.074920654296875\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.985728058386732\n", - "[15:41:49] Simulation (INFO) - Progress... 54%\n", - "[15:41:49] Simulation (INFO) - Progress... 55%\n", - "[15:41:49] Simulation (INFO) - Progress... 55%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.076507568359375\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9848905743017655\n", - "[15:41:49] Simulation (INFO) - Progress... 55%\n", - "[15:41:49] Simulation (INFO) - Progress... 55%\n", - "[15:41:49] Simulation (INFO) - Progress... 55%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.0782470703125\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.984200933510651\n", - "[15:41:49] Simulation (INFO) - Progress... 55%\n", - "[15:41:49] Simulation (INFO) - Progress... 56%\n", - "[15:41:49] Simulation (INFO) - Progress... 56%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.08209228515625\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9833786711539604\n", - "[15:41:49] Simulation (INFO) - Progress... 56%\n", - "[15:41:49] Simulation (INFO) - Progress... 56%\n", - "[15:41:49] Simulation (INFO) - Progress... 56%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.08514404296875\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9829054162238393\n", - "[15:41:49] Simulation (INFO) - Progress... 56%\n", - "[15:41:49] Simulation (INFO) - Progress... 57%\n", - "[15:41:49] Simulation (INFO) - Progress... 57%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.093994140625\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9823548457232609\n", - "[15:41:49] Simulation (INFO) - Progress... 57%\n", - "[15:41:49] Simulation (INFO) - Progress... 57%\n", - "[15:41:49] Simulation (INFO) - Progress... 57%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.10003662109375\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9814914262501793\n", - "[15:41:49] Simulation (INFO) - Progress... 57%\n", - "[15:41:49] Simulation (INFO) - Progress... 58%\n", - "[15:41:49] Simulation (INFO) - Progress... 58%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.116302490234375\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9806647381641405\n", - "[15:41:49] Simulation (INFO) - Progress... 58%\n", - "[15:41:49] Simulation (INFO) - Progress... 58%\n", - "[15:41:49] Simulation (INFO) - Progress... 58%\n", - "[15:41:49] MPS (INFO) - MPS size (MiB)=0.118499755859375\n", - "[15:41:49] MPS (INFO) - MPS fidelity=0.9797474281526156\n", - "[15:41:50] Simulation (INFO) - Progress... 58%\n", - "[15:41:50] Simulation (INFO) - Progress... 59%\n", - "[15:41:50] Simulation (INFO) - Progress... 59%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.137542724609375\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9792041496059278\n", - "[15:41:50] Simulation (INFO) - Progress... 59%\n", - "[15:41:50] Simulation (INFO) - Progress... 59%\n", - "[15:41:50] Simulation (INFO) - Progress... 59%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.150360107421875\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9784485799686532\n", - "[15:41:50] Simulation (INFO) - Progress... 60%\n", - "[15:41:50] Simulation (INFO) - Progress... 60%\n", - "[15:41:50] Simulation (INFO) - Progress... 60%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.175567626953125\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.977661825354294\n", - "[15:41:50] Simulation (INFO) - Progress... 60%\n", - "[15:41:50] Simulation (INFO) - Progress... 60%\n", - "[15:41:50] Simulation (INFO) - Progress... 60%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.192779541015625\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9768742140627227\n", - "[15:41:50] Simulation (INFO) - Progress... 61%\n", - "[15:41:50] Simulation (INFO) - Progress... 61%\n", - "[15:41:50] Simulation (INFO) - Progress... 61%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.236358642578125\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9761008623122066\n", - "[15:41:50] Simulation (INFO) - Progress... 61%\n", - "[15:41:50] Simulation (INFO) - Progress... 61%\n", - "[15:41:50] Simulation (INFO) - Progress... 61%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.24725341796875\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9752497693902976\n", - "[15:41:50] Simulation (INFO) - Progress... 62%\n", - "[15:41:50] Simulation (INFO) - Progress... 62%\n", - "[15:41:50] Simulation (INFO) - Progress... 62%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.277008056640625\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208022\n", - "[15:41:50] Simulation (INFO) - Progress... 62%\n", - "[15:41:50] Simulation (INFO) - Progress... 62%\n", - "[15:41:50] Simulation (INFO) - Progress... 62%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.277008056640625\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208022\n", - "[15:41:50] Simulation (INFO) - Progress... 63%\n", - "[15:41:50] Simulation (INFO) - Progress... 63%\n", - "[15:41:50] Simulation (INFO) - Progress... 63%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208022\n", - "[15:41:50] Simulation (INFO) - Progress... 63%\n", - "[15:41:50] Simulation (INFO) - Progress... 63%\n", - "[15:41:50] Simulation (INFO) - Progress... 63%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208022\n", - "[15:41:50] Simulation (INFO) - Progress... 64%\n", - "[15:41:50] Simulation (INFO) - Progress... 64%\n", - "[15:41:50] Simulation (INFO) - Progress... 64%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208022\n", - "[15:41:50] Simulation (INFO) - Progress... 64%\n", - "[15:41:50] Simulation (INFO) - Progress... 64%\n", - "[15:41:50] Simulation (INFO) - Progress... 64%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208021\n", - "[15:41:50] Simulation (INFO) - Progress... 65%\n", - "[15:41:50] Simulation (INFO) - Progress... 65%\n", - "[15:41:50] Simulation (INFO) - Progress... 65%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208021\n", - "[15:41:50] Simulation (INFO) - Progress... 65%\n", - "[15:41:50] Simulation (INFO) - Progress... 65%\n", - "[15:41:50] Simulation (INFO) - Progress... 65%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208023\n", - "[15:41:50] Simulation (INFO) - Progress... 66%\n", - "[15:41:50] Simulation (INFO) - Progress... 66%\n", - "[15:41:50] Simulation (INFO) - Progress... 66%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284820556640625\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9744546401208023\n", - "[15:41:50] Simulation (INFO) - Progress... 66%\n", - "[15:41:50] MPS (INFO) - MPS size (MiB)=0.284454345703125\n", - "[15:41:50] MPS (INFO) - MPS fidelity=0.9736774416720088\n", - "[15:41:50] Simulation (INFO) - Progress... 66%\n", - "[15:41:50] Simulation (INFO) - Progress... 66%\n", - "[15:41:50] Simulation (INFO) - Progress... 67%\n", - "[15:41:50] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:50] MPS (INFO) - Fidelity before optimisation=0.9736774416720088\n", - "[15:41:50] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9822122042244481\n", - "[15:41:50] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9827583003913757\n", - "[15:41:50] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9829168903407746\n", - "[15:41:50] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.982979344248083\n", - "[15:41:51] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9830095477136648\n", - "[15:41:51] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9830260810282695\n", - "[15:41:51] MPS (INFO) - Final fidelity after optimisation=0.9830260810282695\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.284454345703125\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9825960930899819\n", - "[15:41:51] Simulation (INFO) - Progress... 67%\n", - "[15:41:51] Simulation (INFO) - Progress... 67%\n", - "[15:41:51] Simulation (INFO) - Progress... 67%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.285736083984375\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9819799784936092\n", - "[15:41:51] Simulation (INFO) - Progress... 67%\n", - "[15:41:51] Simulation (INFO) - Progress... 67%\n", - "[15:41:51] Simulation (INFO) - Progress... 68%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.288055419921875\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9811857364040928\n", - "[15:41:51] Simulation (INFO) - Progress... 68%\n", - "[15:41:51] Simulation (INFO) - Progress... 68%\n", - "[15:41:51] Simulation (INFO) - Progress... 68%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.2901611328125\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9805589329022365\n", - "[15:41:51] Simulation (INFO) - Progress... 68%\n", - "[15:41:51] Simulation (INFO) - Progress... 68%\n", - "[15:41:51] Simulation (INFO) - Progress... 69%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.29742431640625\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.979774777914149\n", - "[15:41:51] Simulation (INFO) - Progress... 69%\n", - "[15:41:51] Simulation (INFO) - Progress... 69%\n", - "[15:41:51] Simulation (INFO) - Progress... 69%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.3004150390625\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9790302714655779\n", - "[15:41:51] Simulation (INFO) - Progress... 69%\n", - "[15:41:51] Simulation (INFO) - Progress... 70%\n", - "[15:41:51] Simulation (INFO) - Progress... 70%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.3072509765625\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9781050017075105\n", - "[15:41:51] Simulation (INFO) - Progress... 70%\n", - "[15:41:51] Simulation (INFO) - Progress... 70%\n", - "[15:41:51] Simulation (INFO) - Progress... 70%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.32537841796875\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9772629361326794\n", - "[15:41:51] Simulation (INFO) - Progress... 70%\n", - "[15:41:51] Simulation (INFO) - Progress... 71%\n", - "[15:41:51] Simulation (INFO) - Progress... 71%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.349822998046875\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9763363776760228\n", - "[15:41:51] Simulation (INFO) - Progress... 71%\n", - "[15:41:51] Simulation (INFO) - Progress... 71%\n", - "[15:41:51] Simulation (INFO) - Progress... 71%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.358062744140625\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9754556940251295\n", - "[15:41:51] Simulation (INFO) - Progress... 71%\n", - "[15:41:51] Simulation (INFO) - Progress... 72%\n", - "[15:41:51] Simulation (INFO) - Progress... 72%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.365570068359375\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9745025792245486\n", - "[15:41:51] Simulation (INFO) - Progress... 72%\n", - "[15:41:51] Simulation (INFO) - Progress... 72%\n", - "[15:41:51] Simulation (INFO) - Progress... 72%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.365570068359375\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9745025792245484\n", - "[15:41:51] Simulation (INFO) - Progress... 72%\n", - "[15:41:51] Simulation (INFO) - Progress... 73%\n", - "[15:41:51] Simulation (INFO) - Progress... 73%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.365570068359375\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9745025792245484\n", - "[15:41:51] Simulation (INFO) - Progress... 73%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.365570068359375\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9745025792245482\n", - "[15:41:51] Simulation (INFO) - Progress... 73%\n", - "[15:41:51] Simulation (INFO) - Progress... 73%\n", - "[15:41:51] Simulation (INFO) - Progress... 73%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.365936279296875\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9745025792245482\n", - "[15:41:51] Simulation (INFO) - Progress... 74%\n", - "[15:41:51] Simulation (INFO) - Progress... 74%\n", - "[15:41:51] Simulation (INFO) - Progress... 74%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.366973876953125\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9739737340007235\n", - "[15:41:51] Simulation (INFO) - Progress... 74%\n", - "[15:41:51] Simulation (INFO) - Progress... 74%\n", - "[15:41:51] Simulation (INFO) - Progress... 74%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.369415283203125\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9734939221919511\n", - "[15:41:51] Simulation (INFO) - Progress... 75%\n", - "[15:41:51] Simulation (INFO) - Progress... 75%\n", - "[15:41:51] Simulation (INFO) - Progress... 75%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.374176025390625\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9728701873772134\n", - "[15:41:51] Simulation (INFO) - Progress... 75%\n", - "[15:41:51] Simulation (INFO) - Progress... 75%\n", - "[15:41:51] Simulation (INFO) - Progress... 75%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.378570556640625\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9720376362143338\n", - "[15:41:51] Simulation (INFO) - Progress... 76%\n", - "[15:41:51] Simulation (INFO) - Progress... 76%\n", - "[15:41:51] Simulation (INFO) - Progress... 76%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.383453369140625\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.971167337001675\n", - "[15:41:51] Simulation (INFO) - Progress... 76%\n", - "[15:41:51] Simulation (INFO) - Progress... 76%\n", - "[15:41:51] Simulation (INFO) - Progress... 76%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.39910888671875\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9703795628080001\n", - "[15:41:51] Simulation (INFO) - Progress... 77%\n", - "[15:41:51] Simulation (INFO) - Progress... 77%\n", - "[15:41:51] Simulation (INFO) - Progress... 77%\n", - "[15:41:51] MPS (INFO) - MPS size (MiB)=0.43072509765625\n", - "[15:41:51] MPS (INFO) - MPS fidelity=0.9695212202086415\n", - "[15:41:51] Simulation (INFO) - Progress... 77%\n", - "[15:41:51] Simulation (INFO) - Progress... 77%\n", - "[15:41:51] Simulation (INFO) - Progress... 77%\n", - "[15:41:52] MPS (INFO) - MPS size (MiB)=0.490478515625\n", - "[15:41:52] MPS (INFO) - MPS fidelity=0.9686850339371813\n", - "[15:41:52] Simulation (INFO) - Progress... 78%\n", - "[15:41:52] Simulation (INFO) - Progress... 78%\n", - "[15:41:52] Simulation (INFO) - Progress... 78%\n", - "[15:41:52] MPS (INFO) - MPS size (MiB)=0.5670166015625\n", - "[15:41:52] MPS (INFO) - MPS fidelity=0.9678550687968107\n", - "[15:41:52] Simulation (INFO) - Progress... 78%\n", - "[15:41:52] Simulation (INFO) - Progress... 78%\n", - "[15:41:52] Simulation (INFO) - Progress... 78%\n", - "[15:41:52] MPS (INFO) - MPS size (MiB)=0.61614990234375\n", - "[15:41:52] MPS (INFO) - MPS fidelity=0.9669360842897095\n", - "[15:41:52] Simulation (INFO) - Progress... 79%\n", - "[15:41:52] Simulation (INFO) - Progress... 79%\n", - "[15:41:52] Simulation (INFO) - Progress... 79%\n", - "[15:41:52] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", - "[15:41:52] MPS (INFO) - MPS fidelity=0.9660548082975919\n", - "[15:41:52] Simulation (INFO) - Progress... 79%\n", - "[15:41:52] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", - "[15:41:52] MPS (INFO) - MPS fidelity=0.9660548082975919\n", - "[15:41:52] Simulation (INFO) - Progress... 79%\n", - "[15:41:52] Simulation (INFO) - Progress... 80%\n", - "[15:41:52] Simulation (INFO) - Progress... 80%\n", - "[15:41:52] Simulation (INFO) - Progress... 80%\n", - "[15:41:52] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", - "[15:41:52] MPS (INFO) - MPS fidelity=0.9660548082975922\n", - "[15:41:52] Simulation (INFO) - Progress... 80%\n", - "[15:41:52] Simulation (INFO) - Progress... 80%\n", - "[15:41:52] Simulation (INFO) - Progress... 80%\n", - "[15:41:52] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", - "[15:41:52] MPS (INFO) - MPS fidelity=0.9660548082975922\n", - "[15:41:52] Simulation (INFO) - Progress... 81%\n", - "[15:41:52] Simulation (INFO) - Progress... 81%\n", - "[15:41:52] Simulation (INFO) - Progress... 81%\n", - "[15:41:52] Simulation (INFO) - Progress... 81%\n", - "[15:41:52] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", - "[15:41:52] MPS (INFO) - MPS fidelity=0.9660548082975922\n", - "[15:41:52] Simulation (INFO) - Progress... 81%\n", - "[15:41:52] Simulation (INFO) - Progress... 81%\n", - "[15:41:52] Simulation (INFO) - Progress... 82%\n", - "[15:41:52] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:52] MPS (INFO) - Fidelity before optimisation=0.9660548082975922\n", - "[15:41:52] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9714782872349863\n", - "[15:41:52] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9716864883910468\n", - "[15:41:52] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9717552987705841\n", - "[15:41:52] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:53] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9717903497055657\n", - "[15:41:53] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:53] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9718115474633439\n", - "[15:41:53] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:53] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9718256606609913\n", - "[15:41:53] MPS (INFO) - Final fidelity after optimisation=0.9718256606609913\n", - "[15:41:53] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", - "[15:41:53] MPS (INFO) - MPS fidelity=0.9718256606609913\n", - "[15:41:53] Simulation (INFO) - Progress... 82%\n", - "[15:41:53] Simulation (INFO) - Progress... 82%\n", - "[15:41:53] Simulation (INFO) - Progress... 82%\n", - "[15:41:53] MPS (INFO) - MPS size (MiB)=0.64251708984375\n", - "[15:41:53] MPS (INFO) - MPS fidelity=0.9718256606609913\n", - "[15:41:53] Simulation (INFO) - Progress... 82%\n", - "[15:41:53] Simulation (INFO) - Progress... 82%\n", - "[15:41:53] Simulation (INFO) - Progress... 83%\n", - "[15:41:53] Simulation (INFO) - Progress... 83%\n", - "[15:41:53] MPS (INFO) - MPS size (MiB)=0.645721435546875\n", - "[15:41:53] MPS (INFO) - MPS fidelity=0.971304662959029\n", - "[15:41:53] Simulation (INFO) - Progress... 83%\n", - "[15:41:53] Simulation (INFO) - Progress... 83%\n", - "[15:41:53] Simulation (INFO) - Progress... 83%\n", - "[15:41:53] MPS (INFO) - MPS size (MiB)=0.645721435546875\n", - "[15:41:53] MPS (INFO) - MPS fidelity=0.971304662959029\n", - "[15:41:53] Simulation (INFO) - Progress... 83%\n", - "[15:41:53] Simulation (INFO) - Progress... 84%\n", - "[15:41:53] Simulation (INFO) - Progress... 84%\n", - "[15:41:53] MPS (INFO) - MPS size (MiB)=0.645721435546875\n", - "[15:41:53] MPS (INFO) - MPS fidelity=0.9713046629590292\n", - "[15:41:53] Simulation (INFO) - Progress... 84%\n", - "[15:41:53] Simulation (INFO) - Progress... 84%\n", - "[15:41:53] Simulation (INFO) - Progress... 84%\n", - "[15:41:53] MPS (INFO) - MPS size (MiB)=0.645721435546875\n", - "[15:41:53] MPS (INFO) - MPS fidelity=0.9713046629590292\n", - "[15:41:53] Simulation (INFO) - Progress... 84%\n", - "[15:41:53] Simulation (INFO) - Progress... 85%\n", - "[15:41:53] Simulation (INFO) - Progress... 85%\n", - "[15:41:53] Simulation (INFO) - Progress... 85%\n", - "[15:41:53] MPS (INFO) - MPS size (MiB)=0.65234375\n", - "[15:41:53] MPS (INFO) - MPS fidelity=0.9705519636179583\n", - "[15:41:53] Simulation (INFO) - Progress... 85%\n", - "[15:41:53] Simulation (INFO) - Progress... 85%\n", - "[15:41:53] Simulation (INFO) - Progress... 85%\n", - "[15:41:53] MPS (INFO) - MPS size (MiB)=0.6531982421875\n", - "[15:41:53] MPS (INFO) - MPS fidelity=0.9705519636179583\n", - "[15:41:53] Simulation (INFO) - Progress... 86%\n", - "[15:41:53] Simulation (INFO) - Progress... 86%\n", - "[15:41:53] Simulation (INFO) - Progress... 86%\n", - "[15:41:53] MPS (INFO) - MPS size (MiB)=0.6531982421875\n", - "[15:41:53] MPS (INFO) - MPS fidelity=0.9705519636179583\n", - "[15:41:53] Simulation (INFO) - Progress... 86%\n", - "[15:41:53] Simulation (INFO) - Progress... 86%\n", - "[15:41:53] Simulation (INFO) - Progress... 86%\n", - "[15:41:53] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:53] MPS (INFO) - Fidelity before optimisation=0.9705519636179583\n", - "[15:41:53] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:53] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9710393809351289\n", - "[15:41:53] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9710417093966089\n", - "[15:41:54] MPS (INFO) - Final fidelity after optimisation=0.9710417093966089\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.6531982421875\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9710417093966089\n", - "[15:41:54] Simulation (INFO) - Progress... 87%\n", - "[15:41:54] Simulation (INFO) - Progress... 87%\n", - "[15:41:54] Simulation (INFO) - Progress... 87%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.6531982421875\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9710417093966089\n", - "[15:41:54] Simulation (INFO) - Progress... 87%\n", - "[15:41:54] Simulation (INFO) - Progress... 87%\n", - "[15:41:54] Simulation (INFO) - Progress... 87%\n", - "[15:41:54] Simulation (INFO) - Progress... 88%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.663360595703125\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9703316436673765\n", - "[15:41:54] Simulation (INFO) - Progress... 88%\n", - "[15:41:54] Simulation (INFO) - Progress... 88%\n", - "[15:41:54] Simulation (INFO) - Progress... 88%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.6771240234375\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9697826685947312\n", - "[15:41:54] Simulation (INFO) - Progress... 88%\n", - "[15:41:54] Simulation (INFO) - Progress... 88%\n", - "[15:41:54] Simulation (INFO) - Progress... 89%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.6890869140625\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9688822088585105\n", - "[15:41:54] Simulation (INFO) - Progress... 89%\n", - "[15:41:54] Simulation (INFO) - Progress... 89%\n", - "[15:41:54] Simulation (INFO) - Progress... 89%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.7198486328125\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9681016107658179\n", - "[15:41:54] Simulation (INFO) - Progress... 89%\n", - "[15:41:54] Simulation (INFO) - Progress... 90%\n", - "[15:41:54] Simulation (INFO) - Progress... 90%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.732025146484375\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.967139989859211\n", - "[15:41:54] Simulation (INFO) - Progress... 90%\n", - "[15:41:54] Simulation (INFO) - Progress... 90%\n", - "[15:41:54] Simulation (INFO) - Progress... 90%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.786224365234375\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9667532538346312\n", - "[15:41:54] Simulation (INFO) - Progress... 90%\n", - "[15:41:54] Simulation (INFO) - Progress... 91%\n", - "[15:41:54] Simulation (INFO) - Progress... 91%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.805267333984375\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9657875697333652\n", - "[15:41:54] Simulation (INFO) - Progress... 91%\n", - "[15:41:54] Simulation (INFO) - Progress... 91%\n", - "[15:41:54] Simulation (INFO) - Progress... 91%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.870452880859375\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9649987228797965\n", - "[15:41:54] Simulation (INFO) - Progress... 91%\n", - "[15:41:54] Simulation (INFO) - Progress... 92%\n", - "[15:41:54] Simulation (INFO) - Progress... 92%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=0.927581787109375\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9641126521361515\n", - "[15:41:54] Simulation (INFO) - Progress... 92%\n", - "[15:41:54] Simulation (INFO) - Progress... 92%\n", - "[15:41:54] Simulation (INFO) - Progress... 92%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=1.066741943359375\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9635105846805408\n", - "[15:41:54] Simulation (INFO) - Progress... 92%\n", - "[15:41:54] Simulation (INFO) - Progress... 93%\n", - "[15:41:54] Simulation (INFO) - Progress... 93%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=1.15728759765625\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.962589075282592\n", - "[15:41:54] Simulation (INFO) - Progress... 93%\n", - "[15:41:54] Simulation (INFO) - Progress... 93%\n", - "[15:41:54] Simulation (INFO) - Progress... 93%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=1.43927001953125\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9617602212979602\n", - "[15:41:54] Simulation (INFO) - Progress... 93%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=1.54986572265625\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9608510935810075\n", - "[15:41:54] Simulation (INFO) - Progress... 94%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=1.54986572265625\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9608510935810075\n", - "[15:41:54] Simulation (INFO) - Progress... 94%\n", - "[15:41:54] Simulation (INFO) - Progress... 94%\n", - "[15:41:54] Simulation (INFO) - Progress... 94%\n", - "[15:41:54] MPS (INFO) - MPS size (MiB)=1.54986572265625\n", - "[15:41:54] MPS (INFO) - MPS fidelity=0.9600320773213169\n", - "[15:41:54] Simulation (INFO) - Progress... 94%\n", - "[15:41:54] Simulation (INFO) - Progress... 94%\n", - "[15:41:54] Simulation (INFO) - Progress... 95%\n", - "[15:41:54] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:54] MPS (INFO) - Fidelity before optimisation=0.9600320773213169\n", - "[15:41:54] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9659019975820374\n", - "[15:41:54] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9661427864728673\n", - "[15:41:54] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9662148782989015\n", - "[15:41:55] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9662495844052902\n", - "[15:41:55] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9662703863336176\n", - "[15:41:55] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9662844523829522\n", - "[15:41:55] MPS (INFO) - Final fidelity after optimisation=0.9662844523829522\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.55718994140625\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9653257816354918\n", - "[15:41:55] Simulation (INFO) - Progress... 95%\n", - "[15:41:55] Simulation (INFO) - Progress... 95%\n", - "[15:41:55] Simulation (INFO) - Progress... 95%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.58184814453125\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9644004614054085\n", - "[15:41:55] Simulation (INFO) - Progress... 95%\n", - "[15:41:55] Simulation (INFO) - Progress... 95%\n", - "[15:41:55] Simulation (INFO) - Progress... 96%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.660125732421875\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9634633842376111\n", - "[15:41:55] Simulation (INFO) - Progress... 96%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.660125732421875\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9634633842376111\n", - "[15:41:55] Simulation (INFO) - Progress... 96%\n", - "[15:41:55] Simulation (INFO) - Progress... 96%\n", - "[15:41:55] Simulation (INFO) - Progress... 96%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.660125732421875\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9634633842376111\n", - "[15:41:55] Simulation (INFO) - Progress... 96%\n", - "[15:41:55] Simulation (INFO) - Progress... 97%\n", - "[15:41:55] Simulation (INFO) - Progress... 97%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.662017822265625\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9634633842376114\n", - "[15:41:55] Simulation (INFO) - Progress... 97%\n", - "[15:41:55] Simulation (INFO) - Progress... 97%\n", - "[15:41:55] Simulation (INFO) - Progress... 97%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465783\n", - "[15:41:55] Simulation (INFO) - Progress... 97%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465783\n", - "[15:41:55] Simulation (INFO) - Progress... 98%\n", - "[15:41:55] Simulation (INFO) - Progress... 98%\n", - "[15:41:55] Simulation (INFO) - Progress... 98%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465782\n", - "[15:41:55] Simulation (INFO) - Progress... 98%\n", - "[15:41:55] Simulation (INFO) - Progress... 98%\n", - "[15:41:55] Simulation (INFO) - Progress... 98%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465782\n", - "[15:41:55] Simulation (INFO) - Progress... 99%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465782\n", - "[15:41:55] Simulation (INFO) - Progress... 99%\n", - "[15:41:55] Simulation (INFO) - Progress... 99%\n", - "[15:41:55] Simulation (INFO) - Progress... 99%\n", - "[15:41:55] MPS (INFO) - MPS size (MiB)=1.700042724609375\n", - "[15:41:55] MPS (INFO) - MPS fidelity=0.9625939072465782\n", - "[15:41:55] Simulation (INFO) - Progress... 99%\n", - "[15:41:55] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:55] MPS (INFO) - Fidelity before optimisation=0.9625939072465782\n", - "[15:41:56] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9640884677171835\n", - "[15:41:56] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9641174266253738\n", - "[15:41:56] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9641252811032455\n", - "[15:41:56] MPS (INFO) - Final fidelity after optimisation=0.9641252811032455\n", - "[15:41:56] MPS (INFO) - Applying variational optimisation.\n", - "[15:41:56] MPS (INFO) - Fidelity before optimisation=0.9641252811032455\n", - "[15:41:56] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9641252811032449\n", - "[15:41:56] MPS (INFO) - Doing another optimisation sweep...\n", - "[15:41:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.964125281103245\n", - "[15:41:56] MPS (INFO) - Final fidelity after optimisation=0.964125281103245\n", - "[15:41:56] Simulation (INFO) - Simulation completed.\n", - "[15:41:56] Simulation (INFO) - Final MPS size=1.700042724609375 MiB\n", - "[15:41:56] Simulation (INFO) - Final MPS fidelity=0.964125281103245\n" + "[13:07:49] Simulation (INFO) - Ordering the gates in the circuit to reduce canonicalisation overhead.\n", + "[13:07:49] Simulation (INFO) - Running simulation...\n", + "[13:07:49] Simulation (INFO) - Progress... 0%\n", + "[13:07:49] Simulation (INFO) - Progress... 0%\n", + "[13:07:49] Simulation (INFO) - Progress... 0%\n", + "[13:07:49] Simulation (INFO) - Progress... 0%\n", + "[13:07:49] Simulation (INFO) - Progress... 0%\n", + "[13:07:49] Simulation (INFO) - Progress... 0%\n", + "[13:07:49] Simulation (INFO) - Progress... 1%\n", + "[13:07:49] Simulation (INFO) - Progress... 1%\n", + "[13:07:49] Simulation (INFO) - Progress... 1%\n", + "[13:07:49] Simulation (INFO) - Progress... 1%\n", + "[13:07:49] Simulation (INFO) - Progress... 1%\n", + "[13:07:49] Simulation (INFO) - Progress... 1%\n", + "[13:07:49] Simulation (INFO) - Progress... 2%\n", + "[13:07:49] Simulation (INFO) - Progress... 2%\n", + "[13:07:49] Simulation (INFO) - Progress... 2%\n", + "[13:07:49] Simulation (INFO) - Progress... 2%\n", + "[13:07:49] Simulation (INFO) - Progress... 2%\n", + "[13:07:49] Simulation (INFO) - Progress... 2%\n", + "[13:07:49] Simulation (INFO) - Progress... 3%\n", + "[13:07:49] Simulation (INFO) - Progress... 3%\n", + "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00067138671875\n", + "[13:07:49] MPS (INFO) - MPS fidelity=1.0\n", + "[13:07:49] Simulation (INFO) - Progress... 3%\n", + "[13:07:49] Simulation (INFO) - Progress... 3%\n", + "[13:07:49] Simulation (INFO) - Progress... 3%\n", + "[13:07:49] Simulation (INFO) - Progress... 3%\n", + "[13:07:49] MPS (INFO) - MPS size (MiB)=0.000732421875\n", + "[13:07:49] MPS (INFO) - MPS fidelity=0.9999999999999999\n", + "[13:07:49] Simulation (INFO) - Progress... 4%\n", + "[13:07:49] Simulation (INFO) - Progress... 4%\n", + "[13:07:49] Simulation (INFO) - Progress... 4%\n", + "[13:07:49] MPS (INFO) - MPS size (MiB)=0.0008544921875\n", + "[13:07:49] MPS (INFO) - MPS fidelity=0.9999999999999999\n", + "[13:07:49] Simulation (INFO) - Progress... 4%\n", + "[13:07:49] Simulation (INFO) - Progress... 4%\n", + "[13:07:49] Simulation (INFO) - Progress... 4%\n", + "[13:07:49] MPS (INFO) - MPS size (MiB)=0.0008544921875\n", + "[13:07:49] MPS (INFO) - MPS fidelity=0.9999999999999999\n", + "[13:07:49] Simulation (INFO) - Progress... 5%\n", + "[13:07:49] Simulation (INFO) - Progress... 5%\n", + "[13:07:49] Simulation (INFO) - Progress... 5%\n", + "[13:07:49] Simulation (INFO) - Progress... 5%\n", + "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00091552734375\n", + "[13:07:49] MPS (INFO) - MPS fidelity=0.9999999999999999\n", + "[13:07:49] Simulation (INFO) - Progress... 5%\n", + "[13:07:49] Simulation (INFO) - Progress... 5%\n", + "[13:07:49] Simulation (INFO) - Progress... 6%\n", + "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00103759765625\n", + "[13:07:49] MPS (INFO) - MPS fidelity=0.9999999999999999\n", + "[13:07:49] Simulation (INFO) - Progress... 6%\n", + "[13:07:49] Simulation (INFO) - Progress... 6%\n", + "[13:07:49] Simulation (INFO) - Progress... 6%\n", + "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00103759765625\n", + "[13:07:49] MPS (INFO) - MPS fidelity=0.9999217894870942\n", + "[13:07:49] Simulation (INFO) - Progress... 6%\n", + "[13:07:49] Simulation (INFO) - Progress... 6%\n", + "[13:07:49] Simulation (INFO) - Progress... 7%\n", + "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00115966796875\n", + "[13:07:49] MPS (INFO) - MPS fidelity=0.9997584263446058\n", + "[13:07:49] Simulation (INFO) - Progress... 7%\n", + "[13:07:49] Simulation (INFO) - Progress... 7%\n", + "[13:07:49] Simulation (INFO) - Progress... 7%\n", + "[13:07:49] MPS (INFO) - Applying variational optimisation.\n", + "[13:07:49] MPS (INFO) - Fidelity before optimisation=0.9997584263446058\n", + "[13:07:49] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:49] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.999879599684896\n", + "[13:07:49] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:49] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9998799542127541\n", + "[13:07:49] MPS (INFO) - Final fidelity after optimisation=0.9998799542127541\n", + "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00115966796875\n", + "[13:07:49] MPS (INFO) - MPS fidelity=0.9998799542127541\n", + "[13:07:49] Simulation (INFO) - Progress... 7%\n", + "[13:07:49] Simulation (INFO) - Progress... 7%\n", + "[13:07:49] Simulation (INFO) - Progress... 8%\n", + "[13:07:49] Simulation (INFO) - Progress... 8%\n", + "[13:07:49] MPS (INFO) - MPS size (MiB)=0.001220703125\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9998799542127541\n", + "[13:07:50] Simulation (INFO) - Progress... 8%\n", + "[13:07:50] Simulation (INFO) - Progress... 8%\n", + "[13:07:50] Simulation (INFO) - Progress... 8%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.0013427734375\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9998799542127541\n", + "[13:07:50] Simulation (INFO) - Progress... 8%\n", + "[13:07:50] Simulation (INFO) - Progress... 9%\n", + "[13:07:50] Simulation (INFO) - Progress... 9%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00146484375\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.998931558816809\n", + "[13:07:50] Simulation (INFO) - Progress... 9%\n", + "[13:07:50] Simulation (INFO) - Progress... 9%\n", + "[13:07:50] Simulation (INFO) - Progress... 9%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.001617431640625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361637\n", + "[13:07:50] Simulation (INFO) - Progress... 10%\n", + "[13:07:50] Simulation (INFO) - Progress... 10%\n", + "[13:07:50] Simulation (INFO) - Progress... 10%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.001983642578125\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361637\n", + "[13:07:50] Simulation (INFO) - Progress... 10%\n", + "[13:07:50] Simulation (INFO) - Progress... 10%\n", + "[13:07:50] Simulation (INFO) - Progress... 10%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002166748046875\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361637\n", + "[13:07:50] Simulation (INFO) - Progress... 11%\n", + "[13:07:50] Simulation (INFO) - Progress... 11%\n", + "[13:07:50] Simulation (INFO) - Progress... 11%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002166748046875\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361637\n", + "[13:07:50] Simulation (INFO) - Progress... 11%\n", + "[13:07:50] Simulation (INFO) - Progress... 11%\n", + "[13:07:50] Simulation (INFO) - Progress... 11%\n", + "[13:07:50] Simulation (INFO) - Progress... 12%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002227783203125\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361635\n", + "[13:07:50] Simulation (INFO) - Progress... 12%\n", + "[13:07:50] Simulation (INFO) - Progress... 12%\n", + "[13:07:50] Simulation (INFO) - Progress... 12%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002349853515625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361635\n", + "[13:07:50] Simulation (INFO) - Progress... 12%\n", + "[13:07:50] Simulation (INFO) - Progress... 12%\n", + "[13:07:50] Simulation (INFO) - Progress... 13%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002349853515625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9976865779910287\n", + "[13:07:50] Simulation (INFO) - Progress... 13%\n", + "[13:07:50] Simulation (INFO) - Progress... 13%\n", + "[13:07:50] Simulation (INFO) - Progress... 13%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002655029296875\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9976865779910287\n", + "[13:07:50] Simulation (INFO) - Progress... 13%\n", + "[13:07:50] Simulation (INFO) - Progress... 13%\n", + "[13:07:50] Simulation (INFO) - Progress... 14%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00286865234375\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9969424312499057\n", + "[13:07:50] Simulation (INFO) - Progress... 14%\n", + "[13:07:50] Simulation (INFO) - Progress... 14%\n", + "[13:07:50] Simulation (INFO) - Progress... 14%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00311279296875\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9963524022161957\n", + "[13:07:50] Simulation (INFO) - Progress... 14%\n", + "[13:07:50] Simulation (INFO) - Progress... 14%\n", + "[13:07:50] Simulation (INFO) - Progress... 15%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00335693359375\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9956807669798176\n", + "[13:07:50] Simulation (INFO) - Progress... 15%\n", + "[13:07:50] Simulation (INFO) - Progress... 15%\n", + "[13:07:50] Simulation (INFO) - Progress... 15%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00335693359375\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9956807669798176\n", + "[13:07:50] Simulation (INFO) - Progress... 15%\n", + "[13:07:50] Simulation (INFO) - Progress... 15%\n", + "[13:07:50] Simulation (INFO) - Progress... 16%\n", + "[13:07:50] MPS (INFO) - Applying variational optimisation.\n", + "[13:07:50] MPS (INFO) - Fidelity before optimisation=0.9956807669798176\n", + "[13:07:50] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9969066360097213\n", + "[13:07:50] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9969284260568553\n", + "[13:07:50] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9969300587346095\n", + "[13:07:50] MPS (INFO) - Final fidelity after optimisation=0.9969300587346095\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00335693359375\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9969300587346095\n", + "[13:07:50] Simulation (INFO) - Progress... 16%\n", + "[13:07:50] Simulation (INFO) - Progress... 16%\n", + "[13:07:50] Simulation (INFO) - Progress... 16%\n", + "[13:07:50] Simulation (INFO) - Progress... 16%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00341796875\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9969300587346094\n", + "[13:07:50] Simulation (INFO) - Progress... 16%\n", + "[13:07:50] Simulation (INFO) - Progress... 17%\n", + "[13:07:50] Simulation (INFO) - Progress... 17%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.0035400390625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9969300587346094\n", + "[13:07:50] Simulation (INFO) - Progress... 17%\n", + "[13:07:50] Simulation (INFO) - Progress... 17%\n", + "[13:07:50] Simulation (INFO) - Progress... 17%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.003662109375\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9964085287675873\n", + "[13:07:50] Simulation (INFO) - Progress... 17%\n", + "[13:07:50] Simulation (INFO) - Progress... 18%\n", + "[13:07:50] Simulation (INFO) - Progress... 18%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00396728515625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9964085287675873\n", + "[13:07:50] Simulation (INFO) - Progress... 18%\n", + "[13:07:50] Simulation (INFO) - Progress... 18%\n", + "[13:07:50] Simulation (INFO) - Progress... 18%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00445556640625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9964085287675873\n", + "[13:07:50] Simulation (INFO) - Progress... 18%\n", + "[13:07:50] Simulation (INFO) - Progress... 19%\n", + "[13:07:50] Simulation (INFO) - Progress... 19%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00469970703125\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.995903817286653\n", + "[13:07:50] Simulation (INFO) - Progress... 19%\n", + "[13:07:50] Simulation (INFO) - Progress... 19%\n", + "[13:07:50] Simulation (INFO) - Progress... 19%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00579833984375\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.995903817286653\n", + "[13:07:50] Simulation (INFO) - Progress... 20%\n", + "[13:07:50] Simulation (INFO) - Progress... 20%\n", + "[13:07:50] Simulation (INFO) - Progress... 20%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.006988525390625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", + "[13:07:50] Simulation (INFO) - Progress... 20%\n", + "[13:07:50] Simulation (INFO) - Progress... 20%\n", + "[13:07:50] Simulation (INFO) - Progress... 20%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00799560546875\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", + "[13:07:50] Simulation (INFO) - Progress... 21%\n", + "[13:07:50] Simulation (INFO) - Progress... 21%\n", + "[13:07:50] Simulation (INFO) - Progress... 21%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00799560546875\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", + "[13:07:50] Simulation (INFO) - Progress... 21%\n", + "[13:07:50] Simulation (INFO) - Progress... 21%\n", + "[13:07:50] Simulation (INFO) - Progress... 21%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00799560546875\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", + "[13:07:50] Simulation (INFO) - Progress... 22%\n", + "[13:07:50] Simulation (INFO) - Progress... 22%\n", + "[13:07:50] Simulation (INFO) - Progress... 22%\n", + "[13:07:50] Simulation (INFO) - Progress... 22%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.008056640625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", + "[13:07:50] Simulation (INFO) - Progress... 22%\n", + "[13:07:50] Simulation (INFO) - Progress... 22%\n", + "[13:07:50] Simulation (INFO) - Progress... 23%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.0081787109375\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", + "[13:07:50] Simulation (INFO) - Progress... 23%\n", + "[13:07:50] Simulation (INFO) - Progress... 23%\n", + "[13:07:50] Simulation (INFO) - Progress... 23%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.0084228515625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", + "[13:07:50] Simulation (INFO) - Progress... 23%\n", + "[13:07:50] Simulation (INFO) - Progress... 23%\n", + "[13:07:50] Simulation (INFO) - Progress... 24%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00885009765625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748626\n", + "[13:07:50] Simulation (INFO) - Progress... 24%\n", + "[13:07:50] Simulation (INFO) - Progress... 24%\n", + "[13:07:50] Simulation (INFO) - Progress... 24%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00909423828125\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9953466253004998\n", + "[13:07:50] Simulation (INFO) - Progress... 24%\n", + "[13:07:50] Simulation (INFO) - Progress... 24%\n", + "[13:07:50] Simulation (INFO) - Progress... 25%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00909423828125\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9946324749398359\n", + "[13:07:50] Simulation (INFO) - Progress... 25%\n", + "[13:07:50] Simulation (INFO) - Progress... 25%\n", + "[13:07:50] Simulation (INFO) - Progress... 25%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.009918212890625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9943297562108634\n", + "[13:07:50] Simulation (INFO) - Progress... 25%\n", + "[13:07:50] Simulation (INFO) - Progress... 25%\n", + "[13:07:50] Simulation (INFO) - Progress... 26%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.01129150390625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9937839224972186\n", + "[13:07:50] Simulation (INFO) - Progress... 26%\n", + "[13:07:50] Simulation (INFO) - Progress... 26%\n", + "[13:07:50] Simulation (INFO) - Progress... 26%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.011749267578125\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9931523125088856\n", + "[13:07:50] Simulation (INFO) - Progress... 26%\n", + "[13:07:50] Simulation (INFO) - Progress... 26%\n", + "[13:07:50] Simulation (INFO) - Progress... 27%\n", + "[13:07:50] MPS (INFO) - MPS size (MiB)=0.013824462890625\n", + "[13:07:50] MPS (INFO) - MPS fidelity=0.9922963086133972\n", + "[13:07:50] Simulation (INFO) - Progress... 27%\n", + "[13:07:50] Simulation (INFO) - Progress... 27%\n", + "[13:07:50] Simulation (INFO) - Progress... 27%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.013824462890625\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9922963086133972\n", + "[13:07:51] Simulation (INFO) - Progress... 27%\n", + "[13:07:51] Simulation (INFO) - Progress... 27%\n", + "[13:07:51] Simulation (INFO) - Progress... 28%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.013824462890625\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.992296308613397\n", + "[13:07:51] Simulation (INFO) - Progress... 28%\n", + "[13:07:51] Simulation (INFO) - Progress... 28%\n", + "[13:07:51] Simulation (INFO) - Progress... 28%\n", + "[13:07:51] MPS (INFO) - Applying variational optimisation.\n", + "[13:07:51] MPS (INFO) - Fidelity before optimisation=0.992296308613397\n", + "[13:07:51] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9936350532619016\n", + "[13:07:51] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9936901320991008\n", + "[13:07:51] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9937162633400826\n", + "[13:07:51] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9937332498584915\n", + "[13:07:51] MPS (INFO) - Final fidelity after optimisation=0.9937332498584915\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.013824462890625\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584915\n", + "[13:07:51] Simulation (INFO) - Progress... 28%\n", + "[13:07:51] Simulation (INFO) - Progress... 28%\n", + "[13:07:51] Simulation (INFO) - Progress... 29%\n", + "[13:07:51] Simulation (INFO) - Progress... 29%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.013885498046875\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584915\n", + "[13:07:51] Simulation (INFO) - Progress... 29%\n", + "[13:07:51] Simulation (INFO) - Progress... 29%\n", + "[13:07:51] Simulation (INFO) - Progress... 29%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.014007568359375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584915\n", + "[13:07:51] Simulation (INFO) - Progress... 30%\n", + "[13:07:51] Simulation (INFO) - Progress... 30%\n", + "[13:07:51] Simulation (INFO) - Progress... 30%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.014251708984375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584915\n", + "[13:07:51] Simulation (INFO) - Progress... 30%\n", + "[13:07:51] Simulation (INFO) - Progress... 30%\n", + "[13:07:51] Simulation (INFO) - Progress... 30%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.014739990234375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584918\n", + "[13:07:51] Simulation (INFO) - Progress... 31%\n", + "[13:07:51] Simulation (INFO) - Progress... 31%\n", + "[13:07:51] Simulation (INFO) - Progress... 31%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.015716552734375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584918\n", + "[13:07:51] Simulation (INFO) - Progress... 31%\n", + "[13:07:51] Simulation (INFO) - Progress... 31%\n", + "[13:07:51] Simulation (INFO) - Progress... 31%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.016815185546875\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9932134093534576\n", + "[13:07:51] Simulation (INFO) - Progress... 32%\n", + "[13:07:51] Simulation (INFO) - Progress... 32%\n", + "[13:07:51] Simulation (INFO) - Progress... 32%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.017822265625\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9928837028574017\n", + "[13:07:51] Simulation (INFO) - Progress... 32%\n", + "[13:07:51] Simulation (INFO) - Progress... 32%\n", + "[13:07:51] Simulation (INFO) - Progress... 32%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.01953125\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9928837028574017\n", + "[13:07:51] Simulation (INFO) - Progress... 33%\n", + "[13:07:51] Simulation (INFO) - Progress... 33%\n", + "[13:07:51] Simulation (INFO) - Progress... 33%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.02001953125\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9918985098672729\n", + "[13:07:51] Simulation (INFO) - Progress... 33%\n", + "[13:07:51] Simulation (INFO) - Progress... 33%\n", + "[13:07:51] Simulation (INFO) - Progress... 33%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.0220947265625\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9912041554874009\n", + "[13:07:51] Simulation (INFO) - Progress... 34%\n", + "[13:07:51] Simulation (INFO) - Progress... 34%\n", + "[13:07:51] Simulation (INFO) - Progress... 34%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.02490234375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9903174175990784\n", + "[13:07:51] Simulation (INFO) - Progress... 34%\n", + "[13:07:51] Simulation (INFO) - Progress... 34%\n", + "[13:07:51] Simulation (INFO) - Progress... 34%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025543212890625\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", + "[13:07:51] Simulation (INFO) - Progress... 35%\n", + "[13:07:51] Simulation (INFO) - Progress... 35%\n", + "[13:07:51] Simulation (INFO) - Progress... 35%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025543212890625\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", + "[13:07:51] Simulation (INFO) - Progress... 35%\n", + "[13:07:51] Simulation (INFO) - Progress... 35%\n", + "[13:07:51] Simulation (INFO) - Progress... 35%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025543212890625\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", + "[13:07:51] Simulation (INFO) - Progress... 36%\n", + "[13:07:51] Simulation (INFO) - Progress... 36%\n", + "[13:07:51] Simulation (INFO) - Progress... 36%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025543212890625\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", + "[13:07:51] Simulation (INFO) - Progress... 36%\n", + "[13:07:51] Simulation (INFO) - Progress... 36%\n", + "[13:07:51] Simulation (INFO) - Progress... 36%\n", + "[13:07:51] Simulation (INFO) - Progress... 37%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025604248046875\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", + "[13:07:51] Simulation (INFO) - Progress... 37%\n", + "[13:07:51] Simulation (INFO) - Progress... 37%\n", + "[13:07:51] Simulation (INFO) - Progress... 37%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025726318359375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", + "[13:07:51] Simulation (INFO) - Progress... 37%\n", + "[13:07:51] Simulation (INFO) - Progress... 37%\n", + "[13:07:51] Simulation (INFO) - Progress... 38%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025970458984375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", + "[13:07:51] Simulation (INFO) - Progress... 38%\n", + "[13:07:51] Simulation (INFO) - Progress... 38%\n", + "[13:07:51] Simulation (INFO) - Progress... 38%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.026458740234375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", + "[13:07:51] Simulation (INFO) - Progress... 38%\n", + "[13:07:51] Simulation (INFO) - Progress... 38%\n", + "[13:07:51] Simulation (INFO) - Progress... 39%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.027435302734375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", + "[13:07:51] Simulation (INFO) - Progress... 39%\n", + "[13:07:51] Simulation (INFO) - Progress... 39%\n", + "[13:07:51] Simulation (INFO) - Progress... 39%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.029388427734375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", + "[13:07:51] Simulation (INFO) - Progress... 39%\n", + "[13:07:51] Simulation (INFO) - Progress... 40%\n", + "[13:07:51] Simulation (INFO) - Progress... 40%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.031219482421875\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9889320425653054\n", + "[13:07:51] Simulation (INFO) - Progress... 40%\n", + "[13:07:51] Simulation (INFO) - Progress... 40%\n", + "[13:07:51] Simulation (INFO) - Progress... 40%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.03411865234375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9885110225000973\n", + "[13:07:51] Simulation (INFO) - Progress... 40%\n", + "[13:07:51] Simulation (INFO) - Progress... 41%\n", + "[13:07:51] Simulation (INFO) - Progress... 41%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.03656005859375\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.987710292524794\n", + "[13:07:51] Simulation (INFO) - Progress... 41%\n", + "[13:07:51] Simulation (INFO) - Progress... 41%\n", + "[13:07:51] Simulation (INFO) - Progress... 41%\n", + "[13:07:51] MPS (INFO) - MPS size (MiB)=0.03887939453125\n", + "[13:07:51] MPS (INFO) - MPS fidelity=0.9867894813441014\n", + "[13:07:51] Simulation (INFO) - Progress... 41%\n", + "[13:07:51] Simulation (INFO) - Progress... 42%\n", + "[13:07:51] Simulation (INFO) - Progress... 42%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.04327392578125\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9864127385201381\n", + "[13:07:52] Simulation (INFO) - Progress... 42%\n", + "[13:07:52] Simulation (INFO) - Progress... 42%\n", + "[13:07:52] Simulation (INFO) - Progress... 42%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.0482177734375\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9854360444314847\n", + "[13:07:52] Simulation (INFO) - Progress... 42%\n", + "[13:07:52] Simulation (INFO) - Progress... 43%\n", + "[13:07:52] Simulation (INFO) - Progress... 43%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.0537109375\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9845797921333378\n", + "[13:07:52] Simulation (INFO) - Progress... 43%\n", + "[13:07:52] Simulation (INFO) - Progress... 43%\n", + "[13:07:52] Simulation (INFO) - Progress... 43%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056182861328125\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9841566230600164\n", + "[13:07:52] Simulation (INFO) - Progress... 43%\n", + "[13:07:52] Simulation (INFO) - Progress... 44%\n", + "[13:07:52] Simulation (INFO) - Progress... 44%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056182861328125\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9841566230600164\n", + "[13:07:52] Simulation (INFO) - Progress... 44%\n", + "[13:07:52] Simulation (INFO) - Progress... 44%\n", + "[13:07:52] Simulation (INFO) - Progress... 44%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056182861328125\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9841566230600164\n", + "[13:07:52] Simulation (INFO) - Progress... 44%\n", + "[13:07:52] Simulation (INFO) - Progress... 45%\n", + "[13:07:52] Simulation (INFO) - Progress... 45%\n", + "[13:07:52] MPS (INFO) - Applying variational optimisation.\n", + "[13:07:52] MPS (INFO) - Fidelity before optimisation=0.9841566230600164\n", + "[13:07:52] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9861657072417293\n", + "[13:07:52] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9862250122543205\n", + "[13:07:52] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9862476294181953\n", + "[13:07:52] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9862603678860784\n", + "[13:07:52] MPS (INFO) - Final fidelity after optimisation=0.9862603678860784\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056182861328125\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9862603678860784\n", + "[13:07:52] Simulation (INFO) - Progress... 45%\n", + "[13:07:52] Simulation (INFO) - Progress... 45%\n", + "[13:07:52] Simulation (INFO) - Progress... 45%\n", + "[13:07:52] Simulation (INFO) - Progress... 45%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056243896484375\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9862603678860784\n", + "[13:07:52] Simulation (INFO) - Progress... 46%\n", + "[13:07:52] Simulation (INFO) - Progress... 46%\n", + "[13:07:52] Simulation (INFO) - Progress... 46%\n", + "[13:07:52] Simulation (INFO) - Progress... 46%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056365966796875\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9862603678860784\n", + "[13:07:52] Simulation (INFO) - Progress... 46%\n", + "[13:07:52] Simulation (INFO) - Progress... 46%\n", + "[13:07:52] Simulation (INFO) - Progress... 47%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056365966796875\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9858838523316555\n", + "[13:07:52] Simulation (INFO) - Progress... 47%\n", + "[13:07:52] Simulation (INFO) - Progress... 47%\n", + "[13:07:52] Simulation (INFO) - Progress... 47%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056732177734375\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9858838523316555\n", + "[13:07:52] Simulation (INFO) - Progress... 47%\n", + "[13:07:52] Simulation (INFO) - Progress... 47%\n", + "[13:07:52] Simulation (INFO) - Progress... 48%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056976318359375\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9852517168285035\n", + "[13:07:52] Simulation (INFO) - Progress... 48%\n", + "[13:07:52] Simulation (INFO) - Progress... 48%\n", + "[13:07:52] Simulation (INFO) - Progress... 48%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.058563232421875\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9852517168285035\n", + "[13:07:52] Simulation (INFO) - Progress... 48%\n", + "[13:07:52] Simulation (INFO) - Progress... 48%\n", + "[13:07:52] Simulation (INFO) - Progress... 49%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.060028076171875\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9843273031404238\n", + "[13:07:52] Simulation (INFO) - Progress... 49%\n", + "[13:07:52] Simulation (INFO) - Progress... 49%\n", + "[13:07:52] Simulation (INFO) - Progress... 49%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.06494140625\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9837115795017173\n", + "[13:07:52] Simulation (INFO) - Progress... 49%\n", + "[13:07:52] Simulation (INFO) - Progress... 50%\n", + "[13:07:52] Simulation (INFO) - Progress... 50%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.0682373046875\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9827664306820764\n", + "[13:07:52] Simulation (INFO) - Progress... 50%\n", + "[13:07:52] Simulation (INFO) - Progress... 50%\n", + "[13:07:52] Simulation (INFO) - Progress... 50%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.071533203125\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9818041456762368\n", + "[13:07:52] Simulation (INFO) - Progress... 50%\n", + "[13:07:52] Simulation (INFO) - Progress... 51%\n", + "[13:07:52] Simulation (INFO) - Progress... 51%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.0758056640625\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9811843347916239\n", + "[13:07:52] Simulation (INFO) - Progress... 51%\n", + "[13:07:52] Simulation (INFO) - Progress... 51%\n", + "[13:07:52] Simulation (INFO) - Progress... 51%\n", + "[13:07:52] MPS (INFO) - MPS size (MiB)=0.0831298828125\n", + "[13:07:52] MPS (INFO) - MPS fidelity=0.9804396566497552\n", + "[13:07:52] Simulation (INFO) - Progress... 51%\n", + "[13:07:52] Simulation (INFO) - Progress... 52%\n", + "[13:07:52] Simulation (INFO) - Progress... 52%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.09356689453125\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9797042536815029\n", + "[13:07:53] Simulation (INFO) - Progress... 52%\n", + "[13:07:53] Simulation (INFO) - Progress... 52%\n", + "[13:07:53] Simulation (INFO) - Progress... 52%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.1025390625\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9787337512094221\n", + "[13:07:53] Simulation (INFO) - Progress... 52%\n", + "[13:07:53] Simulation (INFO) - Progress... 53%\n", + "[13:07:53] Simulation (INFO) - Progress... 53%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.106201171875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089927\n", + "[13:07:53] Simulation (INFO) - Progress... 53%\n", + "[13:07:53] Simulation (INFO) - Progress... 53%\n", + "[13:07:53] Simulation (INFO) - Progress... 53%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10711669921875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089927\n", + "[13:07:53] Simulation (INFO) - Progress... 53%\n", + "[13:07:53] Simulation (INFO) - Progress... 54%\n", + "[13:07:53] Simulation (INFO) - Progress... 54%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10711669921875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089927\n", + "[13:07:53] Simulation (INFO) - Progress... 54%\n", + "[13:07:53] Simulation (INFO) - Progress... 54%\n", + "[13:07:53] Simulation (INFO) - Progress... 54%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10711669921875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089926\n", + "[13:07:53] Simulation (INFO) - Progress... 54%\n", + "[13:07:53] Simulation (INFO) - Progress... 55%\n", + "[13:07:53] Simulation (INFO) - Progress... 55%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10711669921875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089926\n", + "[13:07:53] Simulation (INFO) - Progress... 55%\n", + "[13:07:53] Simulation (INFO) - Progress... 55%\n", + "[13:07:53] Simulation (INFO) - Progress... 55%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10711669921875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089928\n", + "[13:07:53] Simulation (INFO) - Progress... 55%\n", + "[13:07:53] Simulation (INFO) - Progress... 56%\n", + "[13:07:53] Simulation (INFO) - Progress... 56%\n", + "[13:07:53] Simulation (INFO) - Progress... 56%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10736083984375\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089928\n", + "[13:07:53] Simulation (INFO) - Progress... 56%\n", + "[13:07:53] Simulation (INFO) - Progress... 56%\n", + "[13:07:53] Simulation (INFO) - Progress... 56%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10784912109375\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089928\n", + "[13:07:53] Simulation (INFO) - Progress... 57%\n", + "[13:07:53] Simulation (INFO) - Progress... 57%\n", + "[13:07:53] Simulation (INFO) - Progress... 57%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.108123779296875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.977670099247772\n", + "[13:07:53] Simulation (INFO) - Progress... 57%\n", + "[13:07:53] Simulation (INFO) - Progress... 57%\n", + "[13:07:53] Simulation (INFO) - Progress... 57%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10931396484375\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9773757304606052\n", + "[13:07:53] Simulation (INFO) - Progress... 58%\n", + "[13:07:53] Simulation (INFO) - Progress... 58%\n", + "[13:07:53] Simulation (INFO) - Progress... 58%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.111053466796875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9767554958542899\n", + "[13:07:53] Simulation (INFO) - Progress... 58%\n", + "[13:07:53] Simulation (INFO) - Progress... 58%\n", + "[13:07:53] Simulation (INFO) - Progress... 58%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.113433837890625\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9761528434196657\n", + "[13:07:53] Simulation (INFO) - Progress... 59%\n", + "[13:07:53] Simulation (INFO) - Progress... 59%\n", + "[13:07:53] Simulation (INFO) - Progress... 59%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.118927001953125\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9754288384480352\n", + "[13:07:53] Simulation (INFO) - Progress... 59%\n", + "[13:07:53] Simulation (INFO) - Progress... 59%\n", + "[13:07:53] Simulation (INFO) - Progress... 60%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.12457275390625\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9745562791745191\n", + "[13:07:53] Simulation (INFO) - Progress... 60%\n", + "[13:07:53] Simulation (INFO) - Progress... 60%\n", + "[13:07:53] Simulation (INFO) - Progress... 60%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.130218505859375\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9737355899102491\n", + "[13:07:53] Simulation (INFO) - Progress... 60%\n", + "[13:07:53] Simulation (INFO) - Progress... 60%\n", + "[13:07:53] Simulation (INFO) - Progress... 61%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.140228271484375\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9730778511018878\n", + "[13:07:53] Simulation (INFO) - Progress... 61%\n", + "[13:07:53] Simulation (INFO) - Progress... 61%\n", + "[13:07:53] Simulation (INFO) - Progress... 61%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.151702880859375\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9722121338904668\n", + "[13:07:53] Simulation (INFO) - Progress... 61%\n", + "[13:07:53] Simulation (INFO) - Progress... 61%\n", + "[13:07:53] Simulation (INFO) - Progress... 62%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.16302490234375\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9712637153710257\n", + "[13:07:53] Simulation (INFO) - Progress... 62%\n", + "[13:07:53] Simulation (INFO) - Progress... 62%\n", + "[13:07:53] Simulation (INFO) - Progress... 62%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.17095947265625\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9704052937857174\n", + "[13:07:53] Simulation (INFO) - Progress... 62%\n", + "[13:07:53] Simulation (INFO) - Progress... 62%\n", + "[13:07:53] Simulation (INFO) - Progress... 63%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9696124478274755\n", + "[13:07:53] Simulation (INFO) - Progress... 63%\n", + "[13:07:53] Simulation (INFO) - Progress... 63%\n", + "[13:07:53] Simulation (INFO) - Progress... 63%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9696124478274755\n", + "[13:07:53] Simulation (INFO) - Progress... 63%\n", + "[13:07:53] Simulation (INFO) - Progress... 63%\n", + "[13:07:53] Simulation (INFO) - Progress... 64%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9696124478274752\n", + "[13:07:53] Simulation (INFO) - Progress... 64%\n", + "[13:07:53] Simulation (INFO) - Progress... 64%\n", + "[13:07:53] Simulation (INFO) - Progress... 64%\n", + "[13:07:53] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", + "[13:07:53] MPS (INFO) - MPS fidelity=0.9696124478274752\n", + "[13:07:53] Simulation (INFO) - Progress... 64%\n", + "[13:07:53] MPS (INFO) - Applying variational optimisation.\n", + "[13:07:53] MPS (INFO) - Fidelity before optimisation=0.9696124478274752\n", + "[13:07:53] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:53] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9748503580516497\n", + "[13:07:53] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9749968653397907\n", + "[13:07:54] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9750403204297021\n", + "[13:07:54] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9750621753095439\n", + "[13:07:54] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9750760500178091\n", + "[13:07:54] MPS (INFO) - Final fidelity after optimisation=0.9750760500178091\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9750760500178093\n", + "[13:07:54] Simulation (INFO) - Progress... 64%\n", + "[13:07:54] Simulation (INFO) - Progress... 65%\n", + "[13:07:54] Simulation (INFO) - Progress... 65%\n", + "[13:07:54] Simulation (INFO) - Progress... 65%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9750760500178093\n", + "[13:07:54] Simulation (INFO) - Progress... 65%\n", + "[13:07:54] Simulation (INFO) - Progress... 65%\n", + "[13:07:54] Simulation (INFO) - Progress... 65%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.179931640625\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9747058237409754\n", + "[13:07:54] Simulation (INFO) - Progress... 66%\n", + "[13:07:54] Simulation (INFO) - Progress... 66%\n", + "[13:07:54] Simulation (INFO) - Progress... 66%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.18121337890625\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9745772952447315\n", + "[13:07:54] Simulation (INFO) - Progress... 66%\n", + "[13:07:54] Simulation (INFO) - Progress... 66%\n", + "[13:07:54] Simulation (INFO) - Progress... 66%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.185272216796875\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9739652250830045\n", + "[13:07:54] Simulation (INFO) - Progress... 67%\n", + "[13:07:54] Simulation (INFO) - Progress... 67%\n", + "[13:07:54] Simulation (INFO) - Progress... 67%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.18792724609375\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9733944368640858\n", + "[13:07:54] Simulation (INFO) - Progress... 67%\n", + "[13:07:54] Simulation (INFO) - Progress... 67%\n", + "[13:07:54] Simulation (INFO) - Progress... 67%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.19647216796875\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.972694143326865\n", + "[13:07:54] Simulation (INFO) - Progress... 68%\n", + "[13:07:54] Simulation (INFO) - Progress... 68%\n", + "[13:07:54] Simulation (INFO) - Progress... 68%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.203033447265625\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9718997086560416\n", + "[13:07:54] Simulation (INFO) - Progress... 68%\n", + "[13:07:54] Simulation (INFO) - Progress... 68%\n", + "[13:07:54] Simulation (INFO) - Progress... 68%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.211639404296875\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9711668230786579\n", + "[13:07:54] Simulation (INFO) - Progress... 69%\n", + "[13:07:54] Simulation (INFO) - Progress... 69%\n", + "[13:07:54] Simulation (INFO) - Progress... 69%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.21630859375\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9703500807776058\n", + "[13:07:54] Simulation (INFO) - Progress... 69%\n", + "[13:07:54] Simulation (INFO) - Progress... 69%\n", + "[13:07:54] Simulation (INFO) - Progress... 70%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.2353515625\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9694719482925707\n", + "[13:07:54] Simulation (INFO) - Progress... 70%\n", + "[13:07:54] Simulation (INFO) - Progress... 70%\n", + "[13:07:54] Simulation (INFO) - Progress... 70%\n", + "[13:07:54] MPS (INFO) - MPS size (MiB)=0.267578125\n", + "[13:07:54] MPS (INFO) - MPS fidelity=0.9686645363954253\n", + "[13:07:54] Simulation (INFO) - Progress... 70%\n", + "[13:07:54] Simulation (INFO) - Progress... 70%\n", + "[13:07:54] Simulation (INFO) - Progress... 71%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.2991943359375\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9677864245613603\n", + "[13:07:55] Simulation (INFO) - Progress... 71%\n", + "[13:07:55] Simulation (INFO) - Progress... 71%\n", + "[13:07:55] Simulation (INFO) - Progress... 71%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.3233642578125\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9668761765880589\n", + "[13:07:55] Simulation (INFO) - Progress... 71%\n", + "[13:07:55] Simulation (INFO) - Progress... 71%\n", + "[13:07:55] Simulation (INFO) - Progress... 72%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.328582763671875\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9665059834306376\n", + "[13:07:55] Simulation (INFO) - Progress... 72%\n", + "[13:07:55] Simulation (INFO) - Progress... 72%\n", + "[13:07:55] Simulation (INFO) - Progress... 72%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.328582763671875\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9665059834306376\n", + "[13:07:55] Simulation (INFO) - Progress... 72%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.328582763671875\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9665059834306376\n", + "[13:07:55] Simulation (INFO) - Progress... 72%\n", + "[13:07:55] Simulation (INFO) - Progress... 73%\n", + "[13:07:55] Simulation (INFO) - Progress... 73%\n", + "[13:07:55] Simulation (INFO) - Progress... 73%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.328582763671875\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9665059834306376\n", + "[13:07:55] Simulation (INFO) - Progress... 73%\n", + "[13:07:55] Simulation (INFO) - Progress... 73%\n", + "[13:07:55] Simulation (INFO) - Progress... 73%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.329315185546875\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9665059834306379\n", + "[13:07:55] Simulation (INFO) - Progress... 74%\n", + "[13:07:55] Simulation (INFO) - Progress... 74%\n", + "[13:07:55] Simulation (INFO) - Progress... 74%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.333526611328125\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9662179790868655\n", + "[13:07:55] Simulation (INFO) - Progress... 74%\n", + "[13:07:55] Simulation (INFO) - Progress... 74%\n", + "[13:07:55] Simulation (INFO) - Progress... 74%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.333526611328125\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9656632028640223\n", + "[13:07:55] Simulation (INFO) - Progress... 75%\n", + "[13:07:55] Simulation (INFO) - Progress... 75%\n", + "[13:07:55] Simulation (INFO) - Progress... 75%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.3414306640625\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9647910655846768\n", + "[13:07:55] Simulation (INFO) - Progress... 75%\n", + "[13:07:55] Simulation (INFO) - Progress... 75%\n", + "[13:07:55] Simulation (INFO) - Progress... 75%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.354339599609375\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.963896037407298\n", + "[13:07:55] Simulation (INFO) - Progress... 76%\n", + "[13:07:55] Simulation (INFO) - Progress... 76%\n", + "[13:07:55] Simulation (INFO) - Progress... 76%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.366729736328125\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9630136852198702\n", + "[13:07:55] Simulation (INFO) - Progress... 76%\n", + "[13:07:55] Simulation (INFO) - Progress... 76%\n", + "[13:07:55] Simulation (INFO) - Progress... 76%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.37542724609375\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9621991577849345\n", + "[13:07:55] Simulation (INFO) - Progress... 77%\n", + "[13:07:55] Simulation (INFO) - Progress... 77%\n", + "[13:07:55] Simulation (INFO) - Progress... 77%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.40240478515625\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9613406445530075\n", + "[13:07:55] Simulation (INFO) - Progress... 77%\n", + "[13:07:55] Simulation (INFO) - Progress... 77%\n", + "[13:07:55] Simulation (INFO) - Progress... 77%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.4468994140625\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9603923338552977\n", + "[13:07:55] Simulation (INFO) - Progress... 78%\n", + "[13:07:55] Simulation (INFO) - Progress... 78%\n", + "[13:07:55] Simulation (INFO) - Progress... 78%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.452880859375\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.959554544788218\n", + "[13:07:55] Simulation (INFO) - Progress... 78%\n", + "[13:07:55] Simulation (INFO) - Progress... 78%\n", + "[13:07:55] Simulation (INFO) - Progress... 78%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.466156005859375\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9586928713117597\n", + "[13:07:55] Simulation (INFO) - Progress... 79%\n", + "[13:07:55] Simulation (INFO) - Progress... 79%\n", + "[13:07:55] Simulation (INFO) - Progress... 79%\n", + "[13:07:55] MPS (INFO) - MPS size (MiB)=0.497894287109375\n", + "[13:07:55] MPS (INFO) - MPS fidelity=0.9578333111030969\n", + "[13:07:55] Simulation (INFO) - Progress... 79%\n", + "[13:07:55] MPS (INFO) - Applying variational optimisation.\n", + "[13:07:55] MPS (INFO) - Fidelity before optimisation=0.9578333111030969\n", + "[13:07:55] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9638633099128788\n", + "[13:07:55] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9641110973253727\n", + "[13:07:55] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9641966129574852\n", + "[13:07:55] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.964241137047175\n", + "[13:07:56] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9642683294864673\n", + "[13:07:56] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9642862317446965\n", + "[13:07:56] MPS (INFO) - Final fidelity after optimisation=0.9642862317446965\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.497894287109375\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9642862317446965\n", + "[13:07:56] Simulation (INFO) - Progress... 79%\n", + "[13:07:56] Simulation (INFO) - Progress... 80%\n", + "[13:07:56] Simulation (INFO) - Progress... 80%\n", + "[13:07:56] Simulation (INFO) - Progress... 80%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.497894287109375\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9642862317446963\n", + "[13:07:56] Simulation (INFO) - Progress... 80%\n", + "[13:07:56] Simulation (INFO) - Progress... 80%\n", + "[13:07:56] Simulation (INFO) - Progress... 80%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.497894287109375\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9642862317446963\n", + "[13:07:56] Simulation (INFO) - Progress... 81%\n", + "[13:07:56] Simulation (INFO) - Progress... 81%\n", + "[13:07:56] Simulation (INFO) - Progress... 81%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.49859619140625\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9634482033158315\n", + "[13:07:56] Simulation (INFO) - Progress... 81%\n", + "[13:07:56] Simulation (INFO) - Progress... 81%\n", + "[13:07:56] Simulation (INFO) - Progress... 81%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.50518798828125\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9627097530284029\n", + "[13:07:56] Simulation (INFO) - Progress... 82%\n", + "[13:07:56] Simulation (INFO) - Progress... 82%\n", + "[13:07:56] Simulation (INFO) - Progress... 82%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.51470947265625\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9619717159253325\n", + "[13:07:56] Simulation (INFO) - Progress... 82%\n", + "[13:07:56] Simulation (INFO) - Progress... 82%\n", + "[13:07:56] Simulation (INFO) - Progress... 82%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.52020263671875\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9610104125613574\n", + "[13:07:56] Simulation (INFO) - Progress... 83%\n", + "[13:07:56] Simulation (INFO) - Progress... 83%\n", + "[13:07:56] Simulation (INFO) - Progress... 83%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.5423583984375\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9602104265798482\n", + "[13:07:56] Simulation (INFO) - Progress... 83%\n", + "[13:07:56] Simulation (INFO) - Progress... 83%\n", + "[13:07:56] Simulation (INFO) - Progress... 83%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.574493408203125\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9593078644311607\n", + "[13:07:56] Simulation (INFO) - Progress... 84%\n", + "[13:07:56] Simulation (INFO) - Progress... 84%\n", + "[13:07:56] Simulation (INFO) - Progress... 84%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.6258544921875\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9584049213960937\n", + "[13:07:56] Simulation (INFO) - Progress... 84%\n", + "[13:07:56] Simulation (INFO) - Progress... 84%\n", + "[13:07:56] Simulation (INFO) - Progress... 84%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.6197509765625\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9575755639326016\n", + "[13:07:56] Simulation (INFO) - Progress... 85%\n", + "[13:07:56] Simulation (INFO) - Progress... 85%\n", + "[13:07:56] Simulation (INFO) - Progress... 85%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.65673828125\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9566270309912319\n", + "[13:07:56] Simulation (INFO) - Progress... 85%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.65673828125\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9566270309912319\n", + "[13:07:56] Simulation (INFO) - Progress... 85%\n", + "[13:07:56] Simulation (INFO) - Progress... 85%\n", + "[13:07:56] Simulation (INFO) - Progress... 86%\n", + "[13:07:56] Simulation (INFO) - Progress... 86%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.65673828125\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9566270309912319\n", + "[13:07:56] Simulation (INFO) - Progress... 86%\n", + "[13:07:56] Simulation (INFO) - Progress... 86%\n", + "[13:07:56] Simulation (INFO) - Progress... 86%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.65673828125\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9566270309912319\n", + "[13:07:56] Simulation (INFO) - Progress... 86%\n", + "[13:07:56] Simulation (INFO) - Progress... 87%\n", + "[13:07:56] Simulation (INFO) - Progress... 87%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.657623291015625\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9566270309912319\n", + "[13:07:56] Simulation (INFO) - Progress... 87%\n", + "[13:07:56] Simulation (INFO) - Progress... 87%\n", + "[13:07:56] Simulation (INFO) - Progress... 87%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.66680908203125\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9557944790732725\n", + "[13:07:56] Simulation (INFO) - Progress... 87%\n", + "[13:07:56] Simulation (INFO) - Progress... 88%\n", + "[13:07:56] Simulation (INFO) - Progress... 88%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.68572998046875\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9549535607073596\n", + "[13:07:56] Simulation (INFO) - Progress... 88%\n", + "[13:07:56] Simulation (INFO) - Progress... 88%\n", + "[13:07:56] Simulation (INFO) - Progress... 88%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.732696533203125\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.954013025700444\n", + "[13:07:56] Simulation (INFO) - Progress... 88%\n", + "[13:07:56] Simulation (INFO) - Progress... 89%\n", + "[13:07:56] Simulation (INFO) - Progress... 89%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.825408935546875\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9530683950725519\n", + "[13:07:56] Simulation (INFO) - Progress... 89%\n", + "[13:07:56] Simulation (INFO) - Progress... 89%\n", + "[13:07:56] Simulation (INFO) - Progress... 89%\n", + "[13:07:56] MPS (INFO) - MPS size (MiB)=0.9710693359375\n", + "[13:07:56] MPS (INFO) - MPS fidelity=0.9521922642825028\n", + "[13:07:56] Simulation (INFO) - Progress... 90%\n", + "[13:07:56] Simulation (INFO) - Progress... 90%\n", + "[13:07:56] Simulation (INFO) - Progress... 90%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.1019287109375\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9512402092586002\n", + "[13:07:57] Simulation (INFO) - Progress... 90%\n", + "[13:07:57] MPS (INFO) - Applying variational optimisation.\n", + "[13:07:57] MPS (INFO) - Fidelity before optimisation=0.9512402092586002\n", + "[13:07:57] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:57] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9577125931210696\n", + "[13:07:57] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:57] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9578860887856777\n", + "[13:07:57] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:57] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9579368838618443\n", + "[13:07:57] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:57] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.957961508212945\n", + "[13:07:57] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:57] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9579759882555418\n", + "[13:07:57] MPS (INFO) - Final fidelity after optimisation=0.9579759882555418\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.1019287109375\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9579759882555418\n", + "[13:07:57] Simulation (INFO) - Progress... 90%\n", + "[13:07:57] Simulation (INFO) - Progress... 90%\n", + "[13:07:57] Simulation (INFO) - Progress... 91%\n", + "[13:07:57] Simulation (INFO) - Progress... 91%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.1019287109375\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9579759882555418\n", + "[13:07:57] Simulation (INFO) - Progress... 91%\n", + "[13:07:57] Simulation (INFO) - Progress... 91%\n", + "[13:07:57] Simulation (INFO) - Progress... 91%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.1019287109375\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9579759882555418\n", + "[13:07:57] Simulation (INFO) - Progress... 91%\n", + "[13:07:57] Simulation (INFO) - Progress... 92%\n", + "[13:07:57] Simulation (INFO) - Progress... 92%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.1019287109375\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.957975988255542\n", + "[13:07:57] Simulation (INFO) - Progress... 92%\n", + "[13:07:57] Simulation (INFO) - Progress... 92%\n", + "[13:07:57] Simulation (INFO) - Progress... 92%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.10516357421875\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9572751185652196\n", + "[13:07:57] Simulation (INFO) - Progress... 92%\n", + "[13:07:57] Simulation (INFO) - Progress... 93%\n", + "[13:07:57] Simulation (INFO) - Progress... 93%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.140625\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9563866601556178\n", + "[13:07:57] Simulation (INFO) - Progress... 93%\n", + "[13:07:57] Simulation (INFO) - Progress... 93%\n", + "[13:07:57] Simulation (INFO) - Progress... 93%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.2252197265625\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9555151328901298\n", + "[13:07:57] Simulation (INFO) - Progress... 93%\n", + "[13:07:57] Simulation (INFO) - Progress... 94%\n", + "[13:07:57] Simulation (INFO) - Progress... 94%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.321044921875\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836132\n", + "[13:07:57] Simulation (INFO) - Progress... 94%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.321044921875\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836132\n", + "[13:07:57] Simulation (INFO) - Progress... 94%\n", + "[13:07:57] Simulation (INFO) - Progress... 94%\n", + "[13:07:57] Simulation (INFO) - Progress... 94%\n", + "[13:07:57] Simulation (INFO) - Progress... 95%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.321044921875\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836132\n", + "[13:07:57] Simulation (INFO) - Progress... 95%\n", + "[13:07:57] Simulation (INFO) - Progress... 95%\n", + "[13:07:57] Simulation (INFO) - Progress... 95%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.321044921875\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836134\n", + "[13:07:57] Simulation (INFO) - Progress... 95%\n", + "[13:07:57] Simulation (INFO) - Progress... 95%\n", + "[13:07:57] Simulation (INFO) - Progress... 96%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.321044921875\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836133\n", + "[13:07:57] Simulation (INFO) - Progress... 96%\n", + "[13:07:57] Simulation (INFO) - Progress... 96%\n", + "[13:07:57] Simulation (INFO) - Progress... 96%\n", + "[13:07:57] MPS (INFO) - MPS size (MiB)=1.32513427734375\n", + "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836133\n", + "[13:07:57] Simulation (INFO) - Progress... 96%\n", + "[13:07:57] Simulation (INFO) - Progress... 96%\n", + "[13:07:57] Simulation (INFO) - Progress... 97%\n", + "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", + "[13:07:58] MPS (INFO) - MPS fidelity=0.9537956845053878\n", + "[13:07:58] Simulation (INFO) - Progress... 97%\n", + "[13:07:58] MPS (INFO) - Applying variational optimisation.\n", + "[13:07:58] MPS (INFO) - Fidelity before optimisation=0.9537956845053878\n", + "[13:07:58] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:58] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9557536327031856\n", + "[13:07:58] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:58] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9557817429040746\n", + "[13:07:58] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:58] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9557872759641832\n", + "[13:07:58] MPS (INFO) - Final fidelity after optimisation=0.9557872759641832\n", + "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", + "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641832\n", + "[13:07:58] Simulation (INFO) - Progress... 97%\n", + "[13:07:58] Simulation (INFO) - Progress... 97%\n", + "[13:07:58] Simulation (INFO) - Progress... 97%\n", + "[13:07:58] Simulation (INFO) - Progress... 97%\n", + "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", + "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641832\n", + "[13:07:58] Simulation (INFO) - Progress... 98%\n", + "[13:07:58] Simulation (INFO) - Progress... 98%\n", + "[13:07:58] Simulation (INFO) - Progress... 98%\n", + "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", + "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641832\n", + "[13:07:58] Simulation (INFO) - Progress... 98%\n", + "[13:07:58] Simulation (INFO) - Progress... 98%\n", + "[13:07:58] Simulation (INFO) - Progress... 98%\n", + "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", + "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641832\n", + "[13:07:58] Simulation (INFO) - Progress... 99%\n", + "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", + "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641831\n", + "[13:07:58] Simulation (INFO) - Progress... 99%\n", + "[13:07:58] Simulation (INFO) - Progress... 99%\n", + "[13:07:58] Simulation (INFO) - Progress... 99%\n", + "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", + "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641831\n", + "[13:07:58] Simulation (INFO) - Progress... 99%\n", + "[13:07:58] MPS (INFO) - Applying variational optimisation.\n", + "[13:07:58] MPS (INFO) - Fidelity before optimisation=0.9557872759641831\n", + "[13:07:58] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:58] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9557872759641844\n", + "[13:07:58] MPS (INFO) - Doing another optimisation sweep...\n", + "[13:07:58] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9557872759641826\n", + "[13:07:58] MPS (INFO) - Final fidelity after optimisation=0.9557872759641826\n", + "[13:07:58] Simulation (INFO) - Simulation completed.\n", + "[13:07:58] Simulation (INFO) - Final TNState size=1.3577880859375 MiB\n", + "[13:07:58] Simulation (INFO) - Final TNState fidelity=0.9557872759641826\n" ] } ], "source": [ "with CuTensorNetHandle() as libhandle:\n", - " config = ConfigMPS(truncation_fidelity=0.999, loglevel=logging.INFO)\n", - " simulate(libhandle, circuit, ContractionAlg.MPSxMPO, config)" + " config = Config(truncation_fidelity=0.999, loglevel=logging.INFO)\n", + " simulate(libhandle, circuit, SimulationAlgorithm.MPSxMPO, config)" ] }, { @@ -2114,9 +2100,9 @@ ], "metadata": { "kernelspec": { - "display_name": "py-cuquantum-23.06.0-mypich-py3.9", + "display_name": "Python 3 (ipykernel)", "language": "python", - "name": "py-cuquantum-23.06.0-mypich-py3.9" + "name": "python3" }, "language_info": { "codemirror_mode": { From f1c9678d57d4225c2be36b1fb6dee1d5ee7ac9dd Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Thu, 16 Nov 2023 03:42:12 -0800 Subject: [PATCH 54/83] Updated MPI example --- examples/mpi/mpi_overlap_bcast_mps.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/mpi/mpi_overlap_bcast_mps.py b/examples/mpi/mpi_overlap_bcast_mps.py index a0d662d9..014a0e82 100644 --- a/examples/mpi/mpi_overlap_bcast_mps.py +++ b/examples/mpi/mpi_overlap_bcast_mps.py @@ -42,10 +42,10 @@ from pytket.circuit import Circuit, fresh_symbol -from pytket.extensions.cutensornet.mps import ( +from pytket.extensions.cutensornet.tnstate import ( simulate, - ConfigMPS, - ContractionAlg, + Config, + SimulationAlgorithm, CuTensorNetHandle, ) @@ -109,7 +109,7 @@ this_proc_mps = [] with CuTensorNetHandle(device_id) as libhandle: # Different handle for each process for circ in this_proc_circs: - mps = simulate(libhandle, circ, ContractionAlg.MPSxGate, ConfigMPS()) + mps = simulate(libhandle, circ, SimulationAlgorithm.MPSxGate, Config()) this_proc_mps.append(mps) if rank == root: From b2a0b48057713dd7daa59db1f763b50e92187ff4 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Wed, 10 Jan 2024 14:14:09 +0000 Subject: [PATCH 55/83] Replacing KL partitioner with KaHyPar --- .../tnstate/cut_rKaHyPar_sea20.ini | 64 +++++++++++++++++ .../cutensornet/tnstate/simulation.py | 70 +++++++++++++++++-- setup.py | 2 +- 3 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 pytket/extensions/cutensornet/tnstate/cut_rKaHyPar_sea20.ini diff --git a/pytket/extensions/cutensornet/tnstate/cut_rKaHyPar_sea20.ini b/pytket/extensions/cutensornet/tnstate/cut_rKaHyPar_sea20.ini new file mode 100644 index 00000000..14d10f58 --- /dev/null +++ b/pytket/extensions/cutensornet/tnstate/cut_rKaHyPar_sea20.ini @@ -0,0 +1,64 @@ +# general +mode=recursive +objective=cut +seed=-1 +cmaxnet=-1 +vcycles=0 +# main -> preprocessing -> min hash sparsifier +p-use-sparsifier=true +p-sparsifier-min-median-he-size=28 +p-sparsifier-max-hyperedge-size=1200 +p-sparsifier-max-cluster-size=10 +p-sparsifier-min-cluster-size=2 +p-sparsifier-num-hash-func=5 +p-sparsifier-combined-num-hash-func=100 +# main -> preprocessing -> community detection +p-detect-communities=true +p-detect-communities-in-ip=false +p-reuse-communities=false +p-max-louvain-pass-iterations=100 +p-min-eps-improvement=0.0001 +p-louvain-edge-weight=hybrid +p-large-he-threshold=1000 +# main -> preprocessing -> large he removal +p-smallest-maxnet-threshold=50000 +p-maxnet-removal-factor=0.01 +# main -> coarsening +c-type=heavy_lazy +c-s=3.25 +c-t=160 +# main -> coarsening -> rating +c-rating-score=heavy_edge +c-rating-use-communities=true +c-rating-heavy_node_penalty=multiplicative +c-rating-acceptance-criterion=best +c-fixed-vertex-acceptance-criterion=free_vertex_only +# main -> initial partitioning +i-mode=direct +i-technique=flat +# initial partitioning -> initial partitioning +i-algo=pool +i-runs=20 +# initial partitioning -> bin packing +i-bp-algorithm=worst_fit +i-bp-heuristic-prepacking=false +i-bp-early-restart=true +i-bp-late-restart=true +# initial partitioning -> local search +i-r-type=twoway_fm +i-r-runs=-1 +i-r-fm-stop=simple +i-r-fm-stop-i=50 +# main -> local search +r-type=twoway_fm_hyperflow_cutter +r-runs=-1 +r-fm-stop=adaptive_opt +r-fm-stop-alpha=1 +r-fm-stop-i=350 +# local_search -> flow scheduling and heuristics +r-flow-execution-policy=exponential +# local_search -> hyperflowcutter configuration +r-hfc-size-constraint=mf-style +r-hfc-scaling=16 +r-hfc-distance-based-piercing=true +r-hfc-mbc=true diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py index 671399ee..316ce53d 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/tnstate/simulation.py @@ -14,10 +14,12 @@ from typing import Optional from enum import Enum +from pathlib import Path from collections import defaultdict # type: ignore import numpy as np # type: ignore -from networkx import Graph, community # type: ignore +import networkx as nx # type: ignore +import kahypar # type: ignore from pytket.circuit import Circuit, Command, Qubit from pytket.transform import Transform @@ -194,23 +196,21 @@ def _get_qubit_partition( ) # Create the connectivity graph in NetworkX - connectivity_graph = Graph() + connectivity_graph = nx.Graph() connectivity_graph.add_nodes_from(circuit.qubits) for (u, v), weight in edge_weights.items(): connectivity_graph.add_edge(u, v, weight=weight) # Apply balanced bisections until each qubit group is small enough - partition = {0: set(circuit.qubits)} + partition = {0: circuit.qubits} stop_bisec = False # Do at least one bisection (TTN reqs >1 leaf nodes) while not stop_bisec: old_partition = partition.copy() for key, group in old_partition.items(): # Apply the balanced bisection on this group - (groupA, groupB) = community.kernighan_lin_bisection( + (groupA, groupB) = _apply_kahypar_bisection( connectivity_graph.subgraph(group), - max_iter=2 * len(group), # Iteractions scaling with number of nodes - weight="weight", ) # Groups A and B are on the same subtree (key separated by +1) partition[2 * key] = groupA @@ -223,6 +223,64 @@ def _get_qubit_partition( return qubit_partition +def _apply_kahypar_bisection( + graph: nx.Graph, +) -> tuple(list[Qubit], list[Qubit]): + """Use KaHyPar to obtain a bisection of the graph. + + Returns: + Two lists, each containing the vertices in either group of the bisection. + """ + vertices = list(graph.nodes) + edges = list(graph.edges) + weight_dict = nx.get_edge_attributes(graph, "weight") + qubit_dict = {q: i for i, q in enumerate(vertices)} + + num_vertices = len(vertices) + num_edges = len(edges) + k = 2 # Number of groups in the partition + epsilon = 0.03 # Imbalance tolerance + + # KaHyPar expects the list of edges to be provided as a continuous set of vertices + # ``edge_stream`` where ``edge_indices`` indicates where each new edge begins + # (the latter is necessary because KaHyPar can accept hyperedges) + edge_stream = [qubit_dict[vertex] for edge in edges for vertex in edge] + edge_indices = [0] + [2*(i+1) for i in range(num_edges)] + edge_weights = [weight_dict[edge] for edge in edges] + vertex_weights = [1 for _ in range(num_vertex)] + + hypergraph = kahypar.Hypergraph( + num_vertices, + num_edges, + edge_indices, + edge_stream, + k, + edge_weights, + vertex_weights, + ) + + # Set up the configuration for KaHyPar + context = kahypar.Context() + context.setK(k) + context.setEpsilon(epsilon) + context.supressOutput(True) + + # Load the default configuration file provided by the KaHyPar devs + relative_path = "pytket/extensions/cutensornet/tnstate/cut_rKaHyPar_sea20.ini" + ini_file = str(Path(__file__).parent / relativ_path) + context.loadINIconfiguration(ini_file) + + # Run the partitioner + kahypar.partition(hypergraph, context) + partition_dict = {i: hypergraph.blockID(i) for i in range(hypergraph.numNodes())} + + # Obtain the two groups of qubits from ``partition_dict`` + groupA = [vertices[i] for i, block in partition_dict if block == 0] + groupB = [vertices[i] for i, block in partition_dict if block == 1] + + return (groupA, groupB) + + def _get_sorted_gates( circuit: Circuit, algorithm: SimulationAlgorithm, diff --git a/setup.py b/setup.py index 592d71f8..cfe2cf9a 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ license="Apache 2", packages=find_namespace_packages(include=["pytket.*"]), include_package_data=True, - install_requires=["pytket ~= 1.11"], + install_requires=["pytket ~= 1.11", "kahypar ~= 1.3.5"], classifiers=[ "Environment :: Console", "Programming Language :: Python :: 3.9", From aa3df63b7c26baa4d7db38e92cbd2d7abe9fd99e Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Wed, 10 Jan 2024 07:57:09 -0800 Subject: [PATCH 56/83] Bugfixes --- .../cutensornet/tnstate/simulation.py | 20 +++++++++++-------- tests/test_tnstate.py | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py index 316ce53d..15708043 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/tnstate/simulation.py @@ -225,7 +225,7 @@ def _get_qubit_partition( def _apply_kahypar_bisection( graph: nx.Graph, -) -> tuple(list[Qubit], list[Qubit]): +) -> tuple[list[Qubit], list[Qubit]]: """Use KaHyPar to obtain a bisection of the graph. Returns: @@ -241,13 +241,18 @@ def _apply_kahypar_bisection( k = 2 # Number of groups in the partition epsilon = 0.03 # Imbalance tolerance + # Special case where the graph has no edges; KaHyPar cannot deal with it + if num_edges == 0: + # Just split the list of vertices in half + return (vertices[: num_vertices // 2], vertices[num_vertices // 2 :]) + # KaHyPar expects the list of edges to be provided as a continuous set of vertices # ``edge_stream`` where ``edge_indices`` indicates where each new edge begins # (the latter is necessary because KaHyPar can accept hyperedges) edge_stream = [qubit_dict[vertex] for edge in edges for vertex in edge] - edge_indices = [0] + [2*(i+1) for i in range(num_edges)] + edge_indices = [0] + [2 * (i + 1) for i in range(num_edges)] edge_weights = [weight_dict[edge] for edge in edges] - vertex_weights = [1 for _ in range(num_vertex)] + vertex_weights = [1 for _ in range(num_vertices)] hypergraph = kahypar.Hypergraph( num_vertices, @@ -263,11 +268,10 @@ def _apply_kahypar_bisection( context = kahypar.Context() context.setK(k) context.setEpsilon(epsilon) - context.supressOutput(True) + context.suppressOutput(True) # Load the default configuration file provided by the KaHyPar devs - relative_path = "pytket/extensions/cutensornet/tnstate/cut_rKaHyPar_sea20.ini" - ini_file = str(Path(__file__).parent / relativ_path) + ini_file = str(Path(__file__).parent / "cut_rKaHyPar_sea20.ini") context.loadINIconfiguration(ini_file) # Run the partitioner @@ -275,8 +279,8 @@ def _apply_kahypar_bisection( partition_dict = {i: hypergraph.blockID(i) for i in range(hypergraph.numNodes())} # Obtain the two groups of qubits from ``partition_dict`` - groupA = [vertices[i] for i, block in partition_dict if block == 0] - groupB = [vertices[i] for i, block in partition_dict if block == 1] + groupA = [vertices[i] for i, block in partition_dict.items() if block == 0] + groupB = [vertices[i] for i, block in partition_dict.items() if block == 1] return (groupA, groupB) diff --git a/tests/test_tnstate.py b/tests/test_tnstate.py index 12dc8c76..f48a18e0 100644 --- a/tests/test_tnstate.py +++ b/tests/test_tnstate.py @@ -487,7 +487,7 @@ def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: 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) + assert np.isclose(ttn_gate.get_fidelity(), 0.64, atol=1e-2) assert ttn_gate.is_valid() assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=cfg._atol) From df01841855d357b1966c139cdb12fd81b6db2aca Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Fri, 12 Jan 2024 10:23:24 +0000 Subject: [PATCH 57/83] Update copyright message --- pytket/extensions/cutensornet/__init__.py | 2 +- pytket/extensions/cutensornet/backends/__init__.py | 2 +- pytket/extensions/cutensornet/backends/cutensornet_backend.py | 2 +- pytket/extensions/cutensornet/general.py | 2 +- pytket/extensions/cutensornet/tensor_network_convert.py | 2 +- pytket/extensions/cutensornet/tnstate/__init__.py | 2 +- pytket/extensions/cutensornet/tnstate/general.py | 2 +- pytket/extensions/cutensornet/tnstate/mps.py | 2 +- pytket/extensions/cutensornet/tnstate/mps_gate.py | 2 +- pytket/extensions/cutensornet/tnstate/mps_mpo.py | 2 +- pytket/extensions/cutensornet/tnstate/simulation.py | 2 +- pytket/extensions/cutensornet/tnstate/ttn.py | 2 +- pytket/extensions/cutensornet/tnstate/ttn_gate.py | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pytket/extensions/cutensornet/__init__.py b/pytket/extensions/cutensornet/__init__.py index f716ff92..5d12f155 100644 --- a/pytket/extensions/cutensornet/__init__.py +++ b/pytket/extensions/cutensornet/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/backends/__init__.py b/pytket/extensions/cutensornet/backends/__init__.py index 2bdfd2f2..54ae3aa7 100644 --- a/pytket/extensions/cutensornet/backends/__init__.py +++ b/pytket/extensions/cutensornet/backends/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/backends/cutensornet_backend.py b/pytket/extensions/cutensornet/backends/cutensornet_backend.py index 31777037..b6e13757 100644 --- a/pytket/extensions/cutensornet/backends/cutensornet_backend.py +++ b/pytket/extensions/cutensornet/backends/cutensornet_backend.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/general.py b/pytket/extensions/cutensornet/general.py index efa80aa8..97abf081 100644 --- a/pytket/extensions/cutensornet/general.py +++ b/pytket/extensions/cutensornet/general.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/tensor_network_convert.py b/pytket/extensions/cutensornet/tensor_network_convert.py index 0d9aad71..62394edd 100644 --- a/pytket/extensions/cutensornet/tensor_network_convert.py +++ b/pytket/extensions/cutensornet/tensor_network_convert.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/tnstate/__init__.py b/pytket/extensions/cutensornet/tnstate/__init__.py index 4fa995cb..5bb2e284 100644 --- a/pytket/extensions/cutensornet/tnstate/__init__.py +++ b/pytket/extensions/cutensornet/tnstate/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/tnstate/general.py index 2c2608e4..d2a11910 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/tnstate/general.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/tnstate/mps.py b/pytket/extensions/cutensornet/tnstate/mps.py index 7aac6763..c90d113f 100644 --- a/pytket/extensions/cutensornet/tnstate/mps.py +++ b/pytket/extensions/cutensornet/tnstate/mps.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/tnstate/mps_gate.py b/pytket/extensions/cutensornet/tnstate/mps_gate.py index 76a12234..883d5774 100644 --- a/pytket/extensions/cutensornet/tnstate/mps_gate.py +++ b/pytket/extensions/cutensornet/tnstate/mps_gate.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/tnstate/mps_mpo.py b/pytket/extensions/cutensornet/tnstate/mps_mpo.py index 915db486..192d2566 100644 --- a/pytket/extensions/cutensornet/tnstate/mps_mpo.py +++ b/pytket/extensions/cutensornet/tnstate/mps_mpo.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/tnstate/simulation.py index 15708043..aaf18b3c 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/tnstate/simulation.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index efc142ce..d8a7fbab 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index cd226c06..fb48249d 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Quantinuum +# Copyright 2019-2024 Quantinuum # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From f5cdc348df9b77f9ede3803971ea421c2e1f922d Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Sat, 13 Jan 2024 01:53:07 +0000 Subject: [PATCH 58/83] Bugfix on bounded chi truncation due to towards_root flag not being updated. I think this only caused more steps of canonicalisation to be done; either than or some extra fidelity was lost that wasn't being properly tracked. --- pytket/extensions/cutensornet/tnstate/ttn_gate.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index cd226c06..54bf76aa 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -270,6 +270,12 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # Canonicalise to this bond (unsafely, so we must reintroduce bond_tensor) bond_tensor = self.canonicalise(path, unsafe=True) + # Flip ``towards_root`` if we have reached the common ancestor + # i.e. if the ``bond_tensor`` needs to go towards a child tensor rather + # than towards the parent + if path == common_path: + towards_root = False + # Apply SVD decomposition on bond_tensor and truncate up to # `self._cfg.chi`. Ask cuTensorNet to contract S directly into U/V and # normalise the singular values so that the sum of its squares is equal @@ -358,6 +364,8 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: self._logger.debug( f"Reduced bond dimension from {bond_tensor.shape[0]} to {V.shape[0]}." ) + # Sanity check: reached the common ancestor and changed direction + assert not towards_root return self From 6c7592c5f8a03a0dd4e6ebb7f5bf124f709be5a4 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Fri, 12 Jan 2024 18:08:17 -0800 Subject: [PATCH 59/83] Fixing small mistake from previous commit --- pytket/extensions/cutensornet/tnstate/ttn_gate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 54bf76aa..40b4fca9 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -273,7 +273,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # Flip ``towards_root`` if we have reached the common ancestor # i.e. if the ``bond_tensor`` needs to go towards a child tensor rather # than towards the parent - if path == common_path: + if len(path) == len(common_path) + 1: towards_root = False # Apply SVD decomposition on bond_tensor and truncate up to From 5e8216dc60fc7e9c3f4df67d4b43d24680a9129d Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Mon, 15 Jan 2024 13:17:17 +0000 Subject: [PATCH 60/83] Code readability improvement. Moved chi truncation implementation to its own helper method. --- .../cutensornet/tnstate/ttn_gate.py | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 40b4fca9..adcd2f46 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -258,22 +258,41 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: ) # Truncate (if needed) bonds along the path from q0 to q1 - trunc_paths = [ # From q0 to the common ancestor + truncation_bonds = [ # From q0 to the common ancestor path_q0[:i] for i in reversed(range(len(common_path) + 1, len(path_q0) + 1)) ] - trunc_paths += [ # From the common ancestor to q1 + truncation_bonds += [ # From the common ancestor to q1 path_q1[:i] for i in range(len(common_path) + 1, len(path_q1) + 1) ] + self._chi_sequential_bond_truncation(truncation_bonds) + + return self + + def _chi_sequential_bond_truncation( + self, + truncation_bonds: list[RootPath], + ) -> None: + """Truncate all bonds in the input list to have a dimension of chi or lower. + + Args: + truncation_bonds: A list of bonds (provided by their RootPath address). + The list must be ordered in such a way that consecutive bonds have + a common tensor and such that the first and last bonds correspond to + physical (qubit) bonds. Hence, this provides the bonds in the path + in the TTN from one physical bond to another. + """ towards_root = True - for path in trunc_paths: + ancestor_level = min(len(bond_address) for bond_address in truncation_bonds) + + for bond_address in truncation_bonds: # Canonicalise to this bond (unsafely, so we must reintroduce bond_tensor) - bond_tensor = self.canonicalise(path, unsafe=True) + bond_tensor = self.canonicalise(bond_address, unsafe=True) # Flip ``towards_root`` if we have reached the common ancestor # i.e. if the ``bond_tensor`` needs to go towards a child tensor rather # than towards the parent - if len(path) == len(common_path) + 1: + if len(bond_address) == ancestor_level: towards_root = False # Apply SVD decomposition on bond_tensor and truncate up to @@ -281,7 +300,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # normalise the singular values so that the sum of its squares is equal # to one (i.e. the TTN is a normalised state after truncation). self._logger.debug( - f"Truncating at {path} to (or below) chosen chi={self._cfg.chi}" + f"Truncating at {bond_address} to (or below) chosen chi={self._cfg.chi}" ) options = {"handle": self._lib.handle, "device_id": self._lib.device_id} @@ -316,23 +335,23 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: self.fidelity *= this_fidelity # Contract V to the parent node of the bond - direction = path[-1] + direction = bond_address[-1] if direction == DirTTN.LEFT: indices = "lrp,sl->srp" else: indices = "lrp,sr->lsp" - self.nodes[path[:-1]].tensor = cq.contract( + self.nodes[bond_address[:-1]].tensor = cq.contract( indices, - self.nodes[path[:-1]].tensor, + self.nodes[bond_address[:-1]].tensor, V, options=options, optimize={"path": [(0, 1)]}, ) # Contract U to the child node of the bond - if self.nodes[path].is_leaf: + if self.nodes[bond_address].is_leaf: n_qbonds = ( - len(self.nodes[path].tensor.shape) - 1 + len(self.nodes[bond_address].tensor.shape) - 1 ) # Total number of physical bonds in this node node_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] else: @@ -340,8 +359,8 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: result_bonds = node_bonds.copy() result_bonds[-1] = "s" - self.nodes[path].tensor = cq.contract( - self.nodes[path].tensor, + self.nodes[bond_address].tensor = cq.contract( + self.nodes[bond_address].tensor, node_bonds, U, ["p", "s"], @@ -355,9 +374,9 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # The next node in the path towards qR loses its canonical form, since # S was contracted to it (either via U or V) if towards_root: - self.nodes[path[:-1]].canonical_form = None + self.nodes[bond_address[:-1]].canonical_form = None else: - self.nodes[path].canonical_form = None + self.nodes[bond_address].canonical_form = None # Report to logger self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") @@ -367,8 +386,6 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # Sanity check: reached the common ancestor and changed direction assert not towards_root - return self - def _create_funnel_tensor(self, path: RootPath, direction: DirTTN) -> Tensor: """Creates a funnel tensor for the given bond. From acd39a40b1c7605c71fab2300d92d6ff8a8e25ed Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Mon, 15 Jan 2024 16:49:55 +0000 Subject: [PATCH 61/83] Added truncation_fidelity approach to TTNxGate --- .../cutensornet/tnstate/ttn_gate.py | 216 ++++++++++++++---- 1 file changed, 175 insertions(+), 41 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index adcd2f46..a9b7bc07 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -265,25 +265,142 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: path_q1[:i] for i in range(len(common_path) + 1, len(path_q1) + 1) ] - self._chi_sequential_bond_truncation(truncation_bonds) + if self._cfg.truncation_fidelity < 1: + # Truncate as much as possible before violating the truncation fidelity + self._fidelity_bound_truncation_largest_first(truncation_bonds) + + else: + # Truncate so that all bonds have dimension less or equal to chi + self._chi_sequential_bond_truncation(truncation_bonds) return self + def _fidelity_bound_truncation_largest_first( + self, + truncation_bonds: list[RootPath], + ) -> None: + """Truncate as much as possible before exceeding the truncation fidelity. + + We first identify the bond in ``truncation_bonds`` with the largest dimension + and truncate it as much as possible before violating the truncation fidelity + and update the bound to the fidelity with the induced error. Then, we repeat + with the next largest bond (now with tighter fidelity threshold) and stop when + we find the first bond that cannot have a single dimension truncated. + + Args: + truncation_bonds: A list of bonds (provided by their RootPath address). + This is expected to provide the bonds in the path in the TTN from one + leaf node to another. + """ + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + + # Sort the bonds in decreasing order in terms of their dimension + truncation_bonds.sort( + key=lambda bond: self.get_dimension(bond, DirTTN.PARENT), + reverse=True, + ) + + # Keep track of the fidelity threshold (tighten it after each truncation) + fidelity_threshold = self._cfg.truncation_fidelity + + # Truncate each bond (in order) down to the fidelity threshold + for bond_address in truncation_bonds: + dimension_before = self.get_dimension(bond_address, DirTTN.PARENT) + + self._logger.debug( + "Truncating bond of dimension " + f"{dimension_before} at {bond_address}." + ) + + # Canonicalise to this bond (unsafely, so we must reintroduce bond_tensor) + bond_tensor = self.canonicalise(bond_address, unsafe=True) + + # Apply SVD decomposition to truncate as much as possible before exceeding + # a `discarded_weight_cutoff` of `1 - fidelity_threshold`. + self._logger.debug( + f"Truncating to target fidelity={fidelity_threshold}" + ) + + svd_method = tensor.SVDMethod( + abs_cutoff=self._cfg.zero, + discarded_weight_cutoff=1 - fidelity_threshold, + partition="U", # Contract S directly into U (towards the child node) + normalization="L2", # Sum of squares singular values must equal 1 + ) + + # Apply the SVD decomposition using the configuration defined above + U, S, V, svd_info = tensor.decompose( + "cp->cs,sp", + bond_tensor, + method=svd_method, + options=options, + return_info=True, + ) + assert S is None # Due to "partition" option in SVDMethod + + # discarded_weight is calculated within cuTensorNet as: + # sum([s**2 for s in S']) + # discarded_weight = 1 - ------------------------- + # sum([s**2 for s in S]) + # where S is the list of original singular values and S' is the set of + # singular values that remain after truncation (before normalisation). + # It can be shown that the fidelity ||^2 (for |phi> and |psi> + # unit vectors before and after truncation) is equal to 1 - disc_weight. + # + # We multiply the fidelity of the current step to the overall fidelity + # to keep track of a lower bound for the fidelity. + trunc_fidelity = 1.0 - svd_info.discarded_weight + self.fidelity *= trunc_fidelity + dimension_after = V.shape[0] + + # Report to logger + self._logger.debug(f"Truncation done. Truncation fidelity={trunc_fidelity}") + self._logger.debug( + f"Reduced bond dimension from {dimension_before} to {dimension_after}." + ) + + # We require that self._cfg.truncation_fidelity > prod(each_trunc_fidelity) + # since the RHS lower bounds the actual update factor to the fidelity. We + # ensure this inequality is satisfied by dividing the fidelity_threshold + # by the fidelity of this truncation. This makes the threshold tighter, + # since trunc_fidelity is less than 1, so division causes a higher value + # of fidelity_threshold. + fidelity_threshold /= trunc_fidelity + + # Contract U and V into the TTN. This reintroduces the data of bond_tensor + # back into the TTN, as required by ``canonicalise(.., unsafe=True)``. + self._contract_decomp_bond_tensor_into_ttn(U, V, bond_address) + + # Since S was contracted with U, and the U contracted with the child node, + # the child node loses its canonical form. + self.nodes[bond_address].canonical_form = None + + # Stop truncating if there was no reduction of dimension in this iteration + if dimension_before == dimension_after: + break + def _chi_sequential_bond_truncation( self, truncation_bonds: list[RootPath], ) -> None: """Truncate all bonds in the input list to have a dimension of chi or lower. + The list ``truncation_bonds`` is explored sequentially, truncating the bonds + one by one. + Args: truncation_bonds: A list of bonds (provided by their RootPath address). The list must be ordered in such a way that consecutive bonds have a common tensor and such that the first and last bonds correspond to - physical (qubit) bonds. Hence, this provides the bonds in the path - in the TTN from one physical bond to another. + the parent bonds of leaf nodes. Hence, this provides the bonds in the + path in the TTN from one leaf node to another. """ - towards_root = True + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + + # Identify what is the level in the tree at which the ancestor node of all + # of these bonds is ancestor_level = min(len(bond_address) for bond_address in truncation_bonds) + towards_root = True # First half of truncation_bonds is path towards ancestor for bond_address in truncation_bonds: # Canonicalise to this bond (unsafely, so we must reintroduce bond_tensor) @@ -303,7 +420,6 @@ def _chi_sequential_bond_truncation( f"Truncating at {bond_address} to (or below) chosen chi={self._cfg.chi}" ) - options = {"handle": self._lib.handle, "device_id": self._lib.device_id} svd_method = tensor.SVDMethod( abs_cutoff=self._cfg.zero, max_extent=self._cfg.chi, @@ -334,42 +450,9 @@ def _chi_sequential_bond_truncation( this_fidelity = 1.0 - svd_info.discarded_weight self.fidelity *= this_fidelity - # Contract V to the parent node of the bond - direction = bond_address[-1] - if direction == DirTTN.LEFT: - indices = "lrp,sl->srp" - else: - indices = "lrp,sr->lsp" - self.nodes[bond_address[:-1]].tensor = cq.contract( - indices, - self.nodes[bond_address[:-1]].tensor, - V, - options=options, - optimize={"path": [(0, 1)]}, - ) - - # Contract U to the child node of the bond - if self.nodes[bond_address].is_leaf: - n_qbonds = ( - len(self.nodes[bond_address].tensor.shape) - 1 - ) # Total number of physical bonds in this node - node_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] - else: - node_bonds = ["l", "r", "p"] - result_bonds = node_bonds.copy() - result_bonds[-1] = "s" - - self.nodes[bond_address].tensor = cq.contract( - self.nodes[bond_address].tensor, - node_bonds, - U, - ["p", "s"], - result_bonds, - options=options, - optimize={"path": [(0, 1)]}, - ) - # With these two contractions, bond_tensor has been reintroduced, as - # required when calling ``canonicalise(.., unsafe=True)`` + # Contract U and V into the TTN. This reintroduces the data of bond_tensor + # back into the TTN, as required by ``canonicalise(.., unsafe=True)``. + self._contract_decomp_bond_tensor_into_ttn(U, V, bond_address) # The next node in the path towards qR loses its canonical form, since # S was contracted to it (either via U or V) @@ -386,6 +469,57 @@ def _chi_sequential_bond_truncation( # Sanity check: reached the common ancestor and changed direction assert not towards_root + def _contract_decomp_bond_tensor_into_ttn( + self, + U: cp.ndarray, + V: cp.ndarray, + bond_address: RootPath + ) -> None: + """Contracts a decomposed bond_tensor back into the TTN. + + Args: + U: The tensor of the decomposition adjacent to the child node of the bond. + V: The tensor of the decomposition adjacent to the parent node of the bond. + bond_address: The address to the bond that was decomposed; explicitly, the + DirTTN.PARENT bond of the corresponding child node. + """ + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + + # Contract V to the parent node of the bond + direction = bond_address[-1] + if direction == DirTTN.LEFT: + indices = "lrp,sl->srp" + else: + indices = "lrp,sr->lsp" + self.nodes[bond_address[:-1]].tensor = cq.contract( + indices, + self.nodes[bond_address[:-1]].tensor, + V, + options=options, + optimize={"path": [(0, 1)]}, + ) + + # Contract U to the child node of the bond + if self.nodes[bond_address].is_leaf: + n_qbonds = ( + len(self.nodes[bond_address].tensor.shape) - 1 + ) # Total number of physical bonds in this node + node_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] + else: + node_bonds = ["l", "r", "p"] + result_bonds = node_bonds.copy() + result_bonds[-1] = "s" + + self.nodes[bond_address].tensor = cq.contract( + self.nodes[bond_address].tensor, + node_bonds, + U, + ["p", "s"], + result_bonds, + options=options, + optimize={"path": [(0, 1)]}, + ) + def _create_funnel_tensor(self, path: RootPath, direction: DirTTN) -> Tensor: """Creates a funnel tensor for the given bond. From 4395cc179af15eea0ddfc4d4a44394b41d97fbb5 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Mon, 15 Jan 2024 16:56:59 +0000 Subject: [PATCH 62/83] Updated tests --- tests/test_tnstate.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_tnstate.py b/tests/test_tnstate.py index 12dc8c76..6e0244d0 100644 --- a/tests/test_tnstate.py +++ b/tests/test_tnstate.py @@ -297,6 +297,7 @@ def test_exact_circ_sim(circuit: Circuit, algorithm: SimulationAlgorithm) -> Non [ SimulationAlgorithm.MPSxGate, SimulationAlgorithm.MPSxMPO, + SimulationAlgorithm.TTNxGate, ], ) def test_approx_circ_sim_gate_fid( @@ -374,6 +375,7 @@ def test_approx_circ_sim_chi(circuit: Circuit, algorithm: SimulationAlgorithm) - [ SimulationAlgorithm.MPSxGate, SimulationAlgorithm.MPSxMPO, + SimulationAlgorithm.TTNxGate, ], ) @pytest.mark.parametrize( @@ -481,12 +483,18 @@ def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: random.seed(1) with CuTensorNetHandle() as libhandle: + # Finite gate fidelity + # Check for TTNxGate + cfg = Config(truncation_fidelity=0.99) + ttn_gate = simulate(libhandle, circuit, SimulationAlgorithm.TTNxGate, cfg) + assert np.isclose(mps_gate.get_fidelity(), 0.4, atol=1e-1) + assert mps_gate.is_valid() + assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=cfg._atol) + # Fixed virtual bond dimension # Check for TTNxGate cfg = Config(chi=120, leaf_size=3) 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) assert ttn_gate.is_valid() assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=cfg._atol) From f42026890f8bb580d02eb075fc4a2d776c67f486 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Mon, 15 Jan 2024 17:22:38 +0000 Subject: [PATCH 63/83] Removed NotImplementedError exception and updated the fidelity of approx tests on TTN. --- pytket/extensions/cutensornet/tnstate/ttn.py | 5 ----- tests/test_tnstate.py | 8 ++++---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index efc142ce..63ba9a5b 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -133,11 +133,6 @@ def __init__( self.nodes: dict[RootPath, TreeNode] = dict() self.qubit_position: dict[Qubit, tuple[RootPath, int]] = dict() - if self._cfg.truncation_fidelity < 1: - raise NotImplementedError( - "Truncation fidelity mode not currently implemented on TTN." - ) - n_groups = len(qubit_partition) if n_groups == 0: # There's no initialisation to be done pass diff --git a/tests/test_tnstate.py b/tests/test_tnstate.py index 6e0244d0..2c05794a 100644 --- a/tests/test_tnstate.py +++ b/tests/test_tnstate.py @@ -487,15 +487,15 @@ def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: # Check for TTNxGate cfg = Config(truncation_fidelity=0.99) ttn_gate = simulate(libhandle, circuit, SimulationAlgorithm.TTNxGate, cfg) - assert np.isclose(mps_gate.get_fidelity(), 0.4, atol=1e-1) - assert mps_gate.is_valid() - assert np.isclose(mps_gate.vdot(mps_gate), 1.0, atol=cfg._atol) + assert np.isclose(ttn_gate.get_fidelity(), 0.69, atol=1e-2) + assert ttn_gate.is_valid() + assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=cfg._atol) # Fixed virtual bond dimension # Check for TTNxGate cfg = Config(chi=120, leaf_size=3) ttn_gate = simulate(libhandle, circuit, SimulationAlgorithm.TTNxGate, cfg) - assert np.isclose(ttn_gate.get_fidelity(), 0.62, atol=1e-2) + assert np.isclose(ttn_gate.get_fidelity(), 0.84, atol=1e-2) assert ttn_gate.is_valid() assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=cfg._atol) From 4390ecb6544419a6eee1b58f9f1879608a6e5630 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 16 Jan 2024 03:04:55 -0800 Subject: [PATCH 64/83] Linting --- pytket/extensions/cutensornet/tnstate/ttn_gate.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index a9b7bc07..8de6f3ba 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -308,8 +308,7 @@ def _fidelity_bound_truncation_largest_first( dimension_before = self.get_dimension(bond_address, DirTTN.PARENT) self._logger.debug( - "Truncating bond of dimension " - f"{dimension_before} at {bond_address}." + "Truncating bond of dimension " f"{dimension_before} at {bond_address}." ) # Canonicalise to this bond (unsafely, so we must reintroduce bond_tensor) @@ -317,9 +316,7 @@ def _fidelity_bound_truncation_largest_first( # Apply SVD decomposition to truncate as much as possible before exceeding # a `discarded_weight_cutoff` of `1 - fidelity_threshold`. - self._logger.debug( - f"Truncating to target fidelity={fidelity_threshold}" - ) + self._logger.debug(f"Truncating to target fidelity={fidelity_threshold}") svd_method = tensor.SVDMethod( abs_cutoff=self._cfg.zero, @@ -470,10 +467,7 @@ def _chi_sequential_bond_truncation( assert not towards_root def _contract_decomp_bond_tensor_into_ttn( - self, - U: cp.ndarray, - V: cp.ndarray, - bond_address: RootPath + self, U: cp.ndarray, V: cp.ndarray, bond_address: RootPath ) -> None: """Contracts a decomposed bond_tensor back into the TTN. From d93418443eafb796fb9e03a71fc2114f68fa2107 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Tue, 23 Jan 2024 17:51:45 +0000 Subject: [PATCH 65/83] Implemented a different approach for gate fidelity truncation. --- .../cutensornet/tnstate/ttn_gate.py | 193 +++++++++++------- 1 file changed, 122 insertions(+), 71 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 8de6f3ba..b75e0332 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -258,70 +258,118 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: ) # Truncate (if needed) bonds along the path from q0 to q1 - truncation_bonds = [ # From q0 to the common ancestor + bonds_from_q0_to_ancestor = [ path_q0[:i] for i in reversed(range(len(common_path) + 1, len(path_q0) + 1)) ] - truncation_bonds += [ # From the common ancestor to q1 + bonds_from_ancestor_to_q1 = [ path_q1[:i] for i in range(len(common_path) + 1, len(path_q1) + 1) ] if self._cfg.truncation_fidelity < 1: # Truncate as much as possible before violating the truncation fidelity - self._fidelity_bound_truncation_largest_first(truncation_bonds) + self._fidelity_bound_sequential_weighted_truncation( + bonds_from_q0_to_ancestor, bonds_from_ancestor_to_q1 + ) else: # Truncate so that all bonds have dimension less or equal to chi - self._chi_sequential_bond_truncation(truncation_bonds) + self._chi_sequential_truncation( + bonds_from_q0_to_ancestor, bonds_from_ancestor_to_q1 + ) return self - def _fidelity_bound_truncation_largest_first( + def _fidelity_bound_sequential_weighted_truncation( self, - truncation_bonds: list[RootPath], + bonds_from_q0_to_ancestor: list[RootPath], + bonds_from_ancestor_to_q1: list[RootPath], ) -> None: - """Truncate as much as possible before exceeding the truncation fidelity. + """Truncate as much as possible up to the truncation fidelity. + + Our target is to assign a local truncation fidelity `f_i` to each bond `i` in + the input lists so that the lower bound of the fidelity satisfies: + + self.fidelity * prod(f_i) < self.fidelity * truncation_fidelity (A) + + Let e_i = 1 - f_i, where we refer to `e_i` as the "truncation error at bond i". + We can use that when e_i is close to zero, the bound: + + prod(1 - e_i) > 1 - sum(e_i) (B) + + is fairly tight, with an inaccuracy of an additive O(e_i^2) term. Hence, for + simplicity we take prod(f_i) ~ 1 - sum(e_i). Let - We first identify the bond in ``truncation_bonds`` with the largest dimension - and truncate it as much as possible before violating the truncation fidelity - and update the bound to the fidelity with the induced error. Then, we repeat - with the next largest bond (now with tighter fidelity threshold) and stop when - we find the first bond that cannot have a single dimension truncated. + `admissible_error` = 1 - `truncation_fidelity` (C) + + and assign each e_i = w_i * `admissible_error` where 0 < w_i < 1 is a weight + factor such that sum(w_i) = 1. Thus, if each bond `i` is truncated to a fidelity + + f_i = 1 - w_i * `admissible_error` (D) + + then the total fidelity factor on the LHS of equation (A) should approximate + `truncation_fidelity`. There is risk of overshooting with truncation and + endinf up with a new `self.fidelity` slightly lower than the target, but this + should be fine in practice, since `self.fidelity` is a lower bound anyway. + Each of the `w_i` weight factors is assigned depending on the bond dimension, + with larger bonds given higher weight, so they are truncated more aggressively. Args: - truncation_bonds: A list of bonds (provided by their RootPath address). - This is expected to provide the bonds in the path in the TTN from one - leaf node to another. + bonds_from_q0_to_ancestor: A list of bonds (each as their RootPath address). + These bonds will be truncated. The list must be ordered in such a way + that consecutive bonds share a common tensor and such that the first + bond in the list corresponds to the leaf node that `q0` is assigned to + and the last bond in the list corresponds to child bond of the common + ancestor between the leaves of `q0` and `q1`. + bonds_from_ancestor_q1: Same as above, but the list starts from the other + child bond of the common ancestor and ends at the leaf node that `q1` + is assigned to. Together, these two lists provide a path in the TTN + from the leaf node of `q0` to the leaf node of `q1`. """ options = {"handle": self._lib.handle, "device_id": self._lib.device_id} + admissible_error = 1 - self._cfg.truncation_fidelity - # Sort the bonds in decreasing order in terms of their dimension - truncation_bonds.sort( - key=lambda bond: self.get_dimension(bond, DirTTN.PARENT), - reverse=True, - ) - - # Keep track of the fidelity threshold (tighten it after each truncation) - fidelity_threshold = self._cfg.truncation_fidelity + # Combine the two lists of bonds, but remember at which entry the direction + # of the path is switched from going towards root to going towards leaves. + truncation_bonds = bonds_from_q0_to_ancestor + bonds_from_ancestor_to_q1 + switch_direction_at = len(bonds_from_q0_to_ancestor) + towards_root = True # First half of truncation_bonds is path towards ancestor - # Truncate each bond (in order) down to the fidelity threshold - for bond_address in truncation_bonds: + # Obtain the dimension of each bond + dimensions = [ + self.get_dimension(bond, DirTTN.PARENT) for bond in truncation_bonds + ] + # Assign the weight `w_i` of each bond. + # NOTE: currently uses w_i = dim_i / sum(dim_i), for no other reason that it is + # simple. Better weight functions may exist and research on this is desirable. + weights = [dim / sum(dimensions) for dim in dimensions] + # Assign a fidelity `f_i` to each bond. + bond_fidelities = [1 - w * admissible_error for w in weights] + + # Truncate each bond as much as possible up to its assigned bond fidelity + for i, bond_address in enumerate(truncation_bonds): dimension_before = self.get_dimension(bond_address, DirTTN.PARENT) - self._logger.debug( - "Truncating bond of dimension " f"{dimension_before} at {bond_address}." - ) - # Canonicalise to this bond (unsafely, so we must reintroduce bond_tensor) bond_tensor = self.canonicalise(bond_address, unsafe=True) + # Flip ``towards_root`` if we have reached the common ancestor + # i.e. if the ``bond_tensor`` needs to go towards a child tensor rather + # than towards the parent + if switch_direction_at == i: + towards_root = False + # Apply SVD decomposition to truncate as much as possible before exceeding - # a `discarded_weight_cutoff` of `1 - fidelity_threshold`. - self._logger.debug(f"Truncating to target fidelity={fidelity_threshold}") + # a `discarded_weight_cutoff` of `1 - f_i`. Contract S directly into U/V and + # normalise the singular values so that the sum of its squares is equal + # to one (i.e. the TTN is a normalised state after truncation). + self._logger.debug( + f"Truncating at {bond_address} to target fidelity={bond_fidelities[i]}" + ) svd_method = tensor.SVDMethod( abs_cutoff=self._cfg.zero, - discarded_weight_cutoff=1 - fidelity_threshold, - partition="U", # Contract S directly into U (towards the child node) + discarded_weight_cutoff=1 - bond_fidelities[i], + partition="V" if towards_root else "U", # Contract S to parent or child normalization="L2", # Sum of squares singular values must equal 1 ) @@ -346,67 +394,69 @@ def _fidelity_bound_truncation_largest_first( # # We multiply the fidelity of the current step to the overall fidelity # to keep track of a lower bound for the fidelity. - trunc_fidelity = 1.0 - svd_info.discarded_weight - self.fidelity *= trunc_fidelity + this_fidelity = 1.0 - svd_info.discarded_weight + self.fidelity *= this_fidelity dimension_after = V.shape[0] - # Report to logger - self._logger.debug(f"Truncation done. Truncation fidelity={trunc_fidelity}") - self._logger.debug( - f"Reduced bond dimension from {dimension_before} to {dimension_after}." - ) - - # We require that self._cfg.truncation_fidelity > prod(each_trunc_fidelity) - # since the RHS lower bounds the actual update factor to the fidelity. We - # ensure this inequality is satisfied by dividing the fidelity_threshold - # by the fidelity of this truncation. This makes the threshold tighter, - # since trunc_fidelity is less than 1, so division causes a higher value - # of fidelity_threshold. - fidelity_threshold /= trunc_fidelity - # Contract U and V into the TTN. This reintroduces the data of bond_tensor # back into the TTN, as required by ``canonicalise(.., unsafe=True)``. self._contract_decomp_bond_tensor_into_ttn(U, V, bond_address) - # Since S was contracted with U, and the U contracted with the child node, - # the child node loses its canonical form. - self.nodes[bond_address].canonical_form = None + # The next node in the path towards qR loses its canonical form, since + # S was contracted to it (either via U or V) + if towards_root: + self.nodes[bond_address[:-1]].canonical_form = None + else: + self.nodes[bond_address].canonical_form = None - # Stop truncating if there was no reduction of dimension in this iteration - if dimension_before == dimension_after: - break + # Report to logger + self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") + self._logger.debug( + f"Reduced bond dimension from {dimension_before} to {dimension_after}." + ) + # Sanity check: reached the common ancestor and changed direction + assert not towards_root - def _chi_sequential_bond_truncation( + def _chi_sequential_truncation( self, - truncation_bonds: list[RootPath], + bonds_from_q0_to_ancestor: list[RootPath], + bonds_from_ancestor_to_q1: list[RootPath], ) -> None: - """Truncate all bonds in the input list to have a dimension of chi or lower. + """Truncate all bonds in the input lists to have a dimension of chi or lower. - The list ``truncation_bonds`` is explored sequentially, truncating the bonds + The lists of bonds are explored sequentially, truncating the bonds one by one. Args: - truncation_bonds: A list of bonds (provided by their RootPath address). - The list must be ordered in such a way that consecutive bonds have - a common tensor and such that the first and last bonds correspond to - the parent bonds of leaf nodes. Hence, this provides the bonds in the - path in the TTN from one leaf node to another. + bonds_from_q0_to_ancestor: A list of bonds (each as their RootPath address). + These bonds will be truncated. The list must be ordered in such a way + that consecutive bonds share a common tensor and such that the first + bond in the list corresponds to the leaf node that `q0` is assigned to + and the last bond in the list corresponds to child bond of the common + ancestor between the leaves of `q0` and `q1`. + bonds_from_ancestor_q1: Same as above, but the list starts from the other + child bond of the common ancestor and ends at the leaf node that `q1` + is assigned to. Together, these two lists provide a path in the TTN + from the leaf node of `q0` to the leaf node of `q1`. """ options = {"handle": self._lib.handle, "device_id": self._lib.device_id} - # Identify what is the level in the tree at which the ancestor node of all - # of these bonds is - ancestor_level = min(len(bond_address) for bond_address in truncation_bonds) + # Combine the two lists of bonds, but remember at which entry the direction + # of the path is switched from going towards root to going towards leaves. + truncation_bonds = bonds_from_q0_to_ancestor + bonds_from_ancestor_to_q1 + switch_direction_at = len(bonds_from_q0_to_ancestor) towards_root = True # First half of truncation_bonds is path towards ancestor - for bond_address in truncation_bonds: + for i, bond_address in enumerate(truncation_bonds): + dimension_before = self.get_dimension(bond_address, DirTTN.PARENT) + # Canonicalise to this bond (unsafely, so we must reintroduce bond_tensor) bond_tensor = self.canonicalise(bond_address, unsafe=True) # Flip ``towards_root`` if we have reached the common ancestor # i.e. if the ``bond_tensor`` needs to go towards a child tensor rather # than towards the parent - if len(bond_address) == ancestor_level: + if switch_direction_at == i: towards_root = False # Apply SVD decomposition on bond_tensor and truncate up to @@ -420,7 +470,7 @@ def _chi_sequential_bond_truncation( svd_method = tensor.SVDMethod( abs_cutoff=self._cfg.zero, max_extent=self._cfg.chi, - partition="V" if towards_root else "U", + partition="V" if towards_root else "U", # Contract S to parent or child normalization="L2", # Sum of squares equal 1 ) @@ -446,6 +496,7 @@ def _chi_sequential_bond_truncation( # to keep track of a lower bound for the fidelity. this_fidelity = 1.0 - svd_info.discarded_weight self.fidelity *= this_fidelity + dimension_after = V.shape[0] # Contract U and V into the TTN. This reintroduces the data of bond_tensor # back into the TTN, as required by ``canonicalise(.., unsafe=True)``. @@ -461,7 +512,7 @@ def _chi_sequential_bond_truncation( # Report to logger self._logger.debug(f"Truncation done. Truncation fidelity={this_fidelity}") self._logger.debug( - f"Reduced bond dimension from {bond_tensor.shape[0]} to {V.shape[0]}." + f"Reduced bond dimension from {dimension_before} to {dimension_after}." ) # Sanity check: reached the common ancestor and changed direction assert not towards_root From ec5c2cb1d4e6d2492801cfb61ce91f3b35fdab6d Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 23 Jan 2024 10:05:48 -0800 Subject: [PATCH 66/83] Update fidelity test --- tests/test_tnstate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_tnstate.py b/tests/test_tnstate.py index 2c05794a..a901883b 100644 --- a/tests/test_tnstate.py +++ b/tests/test_tnstate.py @@ -487,7 +487,7 @@ def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: # Check for TTNxGate cfg = Config(truncation_fidelity=0.99) ttn_gate = simulate(libhandle, circuit, SimulationAlgorithm.TTNxGate, cfg) - assert np.isclose(ttn_gate.get_fidelity(), 0.69, atol=1e-2) + assert np.isclose(ttn_gate.get_fidelity(), 0.70, atol=1e-2) assert ttn_gate.is_valid() assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=cfg._atol) From 924146c2b91ce9d8c0178fe1b32723188a36fa8e Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Tue, 23 Jan 2024 10:40:13 -0800 Subject: [PATCH 67/83] Added some extra debug messages to logger --- .../extensions/cutensornet/tnstate/ttn_gate.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index b75e0332..6817ea7d 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -325,6 +325,9 @@ def _fidelity_bound_sequential_weighted_truncation( is assigned to. Together, these two lists provide a path in the TTN from the leaf node of `q0` to the leaf node of `q1`. """ + self._logger.debug("Starting sequential weighted truncation (fidelity bound).") + initial_fidelity = self.fidelity + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} admissible_error = 1 - self._cfg.truncation_fidelity @@ -414,6 +417,12 @@ def _fidelity_bound_sequential_weighted_truncation( self._logger.debug( f"Reduced bond dimension from {dimension_before} to {dimension_after}." ) + + self._logger.debug( + "Finished sequential weighted truncation (fidelity bound). " + f"Fidelity factor = {self.fidelity / initial_fidelity}" + ) + # Sanity check: reached the common ancestor and changed direction assert not towards_root @@ -439,6 +448,9 @@ def _chi_sequential_truncation( is assigned to. Together, these two lists provide a path in the TTN from the leaf node of `q0` to the leaf node of `q1`. """ + self._logger.debug("Starting sequential truncation (chi bound).") + initial_fidelity = self.fidelity + options = {"handle": self._lib.handle, "device_id": self._lib.device_id} # Combine the two lists of bonds, but remember at which entry the direction @@ -514,6 +526,12 @@ def _chi_sequential_truncation( self._logger.debug( f"Reduced bond dimension from {dimension_before} to {dimension_after}." ) + + self._logger.debug( + "Finished sequential truncation (chi bound). " + f"Fidelity factor = {self.fidelity / initial_fidelity}" + ) + # Sanity check: reached the common ancestor and changed direction assert not towards_root From f8777cc6ed9492aa319303f37264def6b49b30e0 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Sat, 27 Jan 2024 17:23:14 +0000 Subject: [PATCH 68/83] Refactored the code to avoid the use of funnel tensors. --- .../cutensornet/tnstate/ttn_gate.py | 294 +++++++++++------- 1 file changed, 190 insertions(+), 104 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index cd226c06..3c366dfe 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -21,6 +21,7 @@ try: import cuquantum as cq # type: ignore from cuquantum.cutensornet import tensor # type: ignore + from cuquantum.cutensornet.experimental import contract_decompose # type: ignore except ImportError: warnings.warn("local settings failed to import cutensornet", ImportWarning) @@ -159,110 +160,209 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: else: break common_path = tuple(common_dirs) - (qL, qR) = (q0, q1) if path_q0[len(common_path)] == DirTTN.LEFT else (q1, q0) + + # We begin by canonicalising to the left child bond of the common ancestor. + # This canonicalisation could be done later (just before truncation), but + # doing it now will prevent the need to recanonicalise the tensors that have + # grown (by a factor of x16) when introducing this gate. + # The choice of the left child bond is arbitrary, any bond in the TTN that + # is in the arc connecting qL to qR would have worked. + # + # NOTE: In fact, none of the tensors that are affected by the gate need to + # be canonicalised ahead of time, but I don't expect the saving would be + # particularly noticeable, and it'd require some non-trivial refactoring + # of `canonicalise()`. + self.canonicalise( + center=(*common_path, DirTTN.LEFT) + ) + + self._logger.debug(f"Applying gate to the TTN.") # Glossary of bond IDs - # l -> the input bond of the gate on qL - # r -> the input bond of the gate on qR - # L -> the output bond of the gate on qL - # R -> the output bond of the gate on qR - # c -> a child bond of the TTN node - # C -> another child bond of the TTN node + # a -> the input bond of the gate on q0 + # b -> the input bond of the gate on q1 + # A -> the output bond of the gate on q0 + # B -> the output bond of the gate on q1 + # l -> left child bond of the TTN node + # r -> right child bond of the TTN node # p -> the parent bond of the TTN node - # f -> the output bond of a funnel - # F -> the output bond of another funnel - if qL == q0: - gate_bonds = "LRlr" - else: # Implicit swap - gate_bonds = "RLrl" - - # Update the common ancestor tensor with the gate and funnels - left_funnel = self._create_funnel_tensor(common_path, DirTTN.LEFT) - right_funnel = self._create_funnel_tensor(common_path, DirTTN.RIGHT) - - self._logger.debug(f"Including gate in tensor at {common_path}.") - self.nodes[common_path].tensor = cq.contract( - "cCp,fclL,FCrR," + gate_bonds + "->fFp", - self.nodes[common_path].tensor, - left_funnel, - right_funnel, + # s -> the shared bond resulting from a decomposition + # chr(x) -> bond of the x-th qubit in a leaf node + gate_bonds = "ABab" + + # The overall strategy is to connect the `a` bond of the gate tensor to + # the corresponding bond for `q0` in the TTN (so that its bond `A`) becomes + # the new physical bond for `q0`. However, bonds `b` and `B` corresponding to + # `q1` are left open. We combine this `gate_tensor` with the leaf node of `q0` + # and QR-decompose the result; where the Q tensor will be the new + # (canonicalised) leaf node and R becomes our `msg_tensor`. The latter contains + # the open bonds `b` and `B` and our objective is to "push" this `msg_tensor` + # through the TTN towards the leaf node of `q1`. Here, "push through" means + # contract with the next tensor, and apply QR decomposition, so that the + # `msg_tensor` carrying `b` and `B` ends up one bond closer to `q1`. + # Once `msg_tensor` is directly connected to the leaf node containing `q1`, we + # just need to contract them, connecting `b` to `q1`, with `B` becoming the + # new physical bond. + # + # The `msg_tensor` has four bonds. Our convention will be that the first bond + # always corresponds to `B`, the second bond is `b`, the third bond connects + # it to the TTN in the child direction and the fourth connects it to the TTN + # in the `DirTTN.PARENT` direction. If we label the third bond with `l`, then + # the fourth bond will be labelled `L` (and vice versa). Same for `r` and `p`. + + # We begin applying the gate to the TTN by contracting `gate_tensor` into the + # leaf node containing `q0`, with the `b` and `B` bonds of the latter left open. + # We immediately QR-decompose the resulting tensor, + leaf_node = self.nodes[path_q0] + n_qbonds = len(leaf_node.tensor.shape) - 1 # Num of qubit bonds + leaf_bonds = "".join( + "a" if x == bond_q0 else chr(x) + for x in range(n_qbonds) + ) + "p" + Q_bonds = "".join( + "A" if x == bond_q0 else chr(x) + for x in range(n_qbonds) + ) + "s" + R_bonds = "Bbsp" # The `msg_tensor` + + # Apply the contraction followed by a QR decomposition + leaf_node.tensor, msg_tensor = contract_decompose( + f"{leaf_bonds},{gate_bonds}->{Q_bonds},{R_bonds}", + leaf_node.tensor, gate_tensor, + algorithm={"qr_method": True}, options=options, - optimize={"path": [(0, 1), (1, 2), (0, 1)]}, - ) - self.nodes[common_path].canonical_form = None - self._logger.debug( - f"New tensor of shape {self.nodes[common_path].tensor.shape} and " - f"size (MiB)={self.nodes[common_path].tensor.nbytes / 2**20}" + optimize={"path": [(0, 1)]}, ) + # Update the canonical form of the leaf node + leaf_node.canonical_form = DirTTN.PARENT - # For each bond along the path, add two identity dim 2 wires - mid_paths = [ - (path_qX[:i], path_qX[i]) - for path_qX in [path_q0, path_q1] - for i in range(len(common_path) + 1, len(path_qX)) + # We must push the `msg_tensor` all the way to the common ancestor + # of `q0` and `q1`. + bonds_from_q0_to_ancestor = [ + path_q0[:i] for i in reversed(range(len(common_path) + 1, len(path_q0) + 1)) ] - - for path, child_dir in mid_paths: - child_funnel = self._create_funnel_tensor(path, child_dir) - parent_funnel = self._create_funnel_tensor(path, DirTTN.PARENT) - - if child_dir == DirTTN.LEFT: - indices = "fcab,Fpab,cCp->fCF" - else: - indices = "fcab,Fpab,Ccp->CfF" - - self._logger.debug(f"Adding funnels at {path}.") - self.nodes[path].tensor = cq.contract( - indices, - child_funnel, - parent_funnel, - self.nodes[path].tensor, + # Sanity checks: + assert all(len(bond_addresses) != len(common_path)) + assert len(bond_addresses[0]) == len(path_q0) + assert len(bond_addresses[1]) < len(bond_addresses[0]) + + # For all of these nodes; push `msg_tensor` through to their parent bond + for child_bond in bond_addresses[:-1]: # Doesn't do it on common ancestor! + child_dir = child_bond[-1] + parent_bond = child_bond[:-1] + node = self.nodes[parent_bond] + + node_bonds = "lrp" + msg_bonds = "BbLl" if child_dir == DirTTN.LEFT else "BbRr" + Q_bonds = "Lrs" if child_dir == DirTTN.LEFT else "lRs" + R_bonds = "Bbsp" # The new `msg_tensor` + + # Apply the contraction followed by a QR decomposition + node.tensor, msg_tensor = contract_decompose( + f"{node_bonds},{msg_bonds}->{Q_bonds},{R_bonds}", + node.tensor, + msg_tensor, + algorithm={"qr_method": True}, options=options, - optimize={"path": [(1, 2), (0, 1)]}, - ) - self.nodes[path].canonical_form = None - self._logger.debug( - f"New tensor of shape {self.nodes[path].tensor.shape} and " - f"size (MiB)={self.nodes[path].tensor.nbytes / 2**20}" + optimize={"path": [(0, 1)]}, ) - - # Update the leaf tensors containing qL and qR - for path, bond in [self.qubit_position[q] for q in [qL, qR]]: - leaf_node = self.nodes[path] - n_qbonds = ( - len(leaf_node.tensor.shape) - 1 - ) # Total number of physical bonds in this node - - aux_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] - node_bonds = aux_bonds.copy() - node_bonds[bond] = "a" - result_bonds = aux_bonds - result_bonds[bond] = "b" - result_bonds[-1] = "f" - - self._logger.debug(f"Adding funnels at leaf node at {path}.") - leaf_node.tensor = cq.contract( - leaf_node.tensor, - node_bonds, - self._create_funnel_tensor(path, DirTTN.PARENT), - ["f", "p", "a", "b"], - result_bonds, + # Update the canonical form of the node + node.canonical_form = DirTTN.PARENT + + # The `msg_tensor` is now on a child bond of the common ancestor. + # We must push it through to the other child node. + child_bond = bond_addresses[-1] # This is where msg_tensor currently is + child_dir = child_bond[-1] + parent_bond = child_bond[:-1] + common_ancestor_node = self.nodes[parent_bond] + + node_bonds = "lrp" + msg_bonds = "BbLl" if child_dir == DirTTN.LEFT else "BbRr" + Q_bonds = "Lsp" if child_dir == DirTTN.LEFT else "sRp" + R_bonds = "Bbrs" if child_dir == DirTTN.LEFT else "Bbls" # The new `msg_tensor` + + # Apply the contraction followed by a QR decomposition + common_ancestor_node.tensor, msg_tensor = contract_decompose( + f"{node_bonds},{msg_bonds}->{Q_bonds},{R_bonds}", + common_ancestor_node.tensor, + msg_tensor, + algorithm={"qr_method": True}, + options=options, + optimize={"path": [(0, 1)]}, + ) + # Update the canonical form of the node + if child_dir == DirTTN.LEFT: + common_ancestor_node.canonical_form = DirTTN.RIGHT + else: + common_ancestor_node.canonical_form = DirTTN.LEFT + + # We must push the `msg_tensor` from the common ancestor to the leaf node + # containing `q1`. + bonds_addresses = [ + path_q1[:i] for i in range(len(common_path) + 1, len(path_q1) + 1) + ] + # Sanity checks: + assert all(len(bond_addresses) != len(common_path)) + assert len(bond_addresses[-1]) == len(path_q1) + assert len(bond_addresses[0]) < len(bond_addresses[1]) + + # For all of these nodes; push `msg_tensor` through to their child bond + for child_bond in bond_addresses[1:]: # Skip common ancestor: already pushed + child_dir = child_bond[-1] + parent_bond = child_bond[:-1] + node = self.nodes[parent_bond] + + node_bonds = "lrp" + msg_bonds = "BbpP" + Q_bonds = "srP" if child_dir == DirTTN.LEFT else "lsP" + R_bonds = "Bbls" if child_dir == DirTTN.LEFT else "Bbrs" # New `msg_tensor` + + # Apply the contraction followed by a QR decomposition + node.tensor, msg_tensor = contract_decompose( + f"{node_bonds},{msg_bonds}->{Q_bonds},{R_bonds}", + node.tensor, + msg_tensor, + algorithm={"qr_method": True}, options=options, optimize={"path": [(0, 1)]}, ) - leaf_node.canonical_form = None - self._logger.debug( - f"New tensor of shape {leaf_node.tensor.shape} and " - f"size (MiB)={leaf_node.tensor.nbytes / 2**20}" - ) - - # Truncate (if needed) bonds along the path from q0 to q1 - trunc_paths = [ # From q0 to the common ancestor - path_q0[:i] for i in reversed(range(len(common_path) + 1, len(path_q0) + 1)) + # Update the canonical form of the node + node.canonical_form = child_dir + + # Finally, the `msg_tensor` is in the parent bond of the leaf node of `q1`. + # All we need to do is contract the `msg_tensor` into the leaf. + leaf_node = self.nodes[path_q1] + n_qbonds = len(leaf_node.tensor.shape) - 1 # Num of qubit bonds + leaf_bonds = "".join( + "b" if x == bond_q1 else chr(x) # Connect `b` to `q1` + for x in range(n_qbonds) + ) + "p" + msg_bonds = "BbpP" + result_bonds = "".join( + "B" if x == bond_q1 else chr(x) # `B` becomes the new physical bond `q1` + for x in range(n_qbonds) + ) + "P" + + # Apply the contraction + leaf_node.tensor = cq.contract( + f"{leaf_bonds},{msg_bonds}->{result_bonds}", + leaf_node.tensor, + msg_tensor + options=options, + optimize={"path": [(1, 2), (0, 1)]}, + ) + # The leaf node lost its canonical form + leaf_node.canonical_form = None + + # Truncate (if needed) bonds along the arc from `q1` to `q0`. + # We truncate in this direction to take advantage of the canonicalisation + # of the TTN we achieved while pushing the `msg_tensor` from `q0` to `q1`. + trunc_paths = [ # From q1 to the common ancestor + path_q1[:i] for i in reversed(range(len(common_path) + 1, len(path_q1) + 1)) ] - trunc_paths += [ # From the common ancestor to q1 - path_q1[:i] for i in range(len(common_path) + 1, len(path_q1) + 1) + trunc_paths += [ # From the common ancestor to q0 + path_q0[:i] for i in range(len(common_path) + 1, len(path_q0) + 1) ] towards_root = True @@ -360,17 +460,3 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: ) return self - - def _create_funnel_tensor(self, path: RootPath, direction: DirTTN) -> Tensor: - """Creates a funnel tensor for the given bond. - - A funnel tensor is a reshape of an identity, merging three bonds to one. - A funnel tensor has four bonds. It satisfies ``funnel.shape[0] == 4*dim`` - where ``dim`` is the dimension of the bond of ``path`` and ``direction``. - Hence, the first bond of the funnel is the "merged" bond. The other three - satisfy ``funnel.shape[1] == dim`` (this the original bond) and - ``funnel.shape[x] == 2`` for ``x`` 2 and 3. - """ - dim = self.get_dimension(path, direction) - identity = cp.eye(4 * dim, dtype=self._cfg._complex_t) - return cp.reshape(identity, (4 * dim, dim, 2, 2)) From c9fa77ff2125f6d8d0c7c3e567adc021c6abbee5 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Sat, 27 Jan 2024 22:04:49 +0000 Subject: [PATCH 69/83] Some minor fixes and refactoring --- .../cutensornet/tnstate/ttn_gate.py | 115 +++++++----------- 1 file changed, 45 insertions(+), 70 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 3c366dfe..24f299c6 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -111,36 +111,35 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: (path_q0, bond_q0) = self.qubit_position[q0] (path_q1, bond_q1) = self.qubit_position[q1] + # Glossary of bond IDs + # a -> the input bond of the gate on q0 + # b -> the input bond of the gate on q1 + # A -> the output bond of the gate on q0 + # B -> the output bond of the gate on q1 + # l -> left child bond of the TTN node + # r -> right child bond of the TTN node + # p -> the parent bond of the TTN node + # s -> the shared bond resulting from a decomposition + # chr(x) -> bond of the x-th qubit in a leaf node + gate_bonds = "ABab" + # If the two qubits are in the same leaf node, contract the gate with it. - # There is no truncation needed. + # No truncation is required. if path_q0 == path_q1: - n_qbonds = ( - len(self.nodes[path_q0].tensor.shape) - 1 - ) # Total number of physical bonds in this node - - # Glossary of bond IDs - # qX -> where X is the X-th physical bond (qubit) in the TTN node - # p -> the parent bond of the TTN node - # i0 -> the input bond of the gate on q0 - # o0 -> the output bond of the gate on q0 - # i1 -> the input bond of the gate on q1 - # o1 -> the output bond of the gate on q1 - gate_bond = ["o0", "o1", "i0", "i1"] - - aux_bonds = [f"q{x}" for x in range(n_qbonds)] + ["p"] - node_bonds = aux_bonds.copy() - node_bonds[bond_q0] = "i0" - node_bonds[bond_q1] = "i1" - result_bonds = aux_bonds - result_bonds[bond_q0] = "o0" - result_bonds[bond_q1] = "o1" + leaf_node = self.nodes[path_q0] + n_qbonds = len(leaf_node.tensor.shape) - 1 # Num of qubit bonds + aux_bonds = [chr(x) for x in range(n_qbonds)] + aux_bonds[bond_q0] = "a" + aux_bonds[bond_q1] = "b" + leaf_bonds = "".join(aux_bonds) + "p" + aux_bonds[bond_q0] = "A" + aux_bonds[bond_q1] = "B" + result_bonds = "".join(aux_bonds) + "p" self.nodes[path_q0].tensor = cq.contract( - self.nodes[path_q0].tensor, - node_bonds, + f"{leaf_bonds},{gate_bonds}->{result_bonds}", + leaf_node.tensor, gate_tensor, - gate_bond, - result_bonds, options=options, optimize={"path": [(0, 1)]}, ) @@ -172,23 +171,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # be canonicalised ahead of time, but I don't expect the saving would be # particularly noticeable, and it'd require some non-trivial refactoring # of `canonicalise()`. - self.canonicalise( - center=(*common_path, DirTTN.LEFT) - ) - - self._logger.debug(f"Applying gate to the TTN.") - - # Glossary of bond IDs - # a -> the input bond of the gate on q0 - # b -> the input bond of the gate on q1 - # A -> the output bond of the gate on q0 - # B -> the output bond of the gate on q1 - # l -> left child bond of the TTN node - # r -> right child bond of the TTN node - # p -> the parent bond of the TTN node - # s -> the shared bond resulting from a decomposition - # chr(x) -> bond of the x-th qubit in a leaf node - gate_bonds = "ABab" + self.canonicalise(center=(*common_path, DirTTN.LEFT)) # The overall strategy is to connect the `a` bond of the gate tensor to # the corresponding bond for `q0` in the TTN (so that its bond `A`) becomes @@ -215,14 +198,11 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # We immediately QR-decompose the resulting tensor, leaf_node = self.nodes[path_q0] n_qbonds = len(leaf_node.tensor.shape) - 1 # Num of qubit bonds - leaf_bonds = "".join( - "a" if x == bond_q0 else chr(x) - for x in range(n_qbonds) - ) + "p" - Q_bonds = "".join( - "A" if x == bond_q0 else chr(x) - for x in range(n_qbonds) - ) + "s" + aux_bonds = [chr(x) for x in range(n_qbonds)] + aux_bonds[bond_q0] = "a" + leaf_bonds = "".join(aux_bonds) + "p" + aux_bonds[bond_q0] = "A" + Q_bonds = "".join(aux_bonds) + "s" R_bonds = "Bbsp" # The `msg_tensor` # Apply the contraction followed by a QR decomposition @@ -230,7 +210,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: f"{leaf_bonds},{gate_bonds}->{Q_bonds},{R_bonds}", leaf_node.tensor, gate_tensor, - algorithm={"qr_method": True}, + algorithm={"qr_method": tensor.QRMethod()}, options=options, optimize={"path": [(0, 1)]}, ) @@ -239,13 +219,12 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # We must push the `msg_tensor` all the way to the common ancestor # of `q0` and `q1`. - bonds_from_q0_to_ancestor = [ + bond_addresses = [ path_q0[:i] for i in reversed(range(len(common_path) + 1, len(path_q0) + 1)) ] # Sanity checks: - assert all(len(bond_addresses) != len(common_path)) + assert all(len(root_path) != len(common_path) for root_path in bond_addresses) assert len(bond_addresses[0]) == len(path_q0) - assert len(bond_addresses[1]) < len(bond_addresses[0]) # For all of these nodes; push `msg_tensor` through to their parent bond for child_bond in bond_addresses[:-1]: # Doesn't do it on common ancestor! @@ -263,7 +242,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: f"{node_bonds},{msg_bonds}->{Q_bonds},{R_bonds}", node.tensor, msg_tensor, - algorithm={"qr_method": True}, + algorithm={"qr_method": tensor.QRMethod()}, options=options, optimize={"path": [(0, 1)]}, ) @@ -287,7 +266,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: f"{node_bonds},{msg_bonds}->{Q_bonds},{R_bonds}", common_ancestor_node.tensor, msg_tensor, - algorithm={"qr_method": True}, + algorithm={"qr_method": tensor.QRMethod()}, options=options, optimize={"path": [(0, 1)]}, ) @@ -299,13 +278,12 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # We must push the `msg_tensor` from the common ancestor to the leaf node # containing `q1`. - bonds_addresses = [ + bond_addresses = [ path_q1[:i] for i in range(len(common_path) + 1, len(path_q1) + 1) ] # Sanity checks: - assert all(len(bond_addresses) != len(common_path)) + assert all(len(root_path) != len(common_path) for root_path in bond_addresses) assert len(bond_addresses[-1]) == len(path_q1) - assert len(bond_addresses[0]) < len(bond_addresses[1]) # For all of these nodes; push `msg_tensor` through to their child bond for child_bond in bond_addresses[1:]: # Skip common ancestor: already pushed @@ -323,7 +301,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: f"{node_bonds},{msg_bonds}->{Q_bonds},{R_bonds}", node.tensor, msg_tensor, - algorithm={"qr_method": True}, + algorithm={"qr_method": tensor.QRMethod()}, options=options, optimize={"path": [(0, 1)]}, ) @@ -334,23 +312,20 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # All we need to do is contract the `msg_tensor` into the leaf. leaf_node = self.nodes[path_q1] n_qbonds = len(leaf_node.tensor.shape) - 1 # Num of qubit bonds - leaf_bonds = "".join( - "b" if x == bond_q1 else chr(x) # Connect `b` to `q1` - for x in range(n_qbonds) - ) + "p" + aux_bonds = [chr(x) for x in range(n_qbonds)] + aux_bonds[bond_q1] = "b" # Connect `b` to `q1` + leaf_bonds = "".join(aux_bonds) + "p" msg_bonds = "BbpP" - result_bonds = "".join( - "B" if x == bond_q1 else chr(x) # `B` becomes the new physical bond `q1` - for x in range(n_qbonds) - ) + "P" + aux_bonds[bond_q1] = "B" # `B` becomes the new physical bond `q1` + result_bonds = "".join(aux_bonds) + "P" # Apply the contraction leaf_node.tensor = cq.contract( f"{leaf_bonds},{msg_bonds}->{result_bonds}", leaf_node.tensor, - msg_tensor + msg_tensor, options=options, - optimize={"path": [(1, 2), (0, 1)]}, + optimize={"path": [(0, 1)]}, ) # The leaf node lost its canonical form leaf_node.canonical_form = None From 2035091c0699107e2dbcdf0346d86d5f41f0aa07 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Sat, 27 Jan 2024 22:05:41 +0000 Subject: [PATCH 70/83] Fixing a leaf_size in tests so that we test on TTNs of height >2 --- tests/test_tnstate.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/test_tnstate.py b/tests/test_tnstate.py index 12dc8c76..773ca144 100644 --- a/tests/test_tnstate.py +++ b/tests/test_tnstate.py @@ -247,7 +247,7 @@ def test_exact_circ_sim(circuit: Circuit, algorithm: SimulationAlgorithm) -> Non state = circuit.get_statevector() with CuTensorNetHandle() as libhandle: - cfg = Config() + cfg = Config(leaf_size=2) tnstate = simulate(libhandle, circuit, algorithm, cfg) assert tnstate.is_valid() # Check that there was no approximation @@ -306,7 +306,7 @@ def test_approx_circ_sim_gate_fid( circuit, _ = prepare_circuit_mps(circuit) with CuTensorNetHandle() as libhandle: - cfg = Config(truncation_fidelity=0.99) + cfg = Config(truncation_fidelity=0.99, leaf_size=2) tnstate = simulate(libhandle, circuit, algorithm, cfg) assert tnstate.is_valid() # Check that overlap is 1 @@ -351,7 +351,7 @@ def test_approx_circ_sim_chi(circuit: Circuit, algorithm: SimulationAlgorithm) - circuit, _ = prepare_circuit_mps(circuit) with CuTensorNetHandle() as libhandle: - cfg = Config(chi=4) + cfg = Config(chi=4, leaf_size=2) tnstate = simulate(libhandle, circuit, algorithm, cfg) assert tnstate.is_valid() # Check that overlap is 1 @@ -391,14 +391,18 @@ def test_float_point_options( with CuTensorNetHandle() as libhandle: # Exact - cfg = Config(float_precision=fp_precision) + cfg = Config(float_precision=fp_precision, leaf_size=2) tnstate = simulate(libhandle, circuit, algorithm, cfg) assert tnstate.is_valid() # Check that overlap is 1 assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) # Approximate, bound truncation fidelity - cfg = Config(truncation_fidelity=0.99, float_precision=fp_precision) + cfg = Config( + truncation_fidelity=0.99, + float_precision=fp_precision, + leaf_size=2 + ) tnstate = simulate( libhandle, circuit, @@ -410,7 +414,7 @@ def test_float_point_options( assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) # Approximate, bound chi - cfg = Config(chi=4, float_precision=fp_precision) + cfg = Config(chi=4, float_precision=fp_precision, leaf_size=2) tnstate = simulate( libhandle, circuit, @@ -434,7 +438,7 @@ def test_circ_approx_explicit_mps(circuit: Circuit) -> None: with CuTensorNetHandle() as libhandle: # Finite gate fidelity # Check for MPSxGate - cfg = Config(truncation_fidelity=0.99) + cfg = Config(truncation_fidelity=0.99, leaf_size=4) mps_gate = simulate( libhandle, circuit, @@ -458,7 +462,7 @@ def test_circ_approx_explicit_mps(circuit: Circuit) -> None: # Fixed virtual bond dimension # Check for MPSxGate - cfg = Config(chi=8) + cfg = Config(chi=8, leaf_size=4) 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() From 10e04b04ff2c2600586be30577088d501c91eccd Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Sat, 27 Jan 2024 15:05:56 -0800 Subject: [PATCH 71/83] Added some logger messages --- .../extensions/cutensornet/tnstate/ttn_gate.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 24f299c6..559db8a9 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -237,6 +237,11 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: Q_bonds = "Lrs" if child_dir == DirTTN.LEFT else "lRs" R_bonds = "Bbsp" # The new `msg_tensor` + self._logger.debug( + f"Pushing msg_tensor ({msg_tensor.nbytes // 2**20} MiB) through node " + f"({node.tensor.nbytes // 2**20} MiB) at {parent_bond}." + ) + # Apply the contraction followed by a QR decomposition node.tensor, msg_tensor = contract_decompose( f"{node_bonds},{msg_bonds}->{Q_bonds},{R_bonds}", @@ -261,6 +266,12 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: Q_bonds = "Lsp" if child_dir == DirTTN.LEFT else "sRp" R_bonds = "Bbrs" if child_dir == DirTTN.LEFT else "Bbls" # The new `msg_tensor` + self._logger.debug( + f"Pushing msg_tensor ({msg_tensor.nbytes // 2**20} MiB) through node " + f"({common_ancestor_node.tensor.nbytes // 2**20} MiB) at {parent_bond}." + ) + + # Apply the contraction followed by a QR decomposition common_ancestor_node.tensor, msg_tensor = contract_decompose( f"{node_bonds},{msg_bonds}->{Q_bonds},{R_bonds}", @@ -296,6 +307,11 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: Q_bonds = "srP" if child_dir == DirTTN.LEFT else "lsP" R_bonds = "Bbls" if child_dir == DirTTN.LEFT else "Bbrs" # New `msg_tensor` + self._logger.debug( + f"Pushing msg_tensor ({msg_tensor.nbytes // 2**20} MiB) through node " + f"({node.tensor.nbytes // 2**20} MiB) at {parent_bond}." + ) + # Apply the contraction followed by a QR decomposition node.tensor, msg_tensor = contract_decompose( f"{node_bonds},{msg_bonds}->{Q_bonds},{R_bonds}", From 240d9c0a048118e6cace249208654e501b7d65ac Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez <104848389+PabloAndresCQ@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:57:07 +0000 Subject: [PATCH 72/83] Fixing typo in comment Co-authored-by: Iakov Polyak <45295461+yapolyak@users.noreply.github.com> --- pytket/extensions/cutensornet/tnstate/ttn_gate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 6817ea7d..f6dd80aa 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -308,7 +308,8 @@ def _fidelity_bound_sequential_weighted_truncation( then the total fidelity factor on the LHS of equation (A) should approximate `truncation_fidelity`. There is risk of overshooting with truncation and - endinf up with a new `self.fidelity` slightly lower than the target, but this + end up with a new `self.fidelity` slightly lower than the target, but this + should be fine in practice, since `self.fidelity` is a lower bound anyway. Each of the `w_i` weight factors is assigned depending on the bond dimension, with larger bonds given higher weight, so they are truncated more aggressively. From c357532ead265f63f699321ec6816173ed992831 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 1 Feb 2024 15:03:05 +0000 Subject: [PATCH 73/83] Updated docstring. --- pytket/extensions/cutensornet/tnstate/ttn.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/tnstate/ttn.py index 63ba9a5b..3a7ce22d 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/tnstate/ttn.py @@ -123,8 +123,6 @@ def __init__( ``2^l - 1`` for some ``l``. ValueError: If a ``Qubit`` is repeated in ``qubit_partition``. ValueError: If there is only one entry in ``qubit_partition``. - NotImplementedError: If the value of ``truncation_fidelity`` in ``config`` - is smaller than one. """ self._lib = libhandle self._cfg = config From f35fcc368380dc2b4a9b3b9c14f39b790c22ea19 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 1 Feb 2024 16:13:01 +0000 Subject: [PATCH 74/83] Fixed an unfinished comment --- pytket/extensions/cutensornet/tnstate/ttn_gate.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 559db8a9..269f4f28 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -195,7 +195,9 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # We begin applying the gate to the TTN by contracting `gate_tensor` into the # leaf node containing `q0`, with the `b` and `B` bonds of the latter left open. - # We immediately QR-decompose the resulting tensor, + # We immediately QR-decompose the resulting tensor, so that Q becomes the new + # (canonicalised) leaf node and R becomes the `msg_tensor` that we will be + # "pushing" through the rest of the arc towards `q1`. leaf_node = self.nodes[path_q0] n_qbonds = len(leaf_node.tensor.shape) - 1 # Num of qubit bonds aux_bonds = [chr(x) for x in range(n_qbonds)] From 96cca3292c44ddc06b113cbbcaa0a79a9624e546 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Thu, 1 Feb 2024 16:53:22 +0000 Subject: [PATCH 75/83] Cleaned up code around bonds_from_q0_to_ancestor --- .../cutensornet/tnstate/ttn_gate.py | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index a03188ca..1c4d99fa 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -186,7 +186,26 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # Once `msg_tensor` is directly connected to the leaf node containing `q1`, we # just need to contract them, connecting `b` to `q1`, with `B` becoming the # new physical bond. - # + bonds_to_q0 = [ # Bonds in the "arc" from the common ancestor to `q0` + path_q0[:i] for i in range(len(common_path) + 1, len(path_q0) + 1) + ] + # Sanity checks: + assert all( + len(bond_address) != len(common_path) for bond_address in bonds_to_q0 + ) + assert len(bonds_to_q0) == 1 or len(bonds_to_q0[0]) < len(bonds_to_q0[1]) + assert len(bonds_to_q0[-1]) == len(path_q0) + + bonds_to_q1 = [ # Bonds in the "arc" from the common ancestor to `q1` + path_q1[:i] for i in range(len(common_path) + 1, len(path_q1) + 1) + ] + # Sanity checks: + assert all( + len(bond_address) != len(common_path) for bond_address in bonds_to_q1 + ) + assert len(bonds_to_q1) == 1 or len(bonds_to_q1[0]) < len(bonds_to_q1[1]) + assert len(bonds_to_q1[-1]) == len(path_q1) + # The `msg_tensor` has four bonds. Our convention will be that the first bond # always corresponds to `B`, the second bond is `b`, the third bond connects # it to the TTN in the child direction and the fourth connects it to the TTN @@ -221,12 +240,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # We must push the `msg_tensor` all the way to the common ancestor # of `q0` and `q1`. - bond_addresses = [ - path_q0[:i] for i in reversed(range(len(common_path) + 1, len(path_q0) + 1)) - ] - # Sanity checks: - assert all(len(root_path) != len(common_path) for root_path in bond_addresses) - assert len(bond_addresses[0]) == len(path_q0) + bond_addresses = reversed(bonds_to_q0) # From `q0` to the ancestor # For all of these nodes; push `msg_tensor` through to their parent bond for child_bond in bond_addresses[:-1]: # Doesn't do it on common ancestor! @@ -273,7 +287,6 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: f"({common_ancestor_node.tensor.nbytes // 2**20} MiB) at {parent_bond}." ) - # Apply the contraction followed by a QR decomposition common_ancestor_node.tensor, msg_tensor = contract_decompose( f"{node_bonds},{msg_bonds}->{Q_bonds},{R_bonds}", @@ -291,12 +304,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # We must push the `msg_tensor` from the common ancestor to the leaf node # containing `q1`. - bond_addresses = [ - path_q1[:i] for i in range(len(common_path) + 1, len(path_q1) + 1) - ] - # Sanity checks: - assert all(len(root_path) != len(common_path) for root_path in bond_addresses) - assert len(bond_addresses[-1]) == len(path_q1) + bond_addresses = bonds_to_q1 # From ancestor to `q1` # For all of these nodes; push `msg_tensor` through to their child bond for child_bond in bond_addresses[1:]: # Skip common ancestor: already pushed @@ -351,23 +359,16 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # Truncate (if needed) bonds along the arc from `q1` to `q0`. # We truncate in this direction to take advantage of the canonicalisation # of the TTN we achieved while pushing the `msg_tensor` from `q0` to `q1`. - bonds_from_q1_to_ancestor = [ - path_q1[:i] for i in reversed(range(len(common_path) + 1, len(path_q1) + 1)) - ] - bonds_from_ancestor_to_q0 = [ - path_q0[:i] for i in range(len(common_path) + 1, len(path_q0) + 1) - ] - if self._cfg.truncation_fidelity < 1: # Truncate as much as possible before violating the truncation fidelity self._fidelity_bound_sequential_weighted_truncation( - bonds_from_q1_to_ancestor, bonds_from_ancestor_to_q0 + reversed(bonds_to_q1), bonds_to_q0 ) else: # Truncate so that all bonds have dimension less or equal to chi self._chi_sequential_truncation( - bonds_from_q1_to_ancestor, bonds_from_ancestor_to_q0 + reversed(bonds_to_q1), bonds_to_q0 ) return self From 3f1a9468aefeb64687aeff3d8fa8fd993732db3a Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Thu, 1 Feb 2024 09:17:11 -0800 Subject: [PATCH 76/83] Applying lint, mypy and updating explicit fidelity in test --- pytket/extensions/cutensornet/tnstate/ttn_gate.py | 9 +++------ tests/test_tnstate.py | 8 +++----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/tnstate/ttn_gate.py index 1c4d99fa..5c7d2c85 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn_gate.py +++ b/pytket/extensions/cutensornet/tnstate/ttn_gate.py @@ -26,7 +26,6 @@ warnings.warn("local settings failed to import cutensornet", ImportWarning) from pytket.circuit import Op, Qubit -from .general import Tensor from .ttn import TTN, DirTTN, RootPath @@ -240,7 +239,7 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: # We must push the `msg_tensor` all the way to the common ancestor # of `q0` and `q1`. - bond_addresses = reversed(bonds_to_q0) # From `q0` to the ancestor + bond_addresses = list(reversed(bonds_to_q0)) # From `q0` to the ancestor # For all of these nodes; push `msg_tensor` through to their parent bond for child_bond in bond_addresses[:-1]: # Doesn't do it on common ancestor! @@ -362,14 +361,12 @@ def _apply_2q_gate(self, q0: Qubit, q1: Qubit, gate: Op) -> TTNxGate: if self._cfg.truncation_fidelity < 1: # Truncate as much as possible before violating the truncation fidelity self._fidelity_bound_sequential_weighted_truncation( - reversed(bonds_to_q1), bonds_to_q0 + list(reversed(bonds_to_q1)), bonds_to_q0 ) else: # Truncate so that all bonds have dimension less or equal to chi - self._chi_sequential_truncation( - reversed(bonds_to_q1), bonds_to_q0 - ) + self._chi_sequential_truncation(list(reversed(bonds_to_q1)), bonds_to_q0) return self diff --git a/tests/test_tnstate.py b/tests/test_tnstate.py index 1a71ca45..b8253597 100644 --- a/tests/test_tnstate.py +++ b/tests/test_tnstate.py @@ -401,9 +401,7 @@ def test_float_point_options( # Approximate, bound truncation fidelity cfg = Config( - truncation_fidelity=0.99, - float_precision=fp_precision, - leaf_size=2 + truncation_fidelity=0.99, float_precision=fp_precision, leaf_size=2 ) tnstate = simulate( libhandle, @@ -491,7 +489,7 @@ def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: # Check for TTNxGate cfg = Config(truncation_fidelity=0.99) ttn_gate = simulate(libhandle, circuit, SimulationAlgorithm.TTNxGate, cfg) - assert np.isclose(ttn_gate.get_fidelity(), 0.70, atol=1e-2) + assert np.isclose(ttn_gate.get_fidelity(), 0.729, atol=1e-3) assert ttn_gate.is_valid() assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=cfg._atol) @@ -499,7 +497,7 @@ def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: # Check for TTNxGate cfg = Config(chi=120, leaf_size=3) ttn_gate = simulate(libhandle, circuit, SimulationAlgorithm.TTNxGate, cfg) - assert np.isclose(ttn_gate.get_fidelity(), 0.84, atol=1e-2) + assert np.isclose(ttn_gate.get_fidelity(), 0.857, atol=1e-3) assert ttn_gate.is_valid() assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=cfg._atol) From 1221eac4e9bf4777ea04645dadbdd4d283322ead Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Fri, 2 Feb 2024 10:55:40 +0000 Subject: [PATCH 77/83] Renamed tnstate to structured_state --- docs/api.rst | 2 +- docs/modules/structured_state.rst | 61 + docs/modules/tnstate.rst | 61 - examples/mpi/mpi_overlap_bcast_mps.py | 2 +- examples/mps_tutorial.ipynb | 2190 ++++++++--------- .../{tnstate => structured_state}/__init__.py | 2 +- .../cut_rKaHyPar_sea20.ini | 0 .../{tnstate => structured_state}/general.py | 26 +- .../{tnstate => structured_state}/mps.py | 6 +- .../{tnstate => structured_state}/mps_gate.py | 0 .../{tnstate => structured_state}/mps_mpo.py | 0 .../simulation.py | 30 +- .../{tnstate => structured_state}/ttn.py | 8 +- .../{tnstate => structured_state}/ttn_gate.py | 0 ...st_tnstate.py => test_structured_state.py} | 60 +- 15 files changed, 1223 insertions(+), 1225 deletions(-) create mode 100644 docs/modules/structured_state.rst delete mode 100644 docs/modules/tnstate.rst rename pytket/extensions/cutensornet/{tnstate => structured_state}/__init__.py (94%) rename pytket/extensions/cutensornet/{tnstate => structured_state}/cut_rKaHyPar_sea20.ini (100%) rename pytket/extensions/cutensornet/{tnstate => structured_state}/general.py (92%) rename pytket/extensions/cutensornet/{tnstate => structured_state}/mps.py (97%) rename pytket/extensions/cutensornet/{tnstate => structured_state}/mps_gate.py (100%) rename pytket/extensions/cutensornet/{tnstate => structured_state}/mps_mpo.py (100%) rename pytket/extensions/cutensornet/{tnstate => structured_state}/simulation.py (92%) rename pytket/extensions/cutensornet/{tnstate => structured_state}/ttn.py (96%) rename pytket/extensions/cutensornet/{tnstate => structured_state}/ttn_gate.py (100%) rename tests/{test_tnstate.py => test_structured_state.py} (90%) diff --git a/docs/api.rst b/docs/api.rst index fa3f4aab..f669c940 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -3,4 +3,4 @@ API documentation .. toctree:: modules/fullTN.rst - modules/tnstate.rst + modules/structured_state.rst diff --git a/docs/modules/structured_state.rst b/docs/modules/structured_state.rst new file mode 100644 index 00000000..326a43ce --- /dev/null +++ b/docs/modules/structured_state.rst @@ -0,0 +1,61 @@ +Structured state evolution +========================== + +.. automodule:: pytket.extensions.cutensornet.structured_state + + +Simulation +~~~~~~~~~~ + +.. autofunction:: pytket.extensions.cutensornet.structured_state.simulate + +.. autoenum:: pytket.extensions.cutensornet.structured_state.SimulationAlgorithm() + :members: + +.. autoclass:: pytket.extensions.cutensornet.structured_state.Config() + + .. automethod:: __init__ + +.. autoclass:: pytket.extensions.cutensornet.structured_state.CuTensorNetHandle + + +Classes +~~~~~~~ + +.. autoclass:: pytket.extensions.cutensornet.structured_state.StructuredState() + + .. automethod:: __init__ + .. automethod:: is_valid + .. automethod:: apply_gate + .. automethod:: apply_scalar + .. automethod:: vdot + .. automethod:: sample + .. automethod:: measure + .. automethod:: postselect + .. automethod:: expectation_value + .. automethod:: get_fidelity + .. automethod:: get_statevector + .. automethod:: get_amplitude + .. automethod:: get_qubits + .. automethod:: get_byte_size + .. automethod:: get_device_id + .. automethod:: update_libhandle + .. automethod:: copy + +.. autoclass:: pytket.extensions.cutensornet.structured_state.TTNxGate() + + .. automethod:: __init__ + +.. autoclass:: pytket.extensions.cutensornet.structured_state.MPSxGate() + + .. automethod:: __init__ + +.. autoclass:: pytket.extensions.cutensornet.structured_state.MPSxMPO() + + .. automethod:: __init__ + + +Miscellaneous +~~~~~~~~~~~~~ + +.. autofunction:: pytket.extensions.cutensornet.structured_state.prepare_circuit_mps diff --git a/docs/modules/tnstate.rst b/docs/modules/tnstate.rst deleted file mode 100644 index 18589ac7..00000000 --- a/docs/modules/tnstate.rst +++ /dev/null @@ -1,61 +0,0 @@ -TN state evolution -================== - -.. automodule:: pytket.extensions.cutensornet.tnstate - - -Simulation -~~~~~~~~~~ - -.. autofunction:: pytket.extensions.cutensornet.tnstate.simulate - -.. autoenum:: pytket.extensions.cutensornet.tnstate.SimulationAlgorithm() - :members: - -.. autoclass:: pytket.extensions.cutensornet.tnstate.Config() - - .. automethod:: __init__ - -.. autoclass:: pytket.extensions.cutensornet.tnstate.CuTensorNetHandle - - -Classes -~~~~~~~ - -.. autoclass:: pytket.extensions.cutensornet.tnstate.TNState() - - .. automethod:: __init__ - .. automethod:: is_valid - .. automethod:: apply_gate - .. automethod:: apply_scalar - .. automethod:: vdot - .. automethod:: sample - .. automethod:: measure - .. automethod:: postselect - .. automethod:: expectation_value - .. automethod:: get_fidelity - .. automethod:: get_statevector - .. automethod:: get_amplitude - .. automethod:: get_qubits - .. automethod:: get_byte_size - .. automethod:: get_device_id - .. automethod:: update_libhandle - .. automethod:: copy - -.. autoclass:: pytket.extensions.cutensornet.tnstate.TTNxGate() - - .. automethod:: __init__ - -.. autoclass:: pytket.extensions.cutensornet.tnstate.MPSxGate() - - .. automethod:: __init__ - -.. autoclass:: pytket.extensions.cutensornet.tnstate.MPSxMPO() - - .. automethod:: __init__ - - -Miscellaneous -~~~~~~~~~~~~~ - -.. autofunction:: pytket.extensions.cutensornet.tnstate.prepare_circuit_mps diff --git a/examples/mpi/mpi_overlap_bcast_mps.py b/examples/mpi/mpi_overlap_bcast_mps.py index 014a0e82..4d33eed6 100644 --- a/examples/mpi/mpi_overlap_bcast_mps.py +++ b/examples/mpi/mpi_overlap_bcast_mps.py @@ -42,7 +42,7 @@ from pytket.circuit import Circuit, fresh_symbol -from pytket.extensions.cutensornet.tnstate import ( +from pytket.extensions.cutensornet.structured_state import ( simulate, Config, SimulationAlgorithm, diff --git a/examples/mps_tutorial.ipynb b/examples/mps_tutorial.ipynb index de84fc2e..2ae5c1de 100644 --- a/examples/mps_tutorial.ipynb +++ b/examples/mps_tutorial.ipynb @@ -13,7 +13,7 @@ "from pytket import Circuit\n", "from pytket.circuit.display import render_circuit_jupyter\n", "\n", - "from pytket.extensions.cutensornet.tnstate import (\n", + "from pytket.extensions.cutensornet.structured_state import (\n", " CuTensorNetHandle,\n", " Config,\n", " SimulationAlgorithm,\n", @@ -91,14 +91,14 @@ " <!-- Download Vue 3-->\n", "<script type="application/javascript" src="https://cdn.jsdelivr.net/npm/vue@3"></script>\n", "<!-- Download Circuit Renderer with styles -->\n", - "<script type="application/javascript" src="https://unpkg.com/pytket-circuit-renderer@0.6/dist/pytket-circuit-renderer.umd.js"></script>\n", - "<link rel="stylesheet" href="https://unpkg.com/pytket-circuit-renderer@0.6/dist/pytket-circuit-renderer.css">\n", + "<script type="application/javascript" src="https://unpkg.com/pytket-circuit-renderer@0.7/dist/pytket-circuit-renderer.umd.js"></script>\n", + "<link rel="stylesheet" href="https://unpkg.com/pytket-circuit-renderer@0.7/dist/pytket-circuit-renderer.css">\n", "</head>\n", "<body>\n", "\n", "\n", "\n", - " <div id="circuit-display-vue-container-9a33e5e8-cd1f-4fc5-b70a-72d2ba2cbc6e" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-3b04fb9a-abe7-403a-bbff-d603b684fb93" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["q", [0]], ["q", [1]]], "op": {"type": "CZ"}}, {"args": [["q", [2]]], "op": {"type": "H"}}, {"args": [["q", [3]], ["q", [4]]], "op": {"type": "CX"}}, {"args": [["q", [0]]], "op": {"params": ["0.2"], "type": "Ry"}}, {"args": [["q", [2]], ["q", [1]]], "op": {"params": ["0.3", "0.5", "0.7"], "type": "TK2"}}, {"args": [["q", [4]], ["q", [3]]], "op": {"params": ["0.1"], "type": "ZZPhase"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["q", [0]], ["q", [0]]], [["q", [1]], ["q", [1]]], [["q", [2]], ["q", [2]]], [["q", [3]], ["q", [3]]], [["q", [4]], ["q", [4]]]], "phase": "0.0", "qubits": [["q", [0]], ["q", [1]], ["q", [2]], ["q", [3]], ["q", [4]]]}</div>\n", " </div>\n", @@ -108,7 +108,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "9a33e5e8-cd1f-4fc5-b70a-72d2ba2cbc6e";\n", + " const circuitRendererUid = "3b04fb9a-abe7-403a-bbff-d603b684fb93";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -286,7 +286,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjMAAAGwCAYAAABcnuQpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA7WklEQVR4nO3deXQUVf7+8aeJSWdvIJqNRAibiggKCMKI4CghoIgyPxcUBHFXxIAzMAzyBRwFZQ6CiDoiI+jgiHMU3GVglEVlByPrQdQAAROjEDshkADJ/f2RSUOTQJLOUl3J+3VOH+lbS39uVUE/Vt2qdhhjjAAAAGyqkdUFAAAAVAdhBgAA2BphBgAA2BphBgAA2BphBgAA2BphBgAA2BphBgAA2Np5VhdQ24qLi/XTTz8pIiJCDofD6nIAAEAlGGOUl5en+Ph4NWp07nMv9T7M/PTTT0pMTLS6DAAA4IOMjAwlJCScc556H2YiIiIklWyMyMhIi6sBAACVkZubq8TERM/3+LnU+zBTemkpMjKSMAMAgM1UZogIA4ABAICtEWYAAICtEWYAAICt1fsxMwAA/1ZUVKQTJ05YXQbqWGBgoAICAmpkXYQZAIAljDHKysrSb7/9ZnUpsEjjxo0VGxtb7efAEWYAAJYoDTLR0dEKDQ3lwaYNiDFGR48eVXZ2tiQpLi6uWusjzAAA6lxRUZEnyERFRVldDiwQEhIiScrOzlZ0dHS1LjkxABgAUOdKx8iEhoZaXAmsVLr/qztmijADALAMl5Yatpra/4QZNGwFbsl9sPxp7oMl0wEAfo0wg4arwC0t/IO0oL/kPuA9zX2gpH3hHwg0AODnCDNouAqPSPm/SDl7pQU3nAo07gMl73P2lkwvPGJllQBsZO/evXI4HEpLS7O6lAaFMIOGy9VMGv6J1KTFqUCzf/2pINOkRcl0VzNr6wRwVkXFRmt/OKQP0g5q7Q+HVFRsau2zHA7HOV/Dhw+vtc/GuXFrNho2V0JJYCkNMK8nl7R7gkyCldUBOIel2zM15aOdynQXeNriXMGaNKCdUtpX77kl5cnMzPT8+Z133tH//d//affu3Z62kJAQ5eTk1PjnVkZRUZEcDocaNWqY5ygaZq+B07kSpFvmerfdMpcgA/ixpdsz9fDCLV5BRpKy3AV6eOEWLd2eeZYlfRcbG+t5uVwuORyOMm2lfvzxR1177bUKDQ1Vx44dtXbtWq91rVmzRtdcc41CQkKUmJioUaNGKT8/3zM9JydHd999t5o0aaLQ0FD169dPe/bs8UxfsGCBGjdurI8//ljt2rWT0+nUl19+qcDAQGVlZXl91hNPPKFrrrmmxreHPyHMAO4D0pIHvNuWPFB2UDAAv1BUbDTlo50q74JSaduUj3bW6iWnikyYMEF//OMflZaWprZt22rw4ME6efKkJGnbtm3q27evBg0apK1bt+qdd97RV199pZEjR3qWHz58uDZt2qQPP/xQa9eulTFG/fv393oey9GjRzVt2jTNmzdPO3bsUJcuXdSyZUv985//9Mxz8uRJLVy4UPfcc0/ddd4ChBk0bKcP9m3SQhqxzHsMDYEG8Dsb0g+XOSNzOiMp012gDemH666oM/zxj3/UDTfcoLZt22rKlCnat2+fvv/+e0nS3/72N915551KTU1VmzZt1KNHD82ePVtvvvmmCgoKtGfPHn344YeaN2+eevbsqY4dO+qtt97SwYMH9f7773s+48SJE3r55ZfVo0cPXXTRRQoLC9O9996r+fPne+b55JNPdPToUd122211vQnqFGEGDZf7YNnBvhd2Kzso+GzPoQFgiey8swcZX+arDR06dPD8ufR3h0p/h2jz5s1asGCBwsPDPa++ffuquLhY6enp2rVrl8477zx169bNs46oqChddNFF2rVrl6ctKCjI63OkkjM633//vdatWydJev3113XbbbcpLCys1vrqDxgAjIbLGS6FXVDy59MH+54+KDjsgpL5APiN6IjgGp2vNgQGBnr+XPqU2+LiYs9/H3zwQY0aNarMchdeeKG+++67ctdpjPF6Ym5ISEiZJ+hGR0drwIABmj9/vlq2bKlPP/1UK1eurG53/B5hBg1XsEsa8l7Jc2TOvP3alSAN/7QkyAS7yl8egCW6JjVVnCtYWe6CcsfNOCTFuoLVNalpXZdWKZ06ddKOHTvUunXrcqe3a9dOJ0+e1Pr169WjRw9J0qFDh/Tdd9/pkksuqXD99913n+644w4lJCSoVatW+t3vflej9fsjLjOhYQt2nf05Mq5mBBnADwU0cmjSgHaSSoLL6UrfTxrQTgGN/PN3n8aNG6e1a9fq0UcfVVpammeMzGOPPSZJatOmjQYOHKj7779fX331lb799lsNGTJEzZo108CBAytcf9++feVyufT000/X+4G/pQgzAADbSWkfp1eGdFKsy/tSUqwrWK8M6VQrz5mpKR06dNCqVau0Z88e9ezZU1dccYUmTpzoGVsjSfPnz1fnzp114403qnv37jLG6NNPP/W6fHU2jRo10vDhw1VUVKS77767NrviNxzGGOvuXasDubm5crlccrvdioyMtLocAICkgoICpaenKykpScHBvo9tKSo22pB+WNl5BYqOKLm05K9nZOrS/fffr59//lkffvih1aWc07mOg6p8fzNmBgBgWwGNHOreKsrqMvyG2+3Wxo0b9dZbb+mDDz6wupw6Q5gBAKCeGDhwoDZs2KAHH3xQffr0sbqcOkOYAQCgnmgIt2GXhwHAAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADUE8OHD9fNN99sdRl1jjADAEAVDB8+XA6Ho8wrJSXF6tL0wgsvaMGCBVaXIank18Lff//9OvksnjMDALCfAnf5v3gvSe6Dtf6L9ykpKZo/f75Xm9PprLXPq0hRUZEcDodcrob547icmQEA2EuBW1r4B2lBf8l9wHua+0BJ+8I/lMxXS5xOp2JjY71eTZo00cqVKxUUFKQvv/zSM++MGTN0/vnnKzMzU5LUu3dvjRw5UiNHjlTjxo0VFRWlJ598Uqf/VOLx48c1duxYNWvWTGFhYerWrZvXA/EWLFigxo0b6+OPP1a7du3kdDq1b9++MpeZevfurccee0ypqalq0qSJYmJiNHfuXOXn5+uee+5RRESEWrVqpc8++8yrfzt37lT//v0VHh6umJgYDR06VL/++qvXekeNGqWxY8eqadOmio2N1eTJkz3TW7RoIUm65ZZb5HA4PO9rC2EGAGAvhUek/F+knL3SghtOBRr3gZL3OXtLphceqfPSevfurdTUVA0dOlRut1vffvutJkyYoNdee83rV7HfeOMNnXfeeVq/fr1mz56tmTNnat68eZ7p99xzj77++mstWrRIW7du1a233qqUlBTt2bPHM8/Ro0c1bdo0zZs3Tzt27FB0dHS5Nb3xxhs6//zztWHDBj322GN6+OGHdeutt6pHjx7asmWL+vbtq6FDh+ro0aOSpMzMTPXq1UuXX365Nm3apKVLl+rnn3/WbbfdVma9YWFhWr9+vaZPn66nnnpKy5cvlyRt3LhRUsmvf2dmZnre1xpTz7ndbiPJ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AKrGp8tMQ4YM0dGjR9WqVSuFhoaWGQB8+PDhGikOAACgIj6FmVmzZtVwGQAAAL7xKcwMGzaspusAAADwiU9h5nTHjh3TiRMnvNoYHAwAAOqKTwOA8/PzNXLkSEVHRys8PFxNmjTxegEAANQVn8LM2LFj9cUXX+jll1+W0+nUvHnzNGXKFMXHx+vNN9+s6RoBAADOyqfLTB999JHefPNN9e7dWyNGjFDPnj3VunVrNW/eXG+99Zbuuuuumq4TAACgXD6dmTl8+LCSkpIklYyPKb0V++qrr9bq1atrrjoAAIAK+BRmWrZs6XlgXrt27fTvf/9bUskZm8aNG9dUbQAAABXyKczcc889+vbbbyVJ48eP94ydGT16tP70pz9Vej3Tpk3TlVdeqYiICEVHR+vmm2/W7t27veYxxmjy5MmKj49XSEiIevfurR07dvhSNgAAqIccxhhT3ZXs379fmzZtUqtWrdSxY8dKL5eSkqI77rhDV155pU6ePKkJEyZo27Zt2rlzp8LCwiRJzz33nJ555hktWLBAbdu21dNPP63Vq1dr9+7dioiIqPAzcnNz5XK55Ha7uWUcAACbqMr3d5XCzPr163X48GH169fP0/bmm29q0qRJys/P180336wXX3xRTqfTp8J/+eUXRUdHa9WqVbrmmmtkjFF8fLxSU1M1btw4SVJhYaFiYmL03HPP6cEHH6xwnYQZAADspyrf31W6zDR58mRt3brV837btm269957df3112v8+PH66KOPNG3aNN+qluR2uyVJTZs2lSSlp6crKytLycnJnnmcTqd69eqlNWvWlLuOwsJC5ebmer0AAED9VaUwk5aWpuuuu87zftGiRerWrZtee+01jR49WrNnz/YMBq4qY4zGjBmjq6++Wu3bt5ckZWVlSZJiYmK85o2JifFMO9O0adPkcrk8r8TERJ/qAQAA9lClMJOTk+MVLFatWqWUlBTP+yuvvFIZGRk+FTJy5Eht3bpVb7/9dplpDofD670xpkxbqfHjx8vtdntevtYDAADsoUphJiYmRunp6ZKk48ePa8uWLerevbtnel5engIDA6tcxGOPPaYPP/xQK1asUEJCgqc9NjZWksqchcnOzi5ztqaU0+lUZGSk1wsAANRfVQozKSkp+vOf/6wvv/xS48ePV2hoqHr27OmZvnXrVrVq1arS6zPGaOTIkVq8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uuuu02WXXaZbb71Vr732mnJycs65TgA1gzAD4JzCwsLUunVrtW7dWl27dtU//vEP5efn67XXXpMkrVu3TnfccYf69eunjz/+WN98840mTJig48ePe9YxefJk7dixQzfccIO++OILtWvXTkuWLCn386oyb2Jionbv3q2XXnpJISEheuSRR3TNNdd4jdc504wZMzRz5kyNHTtWX3zxhdLS0tS3b1+vestTXFysAQMGeIJd6WvPnj265pprFBAQoOXLl+uzzz5Tu3bt9OKLL+qiiy5Senp6RZsYQDURZgBUicPhUKNGjXTs2DFJ0tdff63mzZtrwoQJ6tKli9q0aeN11qZU27ZtNXr0aC1btkyDBg3S/Pnzz/oZVZk3JCREN910k2bPnq2VK1dq7dq12rZtmyQpKChIRUVFXvN/+eWXGjhwoIYMGaKOHTuqZcuW2rNnj9c85S3XqVMn7dixQy1atPCEu9JXWFiYZ9v87ne/05QpU/TNN98oKCjorEEMQM0hzAA4p8LCQmVlZSkrK0u7du3SY489piNHjmjAgAGSpNatW2v//v1atGiRfvjhB82ePdvrC/zYsWMaOXKkVq5cqX379unrr7/Wxo0bdckll5T5rKrMK5XcCfWPf/xD27dv148//qh//vOfCgkJUfPmzSWVPGdm9erVOnjwoOeuo9atW2v58uVas2aNdu3apQcffFBZWVle623RooXWr1+vvXv36tdff1VxcbEeffRRHT58WIMHD9aGDRv0448/atmyZRoxYoSKioq0fv16TZ06VZs2bdL+/fu1ePFi/fLLL2etHUANsnrQDgD/NWzYMCPJ84qIiDBXXnmleffdd73m+9Of/mSioqJMeHi4uf32283MmTM9g3oLCwvNHXfcYRITE01QUJCJj483I0eONMeOHTPGeA8ArmjeMy1ZssR069bNREZGmrCwMHPVVVeZ//73v57pa9euNR06dDBOp9OU/nN36NAhM3DgQBMeHm6io6PNk08+ae6++24zcOBAz3K7d+82V111lQkJCTGSTHp6ujHGmO+++87ccsstpnHjxiYkJMRcfPHFJjU11RQXF5udO3eavn37mgsuuMA4nU7Ttm1b8+KLL9bAXgBQEYcx3DcIAADsi8tMAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1ggzAADA1v4/nwmLo90jSN4AAAAASUVORK5CYII=", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjIAAAGwCAYAAACzXI8XAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA+EElEQVR4nO3deVyVdd7/8fcBWZTlICiLAYJZGeNSWirTZkmKlqNpZZa51Oidabk0U3qXqTWNLXdj2qiZzUj+rGzTyposM7GmyAUzc4kxBxWVpVwOggIG1+8P4uQRUDgC17ng9Xw8zkPP91rO51rgvLmW72UzDMMQAACABXmZXQAAAIC7CDIAAMCyCDIAAMCyCDIAAMCyCDIAAMCyCDIAAMCyCDIAAMCympldQH0rKyvToUOHFBQUJJvNZnY5AACgBgzD0PHjx9WmTRt5eVV/3KXRB5lDhw4pJibG7DIAAIAbsrKyFB0dXe3wRh9kgoKCJJWviODgYJOrAQAANZGfn6+YmBjn93h1Gn2QqTidFBwcTJABAMBiznVZCBf7AgAAyyLIAAAAyyLIAAAAy2r018gAADxXWVmZSkpKzC4DJvDx8ZG3t/d5z4cgAwAwRUlJiTIzM1VWVmZ2KTBJSEiIIiMjz6ufN4IMAKDBGYah7OxseXt7KyYm5qwdnqHxMQxDJ06cUF5eniQpKirK7XkRZAAADe6XX37RiRMn1KZNG7Vo0cLscmCC5s2bS5Ly8vIUHh7u9mkmIjAAoMGVlpZKknx9fU2uBGaqCLGnTp1yex4EGQCAaXgGXtNWF9ufIFMbRQ7JcbDqYY6D5cMBAECDIcjUVJFDWjZESukvOQ64DnMcKG9fNoQwAwBAAyLI1FRxgVT4k3R0r5Ry029hxnGg/P3RveXDiwvMrBIAYKLU1FTZbDYdO3bM7FKaDIJMTdkvkEZ9JLWM+y3M7N/wW4hpGVc+3H6BuXUCQBNSWmYobc9hvb/1oNL2HFZpmVFvn2Wz2c76mjlzZr19NqrH7de1YY8uDysV4eWffcrbnSEm2szqAKBJWb09W7NW7VS2o8jZFmX314wBCUru6H6/JNXJzs52/v/NN9/U448/royMDGdbYGCgNm/eXOefWxMlJSVN9g4wjsjUlj1auuVl17ZbXibEAEADWr09W+OWbXEJMZKU4yjSuGVbtHp7djVTui8yMtL5stvtstlsLm2BgYHOcdPT03XFFVeoRYsW+v3vf+8SeCTp/fffV9euXeXv76927dpp1qxZ+uWXX5zD9+/fr4EDByowMFDBwcG6/fbblZub6xw+c+ZMXXbZZXrllVcUHx8vf39/LV26VGFhYSouLnb5rEGDBunuu++u8/XhKQgyteU4IK0c69q2cmzlC4ABAPWitMzQrFU7VdVJpIq2Wat21utppnN59NFH9fzzz2vz5s1q1qyZ7rnnHuewL7/8UiNGjNDEiRO1c+dOLVq0SCkpKXrqqacklT9/auDAgTpy5IjWr1+vNWvW6L///a+GDh3q8hk//vij3n33Xa1YsUJbt27VbbfdptLSUn3wwQfOcfLy8vTRRx+5fH5jQ5CpjdMv7G0ZJ93zqes1M4QZAKh3GzOPVDoSczpDUrajSBszjzRcUWd46qmndN111ykhIUFTp07V119/raKi8ppnzZqlqVOnauTIkWrXrp1uvPFGPfnkk1q0aJEkae3atfr+++/1+uuvq1u3burRo4eWLl2q9evXa9OmTc7PKCkp0dKlS3X55Zerc+fOat68ue68804tWbLEOc6yZcsUGxurXr16NejyNySCTE05Dla+sDe2R+ULgKvrZwYAUCfyjlcfYtwZrz507tzZ+f+K5whVPFfou+++0xNPPKHAwEDna8yYMcrOztaJEye0a9cuxcTEKCYmxjmPhIQEhYSEaNeuXc62tm3bqnXr1i6fO2bMGH366ac6eLD8uyglJUWjRo1q1B0PcrFvTfkFSgG/7jCnX9h7+gXAAa3LxwMA1JvwIP86Ha8++Pj4OP9fESIqnvJdUFCgWbNmafDgwZWm8/evec0BAQGV2i6//HJ16dJFS5cuVZ8+fbRjxw599NFHtS3fUggyNeVvl4a/W95PzJm3WNujpVH/Kg8x/nZz6gOAJqJ7fKii7P7KcRRVeZ2MTVKk3V/d40MburQa6dq1qzIyMtS+ffsqh1966aXKyspSVlaW86jMzp07dezYMSUkJJxz/n/84x/1wgsv6ODBg0pKSnI5stMYcWqpNvzt1fcTY7+AEAMADcDby6YZA8q/0M88YVLxfsaABHl7eebplMcff1xLly7VrFmztGPHDu3atUvLly/XY489JklKSkpSp06ddNddd2nLli3auHGjRowYoeuuu05XXHHFOed/55136sCBA1q8eHGjvsi3AkEGAGA5yR2jtHB4V0XaXU/FRNr9tXB413rpR6au9O3bVx9++KE+/fRTXXnllerZs6fmzJmjtm3bSio/FfX++++rZcuWuvbaa5WUlKR27drpzTffrNH87Xa7hgwZosDAQA0aNKgel8Qz2AzDMO/+tAaQn58vu90uh8Oh4OBgs8sBAEgqKipSZmamsw8Ud5WWGdqYeUR5x4sUHlR+OslTj8Q0pN69e+t3v/ud5s2bZ3YpZ3W2/aCm399cIwMAsCxvL5sSLwwzuwyPcfToUaWmpio1NVULFiwwu5wGQZABAKCRuPzyy3X06FE988wzuuSSS8wup0EQZAAAaCT27t1rdgkNjot9AQCAZRFkAACAZRFkAACAZRFkAACAZRFkAACAZRFkAABoJEaNGtUkevM9HUEGAIBaGDVqlGw2W6VXcnKy2aVp7ty5SklJMbsMSeWPWnjvvffq/XPoRwYAYD1FDqm4oOoH+ToOSn6B9fog3+TkZC1ZssSlzc/Pr94+71xKS0tls9lktze9hxdzRAYAYC1FDmnZECmlv+Q44DrMcaC8fdmQ8vHqiZ+fnyIjI11eLVu2VGpqqnx9ffXll186x3322WcVHh6u3NxcSVKvXr00YcIETZgwQXa7Xa1atdL06dN1+qMPi4uL9ac//UkXXHCBAgIC1KNHD6WmpjqHp6SkKCQkRB988IESEhLk5+en/fv3Vzq11KtXLz3wwAOaNGmSWrZsqYiICC1evFiFhYUaPXq0goKC1L59e3388ccuy7d9+3b169dPgYGBioiI0N13362ff/7ZZb4PPvigHn74YYWGhioyMlIzZ850Do+Li5Mk3XLLLbLZbM739YEgAwCwluICqfAn6eheKeWm38KM40D5+6N7y4cXFzR4ab169dKkSZN09913y+Fw6Ntvv9X06dP1yiuvKCIiwjneq6++qmbNmmnjxo2aO3eu/va3v+mVV15xDp8wYYLS0tK0fPlybdu2TbfddpuSk5O1e/du5zgnTpzQM888o1deeUU7duxQeHh4lTW9+uqratWqlTZu3KgHHnhA48aN02233abf//732rJli/r06aO7775bJ06ckCQdO3ZMN9xwgy6//HJt3rxZq1evVm5urm6//fZK8w0ICNCGDRv07LPP6oknntCaNWskSZs2bZIkLVmyRNnZ2c739cJo5BwOhyHJcDgcZpcC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58+vcvizzz6refPm6aWXXtKGDRsUEBCgvn37qqioqIErNce51o8kJScnu+xTb7zxRgNWaL7169dr/Pjx+uabb7RmzRqdOnVKffr0UWFhoXOcyZMna9WqVXr77be1fv16HTp0SIMHDzax6oZTk/UjSWPGjHHZj5599lmTKm540dHRevrpp5Wenq7Nmzfrhhtu0MCBA7Vjxw5JFt5/DNRa9+7djfHjxzvfl5aWGm3atDFmz55tYlWeY8aMGUaXLl3MLsNjSTJWrlzpfF9WVmZERkYazz33nLPt2LFjhp+fn/HGG2+YUKG5zlw/hmEYI0eONAYOHGhKPZ4qLy/PkGSsX7/eMIzyfcbHx8d4++23nePs2rXLkGSkpaWZVaZpzlw/hmEY1113nTFx4kTzivJALVu2NF555RVL7z8ckamlkpISpaenKykpydnm5eWlpKQkpaWlmViZZ9m9e7fatGmjdu3a6a677tL+/fvNLsljZWZmKicnx2Wfstvt6tGjB/vUaVJTUxUeHq5LLrlE48aN0+HDh80uyVQOh0OSFBoaKklKT0/XqVOnXPajDh06KDY2tknuR2eunwqvvfaaWrVqpY4dO2ratGk6ceKEGeWZrrS0VMuXL1dhYaESExMtvf80+odG1rWff/5ZpaWlioiIcGmPiIjQDz/8YFJVnqVHjx5KSUnRJZdcouzsbM2aNUvXXHONtm/frqCgILPL8zg5OTmSVOU+VTGsqUtOTtbgwYMVHx+vPXv26H//93/Vr18/paWlydvb2+zyGlxZWZkmTZqkq666Sh07dpRUvh/5+voqJCTEZdymuB9VtX4k6c4771Tbtm3Vpk0bbdu2TY888ogyMjK0YsUKE6ttWN9//70SExNVVFSkwMBArVy5UgkJCdq6datl9x+CDOpcv379nP/v3LmzevToobZt2+qtt97Svffea2JlsKo77rjD+f9OnTqpc+fOuvDCC5WamqrevXubWJk5xo8fr+3btzf5a8+qU936GTt2rPP/nTp1UlRUlHr37q09e/bowgsvbOgyTXHJJZdo69atcjgceueddzRy5EitX7/e7LLOC6eWaqlVq1by9vaudCV3bm6uIiMjTarKs4WEhOjiiy/Wjz/+aHYpHqliv2Gfqrl27dqpVatWTXKfmjBhgj788EOtW7dO0dHRzvbIyEiVlJTo2LFjLuM3tf2o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", "text/plain": [ "
" ] @@ -467,14 +467,14 @@ " <!-- Download Vue 3-->\n", "<script type="application/javascript" src="https://cdn.jsdelivr.net/npm/vue@3"></script>\n", "<!-- Download Circuit Renderer with styles -->\n", - "<script type="application/javascript" src="https://unpkg.com/pytket-circuit-renderer@0.6/dist/pytket-circuit-renderer.umd.js"></script>\n", - "<link rel="stylesheet" href="https://unpkg.com/pytket-circuit-renderer@0.6/dist/pytket-circuit-renderer.css">\n", + "<script type="application/javascript" src="https://unpkg.com/pytket-circuit-renderer@0.7/dist/pytket-circuit-renderer.umd.js"></script>\n", + "<link rel="stylesheet" href="https://unpkg.com/pytket-circuit-renderer@0.7/dist/pytket-circuit-renderer.css">\n", "</head>\n", "<body>\n", "\n", "\n", "\n", - " <div id="circuit-display-vue-container-90af8c21-b20e-49c8-aac0-c6459bf1d27c" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-535c5469-35ff-4718-8871-500dbc8d4fa7" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["q", [1]]], "op": {"type": "H"}}, {"args": [["q", [2]], ["q", [3]]], "op": {"params": ["0.3"], "type": "ZZPhase"}}, {"args": [["q", [4]]], "op": {"params": ["0.8"], "type": "Ry"}}, {"args": [["q", [0]], ["q", [1]]], "op": {"type": "CX"}}, {"args": [["q", [3]], ["q", [4]]], "op": {"type": "CZ"}}, {"args": [["q", [1]], ["q", [2]]], "op": {"params": ["0.7"], "type": "XXPhase"}}, {"args": [["q", [1]], ["q", [4]]], "op": {"params": ["0.1", "0.2", "0.4"], "type": "TK2"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["q", [0]], ["q", [0]]], [["q", [1]], ["q", [1]]], [["q", [2]], ["q", [2]]], [["q", [3]], ["q", [3]]], [["q", [4]], ["q", [4]]]], "phase": "0.0", "qubits": [["q", [0]], ["q", [1]], ["q", [2]], ["q", [3]], ["q", [4]]]}</div>\n", " </div>\n", @@ -484,7 +484,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "90af8c21-b20e-49c8-aac0-c6459bf1d27c";\n", + " const circuitRendererUid = "535c5469-35ff-4718-8871-500dbc8d4fa7";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -595,14 +595,14 @@ " <!-- Download Vue 3-->\n", "<script type="application/javascript" src="https://cdn.jsdelivr.net/npm/vue@3"></script>\n", "<!-- Download Circuit Renderer with styles -->\n", - "<script type="application/javascript" src="https://unpkg.com/pytket-circuit-renderer@0.6/dist/pytket-circuit-renderer.umd.js"></script>\n", - "<link rel="stylesheet" href="https://unpkg.com/pytket-circuit-renderer@0.6/dist/pytket-circuit-renderer.css">\n", + "<script type="application/javascript" src="https://unpkg.com/pytket-circuit-renderer@0.7/dist/pytket-circuit-renderer.umd.js"></script>\n", + "<link rel="stylesheet" href="https://unpkg.com/pytket-circuit-renderer@0.7/dist/pytket-circuit-renderer.css">\n", "</head>\n", "<body>\n", "\n", "\n", "\n", - " <div id="circuit-display-vue-container-c1a0f1fd-abca-4258-bd95-dede43c8c2fa" class="pytket-circuit-display-container">\n", + " <div id="circuit-display-vue-container-4f3d60e5-071a-4c17-9cf5-8914a1326936" class="pytket-circuit-display-container">\n", " <div style="display: none">\n", " <div id="circuit-json-to-display">{"bits": [], "commands": [{"args": [["node", [1]]], "op": {"params": ["0.8"], "type": "Ry"}}, {"args": [["node", [3]]], "op": {"type": "H"}}, {"args": [["node", [0]], ["node", [1]]], "op": {"type": "SWAP"}}, {"args": [["node", [4]], ["node", [3]]], "op": {"type": "CX"}}, {"args": [["node", [2]], ["node", [1]]], "op": {"params": ["0.3"], "type": "ZZPhase"}}, {"args": [["node", [1]], ["node", [0]]], "op": {"type": "CZ"}}, {"args": [["node", [3]], ["node", [2]]], "op": {"params": ["0.7"], "type": "XXPhase"}}, {"args": [["node", [3]], ["node", [2]]], "op": {"type": "SWAP"}}, {"args": [["node", [2]], ["node", [1]]], "op": {"type": "SWAP"}}, {"args": [["node", [1]], ["node", [0]]], "op": {"params": ["0.1", "0.2", "0.4"], "type": "TK2"}}], "created_qubits": [], "discarded_qubits": [], "implicit_permutation": [[["node", [0]], ["node", [0]]], [["node", [1]], ["node", [1]]], [["node", [2]], ["node", [2]]], [["node", [3]], ["node", [3]]], [["node", [4]], ["node", [4]]]], "phase": "0.0", "qubits": [["node", [0]], ["node", [1]], ["node", [2]], ["node", [3]], ["node", [4]]]}</div>\n", " </div>\n", @@ -612,7 +612,7 @@ " ></circuit-display-container>\n", " </div>\n", " <script type="application/javascript">\n", - " const circuitRendererUid = "c1a0f1fd-abca-4258-bd95-dede43c8c2fa";\n", + " const circuitRendererUid = "4f3d60e5-071a-4c17-9cf5-8914a1326936";\n", " const displayOptions = JSON.parse('{}');\n", "\n", " // Script to initialise the circuit renderer app\n", @@ -775,10 +775,10 @@ "output_type": "stream", "text": [ "Time taken by approximate contraction with bound chi:\n", - "2.36 seconds\n", + "2.87 seconds\n", "\n", "Lower bound of the fidelity:\n", - "0.4226\n" + "0.4137\n" ] } ], @@ -813,10 +813,10 @@ "output_type": "stream", "text": [ "Time taken by approximate contraction with fixed truncation fidelity:\n", - "4.39 seconds\n", + "3.43 seconds\n", "\n", "Lower bound of the fidelity:\n", - "0.9311\n" + "0.9322\n" ] } ], @@ -869,8 +869,8 @@ "output_type": "stream", "text": [ "MPSxGate\n", - "\tTime taken: 1.72 seconds\n", - "\tLower bound of the fidelity: 0.4226\n" + "\tTime taken: 2.05 seconds\n", + "\tLower bound of the fidelity: 0.4137\n" ] } ], @@ -896,8 +896,8 @@ "output_type": "stream", "text": [ "MPSxMPO, default parameters\n", - "\tTime taken: 13.55 seconds\n", - "\tLower bound of the fidelity: 0.4532\n" + "\tTime taken: 11.74 seconds\n", + "\tLower bound of the fidelity: 0.4426\n" ] } ], @@ -923,8 +923,8 @@ "output_type": "stream", "text": [ "MPSxMPO, custom parameters\n", - "\tTime taken: 19.74 seconds\n", - "\tLower bound of the fidelity: 0.4737\n" + "\tTime taken: 18.25 seconds\n", + "\tLower bound of the fidelity: 0.468\n" ] } ], @@ -976,7 +976,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 21, @@ -1010,1076 +1010,1074 @@ "name": "stderr", "output_type": "stream", "text": [ - "[13:07:49] Simulation (INFO) - Ordering the gates in the circuit to reduce canonicalisation overhead.\n", - "[13:07:49] Simulation (INFO) - Running simulation...\n", - "[13:07:49] Simulation (INFO) - Progress... 0%\n", - "[13:07:49] Simulation (INFO) - Progress... 0%\n", - "[13:07:49] Simulation (INFO) - Progress... 0%\n", - "[13:07:49] Simulation (INFO) - Progress... 0%\n", - "[13:07:49] Simulation (INFO) - Progress... 0%\n", - "[13:07:49] Simulation (INFO) - Progress... 0%\n", - "[13:07:49] Simulation (INFO) - Progress... 1%\n", - "[13:07:49] Simulation (INFO) - Progress... 1%\n", - "[13:07:49] Simulation (INFO) - Progress... 1%\n", - "[13:07:49] Simulation (INFO) - Progress... 1%\n", - "[13:07:49] Simulation (INFO) - Progress... 1%\n", - "[13:07:49] Simulation (INFO) - Progress... 1%\n", - "[13:07:49] Simulation (INFO) - Progress... 2%\n", - "[13:07:49] Simulation (INFO) - Progress... 2%\n", - "[13:07:49] Simulation (INFO) - Progress... 2%\n", - "[13:07:49] Simulation (INFO) - Progress... 2%\n", - "[13:07:49] Simulation (INFO) - Progress... 2%\n", - "[13:07:49] Simulation (INFO) - Progress... 2%\n", - "[13:07:49] Simulation (INFO) - Progress... 3%\n", - "[13:07:49] Simulation (INFO) - Progress... 3%\n", - "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00067138671875\n", - "[13:07:49] MPS (INFO) - MPS fidelity=1.0\n", - "[13:07:49] Simulation (INFO) - Progress... 3%\n", - "[13:07:49] Simulation (INFO) - Progress... 3%\n", - "[13:07:49] Simulation (INFO) - Progress... 3%\n", - "[13:07:49] Simulation (INFO) - Progress... 3%\n", - "[13:07:49] MPS (INFO) - MPS size (MiB)=0.000732421875\n", - "[13:07:49] MPS (INFO) - MPS fidelity=0.9999999999999999\n", - "[13:07:49] Simulation (INFO) - Progress... 4%\n", - "[13:07:49] Simulation (INFO) - Progress... 4%\n", - "[13:07:49] Simulation (INFO) - Progress... 4%\n", - "[13:07:49] MPS (INFO) - MPS size (MiB)=0.0008544921875\n", - "[13:07:49] MPS (INFO) - MPS fidelity=0.9999999999999999\n", - "[13:07:49] Simulation (INFO) - Progress... 4%\n", - "[13:07:49] Simulation (INFO) - Progress... 4%\n", - "[13:07:49] Simulation (INFO) - Progress... 4%\n", - "[13:07:49] MPS (INFO) - MPS size (MiB)=0.0008544921875\n", - "[13:07:49] MPS (INFO) - MPS fidelity=0.9999999999999999\n", - "[13:07:49] Simulation (INFO) - Progress... 5%\n", - "[13:07:49] Simulation (INFO) - Progress... 5%\n", - "[13:07:49] Simulation (INFO) - Progress... 5%\n", - "[13:07:49] Simulation (INFO) - Progress... 5%\n", - "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00091552734375\n", - "[13:07:49] MPS (INFO) - MPS fidelity=0.9999999999999999\n", - "[13:07:49] Simulation (INFO) - Progress... 5%\n", - "[13:07:49] Simulation (INFO) - Progress... 5%\n", - "[13:07:49] Simulation (INFO) - Progress... 6%\n", - "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00103759765625\n", - "[13:07:49] MPS (INFO) - MPS fidelity=0.9999999999999999\n", - "[13:07:49] Simulation (INFO) - Progress... 6%\n", - "[13:07:49] Simulation (INFO) - Progress... 6%\n", - "[13:07:49] Simulation (INFO) - Progress... 6%\n", - "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00103759765625\n", - "[13:07:49] MPS (INFO) - MPS fidelity=0.9999217894870942\n", - "[13:07:49] Simulation (INFO) - Progress... 6%\n", - "[13:07:49] Simulation (INFO) - Progress... 6%\n", - "[13:07:49] Simulation (INFO) - Progress... 7%\n", - "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00115966796875\n", - "[13:07:49] MPS (INFO) - MPS fidelity=0.9997584263446058\n", - "[13:07:49] Simulation (INFO) - Progress... 7%\n", - "[13:07:49] Simulation (INFO) - Progress... 7%\n", - "[13:07:49] Simulation (INFO) - Progress... 7%\n", - "[13:07:49] MPS (INFO) - Applying variational optimisation.\n", - "[13:07:49] MPS (INFO) - Fidelity before optimisation=0.9997584263446058\n", - "[13:07:49] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:49] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.999879599684896\n", - "[13:07:49] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:49] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9998799542127541\n", - "[13:07:49] MPS (INFO) - Final fidelity after optimisation=0.9998799542127541\n", - "[13:07:49] MPS (INFO) - MPS size (MiB)=0.00115966796875\n", - "[13:07:49] MPS (INFO) - MPS fidelity=0.9998799542127541\n", - "[13:07:49] Simulation (INFO) - Progress... 7%\n", - "[13:07:49] Simulation (INFO) - Progress... 7%\n", - "[13:07:49] Simulation (INFO) - Progress... 8%\n", - "[13:07:49] Simulation (INFO) - Progress... 8%\n", - "[13:07:49] MPS (INFO) - MPS size (MiB)=0.001220703125\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9998799542127541\n", - "[13:07:50] Simulation (INFO) - Progress... 8%\n", - "[13:07:50] Simulation (INFO) - Progress... 8%\n", - "[13:07:50] Simulation (INFO) - Progress... 8%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.0013427734375\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9998799542127541\n", - "[13:07:50] Simulation (INFO) - Progress... 8%\n", - "[13:07:50] Simulation (INFO) - Progress... 9%\n", - "[13:07:50] Simulation (INFO) - Progress... 9%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00146484375\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.998931558816809\n", - "[13:07:50] Simulation (INFO) - Progress... 9%\n", - "[13:07:50] Simulation (INFO) - Progress... 9%\n", - "[13:07:50] Simulation (INFO) - Progress... 9%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.001617431640625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361637\n", - "[13:07:50] Simulation (INFO) - Progress... 10%\n", - "[13:07:50] Simulation (INFO) - Progress... 10%\n", - "[13:07:50] Simulation (INFO) - Progress... 10%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.001983642578125\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361637\n", - "[13:07:50] Simulation (INFO) - Progress... 10%\n", - "[13:07:50] Simulation (INFO) - Progress... 10%\n", - "[13:07:50] Simulation (INFO) - Progress... 10%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002166748046875\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361637\n", - "[13:07:50] Simulation (INFO) - Progress... 11%\n", - "[13:07:50] Simulation (INFO) - Progress... 11%\n", - "[13:07:50] Simulation (INFO) - Progress... 11%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002166748046875\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361637\n", - "[13:07:50] Simulation (INFO) - Progress... 11%\n", - "[13:07:50] Simulation (INFO) - Progress... 11%\n", - "[13:07:50] Simulation (INFO) - Progress... 11%\n", - "[13:07:50] Simulation (INFO) - Progress... 12%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002227783203125\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361635\n", - "[13:07:50] Simulation (INFO) - Progress... 12%\n", - "[13:07:50] Simulation (INFO) - Progress... 12%\n", - "[13:07:50] Simulation (INFO) - Progress... 12%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002349853515625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9983070174361635\n", - "[13:07:50] Simulation (INFO) - Progress... 12%\n", - "[13:07:50] Simulation (INFO) - Progress... 12%\n", - "[13:07:50] Simulation (INFO) - Progress... 13%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002349853515625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9976865779910287\n", - "[13:07:50] Simulation (INFO) - Progress... 13%\n", - "[13:07:50] Simulation (INFO) - Progress... 13%\n", - "[13:07:50] Simulation (INFO) - Progress... 13%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.002655029296875\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9976865779910287\n", - "[13:07:50] Simulation (INFO) - Progress... 13%\n", - "[13:07:50] Simulation (INFO) - Progress... 13%\n", - "[13:07:50] Simulation (INFO) - Progress... 14%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00286865234375\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9969424312499057\n", - "[13:07:50] Simulation (INFO) - Progress... 14%\n", - "[13:07:50] Simulation (INFO) - Progress... 14%\n", - "[13:07:50] Simulation (INFO) - Progress... 14%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00311279296875\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9963524022161957\n", - "[13:07:50] Simulation (INFO) - Progress... 14%\n", - "[13:07:50] Simulation (INFO) - Progress... 14%\n", - "[13:07:50] Simulation (INFO) - Progress... 15%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00335693359375\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9956807669798176\n", - "[13:07:50] Simulation (INFO) - Progress... 15%\n", - "[13:07:50] Simulation (INFO) - Progress... 15%\n", - "[13:07:50] Simulation (INFO) - Progress... 15%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00335693359375\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9956807669798176\n", - "[13:07:50] Simulation (INFO) - Progress... 15%\n", - "[13:07:50] Simulation (INFO) - Progress... 15%\n", - "[13:07:50] Simulation (INFO) - Progress... 16%\n", - "[13:07:50] MPS (INFO) - Applying variational optimisation.\n", - "[13:07:50] MPS (INFO) - Fidelity before optimisation=0.9956807669798176\n", - "[13:07:50] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9969066360097213\n", - "[13:07:50] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9969284260568553\n", - "[13:07:50] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:50] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9969300587346095\n", - "[13:07:50] MPS (INFO) - Final fidelity after optimisation=0.9969300587346095\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00335693359375\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9969300587346095\n", - "[13:07:50] Simulation (INFO) - Progress... 16%\n", - "[13:07:50] Simulation (INFO) - Progress... 16%\n", - "[13:07:50] Simulation (INFO) - Progress... 16%\n", - "[13:07:50] Simulation (INFO) - Progress... 16%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00341796875\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9969300587346094\n", - "[13:07:50] Simulation (INFO) - Progress... 16%\n", - "[13:07:50] Simulation (INFO) - Progress... 17%\n", - "[13:07:50] Simulation (INFO) - Progress... 17%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.0035400390625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9969300587346094\n", - "[13:07:50] Simulation (INFO) - Progress... 17%\n", - "[13:07:50] Simulation (INFO) - Progress... 17%\n", - "[13:07:50] Simulation (INFO) - Progress... 17%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.003662109375\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9964085287675873\n", - "[13:07:50] Simulation (INFO) - Progress... 17%\n", - "[13:07:50] Simulation (INFO) - Progress... 18%\n", - "[13:07:50] Simulation (INFO) - Progress... 18%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00396728515625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9964085287675873\n", - "[13:07:50] Simulation (INFO) - Progress... 18%\n", - "[13:07:50] Simulation (INFO) - Progress... 18%\n", - "[13:07:50] Simulation (INFO) - Progress... 18%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00445556640625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9964085287675873\n", - "[13:07:50] Simulation (INFO) - Progress... 18%\n", - "[13:07:50] Simulation (INFO) - Progress... 19%\n", - "[13:07:50] Simulation (INFO) - Progress... 19%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00469970703125\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.995903817286653\n", - "[13:07:50] Simulation (INFO) - Progress... 19%\n", - "[13:07:50] Simulation (INFO) - Progress... 19%\n", - "[13:07:50] Simulation (INFO) - Progress... 19%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00579833984375\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.995903817286653\n", - "[13:07:50] Simulation (INFO) - Progress... 20%\n", - "[13:07:50] Simulation (INFO) - Progress... 20%\n", - "[13:07:50] Simulation (INFO) - Progress... 20%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.006988525390625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", - "[13:07:50] Simulation (INFO) - Progress... 20%\n", - "[13:07:50] Simulation (INFO) - Progress... 20%\n", - "[13:07:50] Simulation (INFO) - Progress... 20%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00799560546875\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", - "[13:07:50] Simulation (INFO) - Progress... 21%\n", - "[13:07:50] Simulation (INFO) - Progress... 21%\n", - "[13:07:50] Simulation (INFO) - Progress... 21%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00799560546875\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", - "[13:07:50] Simulation (INFO) - Progress... 21%\n", - "[13:07:50] Simulation (INFO) - Progress... 21%\n", - "[13:07:50] Simulation (INFO) - Progress... 21%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00799560546875\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", - "[13:07:50] Simulation (INFO) - Progress... 22%\n", - "[13:07:50] Simulation (INFO) - Progress... 22%\n", - "[13:07:50] Simulation (INFO) - Progress... 22%\n", - "[13:07:50] Simulation (INFO) - Progress... 22%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.008056640625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", - "[13:07:50] Simulation (INFO) - Progress... 22%\n", - "[13:07:50] Simulation (INFO) - Progress... 22%\n", - "[13:07:50] Simulation (INFO) - Progress... 23%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.0081787109375\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", - "[13:07:50] Simulation (INFO) - Progress... 23%\n", - "[13:07:50] Simulation (INFO) - Progress... 23%\n", - "[13:07:50] Simulation (INFO) - Progress... 23%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.0084228515625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748628\n", - "[13:07:50] Simulation (INFO) - Progress... 23%\n", - "[13:07:50] Simulation (INFO) - Progress... 23%\n", - "[13:07:50] Simulation (INFO) - Progress... 24%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00885009765625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9954051558748626\n", - "[13:07:50] Simulation (INFO) - Progress... 24%\n", - "[13:07:50] Simulation (INFO) - Progress... 24%\n", - "[13:07:50] Simulation (INFO) - Progress... 24%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00909423828125\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9953466253004998\n", - "[13:07:50] Simulation (INFO) - Progress... 24%\n", - "[13:07:50] Simulation (INFO) - Progress... 24%\n", - "[13:07:50] Simulation (INFO) - Progress... 25%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.00909423828125\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9946324749398359\n", - "[13:07:50] Simulation (INFO) - Progress... 25%\n", - "[13:07:50] Simulation (INFO) - Progress... 25%\n", - "[13:07:50] Simulation (INFO) - Progress... 25%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.009918212890625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9943297562108634\n", - "[13:07:50] Simulation (INFO) - Progress... 25%\n", - "[13:07:50] Simulation (INFO) - Progress... 25%\n", - "[13:07:50] Simulation (INFO) - Progress... 26%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.01129150390625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9937839224972186\n", - "[13:07:50] Simulation (INFO) - Progress... 26%\n", - "[13:07:50] Simulation (INFO) - Progress... 26%\n", - "[13:07:50] Simulation (INFO) - Progress... 26%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.011749267578125\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9931523125088856\n", - "[13:07:50] Simulation (INFO) - Progress... 26%\n", - "[13:07:50] Simulation (INFO) - Progress... 26%\n", - "[13:07:50] Simulation (INFO) - Progress... 27%\n", - "[13:07:50] MPS (INFO) - MPS size (MiB)=0.013824462890625\n", - "[13:07:50] MPS (INFO) - MPS fidelity=0.9922963086133972\n", - "[13:07:50] Simulation (INFO) - Progress... 27%\n", - "[13:07:50] Simulation (INFO) - Progress... 27%\n", - "[13:07:50] Simulation (INFO) - Progress... 27%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.013824462890625\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9922963086133972\n", - "[13:07:51] Simulation (INFO) - Progress... 27%\n", - "[13:07:51] Simulation (INFO) - Progress... 27%\n", - "[13:07:51] Simulation (INFO) - Progress... 28%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.013824462890625\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.992296308613397\n", - "[13:07:51] Simulation (INFO) - Progress... 28%\n", - "[13:07:51] Simulation (INFO) - Progress... 28%\n", - "[13:07:51] Simulation (INFO) - Progress... 28%\n", - "[13:07:51] MPS (INFO) - Applying variational optimisation.\n", - "[13:07:51] MPS (INFO) - Fidelity before optimisation=0.992296308613397\n", - "[13:07:51] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9936350532619016\n", - "[13:07:51] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9936901320991008\n", - "[13:07:51] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9937162633400826\n", - "[13:07:51] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:51] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9937332498584915\n", - "[13:07:51] MPS (INFO) - Final fidelity after optimisation=0.9937332498584915\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.013824462890625\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584915\n", - "[13:07:51] Simulation (INFO) - Progress... 28%\n", - "[13:07:51] Simulation (INFO) - Progress... 28%\n", - "[13:07:51] Simulation (INFO) - Progress... 29%\n", - "[13:07:51] Simulation (INFO) - Progress... 29%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.013885498046875\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584915\n", - "[13:07:51] Simulation (INFO) - Progress... 29%\n", - "[13:07:51] Simulation (INFO) - Progress... 29%\n", - "[13:07:51] Simulation (INFO) - Progress... 29%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.014007568359375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584915\n", - "[13:07:51] Simulation (INFO) - Progress... 30%\n", - "[13:07:51] Simulation (INFO) - Progress... 30%\n", - "[13:07:51] Simulation (INFO) - Progress... 30%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.014251708984375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584915\n", - "[13:07:51] Simulation (INFO) - Progress... 30%\n", - "[13:07:51] Simulation (INFO) - Progress... 30%\n", - "[13:07:51] Simulation (INFO) - Progress... 30%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.014739990234375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584918\n", - "[13:07:51] Simulation (INFO) - Progress... 31%\n", - "[13:07:51] Simulation (INFO) - Progress... 31%\n", - "[13:07:51] Simulation (INFO) - Progress... 31%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.015716552734375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9937332498584918\n", - "[13:07:51] Simulation (INFO) - Progress... 31%\n", - "[13:07:51] Simulation (INFO) - Progress... 31%\n", - "[13:07:51] Simulation (INFO) - Progress... 31%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.016815185546875\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9932134093534576\n", - "[13:07:51] Simulation (INFO) - Progress... 32%\n", - "[13:07:51] Simulation (INFO) - Progress... 32%\n", - "[13:07:51] Simulation (INFO) - Progress... 32%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.017822265625\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9928837028574017\n", - "[13:07:51] Simulation (INFO) - Progress... 32%\n", - "[13:07:51] Simulation (INFO) - Progress... 32%\n", - "[13:07:51] Simulation (INFO) - Progress... 32%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.01953125\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9928837028574017\n", - "[13:07:51] Simulation (INFO) - Progress... 33%\n", - "[13:07:51] Simulation (INFO) - Progress... 33%\n", - "[13:07:51] Simulation (INFO) - Progress... 33%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.02001953125\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9918985098672729\n", - "[13:07:51] Simulation (INFO) - Progress... 33%\n", - "[13:07:51] Simulation (INFO) - Progress... 33%\n", - "[13:07:51] Simulation (INFO) - Progress... 33%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.0220947265625\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9912041554874009\n", - "[13:07:51] Simulation (INFO) - Progress... 34%\n", - "[13:07:51] Simulation (INFO) - Progress... 34%\n", - "[13:07:51] Simulation (INFO) - Progress... 34%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.02490234375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9903174175990784\n", - "[13:07:51] Simulation (INFO) - Progress... 34%\n", - "[13:07:51] Simulation (INFO) - Progress... 34%\n", - "[13:07:51] Simulation (INFO) - Progress... 34%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025543212890625\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", - "[13:07:51] Simulation (INFO) - Progress... 35%\n", - "[13:07:51] Simulation (INFO) - Progress... 35%\n", - "[13:07:51] Simulation (INFO) - Progress... 35%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025543212890625\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", - "[13:07:51] Simulation (INFO) - Progress... 35%\n", - "[13:07:51] Simulation (INFO) - Progress... 35%\n", - "[13:07:51] Simulation (INFO) - Progress... 35%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025543212890625\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", - "[13:07:51] Simulation (INFO) - Progress... 36%\n", - "[13:07:51] Simulation (INFO) - Progress... 36%\n", - "[13:07:51] Simulation (INFO) - Progress... 36%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025543212890625\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", - "[13:07:51] Simulation (INFO) - Progress... 36%\n", - "[13:07:51] Simulation (INFO) - Progress... 36%\n", - "[13:07:51] Simulation (INFO) - Progress... 36%\n", - "[13:07:51] Simulation (INFO) - Progress... 37%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025604248046875\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", - "[13:07:51] Simulation (INFO) - Progress... 37%\n", - "[13:07:51] Simulation (INFO) - Progress... 37%\n", - "[13:07:51] Simulation (INFO) - Progress... 37%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025726318359375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", - "[13:07:51] Simulation (INFO) - Progress... 37%\n", - "[13:07:51] Simulation (INFO) - Progress... 37%\n", - "[13:07:51] Simulation (INFO) - Progress... 38%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.025970458984375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", - "[13:07:51] Simulation (INFO) - Progress... 38%\n", - "[13:07:51] Simulation (INFO) - Progress... 38%\n", - "[13:07:51] Simulation (INFO) - Progress... 38%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.026458740234375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", - "[13:07:51] Simulation (INFO) - Progress... 38%\n", - "[13:07:51] Simulation (INFO) - Progress... 38%\n", - "[13:07:51] Simulation (INFO) - Progress... 39%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.027435302734375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", - "[13:07:51] Simulation (INFO) - Progress... 39%\n", - "[13:07:51] Simulation (INFO) - Progress... 39%\n", - "[13:07:51] Simulation (INFO) - Progress... 39%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.029388427734375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9894623237215003\n", - "[13:07:51] Simulation (INFO) - Progress... 39%\n", - "[13:07:51] Simulation (INFO) - Progress... 40%\n", - "[13:07:51] Simulation (INFO) - Progress... 40%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.031219482421875\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9889320425653054\n", - "[13:07:51] Simulation (INFO) - Progress... 40%\n", - "[13:07:51] Simulation (INFO) - Progress... 40%\n", - "[13:07:51] Simulation (INFO) - Progress... 40%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.03411865234375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9885110225000973\n", - "[13:07:51] Simulation (INFO) - Progress... 40%\n", - "[13:07:51] Simulation (INFO) - Progress... 41%\n", - "[13:07:51] Simulation (INFO) - Progress... 41%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.03656005859375\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.987710292524794\n", - "[13:07:51] Simulation (INFO) - Progress... 41%\n", - "[13:07:51] Simulation (INFO) - Progress... 41%\n", - "[13:07:51] Simulation (INFO) - Progress... 41%\n", - "[13:07:51] MPS (INFO) - MPS size (MiB)=0.03887939453125\n", - "[13:07:51] MPS (INFO) - MPS fidelity=0.9867894813441014\n", - "[13:07:51] Simulation (INFO) - Progress... 41%\n", - "[13:07:51] Simulation (INFO) - Progress... 42%\n", - "[13:07:51] Simulation (INFO) - Progress... 42%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.04327392578125\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9864127385201381\n", - "[13:07:52] Simulation (INFO) - Progress... 42%\n", - "[13:07:52] Simulation (INFO) - Progress... 42%\n", - "[13:07:52] Simulation (INFO) - Progress... 42%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.0482177734375\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9854360444314847\n", - "[13:07:52] Simulation (INFO) - Progress... 42%\n", - "[13:07:52] Simulation (INFO) - Progress... 43%\n", - "[13:07:52] Simulation (INFO) - Progress... 43%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.0537109375\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9845797921333378\n", - "[13:07:52] Simulation (INFO) - Progress... 43%\n", - "[13:07:52] Simulation (INFO) - Progress... 43%\n", - "[13:07:52] Simulation (INFO) - Progress... 43%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056182861328125\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9841566230600164\n", - "[13:07:52] Simulation (INFO) - Progress... 43%\n", - "[13:07:52] Simulation (INFO) - Progress... 44%\n", - "[13:07:52] Simulation (INFO) - Progress... 44%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056182861328125\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9841566230600164\n", - "[13:07:52] Simulation (INFO) - Progress... 44%\n", - "[13:07:52] Simulation (INFO) - Progress... 44%\n", - "[13:07:52] Simulation (INFO) - Progress... 44%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056182861328125\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9841566230600164\n", - "[13:07:52] Simulation (INFO) - Progress... 44%\n", - "[13:07:52] Simulation (INFO) - Progress... 45%\n", - "[13:07:52] Simulation (INFO) - Progress... 45%\n", - "[13:07:52] MPS (INFO) - Applying variational optimisation.\n", - "[13:07:52] MPS (INFO) - Fidelity before optimisation=0.9841566230600164\n", - "[13:07:52] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9861657072417293\n", - "[13:07:52] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9862250122543205\n", - "[13:07:52] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9862476294181953\n", - "[13:07:52] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:52] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9862603678860784\n", - "[13:07:52] MPS (INFO) - Final fidelity after optimisation=0.9862603678860784\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056182861328125\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9862603678860784\n", - "[13:07:52] Simulation (INFO) - Progress... 45%\n", - "[13:07:52] Simulation (INFO) - Progress... 45%\n", - "[13:07:52] Simulation (INFO) - Progress... 45%\n", - "[13:07:52] Simulation (INFO) - Progress... 45%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056243896484375\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9862603678860784\n", - "[13:07:52] Simulation (INFO) - Progress... 46%\n", - "[13:07:52] Simulation (INFO) - Progress... 46%\n", - "[13:07:52] Simulation (INFO) - Progress... 46%\n", - "[13:07:52] Simulation (INFO) - Progress... 46%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056365966796875\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9862603678860784\n", - "[13:07:52] Simulation (INFO) - Progress... 46%\n", - "[13:07:52] Simulation (INFO) - Progress... 46%\n", - "[13:07:52] Simulation (INFO) - Progress... 47%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056365966796875\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9858838523316555\n", - "[13:07:52] Simulation (INFO) - Progress... 47%\n", - "[13:07:52] Simulation (INFO) - Progress... 47%\n", - "[13:07:52] Simulation (INFO) - Progress... 47%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056732177734375\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9858838523316555\n", - "[13:07:52] Simulation (INFO) - Progress... 47%\n", - "[13:07:52] Simulation (INFO) - Progress... 47%\n", - "[13:07:52] Simulation (INFO) - Progress... 48%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.056976318359375\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9852517168285035\n", - "[13:07:52] Simulation (INFO) - Progress... 48%\n", - "[13:07:52] Simulation (INFO) - Progress... 48%\n", - "[13:07:52] Simulation (INFO) - Progress... 48%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.058563232421875\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9852517168285035\n", - "[13:07:52] Simulation (INFO) - Progress... 48%\n", - "[13:07:52] Simulation (INFO) - Progress... 48%\n", - "[13:07:52] Simulation (INFO) - Progress... 49%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.060028076171875\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9843273031404238\n", - "[13:07:52] Simulation (INFO) - Progress... 49%\n", - "[13:07:52] Simulation (INFO) - Progress... 49%\n", - "[13:07:52] Simulation (INFO) - Progress... 49%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.06494140625\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9837115795017173\n", - "[13:07:52] Simulation (INFO) - Progress... 49%\n", - "[13:07:52] Simulation (INFO) - Progress... 50%\n", - "[13:07:52] Simulation (INFO) - Progress... 50%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.0682373046875\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9827664306820764\n", - "[13:07:52] Simulation (INFO) - Progress... 50%\n", - "[13:07:52] Simulation (INFO) - Progress... 50%\n", - "[13:07:52] Simulation (INFO) - Progress... 50%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.071533203125\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9818041456762368\n", - "[13:07:52] Simulation (INFO) - Progress... 50%\n", - "[13:07:52] Simulation (INFO) - Progress... 51%\n", - "[13:07:52] Simulation (INFO) - Progress... 51%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.0758056640625\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9811843347916239\n", - "[13:07:52] Simulation (INFO) - Progress... 51%\n", - "[13:07:52] Simulation (INFO) - Progress... 51%\n", - "[13:07:52] Simulation (INFO) - Progress... 51%\n", - "[13:07:52] MPS (INFO) - MPS size (MiB)=0.0831298828125\n", - "[13:07:52] MPS (INFO) - MPS fidelity=0.9804396566497552\n", - "[13:07:52] Simulation (INFO) - Progress... 51%\n", - "[13:07:52] Simulation (INFO) - Progress... 52%\n", - "[13:07:52] Simulation (INFO) - Progress... 52%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.09356689453125\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9797042536815029\n", - "[13:07:53] Simulation (INFO) - Progress... 52%\n", - "[13:07:53] Simulation (INFO) - Progress... 52%\n", - "[13:07:53] Simulation (INFO) - Progress... 52%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.1025390625\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9787337512094221\n", - "[13:07:53] Simulation (INFO) - Progress... 52%\n", - "[13:07:53] Simulation (INFO) - Progress... 53%\n", - "[13:07:53] Simulation (INFO) - Progress... 53%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.106201171875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089927\n", - "[13:07:53] Simulation (INFO) - Progress... 53%\n", - "[13:07:53] Simulation (INFO) - Progress... 53%\n", - "[13:07:53] Simulation (INFO) - Progress... 53%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10711669921875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089927\n", - "[13:07:53] Simulation (INFO) - Progress... 53%\n", - "[13:07:53] Simulation (INFO) - Progress... 54%\n", - "[13:07:53] Simulation (INFO) - Progress... 54%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10711669921875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089927\n", - "[13:07:53] Simulation (INFO) - Progress... 54%\n", - "[13:07:53] Simulation (INFO) - Progress... 54%\n", - "[13:07:53] Simulation (INFO) - Progress... 54%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10711669921875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089926\n", - "[13:07:53] Simulation (INFO) - Progress... 54%\n", - "[13:07:53] Simulation (INFO) - Progress... 55%\n", - "[13:07:53] Simulation (INFO) - Progress... 55%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10711669921875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089926\n", - "[13:07:53] Simulation (INFO) - Progress... 55%\n", - "[13:07:53] Simulation (INFO) - Progress... 55%\n", - "[13:07:53] Simulation (INFO) - Progress... 55%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10711669921875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089928\n", - "[13:07:53] Simulation (INFO) - Progress... 55%\n", - "[13:07:53] Simulation (INFO) - Progress... 56%\n", - "[13:07:53] Simulation (INFO) - Progress... 56%\n", - "[13:07:53] Simulation (INFO) - Progress... 56%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10736083984375\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089928\n", - "[13:07:53] Simulation (INFO) - Progress... 56%\n", - "[13:07:53] Simulation (INFO) - Progress... 56%\n", - "[13:07:53] Simulation (INFO) - Progress... 56%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10784912109375\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9779883667089928\n", - "[13:07:53] Simulation (INFO) - Progress... 57%\n", - "[13:07:53] Simulation (INFO) - Progress... 57%\n", - "[13:07:53] Simulation (INFO) - Progress... 57%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.108123779296875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.977670099247772\n", - "[13:07:53] Simulation (INFO) - Progress... 57%\n", - "[13:07:53] Simulation (INFO) - Progress... 57%\n", - "[13:07:53] Simulation (INFO) - Progress... 57%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.10931396484375\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9773757304606052\n", - "[13:07:53] Simulation (INFO) - Progress... 58%\n", - "[13:07:53] Simulation (INFO) - Progress... 58%\n", - "[13:07:53] Simulation (INFO) - Progress... 58%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.111053466796875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9767554958542899\n", - "[13:07:53] Simulation (INFO) - Progress... 58%\n", - "[13:07:53] Simulation (INFO) - Progress... 58%\n", - "[13:07:53] Simulation (INFO) - Progress... 58%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.113433837890625\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9761528434196657\n", - "[13:07:53] Simulation (INFO) - Progress... 59%\n", - "[13:07:53] Simulation (INFO) - Progress... 59%\n", - "[13:07:53] Simulation (INFO) - Progress... 59%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.118927001953125\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9754288384480352\n", - "[13:07:53] Simulation (INFO) - Progress... 59%\n", - "[13:07:53] Simulation (INFO) - Progress... 59%\n", - "[13:07:53] Simulation (INFO) - Progress... 60%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.12457275390625\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9745562791745191\n", - "[13:07:53] Simulation (INFO) - Progress... 60%\n", - "[13:07:53] Simulation (INFO) - Progress... 60%\n", - "[13:07:53] Simulation (INFO) - Progress... 60%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.130218505859375\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9737355899102491\n", - "[13:07:53] Simulation (INFO) - Progress... 60%\n", - "[13:07:53] Simulation (INFO) - Progress... 60%\n", - "[13:07:53] Simulation (INFO) - Progress... 61%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.140228271484375\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9730778511018878\n", - "[13:07:53] Simulation (INFO) - Progress... 61%\n", - "[13:07:53] Simulation (INFO) - Progress... 61%\n", - "[13:07:53] Simulation (INFO) - Progress... 61%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.151702880859375\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9722121338904668\n", - "[13:07:53] Simulation (INFO) - Progress... 61%\n", - "[13:07:53] Simulation (INFO) - Progress... 61%\n", - "[13:07:53] Simulation (INFO) - Progress... 62%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.16302490234375\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9712637153710257\n", - "[13:07:53] Simulation (INFO) - Progress... 62%\n", - "[13:07:53] Simulation (INFO) - Progress... 62%\n", - "[13:07:53] Simulation (INFO) - Progress... 62%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.17095947265625\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9704052937857174\n", - "[13:07:53] Simulation (INFO) - Progress... 62%\n", - "[13:07:53] Simulation (INFO) - Progress... 62%\n", - "[13:07:53] Simulation (INFO) - Progress... 63%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9696124478274755\n", - "[13:07:53] Simulation (INFO) - Progress... 63%\n", - "[13:07:53] Simulation (INFO) - Progress... 63%\n", - "[13:07:53] Simulation (INFO) - Progress... 63%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9696124478274755\n", - "[13:07:53] Simulation (INFO) - Progress... 63%\n", - "[13:07:53] Simulation (INFO) - Progress... 63%\n", - "[13:07:53] Simulation (INFO) - Progress... 64%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9696124478274752\n", - "[13:07:53] Simulation (INFO) - Progress... 64%\n", - "[13:07:53] Simulation (INFO) - Progress... 64%\n", - "[13:07:53] Simulation (INFO) - Progress... 64%\n", - "[13:07:53] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", - "[13:07:53] MPS (INFO) - MPS fidelity=0.9696124478274752\n", - "[13:07:53] Simulation (INFO) - Progress... 64%\n", - "[13:07:53] MPS (INFO) - Applying variational optimisation.\n", - "[13:07:53] MPS (INFO) - Fidelity before optimisation=0.9696124478274752\n", - "[13:07:53] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:53] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9748503580516497\n", - "[13:07:53] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9749968653397907\n", - "[13:07:54] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9750403204297021\n", - "[13:07:54] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9750621753095439\n", - "[13:07:54] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:54] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9750760500178091\n", - "[13:07:54] MPS (INFO) - Final fidelity after optimisation=0.9750760500178091\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9750760500178093\n", - "[13:07:54] Simulation (INFO) - Progress... 64%\n", - "[13:07:54] Simulation (INFO) - Progress... 65%\n", - "[13:07:54] Simulation (INFO) - Progress... 65%\n", - "[13:07:54] Simulation (INFO) - Progress... 65%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.17938232421875\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9750760500178093\n", - "[13:07:54] Simulation (INFO) - Progress... 65%\n", - "[13:07:54] Simulation (INFO) - Progress... 65%\n", - "[13:07:54] Simulation (INFO) - Progress... 65%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.179931640625\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9747058237409754\n", - "[13:07:54] Simulation (INFO) - Progress... 66%\n", - "[13:07:54] Simulation (INFO) - Progress... 66%\n", - "[13:07:54] Simulation (INFO) - Progress... 66%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.18121337890625\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9745772952447315\n", - "[13:07:54] Simulation (INFO) - Progress... 66%\n", - "[13:07:54] Simulation (INFO) - Progress... 66%\n", - "[13:07:54] Simulation (INFO) - Progress... 66%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.185272216796875\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9739652250830045\n", - "[13:07:54] Simulation (INFO) - Progress... 67%\n", - "[13:07:54] Simulation (INFO) - Progress... 67%\n", - "[13:07:54] Simulation (INFO) - Progress... 67%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.18792724609375\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9733944368640858\n", - "[13:07:54] Simulation (INFO) - Progress... 67%\n", - "[13:07:54] Simulation (INFO) - Progress... 67%\n", - "[13:07:54] Simulation (INFO) - Progress... 67%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.19647216796875\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.972694143326865\n", - "[13:07:54] Simulation (INFO) - Progress... 68%\n", - "[13:07:54] Simulation (INFO) - Progress... 68%\n", - "[13:07:54] Simulation (INFO) - Progress... 68%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.203033447265625\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9718997086560416\n", - "[13:07:54] Simulation (INFO) - Progress... 68%\n", - "[13:07:54] Simulation (INFO) - Progress... 68%\n", - "[13:07:54] Simulation (INFO) - Progress... 68%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.211639404296875\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9711668230786579\n", - "[13:07:54] Simulation (INFO) - Progress... 69%\n", - "[13:07:54] Simulation (INFO) - Progress... 69%\n", - "[13:07:54] Simulation (INFO) - Progress... 69%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.21630859375\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9703500807776058\n", - "[13:07:54] Simulation (INFO) - Progress... 69%\n", - "[13:07:54] Simulation (INFO) - Progress... 69%\n", - "[13:07:54] Simulation (INFO) - Progress... 70%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.2353515625\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9694719482925707\n", - "[13:07:54] Simulation (INFO) - Progress... 70%\n", - "[13:07:54] Simulation (INFO) - Progress... 70%\n", - "[13:07:54] Simulation (INFO) - Progress... 70%\n", - "[13:07:54] MPS (INFO) - MPS size (MiB)=0.267578125\n", - "[13:07:54] MPS (INFO) - MPS fidelity=0.9686645363954253\n", - "[13:07:54] Simulation (INFO) - Progress... 70%\n", - "[13:07:54] Simulation (INFO) - Progress... 70%\n", - "[13:07:54] Simulation (INFO) - Progress... 71%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.2991943359375\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9677864245613603\n", - "[13:07:55] Simulation (INFO) - Progress... 71%\n", - "[13:07:55] Simulation (INFO) - Progress... 71%\n", - "[13:07:55] Simulation (INFO) - Progress... 71%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.3233642578125\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9668761765880589\n", - "[13:07:55] Simulation (INFO) - Progress... 71%\n", - "[13:07:55] Simulation (INFO) - Progress... 71%\n", - "[13:07:55] Simulation (INFO) - Progress... 72%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.328582763671875\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9665059834306376\n", - "[13:07:55] Simulation (INFO) - Progress... 72%\n", - "[13:07:55] Simulation (INFO) - Progress... 72%\n", - "[13:07:55] Simulation (INFO) - Progress... 72%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.328582763671875\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9665059834306376\n", - "[13:07:55] Simulation (INFO) - Progress... 72%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.328582763671875\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9665059834306376\n", - "[13:07:55] Simulation (INFO) - Progress... 72%\n", - "[13:07:55] Simulation (INFO) - Progress... 73%\n", - "[13:07:55] Simulation (INFO) - Progress... 73%\n", - "[13:07:55] Simulation (INFO) - Progress... 73%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.328582763671875\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9665059834306376\n", - "[13:07:55] Simulation (INFO) - Progress... 73%\n", - "[13:07:55] Simulation (INFO) - Progress... 73%\n", - "[13:07:55] Simulation (INFO) - Progress... 73%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.329315185546875\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9665059834306379\n", - "[13:07:55] Simulation (INFO) - Progress... 74%\n", - "[13:07:55] Simulation (INFO) - Progress... 74%\n", - "[13:07:55] Simulation (INFO) - Progress... 74%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.333526611328125\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9662179790868655\n", - "[13:07:55] Simulation (INFO) - Progress... 74%\n", - "[13:07:55] Simulation (INFO) - Progress... 74%\n", - "[13:07:55] Simulation (INFO) - Progress... 74%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.333526611328125\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9656632028640223\n", - "[13:07:55] Simulation (INFO) - Progress... 75%\n", - "[13:07:55] Simulation (INFO) - Progress... 75%\n", - "[13:07:55] Simulation (INFO) - Progress... 75%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.3414306640625\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9647910655846768\n", - "[13:07:55] Simulation (INFO) - Progress... 75%\n", - "[13:07:55] Simulation (INFO) - Progress... 75%\n", - "[13:07:55] Simulation (INFO) - Progress... 75%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.354339599609375\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.963896037407298\n", - "[13:07:55] Simulation (INFO) - Progress... 76%\n", - "[13:07:55] Simulation (INFO) - Progress... 76%\n", - "[13:07:55] Simulation (INFO) - Progress... 76%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.366729736328125\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9630136852198702\n", - "[13:07:55] Simulation (INFO) - Progress... 76%\n", - "[13:07:55] Simulation (INFO) - Progress... 76%\n", - "[13:07:55] Simulation (INFO) - Progress... 76%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.37542724609375\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9621991577849345\n", - "[13:07:55] Simulation (INFO) - Progress... 77%\n", - "[13:07:55] Simulation (INFO) - Progress... 77%\n", - "[13:07:55] Simulation (INFO) - Progress... 77%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.40240478515625\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9613406445530075\n", - "[13:07:55] Simulation (INFO) - Progress... 77%\n", - "[13:07:55] Simulation (INFO) - Progress... 77%\n", - "[13:07:55] Simulation (INFO) - Progress... 77%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.4468994140625\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9603923338552977\n", - "[13:07:55] Simulation (INFO) - Progress... 78%\n", - "[13:07:55] Simulation (INFO) - Progress... 78%\n", - "[13:07:55] Simulation (INFO) - Progress... 78%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.452880859375\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.959554544788218\n", - "[13:07:55] Simulation (INFO) - Progress... 78%\n", - "[13:07:55] Simulation (INFO) - Progress... 78%\n", - "[13:07:55] Simulation (INFO) - Progress... 78%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.466156005859375\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9586928713117597\n", - "[13:07:55] Simulation (INFO) - Progress... 79%\n", - "[13:07:55] Simulation (INFO) - Progress... 79%\n", - "[13:07:55] Simulation (INFO) - Progress... 79%\n", - "[13:07:55] MPS (INFO) - MPS size (MiB)=0.497894287109375\n", - "[13:07:55] MPS (INFO) - MPS fidelity=0.9578333111030969\n", - "[13:07:55] Simulation (INFO) - Progress... 79%\n", - "[13:07:55] MPS (INFO) - Applying variational optimisation.\n", - "[13:07:55] MPS (INFO) - Fidelity before optimisation=0.9578333111030969\n", - "[13:07:55] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9638633099128788\n", - "[13:07:55] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9641110973253727\n", - "[13:07:55] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:55] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9641966129574852\n", - "[13:07:55] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.964241137047175\n", - "[13:07:56] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9642683294864673\n", - "[13:07:56] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:56] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9642862317446965\n", - "[13:07:56] MPS (INFO) - Final fidelity after optimisation=0.9642862317446965\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.497894287109375\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9642862317446965\n", - "[13:07:56] Simulation (INFO) - Progress... 79%\n", - "[13:07:56] Simulation (INFO) - Progress... 80%\n", - "[13:07:56] Simulation (INFO) - Progress... 80%\n", - "[13:07:56] Simulation (INFO) - Progress... 80%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.497894287109375\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9642862317446963\n", - "[13:07:56] Simulation (INFO) - Progress... 80%\n", - "[13:07:56] Simulation (INFO) - Progress... 80%\n", - "[13:07:56] Simulation (INFO) - Progress... 80%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.497894287109375\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9642862317446963\n", - "[13:07:56] Simulation (INFO) - Progress... 81%\n", - "[13:07:56] Simulation (INFO) - Progress... 81%\n", - "[13:07:56] Simulation (INFO) - Progress... 81%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.49859619140625\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9634482033158315\n", - "[13:07:56] Simulation (INFO) - Progress... 81%\n", - "[13:07:56] Simulation (INFO) - Progress... 81%\n", - "[13:07:56] Simulation (INFO) - Progress... 81%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.50518798828125\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9627097530284029\n", - "[13:07:56] Simulation (INFO) - Progress... 82%\n", - "[13:07:56] Simulation (INFO) - Progress... 82%\n", - "[13:07:56] Simulation (INFO) - Progress... 82%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.51470947265625\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9619717159253325\n", - "[13:07:56] Simulation (INFO) - Progress... 82%\n", - "[13:07:56] Simulation (INFO) - Progress... 82%\n", - "[13:07:56] Simulation (INFO) - Progress... 82%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.52020263671875\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9610104125613574\n", - "[13:07:56] Simulation (INFO) - Progress... 83%\n", - "[13:07:56] Simulation (INFO) - Progress... 83%\n", - "[13:07:56] Simulation (INFO) - Progress... 83%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.5423583984375\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9602104265798482\n", - "[13:07:56] Simulation (INFO) - Progress... 83%\n", - "[13:07:56] Simulation (INFO) - Progress... 83%\n", - "[13:07:56] Simulation (INFO) - Progress... 83%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.574493408203125\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9593078644311607\n", - "[13:07:56] Simulation (INFO) - Progress... 84%\n", - "[13:07:56] Simulation (INFO) - Progress... 84%\n", - "[13:07:56] Simulation (INFO) - Progress... 84%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.6258544921875\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9584049213960937\n", - "[13:07:56] Simulation (INFO) - Progress... 84%\n", - "[13:07:56] Simulation (INFO) - Progress... 84%\n", - "[13:07:56] Simulation (INFO) - Progress... 84%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.6197509765625\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9575755639326016\n", - "[13:07:56] Simulation (INFO) - Progress... 85%\n", - "[13:07:56] Simulation (INFO) - Progress... 85%\n", - "[13:07:56] Simulation (INFO) - Progress... 85%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.65673828125\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9566270309912319\n", - "[13:07:56] Simulation (INFO) - Progress... 85%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.65673828125\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9566270309912319\n", - "[13:07:56] Simulation (INFO) - Progress... 85%\n", - "[13:07:56] Simulation (INFO) - Progress... 85%\n", - "[13:07:56] Simulation (INFO) - Progress... 86%\n", - "[13:07:56] Simulation (INFO) - Progress... 86%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.65673828125\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9566270309912319\n", - "[13:07:56] Simulation (INFO) - Progress... 86%\n", - "[13:07:56] Simulation (INFO) - Progress... 86%\n", - "[13:07:56] Simulation (INFO) - Progress... 86%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.65673828125\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9566270309912319\n", - "[13:07:56] Simulation (INFO) - Progress... 86%\n", - "[13:07:56] Simulation (INFO) - Progress... 87%\n", - "[13:07:56] Simulation (INFO) - Progress... 87%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.657623291015625\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9566270309912319\n", - "[13:07:56] Simulation (INFO) - Progress... 87%\n", - "[13:07:56] Simulation (INFO) - Progress... 87%\n", - "[13:07:56] Simulation (INFO) - Progress... 87%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.66680908203125\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9557944790732725\n", - "[13:07:56] Simulation (INFO) - Progress... 87%\n", - "[13:07:56] Simulation (INFO) - Progress... 88%\n", - "[13:07:56] Simulation (INFO) - Progress... 88%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.68572998046875\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9549535607073596\n", - "[13:07:56] Simulation (INFO) - Progress... 88%\n", - "[13:07:56] Simulation (INFO) - Progress... 88%\n", - "[13:07:56] Simulation (INFO) - Progress... 88%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.732696533203125\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.954013025700444\n", - "[13:07:56] Simulation (INFO) - Progress... 88%\n", - "[13:07:56] Simulation (INFO) - Progress... 89%\n", - "[13:07:56] Simulation (INFO) - Progress... 89%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.825408935546875\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9530683950725519\n", - "[13:07:56] Simulation (INFO) - Progress... 89%\n", - "[13:07:56] Simulation (INFO) - Progress... 89%\n", - "[13:07:56] Simulation (INFO) - Progress... 89%\n", - "[13:07:56] MPS (INFO) - MPS size (MiB)=0.9710693359375\n", - "[13:07:56] MPS (INFO) - MPS fidelity=0.9521922642825028\n", - "[13:07:56] Simulation (INFO) - Progress... 90%\n", - "[13:07:56] Simulation (INFO) - Progress... 90%\n", - "[13:07:56] Simulation (INFO) - Progress... 90%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.1019287109375\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9512402092586002\n", - "[13:07:57] Simulation (INFO) - Progress... 90%\n", - "[13:07:57] MPS (INFO) - Applying variational optimisation.\n", - "[13:07:57] MPS (INFO) - Fidelity before optimisation=0.9512402092586002\n", - "[13:07:57] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:57] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9577125931210696\n", - "[13:07:57] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:57] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9578860887856777\n", - "[13:07:57] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:57] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9579368838618443\n", - "[13:07:57] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:57] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.957961508212945\n", - "[13:07:57] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:57] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9579759882555418\n", - "[13:07:57] MPS (INFO) - Final fidelity after optimisation=0.9579759882555418\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.1019287109375\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9579759882555418\n", - "[13:07:57] Simulation (INFO) - Progress... 90%\n", - "[13:07:57] Simulation (INFO) - Progress... 90%\n", - "[13:07:57] Simulation (INFO) - Progress... 91%\n", - "[13:07:57] Simulation (INFO) - Progress... 91%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.1019287109375\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9579759882555418\n", - "[13:07:57] Simulation (INFO) - Progress... 91%\n", - "[13:07:57] Simulation (INFO) - Progress... 91%\n", - "[13:07:57] Simulation (INFO) - Progress... 91%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.1019287109375\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9579759882555418\n", - "[13:07:57] Simulation (INFO) - Progress... 91%\n", - "[13:07:57] Simulation (INFO) - Progress... 92%\n", - "[13:07:57] Simulation (INFO) - Progress... 92%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.1019287109375\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.957975988255542\n", - "[13:07:57] Simulation (INFO) - Progress... 92%\n", - "[13:07:57] Simulation (INFO) - Progress... 92%\n", - "[13:07:57] Simulation (INFO) - Progress... 92%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.10516357421875\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9572751185652196\n", - "[13:07:57] Simulation (INFO) - Progress... 92%\n", - "[13:07:57] Simulation (INFO) - Progress... 93%\n", - "[13:07:57] Simulation (INFO) - Progress... 93%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.140625\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9563866601556178\n", - "[13:07:57] Simulation (INFO) - Progress... 93%\n", - "[13:07:57] Simulation (INFO) - Progress... 93%\n", - "[13:07:57] Simulation (INFO) - Progress... 93%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.2252197265625\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9555151328901298\n", - "[13:07:57] Simulation (INFO) - Progress... 93%\n", - "[13:07:57] Simulation (INFO) - Progress... 94%\n", - "[13:07:57] Simulation (INFO) - Progress... 94%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.321044921875\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836132\n", - "[13:07:57] Simulation (INFO) - Progress... 94%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.321044921875\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836132\n", - "[13:07:57] Simulation (INFO) - Progress... 94%\n", - "[13:07:57] Simulation (INFO) - Progress... 94%\n", - "[13:07:57] Simulation (INFO) - Progress... 94%\n", - "[13:07:57] Simulation (INFO) - Progress... 95%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.321044921875\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836132\n", - "[13:07:57] Simulation (INFO) - Progress... 95%\n", - "[13:07:57] Simulation (INFO) - Progress... 95%\n", - "[13:07:57] Simulation (INFO) - Progress... 95%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.321044921875\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836134\n", - "[13:07:57] Simulation (INFO) - Progress... 95%\n", - "[13:07:57] Simulation (INFO) - Progress... 95%\n", - "[13:07:57] Simulation (INFO) - Progress... 96%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.321044921875\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836133\n", - "[13:07:57] Simulation (INFO) - Progress... 96%\n", - "[13:07:57] Simulation (INFO) - Progress... 96%\n", - "[13:07:57] Simulation (INFO) - Progress... 96%\n", - "[13:07:57] MPS (INFO) - MPS size (MiB)=1.32513427734375\n", - "[13:07:57] MPS (INFO) - MPS fidelity=0.9545972393836133\n", - "[13:07:57] Simulation (INFO) - Progress... 96%\n", - "[13:07:57] Simulation (INFO) - Progress... 96%\n", - "[13:07:57] Simulation (INFO) - Progress... 97%\n", - "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", - "[13:07:58] MPS (INFO) - MPS fidelity=0.9537956845053878\n", - "[13:07:58] Simulation (INFO) - Progress... 97%\n", - "[13:07:58] MPS (INFO) - Applying variational optimisation.\n", - "[13:07:58] MPS (INFO) - Fidelity before optimisation=0.9537956845053878\n", - "[13:07:58] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:58] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9557536327031856\n", - "[13:07:58] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:58] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9557817429040746\n", - "[13:07:58] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:58] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9557872759641832\n", - "[13:07:58] MPS (INFO) - Final fidelity after optimisation=0.9557872759641832\n", - "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", - "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641832\n", - "[13:07:58] Simulation (INFO) - Progress... 97%\n", - "[13:07:58] Simulation (INFO) - Progress... 97%\n", - "[13:07:58] Simulation (INFO) - Progress... 97%\n", - "[13:07:58] Simulation (INFO) - Progress... 97%\n", - "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", - "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641832\n", - "[13:07:58] Simulation (INFO) - Progress... 98%\n", - "[13:07:58] Simulation (INFO) - Progress... 98%\n", - "[13:07:58] Simulation (INFO) - Progress... 98%\n", - "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", - "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641832\n", - "[13:07:58] Simulation (INFO) - Progress... 98%\n", - "[13:07:58] Simulation (INFO) - Progress... 98%\n", - "[13:07:58] Simulation (INFO) - Progress... 98%\n", - "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", - "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641832\n", - "[13:07:58] Simulation (INFO) - Progress... 99%\n", - "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", - "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641831\n", - "[13:07:58] Simulation (INFO) - Progress... 99%\n", - "[13:07:58] Simulation (INFO) - Progress... 99%\n", - "[13:07:58] Simulation (INFO) - Progress... 99%\n", - "[13:07:58] MPS (INFO) - MPS size (MiB)=1.3577880859375\n", - "[13:07:58] MPS (INFO) - MPS fidelity=0.9557872759641831\n", - "[13:07:58] Simulation (INFO) - Progress... 99%\n", - "[13:07:58] MPS (INFO) - Applying variational optimisation.\n", - "[13:07:58] MPS (INFO) - Fidelity before optimisation=0.9557872759641831\n", - "[13:07:58] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:58] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9557872759641844\n", - "[13:07:58] MPS (INFO) - Doing another optimisation sweep...\n", - "[13:07:58] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9557872759641826\n", - "[13:07:58] MPS (INFO) - Final fidelity after optimisation=0.9557872759641826\n", - "[13:07:58] Simulation (INFO) - Simulation completed.\n", - "[13:07:58] Simulation (INFO) - Final TNState size=1.3577880859375 MiB\n", - "[13:07:58] Simulation (INFO) - Final TNState fidelity=0.9557872759641826\n" + "[10:54:19] Simulation (INFO) - Ordering the gates in the circuit to reduce canonicalisation overhead.\n", + "[10:54:19] Simulation (INFO) - Running simulation...\n", + "[10:54:19] Simulation (INFO) - Progress... 0%\n", + "[10:54:19] Simulation (INFO) - Progress... 0%\n", + "[10:54:19] Simulation (INFO) - Progress... 0%\n", + "[10:54:19] Simulation (INFO) - Progress... 0%\n", + "[10:54:19] Simulation (INFO) - Progress... 0%\n", + "[10:54:19] Simulation (INFO) - Progress... 0%\n", + "[10:54:19] Simulation (INFO) - Progress... 1%\n", + "[10:54:19] Simulation (INFO) - Progress... 1%\n", + "[10:54:19] Simulation (INFO) - Progress... 1%\n", + "[10:54:19] Simulation (INFO) - Progress... 1%\n", + "[10:54:19] Simulation (INFO) - Progress... 1%\n", + "[10:54:19] Simulation (INFO) - Progress... 1%\n", + "[10:54:19] Simulation (INFO) - Progress... 2%\n", + "[10:54:19] Simulation (INFO) - Progress... 2%\n", + "[10:54:19] Simulation (INFO) - Progress... 2%\n", + "[10:54:19] Simulation (INFO) - Progress... 2%\n", + "[10:54:19] Simulation (INFO) - Progress... 2%\n", + "[10:54:19] Simulation (INFO) - Progress... 2%\n", + "[10:54:19] Simulation (INFO) - Progress... 3%\n", + "[10:54:19] Simulation (INFO) - Progress... 3%\n", + "[10:54:19] MPS (INFO) - MPS size (MiB)=0.00067138671875\n", + "[10:54:19] MPS (INFO) - MPS fidelity=1.0\n", + "[10:54:19] Simulation (INFO) - Progress... 3%\n", + "[10:54:19] Simulation (INFO) - Progress... 3%\n", + "[10:54:19] Simulation (INFO) - Progress... 3%\n", + "[10:54:19] Simulation (INFO) - Progress... 3%\n", + "[10:54:19] MPS (INFO) - MPS size (MiB)=0.000732421875\n", + "[10:54:19] MPS (INFO) - MPS fidelity=1.0\n", + "[10:54:19] Simulation (INFO) - Progress... 4%\n", + "[10:54:19] Simulation (INFO) - Progress... 4%\n", + "[10:54:19] Simulation (INFO) - Progress... 4%\n", + "[10:54:19] MPS (INFO) - MPS size (MiB)=0.0008544921875\n", + "[10:54:19] MPS (INFO) - MPS fidelity=0.9999999999999998\n", + "[10:54:19] Simulation (INFO) - Progress... 4%\n", + "[10:54:19] Simulation (INFO) - Progress... 4%\n", + "[10:54:19] Simulation (INFO) - Progress... 4%\n", + "[10:54:19] MPS (INFO) - MPS size (MiB)=0.0008544921875\n", + "[10:54:19] MPS (INFO) - MPS fidelity=0.9999999999999998\n", + "[10:54:19] Simulation (INFO) - Progress... 5%\n", + "[10:54:19] Simulation (INFO) - Progress... 5%\n", + "[10:54:20] Simulation (INFO) - Progress... 5%\n", + "[10:54:20] Simulation (INFO) - Progress... 5%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00091552734375\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9999999999999998\n", + "[10:54:20] Simulation (INFO) - Progress... 5%\n", + "[10:54:20] Simulation (INFO) - Progress... 5%\n", + "[10:54:20] Simulation (INFO) - Progress... 6%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00103759765625\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9999999999999996\n", + "[10:54:20] Simulation (INFO) - Progress... 6%\n", + "[10:54:20] Simulation (INFO) - Progress... 6%\n", + "[10:54:20] Simulation (INFO) - Progress... 6%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00128173828125\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9999999999999996\n", + "[10:54:20] Simulation (INFO) - Progress... 6%\n", + "[10:54:20] Simulation (INFO) - Progress... 6%\n", + "[10:54:20] Simulation (INFO) - Progress... 7%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00164794921875\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9999999999999996\n", + "[10:54:20] Simulation (INFO) - Progress... 7%\n", + "[10:54:20] Simulation (INFO) - Progress... 7%\n", + "[10:54:20] Simulation (INFO) - Progress... 7%\n", + "[10:54:20] MPS (INFO) - Applying variational optimisation.\n", + "[10:54:20] MPS (INFO) - Fidelity before optimisation=0.9999999999999996\n", + "[10:54:20] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=1.0000000000000007\n", + "[10:54:20] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9999999999999998\n", + "[10:54:20] MPS (INFO) - Final fidelity after optimisation=0.9999999999999998\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00164794921875\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9999999999999998\n", + "[10:54:20] Simulation (INFO) - Progress... 7%\n", + "[10:54:20] Simulation (INFO) - Progress... 7%\n", + "[10:54:20] Simulation (INFO) - Progress... 8%\n", + "[10:54:20] Simulation (INFO) - Progress... 8%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.001708984375\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9999999999999998\n", + "[10:54:20] Simulation (INFO) - Progress... 8%\n", + "[10:54:20] Simulation (INFO) - Progress... 8%\n", + "[10:54:20] Simulation (INFO) - Progress... 8%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.0018310546875\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9999999999999998\n", + "[10:54:20] Simulation (INFO) - Progress... 8%\n", + "[10:54:20] Simulation (INFO) - Progress... 9%\n", + "[10:54:20] Simulation (INFO) - Progress... 9%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.0018310546875\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9991314405479996\n", + "[10:54:20] Simulation (INFO) - Progress... 9%\n", + "[10:54:20] Simulation (INFO) - Progress... 9%\n", + "[10:54:20] Simulation (INFO) - Progress... 9%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.002197265625\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9991314405479996\n", + "[10:54:20] Simulation (INFO) - Progress... 10%\n", + "[10:54:20] Simulation (INFO) - Progress... 10%\n", + "[10:54:20] Simulation (INFO) - Progress... 10%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.0029296875\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.99827508368156\n", + "[10:54:20] Simulation (INFO) - Progress... 10%\n", + "[10:54:20] Simulation (INFO) - Progress... 10%\n", + "[10:54:20] Simulation (INFO) - Progress... 10%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.0029296875\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.99827508368156\n", + "[10:54:20] Simulation (INFO) - Progress... 11%\n", + "[10:54:20] Simulation (INFO) - Progress... 11%\n", + "[10:54:20] Simulation (INFO) - Progress... 11%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.0029296875\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.99827508368156\n", + "[10:54:20] Simulation (INFO) - Progress... 11%\n", + "[10:54:20] Simulation (INFO) - Progress... 11%\n", + "[10:54:20] Simulation (INFO) - Progress... 11%\n", + "[10:54:20] Simulation (INFO) - Progress... 12%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00299072265625\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.99827508368156\n", + "[10:54:20] Simulation (INFO) - Progress... 12%\n", + "[10:54:20] Simulation (INFO) - Progress... 12%\n", + "[10:54:20] Simulation (INFO) - Progress... 12%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00311279296875\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9982750836815598\n", + "[10:54:20] Simulation (INFO) - Progress... 12%\n", + "[10:54:20] Simulation (INFO) - Progress... 12%\n", + "[10:54:20] Simulation (INFO) - Progress... 13%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00335693359375\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9982750836815598\n", + "[10:54:20] Simulation (INFO) - Progress... 13%\n", + "[10:54:20] Simulation (INFO) - Progress... 13%\n", + "[10:54:20] Simulation (INFO) - Progress... 13%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00372314453125\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9982750836815598\n", + "[10:54:20] Simulation (INFO) - Progress... 13%\n", + "[10:54:20] Simulation (INFO) - Progress... 13%\n", + "[10:54:20] Simulation (INFO) - Progress... 14%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00421142578125\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9982750836815596\n", + "[10:54:20] Simulation (INFO) - Progress... 14%\n", + "[10:54:20] Simulation (INFO) - Progress... 14%\n", + "[10:54:20] Simulation (INFO) - Progress... 14%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00555419921875\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9982750836815596\n", + "[10:54:20] Simulation (INFO) - Progress... 14%\n", + "[10:54:20] Simulation (INFO) - Progress... 14%\n", + "[10:54:20] Simulation (INFO) - Progress... 15%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00592041015625\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9982750836815596\n", + "[10:54:20] Simulation (INFO) - Progress... 15%\n", + "[10:54:20] Simulation (INFO) - Progress... 15%\n", + "[10:54:20] Simulation (INFO) - Progress... 15%\n", + "[10:54:20] MPS (INFO) - MPS size (MiB)=0.00592041015625\n", + "[10:54:20] MPS (INFO) - MPS fidelity=0.9982750836815596\n", + "[10:54:20] Simulation (INFO) - Progress... 15%\n", + "[10:54:20] Simulation (INFO) - Progress... 15%\n", + "[10:54:20] Simulation (INFO) - Progress... 16%\n", + "[10:54:20] MPS (INFO) - Applying variational optimisation.\n", + "[10:54:20] MPS (INFO) - Fidelity before optimisation=0.9982750836815596\n", + "[10:54:20] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:20] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9993723442011109\n", + "[10:54:20] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:21] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.999372476136822\n", + "[10:54:21] MPS (INFO) - Final fidelity after optimisation=0.999372476136822\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.00592041015625\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.999372476136822\n", + "[10:54:21] Simulation (INFO) - Progress... 16%\n", + "[10:54:21] Simulation (INFO) - Progress... 16%\n", + "[10:54:21] Simulation (INFO) - Progress... 16%\n", + "[10:54:21] Simulation (INFO) - Progress... 16%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.0059814453125\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.999372476136822\n", + "[10:54:21] Simulation (INFO) - Progress... 16%\n", + "[10:54:21] Simulation (INFO) - Progress... 17%\n", + "[10:54:21] Simulation (INFO) - Progress... 17%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.006103515625\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.999372476136822\n", + "[10:54:21] Simulation (INFO) - Progress... 17%\n", + "[10:54:21] Simulation (INFO) - Progress... 17%\n", + "[10:54:21] Simulation (INFO) - Progress... 17%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.006103515625\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9993622074384583\n", + "[10:54:21] Simulation (INFO) - Progress... 17%\n", + "[10:54:21] Simulation (INFO) - Progress... 18%\n", + "[10:54:21] Simulation (INFO) - Progress... 18%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.0064697265625\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9993622074384583\n", + "[10:54:21] Simulation (INFO) - Progress... 18%\n", + "[10:54:21] Simulation (INFO) - Progress... 18%\n", + "[10:54:21] Simulation (INFO) - Progress... 18%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.0067138671875\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9990943548110807\n", + "[10:54:21] Simulation (INFO) - Progress... 18%\n", + "[10:54:21] Simulation (INFO) - Progress... 19%\n", + "[10:54:21] Simulation (INFO) - Progress... 19%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.00726318359375\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9983440200561307\n", + "[10:54:21] Simulation (INFO) - Progress... 19%\n", + "[10:54:21] Simulation (INFO) - Progress... 19%\n", + "[10:54:21] Simulation (INFO) - Progress... 19%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.00897216796875\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9983440200561309\n", + "[10:54:21] Simulation (INFO) - Progress... 20%\n", + "[10:54:21] Simulation (INFO) - Progress... 20%\n", + "[10:54:21] Simulation (INFO) - Progress... 20%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.01043701171875\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9976943520568935\n", + "[10:54:21] Simulation (INFO) - Progress... 20%\n", + "[10:54:21] Simulation (INFO) - Progress... 20%\n", + "[10:54:21] Simulation (INFO) - Progress... 20%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9967391001964958\n", + "[10:54:21] Simulation (INFO) - Progress... 21%\n", + "[10:54:21] Simulation (INFO) - Progress... 21%\n", + "[10:54:21] Simulation (INFO) - Progress... 21%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9967391001964958\n", + "[10:54:21] Simulation (INFO) - Progress... 21%\n", + "[10:54:21] Simulation (INFO) - Progress... 21%\n", + "[10:54:21] Simulation (INFO) - Progress... 21%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.009979248046875\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9967391001964958\n", + "[10:54:21] Simulation (INFO) - Progress... 22%\n", + "[10:54:21] Simulation (INFO) - Progress... 22%\n", + "[10:54:21] Simulation (INFO) - Progress... 22%\n", + "[10:54:21] Simulation (INFO) - Progress... 22%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.010040283203125\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9967391001964958\n", + "[10:54:21] Simulation (INFO) - Progress... 22%\n", + "[10:54:21] Simulation (INFO) - Progress... 22%\n", + "[10:54:21] Simulation (INFO) - Progress... 23%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.010162353515625\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9967391001964954\n", + "[10:54:21] Simulation (INFO) - Progress... 23%\n", + "[10:54:21] Simulation (INFO) - Progress... 23%\n", + "[10:54:21] Simulation (INFO) - Progress... 23%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.010284423828125\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9966643286899265\n", + "[10:54:21] Simulation (INFO) - Progress... 23%\n", + "[10:54:21] Simulation (INFO) - Progress... 23%\n", + "[10:54:21] Simulation (INFO) - Progress... 24%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.01043701171875\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9964837554756931\n", + "[10:54:21] Simulation (INFO) - Progress... 24%\n", + "[10:54:21] Simulation (INFO) - Progress... 24%\n", + "[10:54:21] Simulation (INFO) - Progress... 24%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.0108642578125\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9964837554756929\n", + "[10:54:21] Simulation (INFO) - Progress... 24%\n", + "[10:54:21] Simulation (INFO) - Progress... 24%\n", + "[10:54:21] Simulation (INFO) - Progress... 25%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.011688232421875\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9961589960138393\n", + "[10:54:21] Simulation (INFO) - Progress... 25%\n", + "[10:54:21] Simulation (INFO) - Progress... 25%\n", + "[10:54:21] Simulation (INFO) - Progress... 25%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.012481689453125\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9957142860371432\n", + "[10:54:21] Simulation (INFO) - Progress... 25%\n", + "[10:54:21] Simulation (INFO) - Progress... 25%\n", + "[10:54:21] Simulation (INFO) - Progress... 26%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.0147705078125\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9951708417671544\n", + "[10:54:21] Simulation (INFO) - Progress... 26%\n", + "[10:54:21] Simulation (INFO) - Progress... 26%\n", + "[10:54:21] Simulation (INFO) - Progress... 26%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.01812744140625\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9943929555227694\n", + "[10:54:21] Simulation (INFO) - Progress... 26%\n", + "[10:54:21] Simulation (INFO) - Progress... 26%\n", + "[10:54:21] Simulation (INFO) - Progress... 27%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.01934814453125\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9939203608165895\n", + "[10:54:21] Simulation (INFO) - Progress... 27%\n", + "[10:54:21] Simulation (INFO) - Progress... 27%\n", + "[10:54:21] Simulation (INFO) - Progress... 27%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.019866943359375\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9939203608165895\n", + "[10:54:21] Simulation (INFO) - Progress... 27%\n", + "[10:54:21] Simulation (INFO) - Progress... 27%\n", + "[10:54:21] Simulation (INFO) - Progress... 28%\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.019866943359375\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9939203608165895\n", + "[10:54:21] Simulation (INFO) - Progress... 28%\n", + "[10:54:21] Simulation (INFO) - Progress... 28%\n", + "[10:54:21] Simulation (INFO) - Progress... 28%\n", + "[10:54:21] MPS (INFO) - Applying variational optimisation.\n", + "[10:54:21] MPS (INFO) - Fidelity before optimisation=0.9939203608165895\n", + "[10:54:21] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:21] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9955357632585216\n", + "[10:54:21] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:21] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9955702116444642\n", + "[10:54:21] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:21] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9955834041401609\n", + "[10:54:21] MPS (INFO) - Final fidelity after optimisation=0.9955834041401609\n", + "[10:54:21] MPS (INFO) - MPS size (MiB)=0.019866943359375\n", + "[10:54:21] MPS (INFO) - MPS fidelity=0.9955834041401609\n", + "[10:54:21] Simulation (INFO) - Progress... 28%\n", + "[10:54:22] Simulation (INFO) - Progress... 28%\n", + "[10:54:22] Simulation (INFO) - Progress... 29%\n", + "[10:54:22] Simulation (INFO) - Progress... 29%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.019927978515625\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9955834041401609\n", + "[10:54:22] Simulation (INFO) - Progress... 29%\n", + "[10:54:22] Simulation (INFO) - Progress... 29%\n", + "[10:54:22] Simulation (INFO) - Progress... 29%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.020050048828125\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9955834041401609\n", + "[10:54:22] Simulation (INFO) - Progress... 30%\n", + "[10:54:22] Simulation (INFO) - Progress... 30%\n", + "[10:54:22] Simulation (INFO) - Progress... 30%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.020294189453125\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9955834041401609\n", + "[10:54:22] Simulation (INFO) - Progress... 30%\n", + "[10:54:22] Simulation (INFO) - Progress... 30%\n", + "[10:54:22] Simulation (INFO) - Progress... 30%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.020721435546875\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9955834041401607\n", + "[10:54:22] Simulation (INFO) - Progress... 31%\n", + "[10:54:22] Simulation (INFO) - Progress... 31%\n", + "[10:54:22] Simulation (INFO) - Progress... 31%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.02093505859375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9951041950080444\n", + "[10:54:22] Simulation (INFO) - Progress... 31%\n", + "[10:54:22] Simulation (INFO) - Progress... 31%\n", + "[10:54:22] Simulation (INFO) - Progress... 31%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.02142333984375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9945177435627294\n", + "[10:54:22] Simulation (INFO) - Progress... 32%\n", + "[10:54:22] Simulation (INFO) - Progress... 32%\n", + "[10:54:22] Simulation (INFO) - Progress... 32%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.02215576171875\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9940411739284708\n", + "[10:54:22] Simulation (INFO) - Progress... 32%\n", + "[10:54:22] Simulation (INFO) - Progress... 32%\n", + "[10:54:22] Simulation (INFO) - Progress... 32%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.02294921875\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9935646527445069\n", + "[10:54:22] Simulation (INFO) - Progress... 33%\n", + "[10:54:22] Simulation (INFO) - Progress... 33%\n", + "[10:54:22] Simulation (INFO) - Progress... 33%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.025390625\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9928113022215258\n", + "[10:54:22] Simulation (INFO) - Progress... 33%\n", + "[10:54:22] Simulation (INFO) - Progress... 33%\n", + "[10:54:22] Simulation (INFO) - Progress... 33%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.029052734375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9919283402969962\n", + "[10:54:22] Simulation (INFO) - Progress... 34%\n", + "[10:54:22] Simulation (INFO) - Progress... 34%\n", + "[10:54:22] Simulation (INFO) - Progress... 34%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.0325927734375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9912000768171119\n", + "[10:54:22] Simulation (INFO) - Progress... 34%\n", + "[10:54:22] Simulation (INFO) - Progress... 34%\n", + "[10:54:22] Simulation (INFO) - Progress... 34%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.03411865234375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9905931522444028\n", + "[10:54:22] Simulation (INFO) - Progress... 35%\n", + "[10:54:22] Simulation (INFO) - Progress... 35%\n", + "[10:54:22] Simulation (INFO) - Progress... 35%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.03411865234375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9905931522444028\n", + "[10:54:22] Simulation (INFO) - Progress... 35%\n", + "[10:54:22] Simulation (INFO) - Progress... 35%\n", + "[10:54:22] Simulation (INFO) - Progress... 35%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.03411865234375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9905931522444028\n", + "[10:54:22] Simulation (INFO) - Progress... 36%\n", + "[10:54:22] Simulation (INFO) - Progress... 36%\n", + "[10:54:22] Simulation (INFO) - Progress... 36%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.03411865234375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9905931522444028\n", + "[10:54:22] Simulation (INFO) - Progress... 36%\n", + "[10:54:22] Simulation (INFO) - Progress... 36%\n", + "[10:54:22] Simulation (INFO) - Progress... 36%\n", + "[10:54:22] Simulation (INFO) - Progress... 37%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.0341796875\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9905931522444028\n", + "[10:54:22] Simulation (INFO) - Progress... 37%\n", + "[10:54:22] Simulation (INFO) - Progress... 37%\n", + "[10:54:22] Simulation (INFO) - Progress... 37%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.0343017578125\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9905931522444028\n", + "[10:54:22] Simulation (INFO) - Progress... 37%\n", + "[10:54:22] Simulation (INFO) - Progress... 37%\n", + "[10:54:22] Simulation (INFO) - Progress... 38%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.0345458984375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9905931522444028\n", + "[10:54:22] Simulation (INFO) - Progress... 38%\n", + "[10:54:22] Simulation (INFO) - Progress... 38%\n", + "[10:54:22] Simulation (INFO) - Progress... 38%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.0350341796875\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9905931522444028\n", + "[10:54:22] Simulation (INFO) - Progress... 38%\n", + "[10:54:22] Simulation (INFO) - Progress... 38%\n", + "[10:54:22] Simulation (INFO) - Progress... 39%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.0352783203125\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9899877288151842\n", + "[10:54:22] Simulation (INFO) - Progress... 39%\n", + "[10:54:22] Simulation (INFO) - Progress... 39%\n", + "[10:54:22] Simulation (INFO) - Progress... 39%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.03582763671875\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9897933677203272\n", + "[10:54:22] Simulation (INFO) - Progress... 39%\n", + "[10:54:22] Simulation (INFO) - Progress... 40%\n", + "[10:54:22] Simulation (INFO) - Progress... 40%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.0364990234375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9893753998018908\n", + "[10:54:22] Simulation (INFO) - Progress... 40%\n", + "[10:54:22] Simulation (INFO) - Progress... 40%\n", + "[10:54:22] Simulation (INFO) - Progress... 40%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.03759765625\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9891162458703916\n", + "[10:54:22] Simulation (INFO) - Progress... 40%\n", + "[10:54:22] Simulation (INFO) - Progress... 41%\n", + "[10:54:22] Simulation (INFO) - Progress... 41%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.039154052734375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9888021996671446\n", + "[10:54:22] Simulation (INFO) - Progress... 41%\n", + "[10:54:22] Simulation (INFO) - Progress... 41%\n", + "[10:54:22] Simulation (INFO) - Progress... 41%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.041595458984375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9883552975481354\n", + "[10:54:22] Simulation (INFO) - Progress... 41%\n", + "[10:54:22] Simulation (INFO) - Progress... 42%\n", + "[10:54:22] Simulation (INFO) - Progress... 42%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.046905517578125\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9876998537193106\n", + "[10:54:22] Simulation (INFO) - Progress... 42%\n", + "[10:54:22] Simulation (INFO) - Progress... 42%\n", + "[10:54:22] Simulation (INFO) - Progress... 42%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.048980712890625\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9869919118956609\n", + "[10:54:22] Simulation (INFO) - Progress... 42%\n", + "[10:54:22] Simulation (INFO) - Progress... 43%\n", + "[10:54:22] Simulation (INFO) - Progress... 43%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.057037353515625\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9863296634721933\n", + "[10:54:22] Simulation (INFO) - Progress... 43%\n", + "[10:54:22] Simulation (INFO) - Progress... 43%\n", + "[10:54:22] Simulation (INFO) - Progress... 43%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.05804443359375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9863296634721933\n", + "[10:54:22] Simulation (INFO) - Progress... 43%\n", + "[10:54:22] Simulation (INFO) - Progress... 44%\n", + "[10:54:22] Simulation (INFO) - Progress... 44%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.05804443359375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9863296634721933\n", + "[10:54:22] Simulation (INFO) - Progress... 44%\n", + "[10:54:22] Simulation (INFO) - Progress... 44%\n", + "[10:54:22] Simulation (INFO) - Progress... 44%\n", + "[10:54:22] MPS (INFO) - MPS size (MiB)=0.05804443359375\n", + "[10:54:22] MPS (INFO) - MPS fidelity=0.9863296634721933\n", + "[10:54:22] Simulation (INFO) - Progress... 44%\n", + "[10:54:22] Simulation (INFO) - Progress... 45%\n", + "[10:54:22] Simulation (INFO) - Progress... 45%\n", + "[10:54:22] MPS (INFO) - Applying variational optimisation.\n", + "[10:54:22] MPS (INFO) - Fidelity before optimisation=0.9863296634721933\n", + "[10:54:22] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:22] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9890505221400125\n", + "[10:54:22] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:22] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9890889999983853\n", + "[10:54:22] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:23] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9890986740334062\n", + "[10:54:23] MPS (INFO) - Final fidelity after optimisation=0.9890986740334062\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.05804443359375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9890986740334062\n", + "[10:54:23] Simulation (INFO) - Progress... 45%\n", + "[10:54:23] Simulation (INFO) - Progress... 45%\n", + "[10:54:23] Simulation (INFO) - Progress... 45%\n", + "[10:54:23] Simulation (INFO) - Progress... 45%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.05810546875\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9890986740334062\n", + "[10:54:23] Simulation (INFO) - Progress... 46%\n", + "[10:54:23] Simulation (INFO) - Progress... 46%\n", + "[10:54:23] Simulation (INFO) - Progress... 46%\n", + "[10:54:23] Simulation (INFO) - Progress... 46%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.0582275390625\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9890986740334062\n", + "[10:54:23] Simulation (INFO) - Progress... 46%\n", + "[10:54:23] Simulation (INFO) - Progress... 46%\n", + "[10:54:23] Simulation (INFO) - Progress... 47%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.0584716796875\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9890986740334062\n", + "[10:54:23] Simulation (INFO) - Progress... 47%\n", + "[10:54:23] Simulation (INFO) - Progress... 47%\n", + "[10:54:23] Simulation (INFO) - Progress... 47%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.0589599609375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9890986740334062\n", + "[10:54:23] Simulation (INFO) - Progress... 47%\n", + "[10:54:23] Simulation (INFO) - Progress... 47%\n", + "[10:54:23] Simulation (INFO) - Progress... 48%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.0596923828125\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9885483797287798\n", + "[10:54:23] Simulation (INFO) - Progress... 48%\n", + "[10:54:23] Simulation (INFO) - Progress... 48%\n", + "[10:54:23] Simulation (INFO) - Progress... 48%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.0611572265625\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9885483797287798\n", + "[10:54:23] Simulation (INFO) - Progress... 48%\n", + "[10:54:23] Simulation (INFO) - Progress... 48%\n", + "[10:54:23] Simulation (INFO) - Progress... 49%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.06201171875\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9881174872282237\n", + "[10:54:23] Simulation (INFO) - Progress... 49%\n", + "[10:54:23] Simulation (INFO) - Progress... 49%\n", + "[10:54:23] Simulation (INFO) - Progress... 49%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.063201904296875\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9875262708276239\n", + "[10:54:23] Simulation (INFO) - Progress... 49%\n", + "[10:54:23] Simulation (INFO) - Progress... 50%\n", + "[10:54:23] Simulation (INFO) - Progress... 50%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.065277099609375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.986686602346878\n", + "[10:54:23] Simulation (INFO) - Progress... 50%\n", + "[10:54:23] Simulation (INFO) - Progress... 50%\n", + "[10:54:23] Simulation (INFO) - Progress... 50%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.06817626953125\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9857355073118286\n", + "[10:54:23] Simulation (INFO) - Progress... 50%\n", + "[10:54:23] Simulation (INFO) - Progress... 51%\n", + "[10:54:23] Simulation (INFO) - Progress... 51%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.07293701171875\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9851580066063106\n", + "[10:54:23] Simulation (INFO) - Progress... 51%\n", + "[10:54:23] Simulation (INFO) - Progress... 51%\n", + "[10:54:23] Simulation (INFO) - Progress... 51%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.08172607421875\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9842229282821433\n", + "[10:54:23] Simulation (INFO) - Progress... 51%\n", + "[10:54:23] Simulation (INFO) - Progress... 52%\n", + "[10:54:23] Simulation (INFO) - Progress... 52%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.09393310546875\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9834526830890171\n", + "[10:54:23] Simulation (INFO) - Progress... 52%\n", + "[10:54:23] Simulation (INFO) - Progress... 52%\n", + "[10:54:23] Simulation (INFO) - Progress... 52%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.11138916015625\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9825624978338594\n", + "[10:54:23] Simulation (INFO) - Progress... 52%\n", + "[10:54:23] Simulation (INFO) - Progress... 53%\n", + "[10:54:23] Simulation (INFO) - Progress... 53%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.118255615234375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9817993631113072\n", + "[10:54:23] Simulation (INFO) - Progress... 53%\n", + "[10:54:23] Simulation (INFO) - Progress... 53%\n", + "[10:54:23] Simulation (INFO) - Progress... 53%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.118255615234375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9817993631113072\n", + "[10:54:23] Simulation (INFO) - Progress... 53%\n", + "[10:54:23] Simulation (INFO) - Progress... 54%\n", + "[10:54:23] Simulation (INFO) - Progress... 54%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.118255615234375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9817993631113072\n", + "[10:54:23] Simulation (INFO) - Progress... 54%\n", + "[10:54:23] Simulation (INFO) - Progress... 54%\n", + "[10:54:23] Simulation (INFO) - Progress... 54%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.118255615234375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9817993631113072\n", + "[10:54:23] Simulation (INFO) - Progress... 54%\n", + "[10:54:23] Simulation (INFO) - Progress... 55%\n", + "[10:54:23] Simulation (INFO) - Progress... 55%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.118255615234375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9817993631113072\n", + "[10:54:23] Simulation (INFO) - Progress... 55%\n", + "[10:54:23] Simulation (INFO) - Progress... 55%\n", + "[10:54:23] Simulation (INFO) - Progress... 55%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.118255615234375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9817993631113072\n", + "[10:54:23] Simulation (INFO) - Progress... 55%\n", + "[10:54:23] Simulation (INFO) - Progress... 56%\n", + "[10:54:23] Simulation (INFO) - Progress... 56%\n", + "[10:54:23] Simulation (INFO) - Progress... 56%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.118621826171875\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9817993631113072\n", + "[10:54:23] Simulation (INFO) - Progress... 56%\n", + "[10:54:23] Simulation (INFO) - Progress... 56%\n", + "[10:54:23] Simulation (INFO) - Progress... 56%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.118621826171875\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9816317612554827\n", + "[10:54:23] Simulation (INFO) - Progress... 57%\n", + "[10:54:23] Simulation (INFO) - Progress... 57%\n", + "[10:54:23] Simulation (INFO) - Progress... 57%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.119964599609375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9816317612554827\n", + "[10:54:23] Simulation (INFO) - Progress... 57%\n", + "[10:54:23] Simulation (INFO) - Progress... 57%\n", + "[10:54:23] Simulation (INFO) - Progress... 57%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.120941162109375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9810389197426292\n", + "[10:54:23] Simulation (INFO) - Progress... 58%\n", + "[10:54:23] Simulation (INFO) - Progress... 58%\n", + "[10:54:23] Simulation (INFO) - Progress... 58%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.120941162109375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9804444980353678\n", + "[10:54:23] Simulation (INFO) - Progress... 58%\n", + "[10:54:23] Simulation (INFO) - Progress... 58%\n", + "[10:54:23] Simulation (INFO) - Progress... 58%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.121978759765625\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9796966778644127\n", + "[10:54:23] Simulation (INFO) - Progress... 59%\n", + "[10:54:23] Simulation (INFO) - Progress... 59%\n", + "[10:54:23] Simulation (INFO) - Progress... 59%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.12255859375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.978836271988995\n", + "[10:54:23] Simulation (INFO) - Progress... 59%\n", + "[10:54:23] Simulation (INFO) - Progress... 59%\n", + "[10:54:23] Simulation (INFO) - Progress... 60%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.126068115234375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9781525336471664\n", + "[10:54:23] Simulation (INFO) - Progress... 60%\n", + "[10:54:23] Simulation (INFO) - Progress... 60%\n", + "[10:54:23] Simulation (INFO) - Progress... 60%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.132476806640625\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9771848210545127\n", + "[10:54:23] Simulation (INFO) - Progress... 60%\n", + "[10:54:23] Simulation (INFO) - Progress... 60%\n", + "[10:54:23] Simulation (INFO) - Progress... 61%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.145294189453125\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9764776315208572\n", + "[10:54:23] Simulation (INFO) - Progress... 61%\n", + "[10:54:23] Simulation (INFO) - Progress... 61%\n", + "[10:54:23] Simulation (INFO) - Progress... 61%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.164337158203125\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9756166145051132\n", + "[10:54:23] Simulation (INFO) - Progress... 61%\n", + "[10:54:23] Simulation (INFO) - Progress... 61%\n", + "[10:54:23] Simulation (INFO) - Progress... 62%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.195098876953125\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9748320300628435\n", + "[10:54:23] Simulation (INFO) - Progress... 62%\n", + "[10:54:23] Simulation (INFO) - Progress... 62%\n", + "[10:54:23] Simulation (INFO) - Progress... 62%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.23297119140625\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9739764573652526\n", + "[10:54:23] Simulation (INFO) - Progress... 62%\n", + "[10:54:23] Simulation (INFO) - Progress... 62%\n", + "[10:54:23] Simulation (INFO) - Progress... 63%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.23675537109375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9739764573652526\n", + "[10:54:23] Simulation (INFO) - Progress... 63%\n", + "[10:54:23] Simulation (INFO) - Progress... 63%\n", + "[10:54:23] Simulation (INFO) - Progress... 63%\n", + "[10:54:23] MPS (INFO) - MPS size (MiB)=0.23675537109375\n", + "[10:54:23] MPS (INFO) - MPS fidelity=0.9739764573652526\n", + "[10:54:23] Simulation (INFO) - Progress... 63%\n", + "[10:54:23] Simulation (INFO) - Progress... 63%\n", + "[10:54:23] Simulation (INFO) - Progress... 64%\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.23675537109375\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.9739764573652526\n", + "[10:54:24] Simulation (INFO) - Progress... 64%\n", + "[10:54:24] Simulation (INFO) - Progress... 64%\n", + "[10:54:24] Simulation (INFO) - Progress... 64%\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.23675537109375\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.9739764573652526\n", + "[10:54:24] Simulation (INFO) - Progress... 64%\n", + "[10:54:24] MPS (INFO) - Applying variational optimisation.\n", + "[10:54:24] MPS (INFO) - Fidelity before optimisation=0.9739764573652526\n", + "[10:54:24] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:24] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9782513874640462\n", + "[10:54:24] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:24] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9784099788957649\n", + "[10:54:24] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:24] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9784751549174159\n", + "[10:54:24] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:24] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9785148389318197\n", + "[10:54:24] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:24] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9785415657315543\n", + "[10:54:24] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:24] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9785601642481355\n", + "[10:54:24] MPS (INFO) - Final fidelity after optimisation=0.9785601642481355\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.23675537109375\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.9785601642481355\n", + "[10:54:24] Simulation (INFO) - Progress... 64%\n", + "[10:54:24] Simulation (INFO) - Progress... 65%\n", + "[10:54:24] Simulation (INFO) - Progress... 65%\n", + "[10:54:24] Simulation (INFO) - Progress... 65%\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.23675537109375\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.9785601642481355\n", + "[10:54:24] Simulation (INFO) - Progress... 65%\n", + "[10:54:24] Simulation (INFO) - Progress... 65%\n", + "[10:54:24] Simulation (INFO) - Progress... 65%\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.23748779296875\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.9782826660913593\n", + "[10:54:24] Simulation (INFO) - Progress... 66%\n", + "[10:54:24] Simulation (INFO) - Progress... 66%\n", + "[10:54:24] Simulation (INFO) - Progress... 66%\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.237945556640625\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.9777305146598142\n", + "[10:54:24] Simulation (INFO) - Progress... 66%\n", + "[10:54:24] Simulation (INFO) - Progress... 66%\n", + "[10:54:24] Simulation (INFO) - Progress... 66%\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.238983154296875\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.9767619685870496\n", + "[10:54:24] Simulation (INFO) - Progress... 67%\n", + "[10:54:24] Simulation (INFO) - Progress... 67%\n", + "[10:54:24] Simulation (INFO) - Progress... 67%\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.241424560546875\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.9759359374381807\n", + "[10:54:24] Simulation (INFO) - Progress... 67%\n", + "[10:54:24] Simulation (INFO) - Progress... 67%\n", + "[10:54:24] Simulation (INFO) - Progress... 67%\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.244781494140625\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.975384275853116\n", + "[10:54:24] Simulation (INFO) - Progress... 68%\n", + "[10:54:24] Simulation (INFO) - Progress... 68%\n", + "[10:54:24] Simulation (INFO) - Progress... 68%\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.24920654296875\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.9748864247538034\n", + "[10:54:24] Simulation (INFO) - Progress... 68%\n", + "[10:54:24] Simulation (INFO) - Progress... 68%\n", + "[10:54:24] Simulation (INFO) - Progress... 68%\n", + "[10:54:24] MPS (INFO) - MPS size (MiB)=0.25775146484375\n", + "[10:54:24] MPS (INFO) - MPS fidelity=0.9739666699427678\n", + "[10:54:24] Simulation (INFO) - Progress... 69%\n", + "[10:54:24] Simulation (INFO) - Progress... 69%\n", + "[10:54:24] Simulation (INFO) - Progress... 69%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.27679443359375\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9731228436119972\n", + "[10:54:25] Simulation (INFO) - Progress... 69%\n", + "[10:54:25] Simulation (INFO) - Progress... 69%\n", + "[10:54:25] Simulation (INFO) - Progress... 70%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.307464599609375\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9722506169982225\n", + "[10:54:25] Simulation (INFO) - Progress... 70%\n", + "[10:54:25] Simulation (INFO) - Progress... 70%\n", + "[10:54:25] Simulation (INFO) - Progress... 70%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.358123779296875\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9713035867571528\n", + "[10:54:25] Simulation (INFO) - Progress... 70%\n", + "[10:54:25] Simulation (INFO) - Progress... 70%\n", + "[10:54:25] Simulation (INFO) - Progress... 71%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.394744873046875\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9703771300672412\n", + "[10:54:25] Simulation (INFO) - Progress... 71%\n", + "[10:54:25] Simulation (INFO) - Progress... 71%\n", + "[10:54:25] Simulation (INFO) - Progress... 71%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.413330078125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9695499877613101\n", + "[10:54:25] Simulation (INFO) - Progress... 71%\n", + "[10:54:25] Simulation (INFO) - Progress... 71%\n", + "[10:54:25] Simulation (INFO) - Progress... 72%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.413330078125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9695499877613101\n", + "[10:54:25] Simulation (INFO) - Progress... 72%\n", + "[10:54:25] Simulation (INFO) - Progress... 72%\n", + "[10:54:25] Simulation (INFO) - Progress... 72%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.413330078125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9695499877613101\n", + "[10:54:25] Simulation (INFO) - Progress... 72%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.413330078125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9695499877613101\n", + "[10:54:25] Simulation (INFO) - Progress... 72%\n", + "[10:54:25] Simulation (INFO) - Progress... 73%\n", + "[10:54:25] Simulation (INFO) - Progress... 73%\n", + "[10:54:25] Simulation (INFO) - Progress... 73%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.413330078125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9695499877613101\n", + "[10:54:25] Simulation (INFO) - Progress... 73%\n", + "[10:54:25] Simulation (INFO) - Progress... 73%\n", + "[10:54:25] Simulation (INFO) - Progress... 73%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.41412353515625\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9695499877613101\n", + "[10:54:25] Simulation (INFO) - Progress... 74%\n", + "[10:54:25] Simulation (INFO) - Progress... 74%\n", + "[10:54:25] Simulation (INFO) - Progress... 74%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.415283203125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9687965164769727\n", + "[10:54:25] Simulation (INFO) - Progress... 74%\n", + "[10:54:25] Simulation (INFO) - Progress... 74%\n", + "[10:54:25] Simulation (INFO) - Progress... 74%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.417388916015625\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9681745874180638\n", + "[10:54:25] Simulation (INFO) - Progress... 75%\n", + "[10:54:25] Simulation (INFO) - Progress... 75%\n", + "[10:54:25] Simulation (INFO) - Progress... 75%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.421661376953125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.967316318305627\n", + "[10:54:25] Simulation (INFO) - Progress... 75%\n", + "[10:54:25] Simulation (INFO) - Progress... 75%\n", + "[10:54:25] Simulation (INFO) - Progress... 75%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.427520751953125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9664202106456434\n", + "[10:54:25] Simulation (INFO) - Progress... 76%\n", + "[10:54:25] Simulation (INFO) - Progress... 76%\n", + "[10:54:25] Simulation (INFO) - Progress... 76%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.438018798828125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.965617165150299\n", + "[10:54:25] Simulation (INFO) - Progress... 76%\n", + "[10:54:25] Simulation (INFO) - Progress... 76%\n", + "[10:54:25] Simulation (INFO) - Progress... 76%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.456817626953125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9648266649127764\n", + "[10:54:25] Simulation (INFO) - Progress... 77%\n", + "[10:54:25] Simulation (INFO) - Progress... 77%\n", + "[10:54:25] Simulation (INFO) - Progress... 77%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.492950439453125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.963934771298722\n", + "[10:54:25] Simulation (INFO) - Progress... 77%\n", + "[10:54:25] Simulation (INFO) - Progress... 77%\n", + "[10:54:25] Simulation (INFO) - Progress... 77%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.55267333984375\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9629911974183719\n", + "[10:54:25] Simulation (INFO) - Progress... 78%\n", + "[10:54:25] Simulation (INFO) - Progress... 78%\n", + "[10:54:25] Simulation (INFO) - Progress... 78%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.6326904296875\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9621094113781801\n", + "[10:54:25] Simulation (INFO) - Progress... 78%\n", + "[10:54:25] Simulation (INFO) - Progress... 78%\n", + "[10:54:25] Simulation (INFO) - Progress... 78%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.719970703125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9612431375540139\n", + "[10:54:25] Simulation (INFO) - Progress... 79%\n", + "[10:54:25] Simulation (INFO) - Progress... 79%\n", + "[10:54:25] Simulation (INFO) - Progress... 79%\n", + "[10:54:25] MPS (INFO) - MPS size (MiB)=0.74658203125\n", + "[10:54:25] MPS (INFO) - MPS fidelity=0.9603890887415287\n", + "[10:54:25] Simulation (INFO) - Progress... 79%\n", + "[10:54:25] MPS (INFO) - Applying variational optimisation.\n", + "[10:54:25] MPS (INFO) - Fidelity before optimisation=0.9603890887415287\n", + "[10:54:25] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:25] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9664613835077668\n", + "[10:54:25] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:25] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9667151463640826\n", + "[10:54:25] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:26] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9667973427183506\n", + "[10:54:26] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:26] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9668383700489115\n", + "[10:54:26] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:26] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.966862870923364\n", + "[10:54:26] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:26] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9668789869987773\n", + "[10:54:26] MPS (INFO) - Final fidelity after optimisation=0.9668789869987773\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.74658203125\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9668789869987773\n", + "[10:54:26] Simulation (INFO) - Progress... 79%\n", + "[10:54:26] Simulation (INFO) - Progress... 80%\n", + "[10:54:26] Simulation (INFO) - Progress... 80%\n", + "[10:54:26] Simulation (INFO) - Progress... 80%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.74658203125\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9668789869987773\n", + "[10:54:26] Simulation (INFO) - Progress... 80%\n", + "[10:54:26] Simulation (INFO) - Progress... 80%\n", + "[10:54:26] Simulation (INFO) - Progress... 80%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.74658203125\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9668789869987773\n", + "[10:54:26] Simulation (INFO) - Progress... 81%\n", + "[10:54:26] Simulation (INFO) - Progress... 81%\n", + "[10:54:26] Simulation (INFO) - Progress... 81%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.74859619140625\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9663239978628592\n", + "[10:54:26] Simulation (INFO) - Progress... 81%\n", + "[10:54:26] Simulation (INFO) - Progress... 81%\n", + "[10:54:26] Simulation (INFO) - Progress... 81%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.7542724609375\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9656363866366784\n", + "[10:54:26] Simulation (INFO) - Progress... 82%\n", + "[10:54:26] Simulation (INFO) - Progress... 82%\n", + "[10:54:26] Simulation (INFO) - Progress... 82%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.7640380859375\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9648374074349356\n", + "[10:54:26] Simulation (INFO) - Progress... 82%\n", + "[10:54:26] Simulation (INFO) - Progress... 82%\n", + "[10:54:26] Simulation (INFO) - Progress... 82%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.7830810546875\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9639845933419957\n", + "[10:54:26] Simulation (INFO) - Progress... 83%\n", + "[10:54:26] Simulation (INFO) - Progress... 83%\n", + "[10:54:26] Simulation (INFO) - Progress... 83%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.809661865234375\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9630428029628693\n", + "[10:54:26] Simulation (INFO) - Progress... 83%\n", + "[10:54:26] Simulation (INFO) - Progress... 83%\n", + "[10:54:26] Simulation (INFO) - Progress... 83%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.83819580078125\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.962166197000186\n", + "[10:54:26] Simulation (INFO) - Progress... 84%\n", + "[10:54:26] Simulation (INFO) - Progress... 84%\n", + "[10:54:26] Simulation (INFO) - Progress... 84%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.86993408203125\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9613185341560891\n", + "[10:54:26] Simulation (INFO) - Progress... 84%\n", + "[10:54:26] Simulation (INFO) - Progress... 84%\n", + "[10:54:26] Simulation (INFO) - Progress... 84%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=0.96539306640625\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9604350319109984\n", + "[10:54:26] Simulation (INFO) - Progress... 85%\n", + "[10:54:26] Simulation (INFO) - Progress... 85%\n", + "[10:54:26] Simulation (INFO) - Progress... 85%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=1.067291259765625\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9594791720420855\n", + "[10:54:26] Simulation (INFO) - Progress... 85%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=1.067291259765625\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9594791720420855\n", + "[10:54:26] Simulation (INFO) - Progress... 85%\n", + "[10:54:26] Simulation (INFO) - Progress... 85%\n", + "[10:54:26] Simulation (INFO) - Progress... 86%\n", + "[10:54:26] Simulation (INFO) - Progress... 86%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=1.067291259765625\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9594791720420855\n", + "[10:54:26] Simulation (INFO) - Progress... 86%\n", + "[10:54:26] Simulation (INFO) - Progress... 86%\n", + "[10:54:26] Simulation (INFO) - Progress... 86%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=1.067291259765625\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9594791720420855\n", + "[10:54:26] Simulation (INFO) - Progress... 86%\n", + "[10:54:26] Simulation (INFO) - Progress... 87%\n", + "[10:54:26] Simulation (INFO) - Progress... 87%\n", + "[10:54:26] MPS (INFO) - MPS size (MiB)=1.069000244140625\n", + "[10:54:26] MPS (INFO) - MPS fidelity=0.9594791720420855\n", + "[10:54:27] Simulation (INFO) - Progress... 87%\n", + "[10:54:27] Simulation (INFO) - Progress... 87%\n", + "[10:54:27] Simulation (INFO) - Progress... 87%\n", + "[10:54:27] MPS (INFO) - MPS size (MiB)=1.0777587890625\n", + "[10:54:27] MPS (INFO) - MPS fidelity=0.9587078637634951\n", + "[10:54:27] Simulation (INFO) - Progress... 87%\n", + "[10:54:27] Simulation (INFO) - Progress... 88%\n", + "[10:54:27] Simulation (INFO) - Progress... 88%\n", + "[10:54:27] MPS (INFO) - MPS size (MiB)=1.097900390625\n", + "[10:54:27] MPS (INFO) - MPS fidelity=0.9577597157368264\n", + "[10:54:27] Simulation (INFO) - Progress... 88%\n", + "[10:54:27] Simulation (INFO) - Progress... 88%\n", + "[10:54:27] Simulation (INFO) - Progress... 88%\n", + "[10:54:27] MPS (INFO) - MPS size (MiB)=1.1484375\n", + "[10:54:27] MPS (INFO) - MPS fidelity=0.9569223417730577\n", + "[10:54:27] Simulation (INFO) - Progress... 88%\n", + "[10:54:27] Simulation (INFO) - Progress... 89%\n", + "[10:54:27] Simulation (INFO) - Progress... 89%\n", + "[10:54:27] MPS (INFO) - MPS size (MiB)=1.19775390625\n", + "[10:54:27] MPS (INFO) - MPS fidelity=0.9559847052113304\n", + "[10:54:27] Simulation (INFO) - Progress... 89%\n", + "[10:54:27] Simulation (INFO) - Progress... 89%\n", + "[10:54:27] Simulation (INFO) - Progress... 89%\n", + "[10:54:27] MPS (INFO) - MPS size (MiB)=1.302764892578125\n", + "[10:54:27] MPS (INFO) - MPS fidelity=0.9551000677677791\n", + "[10:54:27] Simulation (INFO) - Progress... 90%\n", + "[10:54:27] Simulation (INFO) - Progress... 90%\n", + "[10:54:27] Simulation (INFO) - Progress... 90%\n", + "[10:54:27] MPS (INFO) - MPS size (MiB)=1.437774658203125\n", + "[10:54:27] MPS (INFO) - MPS fidelity=0.954244636142701\n", + "[10:54:27] Simulation (INFO) - Progress... 90%\n", + "[10:54:27] MPS (INFO) - Applying variational optimisation.\n", + "[10:54:27] MPS (INFO) - Fidelity before optimisation=0.954244636142701\n", + "[10:54:27] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:27] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9607489478098933\n", + "[10:54:27] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:27] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.960931259342657\n", + "[10:54:27] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:27] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9609925299537814\n", + "[10:54:27] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:27] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.961024384193863\n", + "[10:54:27] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:28] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9610437890766452\n", + "[10:54:28] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:28] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9610566895987762\n", + "[10:54:28] MPS (INFO) - Final fidelity after optimisation=0.9610566895987762\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.437774658203125\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.9610566895987762\n", + "[10:54:28] Simulation (INFO) - Progress... 90%\n", + "[10:54:28] Simulation (INFO) - Progress... 90%\n", + "[10:54:28] Simulation (INFO) - Progress... 91%\n", + "[10:54:28] Simulation (INFO) - Progress... 91%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.437774658203125\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.9610566895987762\n", + "[10:54:28] Simulation (INFO) - Progress... 91%\n", + "[10:54:28] Simulation (INFO) - Progress... 91%\n", + "[10:54:28] Simulation (INFO) - Progress... 91%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.437774658203125\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.9610566895987762\n", + "[10:54:28] Simulation (INFO) - Progress... 91%\n", + "[10:54:28] Simulation (INFO) - Progress... 92%\n", + "[10:54:28] Simulation (INFO) - Progress... 92%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.437774658203125\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.9610566895987762\n", + "[10:54:28] Simulation (INFO) - Progress... 92%\n", + "[10:54:28] Simulation (INFO) - Progress... 92%\n", + "[10:54:28] Simulation (INFO) - Progress... 92%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.442535400390625\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.9602301237138147\n", + "[10:54:28] Simulation (INFO) - Progress... 92%\n", + "[10:54:28] Simulation (INFO) - Progress... 93%\n", + "[10:54:28] Simulation (INFO) - Progress... 93%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.474029541015625\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.9593906658288792\n", + "[10:54:28] Simulation (INFO) - Progress... 93%\n", + "[10:54:28] Simulation (INFO) - Progress... 93%\n", + "[10:54:28] Simulation (INFO) - Progress... 93%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.480133056640625\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.9584590204343021\n", + "[10:54:28] Simulation (INFO) - Progress... 93%\n", + "[10:54:28] Simulation (INFO) - Progress... 94%\n", + "[10:54:28] Simulation (INFO) - Progress... 94%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.541473388671875\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.957531480679061\n", + "[10:54:28] Simulation (INFO) - Progress... 94%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.541473388671875\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.957531480679061\n", + "[10:54:28] Simulation (INFO) - Progress... 94%\n", + "[10:54:28] Simulation (INFO) - Progress... 94%\n", + "[10:54:28] Simulation (INFO) - Progress... 94%\n", + "[10:54:28] Simulation (INFO) - Progress... 95%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.541473388671875\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.957531480679061\n", + "[10:54:28] Simulation (INFO) - Progress... 95%\n", + "[10:54:28] Simulation (INFO) - Progress... 95%\n", + "[10:54:28] Simulation (INFO) - Progress... 95%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.541473388671875\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.957531480679061\n", + "[10:54:28] Simulation (INFO) - Progress... 95%\n", + "[10:54:28] Simulation (INFO) - Progress... 95%\n", + "[10:54:28] Simulation (INFO) - Progress... 96%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.541473388671875\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.957531480679061\n", + "[10:54:28] Simulation (INFO) - Progress... 96%\n", + "[10:54:28] Simulation (INFO) - Progress... 96%\n", + "[10:54:28] Simulation (INFO) - Progress... 96%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.545379638671875\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.957531480679061\n", + "[10:54:28] Simulation (INFO) - Progress... 96%\n", + "[10:54:28] Simulation (INFO) - Progress... 96%\n", + "[10:54:28] Simulation (INFO) - Progress... 97%\n", + "[10:54:28] MPS (INFO) - MPS size (MiB)=1.578338623046875\n", + "[10:54:28] MPS (INFO) - MPS fidelity=0.956627902901374\n", + "[10:54:28] Simulation (INFO) - Progress... 97%\n", + "[10:54:28] MPS (INFO) - Applying variational optimisation.\n", + "[10:54:28] MPS (INFO) - Fidelity before optimisation=0.956627902901374\n", + "[10:54:28] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:29] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9587513675893747\n", + "[10:54:29] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:29] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.958807150393876\n", + "[10:54:29] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:29] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9588222182645398\n", + "[10:54:29] MPS (INFO) - Final fidelity after optimisation=0.9588222182645398\n", + "[10:54:29] MPS (INFO) - MPS size (MiB)=1.578338623046875\n", + "[10:54:29] MPS (INFO) - MPS fidelity=0.9588222182645398\n", + "[10:54:29] Simulation (INFO) - Progress... 97%\n", + "[10:54:29] Simulation (INFO) - Progress... 97%\n", + "[10:54:29] Simulation (INFO) - Progress... 97%\n", + "[10:54:29] Simulation (INFO) - Progress... 97%\n", + "[10:54:29] MPS (INFO) - MPS size (MiB)=1.578338623046875\n", + "[10:54:29] MPS (INFO) - MPS fidelity=0.9588222182645398\n", + "[10:54:29] Simulation (INFO) - Progress... 98%\n", + "[10:54:29] Simulation (INFO) - Progress... 98%\n", + "[10:54:29] Simulation (INFO) - Progress... 98%\n", + "[10:54:29] MPS (INFO) - MPS size (MiB)=1.578338623046875\n", + "[10:54:29] MPS (INFO) - MPS fidelity=0.9588222182645398\n", + "[10:54:29] Simulation (INFO) - Progress... 98%\n", + "[10:54:29] Simulation (INFO) - Progress... 98%\n", + "[10:54:29] Simulation (INFO) - Progress... 98%\n", + "[10:54:29] MPS (INFO) - MPS size (MiB)=1.578338623046875\n", + "[10:54:29] MPS (INFO) - MPS fidelity=0.9588222182645398\n", + "[10:54:29] Simulation (INFO) - Progress... 99%\n", + "[10:54:29] MPS (INFO) - MPS size (MiB)=1.578338623046875\n", + "[10:54:29] MPS (INFO) - MPS fidelity=0.9588222182645398\n", + "[10:54:29] Simulation (INFO) - Progress... 99%\n", + "[10:54:29] Simulation (INFO) - Progress... 99%\n", + "[10:54:29] Simulation (INFO) - Progress... 99%\n", + "[10:54:29] MPS (INFO) - MPS size (MiB)=1.578338623046875\n", + "[10:54:29] MPS (INFO) - MPS fidelity=0.9588222182645398\n", + "[10:54:29] Simulation (INFO) - Progress... 99%\n", + "[10:54:29] MPS (INFO) - Applying variational optimisation.\n", + "[10:54:29] MPS (INFO) - Fidelity before optimisation=0.9588222182645398\n", + "[10:54:29] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:29] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.958822218264537\n", + "[10:54:29] MPS (INFO) - Doing another optimisation sweep...\n", + "[10:54:29] MPS (INFO) - Optimisation sweep completed. Current fidelity=0.9588222182645393\n", + "[10:54:29] MPS (INFO) - Final fidelity after optimisation=0.9588222182645393\n", + "[10:54:29] Simulation (INFO) - Simulation completed.\n", + "[10:54:29] Simulation (INFO) - Final StructuredState size=1.578338623046875 MiB\n", + "[10:54:29] Simulation (INFO) - Final StructuredState fidelity=0.9588222182645393\n" ] } ], @@ -2114,7 +2112,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/pytket/extensions/cutensornet/tnstate/__init__.py b/pytket/extensions/cutensornet/structured_state/__init__.py similarity index 94% rename from pytket/extensions/cutensornet/tnstate/__init__.py rename to pytket/extensions/cutensornet/structured_state/__init__.py index 5bb2e284..bdde08f7 100644 --- a/pytket/extensions/cutensornet/tnstate/__init__.py +++ b/pytket/extensions/cutensornet/structured_state/__init__.py @@ -18,7 +18,7 @@ https://github.com/CQCL/pytket-cutensornet. """ -from .general import CuTensorNetHandle, Config, TNState +from .general import CuTensorNetHandle, Config, StructuredState from .simulation import SimulationAlgorithm, simulate, prepare_circuit_mps from .mps import DirMPS, MPS diff --git a/pytket/extensions/cutensornet/tnstate/cut_rKaHyPar_sea20.ini b/pytket/extensions/cutensornet/structured_state/cut_rKaHyPar_sea20.ini similarity index 100% rename from pytket/extensions/cutensornet/tnstate/cut_rKaHyPar_sea20.ini rename to pytket/extensions/cutensornet/structured_state/cut_rKaHyPar_sea20.ini diff --git a/pytket/extensions/cutensornet/tnstate/general.py b/pytket/extensions/cutensornet/structured_state/general.py similarity index 92% rename from pytket/extensions/cutensornet/tnstate/general.py rename to pytket/extensions/cutensornet/structured_state/general.py index d2a11910..dc677e10 100644 --- a/pytket/extensions/cutensornet/tnstate/general.py +++ b/pytket/extensions/cutensornet/structured_state/general.py @@ -72,7 +72,7 @@ def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None: class Config: - """Configuration class for simulation using ``TNState``.""" + """Configuration class for simulation using ``StructuredState``.""" def __init__( self, @@ -85,7 +85,7 @@ def __init__( optim_delta: float = 1e-5, loglevel: int = logging.WARNING, ): - """Instantiate a configuration object for ``TNState`` simulation. + """Instantiate a configuration object for ``StructuredState`` simulation. Note: Providing both a custom ``chi`` and ``truncation_fidelity`` will raise an @@ -192,7 +192,7 @@ def copy(self) -> Config: ) -class TNState(ABC): +class StructuredState(ABC): """Class representing a Tensor Network state.""" @abstractmethod @@ -205,8 +205,8 @@ def is_valid(self) -> bool: raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") @abstractmethod - def apply_gate(self, gate: Command) -> TNState: - """Applies the gate to the TNState. + def apply_gate(self, gate: Command) -> StructuredState: + """Applies the gate to the StructuredState. Args: gate: The gate to be applied. @@ -221,7 +221,7 @@ def apply_gate(self, gate: Command) -> TNState: raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") @abstractmethod - def apply_scalar(self, scalar: complex) -> TNState: + def apply_scalar(self, scalar: complex) -> StructuredState: """Multiplies the state by a complex number. Args: @@ -233,17 +233,17 @@ def apply_scalar(self, scalar: complex) -> TNState: raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") @abstractmethod - def vdot(self, other: TNState) -> complex: + def vdot(self, other: StructuredState) -> complex: """Obtain the inner product of the two states: ````. - It can be used to compute the squared norm of a state ``tnstate`` as - ``tnstate.vdot(tnstate)``. The tensors within the state are not modified. + It can be used to compute the squared norm of a state ``state`` as + ``state.vdot(state)``. The tensors within the state are not modified. Note: The state that is conjugated is ``self``. Args: - other: The other ``TNState``. + other: The other ``StructuredState``. Returns: The resulting complex number. @@ -260,7 +260,7 @@ def sample(self) -> dict[Qubit, int]: Notes: The contents of ``self`` are not updated. This is equivalent to applying - ``tnstate = self.copy()`` then ``tnstate.measure(tnstate.get_qubits())``. + ``state = self.copy()`` then ``state.measure(state.get_qubits())``. Returns: A dictionary mapping each qubit in the state to its 0 or 1 outcome. @@ -348,7 +348,7 @@ def get_amplitude(self, state: int) -> complex: """Returns the amplitude of the chosen computational state. Notes: - The result is equivalent to ``tnstate.get_statevector[b]``, but this method + The result is equivalent to ``state.get_statevector[b]``, but this method is faster when querying a single amplitude (or just a few). Args: @@ -390,7 +390,7 @@ def update_libhandle(self, libhandle: CuTensorNetHandle) -> None: raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") @abstractmethod - def copy(self) -> TNState: + def copy(self) -> StructuredState: """Returns a deep copy of ``self`` on the same device.""" raise NotImplementedError(f"Method not implemented in {type(self).__name__}.") diff --git a/pytket/extensions/cutensornet/tnstate/mps.py b/pytket/extensions/cutensornet/structured_state/mps.py similarity index 97% rename from pytket/extensions/cutensornet/tnstate/mps.py rename to pytket/extensions/cutensornet/structured_state/mps.py index c90d113f..83329f25 100644 --- a/pytket/extensions/cutensornet/tnstate/mps.py +++ b/pytket/extensions/cutensornet/structured_state/mps.py @@ -34,7 +34,7 @@ from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle, Config, TNState, Tensor +from .general import CuTensorNetHandle, Config, StructuredState, Tensor class DirMPS(Enum): @@ -44,7 +44,7 @@ class DirMPS(Enum): RIGHT = 1 -class MPS(TNState): +class MPS(StructuredState): """Represents a state as a Matrix Product State. Attributes: @@ -205,7 +205,7 @@ def apply_gate(self, gate: Command) -> MPS: return self - def apply_scalar(self, scalar: complex) -> TNState: + def apply_scalar(self, scalar: complex) -> MPS: """Multiplies the state by a complex number. Args: diff --git a/pytket/extensions/cutensornet/tnstate/mps_gate.py b/pytket/extensions/cutensornet/structured_state/mps_gate.py similarity index 100% rename from pytket/extensions/cutensornet/tnstate/mps_gate.py rename to pytket/extensions/cutensornet/structured_state/mps_gate.py diff --git a/pytket/extensions/cutensornet/tnstate/mps_mpo.py b/pytket/extensions/cutensornet/structured_state/mps_mpo.py similarity index 100% rename from pytket/extensions/cutensornet/tnstate/mps_mpo.py rename to pytket/extensions/cutensornet/structured_state/mps_mpo.py diff --git a/pytket/extensions/cutensornet/tnstate/simulation.py b/pytket/extensions/cutensornet/structured_state/simulation.py similarity index 92% rename from pytket/extensions/cutensornet/tnstate/simulation.py rename to pytket/extensions/cutensornet/structured_state/simulation.py index aaf18b3c..a760146b 100644 --- a/pytket/extensions/cutensornet/tnstate/simulation.py +++ b/pytket/extensions/cutensornet/structured_state/simulation.py @@ -28,14 +28,14 @@ from pytket.predicates import CompilationUnit from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle, Config, TNState +from .general import CuTensorNetHandle, Config, StructuredState from .mps_gate import MPSxGate from .mps_mpo import MPSxMPO from .ttn_gate import TTNxGate class SimulationAlgorithm(Enum): - """An enum to refer to the TNState contraction algorithm. + """An enum to refer to the StructuredState contraction algorithm. Each enum value corresponds to the class with the same name; see its docs for information about the algorithm. @@ -51,12 +51,12 @@ def simulate( circuit: Circuit, algorithm: SimulationAlgorithm, config: Config, -) -> TNState: - """Simulates the circuit and returns the ``TNState`` representing the final state. +) -> StructuredState: + """Simulates the circuit and returns the ``StructuredState`` representing the final state. Note: A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` - statement. The device where the ``TNState`` is stored will match the one + statement. The device where the ``StructuredState`` is stored will match the one specified by the library handle. The input ``circuit`` must be composed of one-qubit and two-qubit gates only. @@ -70,7 +70,7 @@ def simulate( config: The configuration object for simulation. Returns: - An instance of ``TNState`` containing (an approximation of) the final state + An instance of ``StructuredState`` containing (an approximation of) the final state of the circuit. The instance be of the class matching ``algorithm``. """ logger = set_logger("Simulation", level=config.loglevel) @@ -79,7 +79,7 @@ def simulate( "Ordering the gates in the circuit to reduce canonicalisation overhead." ) if algorithm == SimulationAlgorithm.MPSxGate: - tnstate = MPSxGate( # type: ignore + state = MPSxGate( # type: ignore libhandle, circuit.qubits, config, @@ -87,7 +87,7 @@ def simulate( sorted_gates = _get_sorted_gates(circuit, algorithm) elif algorithm == SimulationAlgorithm.MPSxMPO: - tnstate = MPSxMPO( # type: ignore + state = MPSxMPO( # type: ignore libhandle, circuit.qubits, config, @@ -96,7 +96,7 @@ def simulate( elif algorithm == SimulationAlgorithm.TTNxGate: qubit_partition = _get_qubit_partition(circuit, config.leaf_size) - tnstate = TTNxGate( # type: ignore + state = TTNxGate( # type: ignore libhandle, qubit_partition, config, @@ -106,19 +106,19 @@ def simulate( logger.info("Running simulation...") # Apply the gates for i, g in enumerate(sorted_gates): - tnstate.apply_gate(g) + state.apply_gate(g) logger.info(f"Progress... {(100*i) // len(sorted_gates)}%") # Apply the batched operations that are left (if any) - tnstate._flush() + state._flush() # Apply the circuit's phase to the state - tnstate.apply_scalar(np.exp(1j * np.pi * circuit.phase)) + state.apply_scalar(np.exp(1j * np.pi * circuit.phase)) logger.info("Simulation completed.") - logger.info(f"Final TNState size={tnstate.get_byte_size() / 2**20} MiB") - logger.info(f"Final TNState fidelity={tnstate.fidelity}") - return tnstate + logger.info(f"Final StructuredState size={state.get_byte_size() / 2**20} MiB") + logger.info(f"Final StructuredState fidelity={state.fidelity}") + return state def prepare_circuit_mps(circuit: Circuit) -> tuple[Circuit, dict[Qubit, Qubit]]: diff --git a/pytket/extensions/cutensornet/tnstate/ttn.py b/pytket/extensions/cutensornet/structured_state/ttn.py similarity index 96% rename from pytket/extensions/cutensornet/tnstate/ttn.py rename to pytket/extensions/cutensornet/structured_state/ttn.py index 55061e23..47e9770f 100644 --- a/pytket/extensions/cutensornet/tnstate/ttn.py +++ b/pytket/extensions/cutensornet/structured_state/ttn.py @@ -34,7 +34,7 @@ from pytket.extensions.cutensornet.general import set_logger -from .general import CuTensorNetHandle, Config, TNState, Tensor +from .general import CuTensorNetHandle, Config, StructuredState, Tensor class DirTTN(IntEnum): @@ -76,7 +76,7 @@ def copy(self) -> TreeNode: return new_node -class TTN(TNState): +class TTN(StructuredState): """Represents a state as a Tree Tensor Network. Attributes: @@ -277,7 +277,7 @@ def apply_gate(self, gate: Command) -> TTN: return self - def apply_scalar(self, scalar: complex) -> TNState: + def apply_scalar(self, scalar: complex) -> TTN: """Multiplies the state by a complex number. Args: @@ -573,7 +573,7 @@ def sample(self) -> dict[Qubit, int]: Notes: The contents of ``self`` are not updated. This is equivalent to applying - ``tnstate = self.copy()`` then ``tnstate.measure(tnstate.get_qubits())``. + ``state = self.copy()`` then ``state.measure(state.get_qubits())``. Returns: A dictionary mapping each qubit in the state to its 0 or 1 outcome. diff --git a/pytket/extensions/cutensornet/tnstate/ttn_gate.py b/pytket/extensions/cutensornet/structured_state/ttn_gate.py similarity index 100% rename from pytket/extensions/cutensornet/tnstate/ttn_gate.py rename to pytket/extensions/cutensornet/structured_state/ttn_gate.py diff --git a/tests/test_tnstate.py b/tests/test_structured_state.py similarity index 90% rename from tests/test_tnstate.py rename to tests/test_structured_state.py index b8253597..d08ae50a 100644 --- a/tests/test_tnstate.py +++ b/tests/test_structured_state.py @@ -8,7 +8,7 @@ from pytket.circuit import Circuit, Qubit, OpType # type: ignore from pytket.pauli import Pauli, QubitPauliString # type: ignore -from pytket.extensions.cutensornet.tnstate import ( +from pytket.extensions.cutensornet.structured_state import ( CuTensorNetHandle, Config, MPS, @@ -20,7 +20,7 @@ prepare_circuit_mps, SimulationAlgorithm, ) -from pytket.extensions.cutensornet.tnstate.ttn import RootPath +from pytket.extensions.cutensornet.structured_state.ttn import RootPath from pytket.extensions.cutensornet.utils import circuit_statevector_postselect @@ -244,27 +244,27 @@ def test_exact_circ_sim(circuit: Circuit, algorithm: SimulationAlgorithm) -> Non circuit, _ = prepare_circuit_mps(circuit) n_qubits = len(circuit.qubits) - state = circuit.get_statevector() + state_vec = circuit.get_statevector() with CuTensorNetHandle() as libhandle: cfg = Config(leaf_size=2) - tnstate = simulate(libhandle, circuit, algorithm, cfg) - assert tnstate.is_valid() + state = simulate(libhandle, circuit, algorithm, cfg) + assert state.is_valid() # Check that there was no approximation - assert np.isclose(tnstate.get_fidelity(), 1.0, atol=cfg._atol) + assert np.isclose(state.get_fidelity(), 1.0, atol=cfg._atol) # Check that overlap is 1 - assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) + assert np.isclose(state.vdot(state), 1.0, atol=cfg._atol) # Check that all of the amplitudes are correct for b in range(2**n_qubits): assert np.isclose( - tnstate.get_amplitude(b), - state[b], + state.get_amplitude(b), + state_vec[b], atol=cfg._atol, ) # Check that the statevector is correct - assert np.allclose(tnstate.get_statevector(), state, atol=cfg._atol) + assert np.allclose(state.get_statevector(), state_vec, atol=cfg._atol) @pytest.mark.parametrize( @@ -308,10 +308,10 @@ def test_approx_circ_sim_gate_fid( with CuTensorNetHandle() as libhandle: cfg = Config(truncation_fidelity=0.99, leaf_size=2) - tnstate = simulate(libhandle, circuit, algorithm, cfg) - assert tnstate.is_valid() + state = simulate(libhandle, circuit, algorithm, cfg) + assert state.is_valid() # Check that overlap is 1 - assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) + assert np.isclose(state.vdot(state), 1.0, atol=cfg._atol) @pytest.mark.parametrize( @@ -353,10 +353,10 @@ def test_approx_circ_sim_chi(circuit: Circuit, algorithm: SimulationAlgorithm) - with CuTensorNetHandle() as libhandle: cfg = Config(chi=4, leaf_size=2) - tnstate = simulate(libhandle, circuit, algorithm, cfg) - assert tnstate.is_valid() + state = simulate(libhandle, circuit, algorithm, cfg) + assert state.is_valid() # Check that overlap is 1 - assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) + assert np.isclose(state.vdot(state), 1.0, atol=cfg._atol) @pytest.mark.parametrize( @@ -394,36 +394,36 @@ def test_float_point_options( with CuTensorNetHandle() as libhandle: # Exact cfg = Config(float_precision=fp_precision, leaf_size=2) - tnstate = simulate(libhandle, circuit, algorithm, cfg) - assert tnstate.is_valid() + state = simulate(libhandle, circuit, algorithm, cfg) + assert state.is_valid() # Check that overlap is 1 - assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) + assert np.isclose(state.vdot(state), 1.0, atol=cfg._atol) # Approximate, bound truncation fidelity cfg = Config( truncation_fidelity=0.99, float_precision=fp_precision, leaf_size=2 ) - tnstate = simulate( + state = simulate( libhandle, circuit, algorithm, cfg, ) - assert tnstate.is_valid() + assert state.is_valid() # Check that overlap is 1 - assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) + assert np.isclose(state.vdot(state), 1.0, atol=cfg._atol) # Approximate, bound chi cfg = Config(chi=4, float_precision=fp_precision, leaf_size=2) - tnstate = simulate( + state = simulate( libhandle, circuit, algorithm, cfg, ) - assert tnstate.is_valid() + assert state.is_valid() # Check that overlap is 1 - assert np.isclose(tnstate.vdot(tnstate), 1.0, atol=cfg._atol) + assert np.isclose(state.vdot(state), 1.0, atol=cfg._atol) @pytest.mark.parametrize( @@ -438,7 +438,7 @@ def test_circ_approx_explicit_mps(circuit: Circuit) -> None: with CuTensorNetHandle() as libhandle: # Finite gate fidelity # Check for MPSxGate - cfg = Config(truncation_fidelity=0.99, leaf_size=4) + cfg = Config(truncation_fidelity=0.99, leaf_size=4, float_precision=np.float32) mps_gate = simulate( libhandle, circuit, @@ -462,7 +462,7 @@ def test_circ_approx_explicit_mps(circuit: Circuit) -> None: # Fixed virtual bond dimension # Check for MPSxGate - cfg = Config(chi=8, leaf_size=4) + cfg = Config(chi=8, leaf_size=4, float_precision=np.float32) 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() @@ -487,15 +487,15 @@ def test_circ_approx_explicit_ttn(circuit: Circuit) -> None: with CuTensorNetHandle() as libhandle: # Finite gate fidelity # Check for TTNxGate - cfg = Config(truncation_fidelity=0.99) + cfg = Config(truncation_fidelity=0.99, leaf_size=3, float_precision=np.float32) ttn_gate = simulate(libhandle, circuit, SimulationAlgorithm.TTNxGate, cfg) - assert np.isclose(ttn_gate.get_fidelity(), 0.729, atol=1e-3) + assert np.isclose(ttn_gate.get_fidelity(), 0.769, atol=1e-3) assert ttn_gate.is_valid() assert np.isclose(ttn_gate.vdot(ttn_gate), 1.0, atol=cfg._atol) # Fixed virtual bond dimension # Check for TTNxGate - cfg = Config(chi=120, leaf_size=3) + cfg = Config(chi=120, leaf_size=3, float_precision=np.float32) ttn_gate = simulate(libhandle, circuit, SimulationAlgorithm.TTNxGate, cfg) assert np.isclose(ttn_gate.get_fidelity(), 0.857, atol=1e-3) assert ttn_gate.is_valid() From 90436610bf977d13fc23c329e5986bacc3809805 Mon Sep 17 00:00:00 2001 From: Pablo Andres-Martinez Date: Fri, 2 Feb 2024 03:19:44 -0800 Subject: [PATCH 78/83] Linting and mypy --- pytket/extensions/cutensornet/structured_state/general.py | 4 ++-- pytket/extensions/cutensornet/structured_state/simulation.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pytket/extensions/cutensornet/structured_state/general.py b/pytket/extensions/cutensornet/structured_state/general.py index dc677e10..25d65876 100644 --- a/pytket/extensions/cutensornet/structured_state/general.py +++ b/pytket/extensions/cutensornet/structured_state/general.py @@ -15,7 +15,7 @@ from abc import ABC, abstractmethod import warnings import logging -from typing import Any, Optional, Union +from typing import Any, Optional, Type import numpy as np # type: ignore @@ -78,7 +78,7 @@ def __init__( self, chi: Optional[int] = None, truncation_fidelity: Optional[float] = None, - float_precision: Union[np.float32, np.float64] = np.float64, # type: ignore + float_precision: Type[Any] = np.float64, value_of_zero: float = 1e-16, leaf_size: int = 8, k: int = 4, diff --git a/pytket/extensions/cutensornet/structured_state/simulation.py b/pytket/extensions/cutensornet/structured_state/simulation.py index a760146b..228b1721 100644 --- a/pytket/extensions/cutensornet/structured_state/simulation.py +++ b/pytket/extensions/cutensornet/structured_state/simulation.py @@ -52,7 +52,7 @@ def simulate( algorithm: SimulationAlgorithm, config: Config, ) -> StructuredState: - """Simulates the circuit and returns the ``StructuredState`` representing the final state. + """Simulates the circuit and returns the ``StructuredState`` of the final state. Note: A ``libhandle`` should be created via a ``with CuTensorNet() as libhandle:`` @@ -70,7 +70,7 @@ def simulate( config: The configuration object for simulation. Returns: - An instance of ``StructuredState`` containing (an approximation of) the final state + An instance of ``StructuredState`` for (an approximation of) the final state of the circuit. The instance be of the class matching ``algorithm``. """ logger = set_logger("Simulation", level=config.loglevel) From bdab46a79276051f04455c90a5b956fd270eb799 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Fri, 2 Feb 2024 11:34:12 +0000 Subject: [PATCH 79/83] Updated module docstring --- pytket/extensions/cutensornet/structured_state/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pytket/extensions/cutensornet/structured_state/__init__.py b/pytket/extensions/cutensornet/structured_state/__init__.py index bdde08f7..be9dd9f5 100644 --- a/pytket/extensions/cutensornet/structured_state/__init__.py +++ b/pytket/extensions/cutensornet/structured_state/__init__.py @@ -11,10 +11,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""Module for circuit simulation by state evolution. +"""Module for circuit simulation by state evolution, where the state is +represented by a tensor network with a predefined structure. Approximate tensor network contraction is supported. Both ``MPS`` and ``TTN`` methods are provided. -For an example of its use, see ``examples/mps_tutorial.ipynb`` in +For an example of its use, see the ``examples/`` folder at https://github.com/CQCL/pytket-cutensornet. """ From 4b0ae274c1eb1af5d9f1210c1f5760b18c038e0f Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Fri, 2 Feb 2024 11:34:47 +0000 Subject: [PATCH 80/83] Changed kahypar to strict version requirement --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aafc5481..08912fe6 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ license="Apache 2", packages=find_namespace_packages(include=["pytket.*"]), include_package_data=True, - install_requires=["pytket ~= 1.24", "kahypar ~= 1.3.5"], + install_requires=["pytket ~= 1.24", "kahypar == 1.3.5"], classifiers=[ "Environment :: Console", "Programming Language :: Python :: 3.10", From 94b03fe9c9f1bfa8526af11019fef3d17638cb83 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Fri, 2 Feb 2024 11:47:55 +0000 Subject: [PATCH 81/83] Removed kahypar dependency --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 08912fe6..abc65423 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ license="Apache 2", packages=find_namespace_packages(include=["pytket.*"]), include_package_data=True, - install_requires=["pytket ~= 1.24", "kahypar == 1.3.5"], + install_requires=["pytket ~= 1.24"], classifiers=[ "Environment :: Console", "Programming Language :: Python :: 3.10", From 0206d170018b2ca3fc7396b3c6b3317e73798377 Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Fri, 2 Feb 2024 11:53:41 +0000 Subject: [PATCH 82/83] Flexible kahypar import --- pytket/extensions/cutensornet/structured_state/simulation.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pytket/extensions/cutensornet/structured_state/simulation.py b/pytket/extensions/cutensornet/structured_state/simulation.py index 228b1721..597d0fa9 100644 --- a/pytket/extensions/cutensornet/structured_state/simulation.py +++ b/pytket/extensions/cutensornet/structured_state/simulation.py @@ -19,7 +19,10 @@ import numpy as np # type: ignore import networkx as nx # type: ignore -import kahypar # type: ignore +try: + import kahypar # type: ignore +except ImportError: + warnings.warn("local settings failed to import kahypar", ImportWarning) from pytket.circuit import Circuit, Command, Qubit from pytket.transform import Transform From d96e37f293d073b153f0425ab8b53f4309a2beaa Mon Sep 17 00:00:00 2001 From: PabloAndresCQ Date: Fri, 2 Feb 2024 11:55:46 +0000 Subject: [PATCH 83/83] Linter being pedantic --- pytket/extensions/cutensornet/structured_state/simulation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytket/extensions/cutensornet/structured_state/simulation.py b/pytket/extensions/cutensornet/structured_state/simulation.py index 597d0fa9..0df64f1d 100644 --- a/pytket/extensions/cutensornet/structured_state/simulation.py +++ b/pytket/extensions/cutensornet/structured_state/simulation.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from typing import Optional +import warnings from enum import Enum from pathlib import Path @@ -19,6 +20,7 @@ import numpy as np # type: ignore import networkx as nx # type: ignore + try: import kahypar # type: ignore except ImportError: