quimb.tensor.circuit.pepo

PEPO simple-update circuit simulator.

Classes

CircuitPEPOSimpleUpdate

Quantum circuit simulator that evolves an observable backwards in time,

Module Contents

class quimb.tensor.circuit.pepo.CircuitPEPOSimpleUpdate(N=None, *, edges=None, gates=None, max_bond=None, cutoff=1e-10, gate_contract='reduce-split', gate_opts=None, **circuit_opts)[source]

Bases: quimb.tensor.circuit.exact.Circuit

Quantum circuit simulator that evolves an observable backwards in time, in the Heisenberg picture, by representing it as an arbitrary geometry PEPO and applying the gates with the simple update rule.

Rather than evolving a state forwards, gates are simply recorded as they are applied; no contraction happens until an expectation value is requested. When local_expectation() (or get_evolved_operator()) is called, the local observable is built as a bond dimension 1 PEPO on the supplied edges and the recorded gates are applied in reverse order as \(O \rightarrow G^\dagger O G\), using tensor_network_ag_gate_simple() (Vidal-style gauging plus compression). Gates that fall outside the reverse lightcone of the observable are skipped, since \(G^\dagger G = 1\). The evolved operator is finally projected onto the |00...0> initial state.

This is the Heisenberg-picture companion to CircuitPEPSSimpleUpdate, useful on lattices where evolving the full state is intractable but a single local observable can be evolved in a truncated, gauged operator network.

Parameters:
  • N (int, optional) – The number of qubits. If not given it is inferred from the geometry. Supply it to pad the geometry up to N sites.

  • edges (sequence[tuple[hashable, hashable]], optional) – The edges defining the geometry. A bond is placed between each pair of sites, and two-qubit gates are only supported on these edges. If not given the geometry is inferred from the two-qubit gates.

  • gates (sequence, optional) – If edges is not given, infer the geometry from the two-qubit gates in this sequence (the gates are only inspected here, not applied).

  • max_bond (int, optional) – The maximum bond dimension to compress the operator to as gates are applied during the backwards evolution.

  • cutoff (float, optional) – The singular value cutoff to use when compressing.

  • gate_contract (str, optional) – How to split a two site gate, see tensor_network_ag_gate_simple().

  • gate_opts (dict, optional) – Default options forwarded to gate_simple_ such as max_bond, cutoff and renorm. This is the single source of truth for the compression options; max_bond and cutoff are also exposed as properties.

edges

The unique edges defining the geometry.

Type:

tuple[tuple[hashable, hashable]]

sites

The sites (qubit labels).

Type:

tuple[hashable]

gates

The gates recorded so far.

Type:

tuple[Gate]

Examples

>>> import quimb.tensor as qtn
>>> edges = [(0, 1), (1, 2), (2, 3)]
>>> circ = qtn.CircuitPEPOSimpleUpdate(edges=edges, max_bond=16)
>>> circ.apply_gates(gates)            # no computation happens here
>>> circ.local_expectation(qu.pauli("Z"), 1)   # evolve + contract here

See also

CircuitPEPSSimpleUpdate, CircuitMPS

_edges
_sites
_site_set
_edge_set
copy()[source]

Copy the circuit, carrying over the geometry that the base Circuit copy does not know about.

property edges

The unique edges defining the geometry.

property sites

The sites (qubit labels).

property max_bond

The maximum bond dimension to compress to.

property cutoff

The singular value cutoff to use when compressing.

_init_state(N, dtype='complex128')[source]
_apply_gate(gate, tags=None, **gate_opts)[source]

Apply a Gate to this Circuit. This is the main method that all calls to apply a gate should go through.

Parameters:
  • gate (Gate) – The gate to apply.

  • tags (str or sequence of str, optional) – Tags to add to the gate tensor(s).

apply_gates(gates, progbar=False, **gate_opts)[source]

Apply a sequence of gates to this tensor network quantum circuit.

Parameters:
  • gates (Sequence[Gate] or Sequence[Tuple]) – The sequence of gates to apply.

  • gate_opts – Supplied to apply_gate().

_parse_where(where)[source]
_initial_operator(G, where)[source]

Build the bond dimension 1 PEPO of G acting at where and the identity elsewhere, on the circuit geometry.

get_evolved_operator(G, where, *, max_bond=None, cutoff=None)[source]

Evolve the local observable G at where backwards through the recorded circuit, returning the Heisenberg-picture operator \(U^\dagger G U\) as a gauged PEPO. Gates outside the reverse lightcone of the observable are skipped.

Parameters:
  • G (array_like) – The local operator acting on the site(s) in where.

  • where (hashable or sequence[hashable]) – The site or sites the operator acts on.

  • max_bond (optional) – Override the compression options for this call.

  • cutoff (optional) – Override the compression options for this call.

Return type:

TensorNetworkGenOperator

get_evolved_operator_with_state(G, where, *, max_bond=None, cutoff=None)[source]

Return the evolved operator \(U^\dagger G U\) projected onto the |00...0> initial state on both sides, i.e. the tensor network whose full contraction is \(\langle 0 | U^\dagger G U | 0 \rangle\). The physical indices are projected with isel; the caller can contract the returned network however they like.

local_expectation(G, where, *, max_bond=None, cutoff=None, optimize='auto-hq', **contract_opts)[source]

Compute \(\langle 0 | U^\dagger G U | 0 \rangle\), the expectation of the local operator G at where in the state prepared by the recorded circuit U acting on |00...0>.

Parameters:
  • G (array_like) – The local operator acting on the site(s) in where.

  • where (hashable or sequence[hashable]) – The site or sites the operator acts on.

  • max_bond (optional) – Override the compression options for this call.

  • cutoff (optional) – Override the compression options for this call.

  • optimize (str, optional) – The contraction path optimizer for the final contraction.

  • contract_opts – Supplied to the final contract().

Return type:

scalar

abstractmethod _unsupported(name)[source]
property psi

Tensor network representation of the wavefunction.

to_dense(*args, **kwargs)[source]

Generate the dense representation of the final wavefunction.

Parameters:
  • reverse (bool, optional) – Whether to reverse the order of the subsystems, to match the convention of qiskit for example.

  • optimize (str, optional) – Contraction path optimizer to use for the contraction, can be a non-reusable path optimizer as only called once (though path won’t be cached for later use in that case).

  • dtype (str, optional) – If given, convert the tensors to this dtype prior to contraction.

  • simplify_sequence (str, optional) – Which local tensor network simplifications to perform and in which order, see full_simplify().

  • simplify_atol (float, optional) – The tolerance with which to compare to zero when applying full_simplify().

  • simplify_equalize_norms (bool, optional) – Actively renormalize tensor norms during simplification.

  • backend (str, optional) – Backend to perform the contraction with, e.g. 'numpy', 'cupy' or 'jax'. Passed to cotengra.

  • dtype – Data type to cast the TN to before contraction.

  • rehearse (bool, optional) – If True, generate and cache the simplified tensor network and contraction tree but don’t actually perform the contraction. Returns a dict with keys 'tn' and 'tree' with the tensor network that will be contracted and the corresponding contraction tree if so.

Returns:

psi – The densely represented wavefunction with dtype data.

Return type:

qarray

sample(*args, **kwargs)[source]

Sample the circuit given by gates, C times, using lightcone cancelling and caching marginal distribution results. This is a generator. This proceeds as a chain of marginal computations.

Assuming we have group_size=1, and some ordering of the qubits, \(\{q_0, q_1, q_2, q_3, \ldots\}\) we first compute:

\[p(q_0) = \mathrm{diag} \mathrm{Tr}_{1, 2, 3,\ldots} | \psi_{0} \rangle \langle \psi_{0} |\]

I.e. simply the probability distribution on a single qubit, conditioned on nothing. The subscript on \(\psi\) refers to the fact that we only need gates from the causal cone of qubit 0. From this we can sample an outcome, either 0 or 1, if we call this \(r_0\) we can then move on to the next marginal:

\[p(q_1 | r_0) = \mathrm{diag} \mathrm{Tr}_{2, 3,\ldots} \langle r_0 | \psi_{0, 1} \rangle \langle \psi_{0, 1} | r_0 \rangle\]

I.e. the probability distribution of the next qubit, given our prior result. We can sample from this to get \(r_1\). Then we compute:

\[p(q_2 | r_0 r_1) = \mathrm{diag} \mathrm{Tr}_{3,\ldots} \langle r_0 r_1 | \psi_{0, 1, 2} \rangle \langle \psi_{0, 1, 2} | r_0 r_1 \rangle\]

Eventually we will reach the ‘final marginal’, which we can compute as

\[|\langle r_0 r_1 r_2 r_3 \ldots | \psi \rangle|^2\]

since there is nothing left to trace out.

Parameters:
  • C (int) – The number of times to sample.

  • qubits (None or sequence of int, optional) – Which qubits to measure, defaults (None) to all qubits.

  • order (None or sequence of int, optional) – Which order to measure the qubits in, defaults (None) to an order based on greedily expanding the smallest reverse lightcone. If specified it should be a permutation of qubits.

  • group_size (int, optional) – How many qubits to group together into marginals, the larger this is the fewer marginals need to be computed, which can be faster at the cost of higher memory. The marginal themselves will each be of size 2**group_size.

  • max_marginal_storage (int, optional) – The total cumulative number of marginal probabilites to cache, once this is exceeded caching will be turned off.

  • seed (None or int, optional) – A random seed, passed to numpy.random.seed if given.

  • optimize (str, optional) – Contraction path optimizer to use for the marginals, shouldn’t be a non-reusable path optimizer as called on many different TNs. Passed to cotengra.array_contract_tree().

  • backend (str, optional) – Backend to perform the marginal contraction with, e.g. 'numpy', 'cupy' or 'jax'. Passed to cotengra.

  • dtype (str, optional) – Data type to cast the TN to before contraction.

  • simplify_sequence (str, optional) – Which local tensor network simplifications to perform and in which order, see full_simplify().

  • simplify_atol (float, optional) – The tolerance with which to compare to zero when applying full_simplify().

  • simplify_equalize_norms (bool, optional) – Actively renormalize tensor norms during simplification.

Yields:

bitstrings (sequence of str)

sample_rehearse(*args, **kwargs)[source]

Perform the preparations and contraction tree findings for sample(), caching various intermedidate objects, but don’t perform the main contractions.

Parameters:
  • qubits (None or sequence of int, optional) – Which qubits to measure, defaults (None) to all qubits.

  • order (None or sequence of int, optional) – Which order to measure the qubits in, defaults (None) to an order based on greedily expanding the smallest reverse lightcone.

  • group_size (int, optional) – How many qubits to group together into marginals, the larger this is the fewer marginals need to be computed, which can be faster at the cost of higher memory. The marginal’s size itself is exponential in group_size.

  • result (None or dict[int, str], optional) – Explicitly check the computational cost of this result, assumed to be all zeros if not given.

  • optimize (str, optional) – Contraction path optimizer to use for the marginals, shouldn’t be a non-reusable path optimizer as called on many different TNs. Passed to cotengra.array_contract_tree().

  • simplify_sequence (str, optional) – Which local tensor network simplifications to perform and in which order, see full_simplify().

  • simplify_atol (float, optional) – The tolerance with which to compare to zero when applying full_simplify().

  • simplify_equalize_norms (bool, optional) – Actively renormalize tensor norms during simplification.

  • progbar (bool, optional) – Whether to show the progress of finding each contraction tree.

Returns:

One contraction tree object per grouped marginal computation. The keys of the dict are the qubits the marginal is computed for, the values are a dict containing a representative simplified tensor network (key: ‘tn’) and the main contraction tree (key: ‘tree’).

Return type:

dict[tuple[int], dict]

sample_chaotic(*args, **kwargs)[source]

Sample from this circuit, assuming it to be chaotic. Which is to say, only compute and sample correctly from the final marginal, assuming that the distribution on the other qubits is uniform. Given marginal_qubits=5 for instance, for each sample a random bit-string \(r_0 r_1 r_2 \ldots r_{N - 6}\) for the remaining \(N - 5\) qubits will be chosen, then the final marginal will be computed as

\[p(q_{N-5}q_{N-4}q_{N-3}q_{N-2}q_{N-1} | r_0 r_1 r_2 \ldots r_{N-6}) = |\langle r_0 r_1 r_2 \ldots r_{N - 6} | \psi \rangle|^2\]

and then sampled from. Note the expression on the right hand side has 5 open indices here and so is a tensor, however if marginal_qubits is not too big then the cost of contracting this is very similar to a single amplitude.

Note

This method assumes the circuit is chaotic, if its not, then the samples produced will not be an accurate representation of the probability distribution.

Parameters:
  • C (int) – The number of times to sample.

  • marginal_qubits (int or sequence of int) – The number of qubits to treat as marginal, or the actual qubits. If an int is given then the qubits treated as marginal will be circuit.calc_qubit_ordering()[:marginal_qubits].

  • fix (None or dict[int, str], optional) – Measurement results on other qubits to fix. These will be randomly sampled if fix is not given or a qubit is missing.

  • seed (None or int, optional) – A random seed, passed to numpy.random.seed if given.

  • optimize (str, optional) – Contraction path optimizer to use for the marginal, can be a non-reusable path optimizer as only called once (though path won’t be cached for later use in that case).

  • backend (str, optional) – Backend to perform the marginal contraction with, e.g. 'numpy', 'cupy' or 'jax'. Passed to cotengra.

  • dtype (str, optional) – Data type to cast the TN to before contraction.

  • simplify_sequence (str, optional) – Which local tensor network simplifications to perform and in which order, see full_simplify().

  • simplify_atol (float, optional) – The tolerance with which to compare to zero when applying full_simplify().

  • simplify_equalize_norms (bool, optional) – Actively renormalize tensor norms during simplification.

Yields:

str

sample_chaotic_rehearse(*args, **kwargs)[source]

Rehearse chaotic sampling (perform just the TN simplifications and contraction tree finding).

Parameters:
  • marginal_qubits (int or sequence of int) – The number of qubits to treat as marginal, or the actual qubits. If an int is given then the qubits treated as marginal will be circuit.calc_qubit_ordering()[:marginal_qubits].

  • result (None or dict[int, str], optional) – Explicitly check the computational cost of this result, assumed to be all zeros if not given.

  • optimize (str, optional) – Contraction path optimizer to use for the marginal, can be a non-reusable path optimizer as only called once (though path won’t be cached for later use in that case).

  • simplify_sequence (str, optional) – Which local tensor network simplifications to perform and in which order, see full_simplify().

  • simplify_atol (float, optional) – The tolerance with which to compare to zero when applying full_simplify().

  • simplify_equalize_norms (bool, optional) – Actively renormalize tensor norms during simplification.

  • dtype (str, optional) – Data type to cast the TN to before contraction.

Returns:

The contraction path information for the main computation, the key is the qubits that formed the final marginal. The value is itself a dict with keys 'tn' - a representative tensor network - and 'tree' - the contraction tree.

Return type:

dict[tuple[int], dict]

amplitude(*args, **kwargs)[source]

Get the amplitude coefficient of bitstring b.

\[c_b = \langle b | \psi \rangle\]
Parameters:
  • b (str or sequence of int) – The bitstring to compute the transition amplitude for.

  • optimize (str, optional) – Contraction path optimizer to use for the amplitude, can be a non-reusable path optimizer as only called once (though path won’t be cached for later use in that case).

  • simplify_sequence (str, optional) – Which local tensor network simplifications to perform and in which order, see full_simplify().

  • simplify_atol (float, optional) – The tolerance with which to compare to zero when applying full_simplify().

  • simplify_equalize_norms (bool, optional) – Actively renormalize tensor norms during simplification.

  • backend (str, optional) – Backend to perform the contraction with, e.g. 'numpy', 'cupy' or 'jax'. Passed to cotengra.

  • dtype (str, optional) – Data type to cast the TN to before contraction.

  • rehearse (bool or "tn", optional) – If True, generate and cache the simplified tensor network and contraction tree but don’t actually perform the contraction. Returns a dict with keys "tn" and 'tree' with the tensor network that will be contracted and the corresponding contraction tree if so.

partial_trace(*args, **kwargs)[source]

Perform the partial trace on the circuit wavefunction, retaining only qubits in keep, and making use of reverse lightcone cancellation:

\[\rho_{\bar{q}} = Tr_{\bar{p}} |\psi_{\bar{q}} \rangle \langle \psi_{\bar{q}}|\]

Where \(\bar{q}\) is the set of qubits to keep, \(\psi_{\bar{q}}\) is the circuit wavefunction only with gates in the causal cone of this set, and \(\bar{p}\) is the remaining qubits.

Parameters:
  • keep (int or sequence of int) – The qubit(s) to keep as we trace out the rest.

  • optimize (str, optional) – Contraction path optimizer to use for the reduced density matrix, can be a non-reusable path optimizer as only called once (though path won’t be cached for later use in that case).

  • simplify_sequence (str, optional) – Which local tensor network simplifications to perform and in which order, see full_simplify().

  • simplify_atol (float, optional) – The tolerance with which to compare to zero when applying full_simplify().

  • simplify_equalize_norms (bool, optional) – Actively renormalize tensor norms during simplification.

  • backend (str, optional) – Backend to perform the marginal contraction with, e.g. 'numpy', 'cupy' or 'jax'. Passed to cotengra.

  • dtype (str, optional) – Data type to cast the TN to before contraction.

  • rehearse (bool or "tn", optional) – If True, generate and cache the simplified tensor network and contraction tree but don’t actually perform the contraction. Returns a dict with keys "tn" and 'tree' with the tensor network that will be contracted and the corresponding contraction tree if so.

Return type:

array or dict

property uni