Matrix Product State (MPS)#
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
CQCL/pytket-cutensornet.
Simulation#
- pytket.extensions.cutensornet.mps.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 awith 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 bypytket
can be used.Two-qubit gates must act between adjacent qubits, i.e. on
circuit.qubits[i]
andcircuit.qubits[i+1]
for anyi
. If this is not satisfied by your circuit, consider usingprepare_circuit()
on it.- Parameters:
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.
- enum pytket.extensions.cutensornet.mps.ContractionAlg(value)#
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.
Valid values are as follows:
- MPSxGate = <ContractionAlg.MPSxGate: 0>#
- MPSxMPO = <ContractionAlg.MPSxMPO: 1>#
- class pytket.extensions.cutensornet.mps.ConfigMPS#
Configuration class for simulation using MPS.
- __init__(chi: int | None = None, truncation_fidelity: float | None = None, k: int = 4, optim_delta: float = 1e-05, float_precision: ~numpy.float32 | ~numpy.float64 = <class 'numpy.float64'>, value_of_zero: float = 1e-16, loglevel: int = 30)#
Instantiate a configuration object for MPS simulation.
Note
Providing both a custom
chi
andtruncation_fidelity
will raise an exception. Choose one or the other (or neither, for exact simulation).- Parameters:
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
|<psi|phi>|^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 thanoptim_delta
. Ignored if not using MPSxMPO. Default value is1e-5
.float_precision – The floating point precision used in tensor calculations; choose from
numpy
types:np.float64
ornp.float32
. Complex numbers are represented using two of suchfloat
numbers. Default isnp.float64
.value_of_zero – Any number below this value will be considered equal to zero. Even when no
chi
ortruncation_fidelity
is provided, singular values below this number will be truncated. We suggest to use a value slightly below what your chosenfloat_precision
can reasonably achieve. For instance,1e-16
fornp.float64
precision (default) and1e-7
fornp.float32
.loglevel – Internal logger output level. Use 30 for warnings only, 20 for verbose and 10 for debug mode.
- Raises:
ValueError – If both
chi
andtruncation_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].
- class pytket.extensions.cutensornet.mps.CuTensorNetHandle(device_id: int | None = None)#
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.
Classes#
- class pytket.extensions.cutensornet.mps.MPS#
Represents a state as a Matrix Product State.
- tensors#
A list of tensors in the MPS;
tensors[0]
is the leftmost andtensors[len(self)-1]
is the rightmost;tensors[i]
andtensors[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.- Type:
list[Tensor]
- canonical_form#
A dictionary mapping positions to the canonical form direction of the corresponding tensor, or
None
if it the tensor is not canonicalised.- Type:
dict[int, Optional[DirectionMPS]]
- qubit_position#
A dictionary mapping circuit qubits to the position its tensor is at in the MPS.
- fidelity#
A lower bound of the fidelity, obtained by multiplying the fidelities after each contraction. The fidelity of a contraction corresponds to
|<psi|phi>|^2
where|psi>
and|phi>
are the states before and after truncation (assuming both are normalised).- Type:
- __init__(libhandle: CuTensorNetHandle, qubits: list[pytket.unit_id.Qubit], config: ConfigMPS)#
Initialise an MPS on the computational state
|0>
.Note
A
libhandle
should be created via awith CuTensorNet() as libhandle:
statement. The device where the MPS is stored will match the one specified by the library handle.- Parameters:
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.
- apply_gate(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.
- Parameters:
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.
- vdot(other: MPS) complex #
Obtain the inner product of the two MPS:
<self|other>
.It can be used to compute the squared norm of an MPS
mps
asmps.vdot(mps)
. The tensors within the MPS are not modified.Note
The state that is conjugated is
self
.- Parameters:
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.
- canonicalise(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 ofr_pos
in right orthogonal form.- Parameters:
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.
- sample() dict[pytket.unit_id.Qubit, int] #
Returns a sample from a Z measurement applied on every qubit.
Notes
The MPS
self
is not updated. This is equivalent to applyingmps = self.copy()
thenmps.measure(mps.get_qubits())
.- Returns:
A dictionary mapping each of the qubits in the MPS to their 0 or 1 outcome.
- measure(qubits: set[pytket.unit_id.Qubit]) dict[pytket.unit_id.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.
- Parameters:
qubits – The subset of qubits to be measured.
- Returns:
A dictionary mapping the given
qubits
to their measurement outcome, i.e. either0
or1
.- Raises:
ValueError – If an element in
qubits
is not a qubit in the MPS.
- postselect(qubit_outcomes: dict[pytket.unit_id.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.
- Parameters:
qubit_outcomes – A dictionary mapping a subset of qubits in the MPS to their desired outcome value (either
0
or1
).- 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 than0
or1
.ValueError – If all of the qubits in the MPS are being postselected. Instead, you may wish to use
get_amplitude()
.
- expectation_value(pauli_string: QubitPauliString) float #
Obtains the expectation value of the Pauli string observable.
- Parameters:
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.
- get_statevector() ndarray #
Returns the statevector with qubits in Increasing Lexicographic Order (ILO).
- Raises:
ValueError – If there are no qubits left in the MPS.
- get_amplitude(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).- Parameters:
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.
- get_qubits() set[pytket.unit_id.Qubit] #
Returns the set of qubits that this MPS is defined on.
- get_virtual_dimensions(position: int) tuple[int, int] #
Returns the virtual bonds dimension of the tensor
tensors[position]
.- Parameters:
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.
- get_physical_dimension(position: int) int #
Returns the physical bond dimension of the tensor
tensors[position]
.- Parameters:
position – A position in the MPS.
- Returns:
The dimension of the physical bond.
- Raises:
RuntimeError – If
position
is out of bounds.
- is_valid() 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.
- update_libhandle(libhandle: CuTensorNetHandle) None #
Update the
CuTensorNetHandle
used by thisMPS
object. Multiple objects may use the same handle.- Parameters:
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.
- class pytket.extensions.cutensornet.mps.MPSxGate#
Bases:
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- __init__(libhandle: CuTensorNetHandle, qubits: list[pytket.unit_id.Qubit], config: ConfigMPS)#
Initialise an MPS on the computational state
|0>
.Note
A
libhandle
should be created via awith CuTensorNet() as libhandle:
statement. The device where the MPS is stored will match the one specified by the library handle.- Parameters:
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.
- class pytket.extensions.cutensornet.mps.MPSxMPO#
Bases:
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.- __init__(libhandle: CuTensorNetHandle, qubits: list[pytket.unit_id.Qubit], config: ConfigMPS)#
Initialise an MPS on the computational state
|0>
.Note
A
libhandle
should be created via awith CuTensorNet() as libhandle:
statement. The device where the MPS is stored will match the one specified by the library handle.- Parameters:
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.
Miscellaneous#
- enum pytket.extensions.cutensornet.mps.DirectionMPS(value)#
An enum to refer to relative directions within the MPS.
Valid values are as follows:
- LEFT = <DirectionMPS.LEFT: 0>#
- RIGHT = <DirectionMPS.RIGHT: 1>#
- pytket.extensions.cutensornet.mps.prepare_circuit(circuit: Circuit) tuple[pytket.circuit.Circuit, dict[pytket.unit_id.Qubit, pytket.unit_id.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.- Parameters:
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.