Skip to content

API Reference

An index of the public surface. For what the knobs mean see Concepts, for execute()'s keywords see Circuit Execution, and for runnable code see Examples.

Gate and operator matrix inputs

Anything taking a one- or two-qubit gate or operator (apply_gate_1q, apply_gate_2q, expectation_1q, expectation_2q, apply_kraus_*) accepts a numpy array of dtype complex128: (2, 2) for a 1Q gate, (4, 4) for a 2Q gate, (n_ops, dim, dim) for a stack of Kraus operators. On a qudit site of dimension d, the shapes are (d, d) and (d₁·d₂, d₁·d₂).

A flat list of floats in column-major [re, im, re, im, ...] order is also accepted, with length 8 for a 1Q gate and 32 for a 2Q gate. It is deprecated; use numpy arrays.

Bitstring / qubit ordering

TensorWeaver is little-endian by qubit index: bitstring position i is qubit i. This is the opposite of Qiskit's convention, so bitstrings from mps.sample_bitstring(), mps.sample(), results.cstates, and results.histogram() appear reversed compared to Qiskit output. The same applies to the bitstring argument of mps.amplitude().

High-level

execute

Every keyword, with its default and when to change it, is documented in Circuit Execution.

tensorweaver.execute(circuit, nsamples=..., *, num_qubits=..., bonddim=..., entdim=..., max_bond_dim=..., sv_cutoff=..., algorithm=..., post_compress=..., strict_bonddim=..., seed=..., bitstrings=..., reorderqubits=..., fuse=..., fuse_threshold=..., canonicaldecompose=..., simplify=..., buffer=..., traversal=..., linalg=..., usegpu=..., usemkl=...)

TwSimulator

A reusable simulator. Configuration is fixed at construction; nsamples, seed, and num_qubits are per call.

Bases: LocalBackend

MPS quantum circuit simulator, backed by the TensorWeaver core.

Subclasses :class:mimiqcircuits.backends.LocalBackend, so the usual sim.execute(circuit, nsamples=..., seed=...) call works and returns a :class:mimiqcircuits.QCSResults. :meth:capabilities lists what it can serve.

Every knob below is set on the constructor and fixed for the life of the instance; build a new simulator to change one. Accepting them per call as well would mean distinguishing "not passed" from "passed the default" on every keyword.

Execution mode. The circuit decides, not a keyword. Trailing measurements are split off into a classical projection and do not count. If everything left is unitary, the state is evolved once and nsamples bitstrings are drawn from it (sampling mode, one entry in results.fidelities). If any surviving operation on qubits is non-unitary (a mid-circuit Measure or Reset, a noise channel, qubit loss), the circuit is re-evolved from scratch nsamples times (trajectory mode, one fidelity and one cstate per trajectory).

Parameters:

Name Type Description Default
bonddim int

Maximum MPS bond dimension, the memory and accuracy knob. Default 256. Raising it lets the state carry more entanglement before truncation, at a cost that grows steeply; lowering it truncates earlier and lowers the reported fidelity.

256
entdim int

Maximum bond dimension of the MPO that gates are fused into between state updates. Default 16. Larger values batch more gates per update. A gate whose operator rank exceeds entdim raises rather than being applied at reduced accuracy, so raise entdim if a circuit reports that.

16
sv_cutoff float | None

Singular-value floor applied when an MPO is compressed into the state. None (default) uses the library default 1e-7. Lower it toward machine precision for accuracy, raise it for speed.

None
mpo_cutoff float | None

Singular-value floor applied while gates are fused into the MPO. None (default) uses the library default 1e-15, which is essentially exact. Independent of sv_cutoff; there is rarely a reason to change it.

None
algorithm Algorithm

How each fused MPO is applied to the state. The names read as the method plus what it applies: "dmpo" is direct MPO application, "cmpo" canonical, and "vmpoa" / "vmpob" variational variants a and b. "vmpoa" (default) and "vmpob" run single- and two-site variational sweeps seeded by a zip-up. The default matches MPSSim's (MPO1z), so results are comparable with the incumbent MIMIQ engine without tuning. "dmpo" is the zip-up alone, the cheapest per application.

"cmpo" is a single pass like "dmpo", arranged so the weight it discards is chosen against the state's own Schmidt spectrum. Where the ceiling binds it keeps more of the state and tends to leave it narrower, which makes later gates cheaper too, at close to single-pass cost. It is also the accurate choice under usegpu=True. See the "Choosing an algorithm" page of the manual for how to decide.

'vmpoa'
strict_bonddim bool | None

Whether every intermediate bond must stay within bonddim while an MPO is applied. It never changes the bond dimension of the result, which is at most bonddim either way.

  • None (default): the algorithm's own default, strict for "dmpo", "vmpoa" and "vmpob", relaxed for "cmpo".
  • True, strict: the zip-up ("dmpo", and the seed of the two variational algorithms) cuts its carry to bonddim as it goes, so the memory a run needs follows from bonddim alone. Those cuts are made in a gauge where the singular values are not the state's Schmidt values, so they can drop weight the result did not need to lose. At bonddim < entdim (fused MPOs reach bond dimension entdim) a cut can wipe out the state altogether, so a strict run needs bonddim >= entdim. From there up, "dmpo" suffers most, above all with post_compress=False; the variational sweeps repair most of it. The reported fidelity shows the loss: a strict run is less accurate, not silently wrong.
  • False, relaxed: the zip-up's carry may grow to max(4 * bonddim, D) for an MPO of bond dimension D and is cut to bonddim afterwards, in canonical form. The result is exact whenever it fits under bonddim, and entdim may be anything, at the price of more memory and 1.5 to 4 times the time. For the variational algorithms, it seeds the sweeps from this relaxed zip-up: on 20-qubit random circuits "vmpoa" reached 5 to 15% lower infidelity than strict, and "vmpob" matched strict, at 1.4 to 4 times the time.

"cmpo" has no strict form, so algorithm="cmpo" with strict_bonddim=True raises :class:ValueError here. A run that resolves to strict (True, or None with "dmpo", "vmpoa" or "vmpob") with bonddim < entdim also raises :class:ValueError here, before any circuit runs: raise bonddim to entdim or above, lower entdim, or pass strict_bonddim=False. A value that is neither a bool nor None raises :class:TypeError. Applies on host and device.

None
post_compress bool

Run the global recond and compression sweep after every MPO application. Default True. The sweep restores canonical form, which is what keeps the next zip-up's singular values equal to the physical Schmidt weights. Turning it off defers one sweep to end of circuit: faster on circuits that stay well below bonddim, but bond dimension then creeps on zip-up noise until the zipper's intermediate, which carries the MPO bond index and not the state's, crosses bonddim, and truncation in the wrong gauge corrupts the state. Measured on a 16-qubit QFT at bonddim=64: mirror fidelity 1.0 with it on, 0.535 with it off, and the reported fidelity reads 1.0 either way.

True
seed int | None

Master RNG seed for every run this instance drives. None (default) draws fresh entropy per run. A seed= on :meth:execute overrides it.

None
buffer int

Depth of the gate-fusion pipeline. 0 (default) fuses and applies inline; a positive value runs fusion on a background thread that prepares up to buffer MPOs ahead while evolution applies them, overlapping the fusion of the next block with the application of the current one. Results are identical to buffer=0. The overlap is real parallelism because mps.apply_mpo releases the GIL, and a depth of 2 to 4 is enough because only one or two MPOs are ever in flight.

0
traversal Traversal

Order in which gates are walked while they are fused into MPOs. "sequential" (default) is the circuit's insertion order; "bfs" is a breadth-first topological order that groups mutually independent gates into layers; "dfs" is depth-first, following one dependency chain to its end before backtracking. All three produce the same simulated state and differ only in how gates batch into MPOs, which shifts the intermediate bond dimension and therefore the runtime. "bfs" tends to pack each MPO with more commuting gates, which helps on wide, shallow layers.

'sequential'
linalg Linalg | None

Linear-algebra library: "openblas", "mkl" or "faer". None (default) takes whichever engine this build ships, preferring OpenBLAS: it is vendored into the wheel, so it works from a plain pip install. Where the wheel has no OpenBLAS engine, which is the Windows wheel, the default resolves to MKL instead. "mkl" is Intel MKL, whose runtime the wheel cannot bundle and must come from the environment; if it is absent, the first use raises :class:ImportError with installation instructions. "faer" is a pure-Rust library with no system dependency, usually slower than the other two. A named engine the build does not have raises at first use rather than switching engine. An unknown name raises :class:ValueError. The resolved value is readable as sim.linalg.

None
usegpu bool

Run the state on an NVIDIA GPU. False by default. The whole MPS is moved into device memory once, evolved there, and moved back once, so a circuit pays one transfer at each end rather than one per gate. Requires algorithm="dmpo" or "cmpo", the two single-pass algorithms with a device implementation. The CUDA engine is one module whose host provider is OpenBLAS, so linalg must be unset or "openblas"; any other value raises :class:ValueError. Raises :class:ImportError at first use if this build has no CUDA engine or no GPU is reachable. Prefer "cmpo", the accurate one of the two.

Whether it is faster depends on the bond dimension. The device wins where the state is large and loses where it is small, because each site's truncated SVD carries a fixed launch cost that the host does not pay. Measured on an A30 against one Xeon core, on a 24-qubit brick-layer circuit: 0.8× at bonddim=64, 2.8× at 128, 8.2× at 256, 24× at 512. So below roughly 64 the GPU is slower, and the case for it grows with the problem. Both paths truncate identically, and the reported fidelities agree to every digit.

False
max_num_threads int | None

The most threads a run of this simulator uses, for the linear algebra and for sampling alike. None (default) leaves both to the process setting (see :func:thread_info). The cap is held for the length of each :meth:execute, :meth:evolve, :meth:evolvezerostate and :meth:expectation call and released after, so it never outlives the run. The thread counts are process-wide: simulators running at the same time on different Python threads share them, and each runs at the smallest cap any of them holds. Raises :class:ValueError below 1.

None
usemkl bool | None

Deprecated, use linalg. True means linalg="mkl" and False means linalg="openblas". Passing it emits a :class:DeprecationWarning, and passing it with a linalg that names a different engine raises :class:ValueError.

None

Reordering under traversal applies only to the fusable gates between state-reading or measuring instructions. Measurement, reset, channel, observable (Amplitude / BondDim / SchmidtRank / ExpectationValue), barrier and IfStatement stay put and act as synchronisation points. Within a gate run, two gates are ordered relative to each other only when they share a qubit.

Which engines a wheel carries depends on the platform, because the compute core binds the CBLAS and LAPACKE interfaces directly and no one provider supplies both everywhere: Linux x86-64 ships OpenBLAS, MKL and faer, macOS arm64 OpenBLAS and faer (MKL is x86-only), Windows x86-64 MKL and faer. faer is pure Rust and binds neither interface. This is why linalg defaults to None rather than to an engine name.

Each engine is a separate native module, so linalg and usegpu are per instance and simulators on different engines can run side by side in one process. Their objects do not interoperate: an :class:~tensorweaver.MPS, :class:~tensorweaver.MPO, Rng or :class:~tensorweaver.TwState built by one engine cannot be passed to a simulator or method using the other, which raises TypeError. To carry a state across engines, round-trip it through MPS.serialize() and MPS.deserialize(), which copies it.

Caveats

  • num_qubits= on :meth:execute is a TensorWeaver-specific kwarg (also accepted by :class:quantanium.QuantaniumQCS); :class:mimiqengines.MPSSimulator does not expose it. Code that drives the three simulators with a portable sim.execute(c, nsamples=100, seed=42) shape is unaffected.
  • Amplitudes requested in trajectory mode reflect the last shot's state, not an ensemble average, whenever the circuit contains a mid-circuit measure, reset or IfStatement.
  • RNG resolution priority: a call-site seed= beats the instance seed, which beats fresh entropy from :mod:secrets. The free function tensorweaver.execute(...) defaults to seed=42; this method resolves to a concrete int before forwarding, so the two surfaces never collide.
  • :meth:bind substitutes parameters and re-runs the full compile pipeline; there is no native parametric fast path.
  • :meth:evolve returns (state, TruncationLowerBound) and never promotes to ExactFidelity even when fid == 1.0, because each MPO application is an SVD truncation event whose fidelity is at best a lower bound.

Examples:

>>> import mimiqcircuits as mc
>>> from tensorweaver import TwSimulator
>>> sim = TwSimulator(bonddim=64)
>>> c = mc.Circuit()
>>> c.push(mc.GateH(), 0)
...
>>> c.push(mc.GateCX(), 0, 1)
...
>>> c.push(mc.Measure(), [0, 1], [0, 1])
...
>>> results = sim.execute(c, nsamples=100, seed=42)

usemkl property

Deprecated: whether this simulator runs on MKL. Use linalg.

capabilities()

Capability tokens this simulator can serve.

A copy, so a caller cannot mutate the class-level set.

limits()

Resource limits: the configured bonddim as max_bond_dim.

topology()

Connectivity assumed by the MPS evolution: all-to-all.

Long-range gates cost more bond dimension than neighbouring ones, but no routing or SWAP insertion is required of the caller.

build_state(nq, nb=0, nz=0, **_)

Allocate a fresh |0…0⟩ state on nq qubits.

The MPS is capped at this simulator's bonddim and built on this simulator's engine, so it can only be used with this simulator. nb and nz size the classical-bit and complex registers.

zerostate(nq, nb=0, nz=0)

Back-compat alias for :meth:build_state.

evolvezerostate(circuit, *, seed=None)

Evolve a fresh zero state through circuit and return it.

Returns (state, fidelity), the fidelity being a plain float lower bound on the evolved state. This is a single evolution, so a circuit with mid-circuit measurement or noise gives one trajectory, not an ensemble; use :meth:execute for that.

Seed resolution: call-site seed=, then the instance seed, then fresh cryptographic entropy from :mod:secrets.

compile(circuit)

Lower circuit for the MPS evolution loop.

Populates :attr:CompileMetadata.active_qubits. Decomposition to TensorWeaverBasis and fusion into MPOs happen later, in :meth:evolve. Qubit reordering is not done here: it is a pass, which :meth:execute runs before compiling when asked to (see :meth:reorder_pass).

default_passes()

No passes run by default, so a circuit runs as written.

reorder_pass(method=None, *, seed=None)

The pass execute(reorderqubits=...) adds.

A :class:~tensorweaver.TwReorderQubitsPass scored against this simulator's bonddim. method=None, which is what reorderqubits=True asks for, runs the whole search portfolio; a method name pins one search. To set the pass's alpha or margin, build it yourself and pass it through passes=.

simplify_pass()

The pass execute(simplify=True) adds.

A :class:~mimiqcircuits.SimplifyPass running :data:~mimiqcircuits.DEFAULT_SIMPLIFY_PASSES without resynthesize_2q. That pass rewrites every two-qubit block, which reorders the gates and makes the MPS evolution slower even when it saves a CX. To run it anyway, pass a SimplifyPass through passes=.

expectation(state, op, *qubits)

Compute ⟨ψ|op|ψ⟩ on state without mutating it.

Exact, with no sampling involved. Supports 1- and 2-qubit operators and a :class:PauliString of any length. qubits are the 0-based indices op acts on, in the operator's own leg order, and there must be exactly as many as the operator has qubits.

Raises:

Type Description
ValueError

if the qubit count does not match the operator, or if a dense operator acts on three or more qubits.

evolve(state, compiled, *, rng=None, callback=None, stopped=None)

Evolve state under compiled, in place.

Returns (state, TruncationLowerBound). The product of the per-step fidelities is always wrapped as :class:TruncationLowerBound, never collapsed to :class:ExactFidelity even at fid == 1.0, because that scalar bounds the fidelity from below rather than claiming the exact state was reproduced.

Raises:

Type Description
TypeError

if compiled is not a :class:CompiledCircuit.

apply_segment(state, insts, *, rng=None)

Fuse a run of loss-free instructions into MPOs and apply it.

Returns the truncation fidelity of the run. Every qubit in the run is present, so fusing it cannot bury an operation a fired loss should have dropped. The run is padded to the register width, so one that misses the high qubits still builds full-width MPOs.

apply_instruction(state, inst, *, rng=None)

Apply a single instruction the driver issues around a loss boundary.

These are a reload Reset, a Check or MeasureCheck SetBit, or a partial-loss rewrite. A multi-qubit gate has to be fused into an MPO, so it is routed through :meth:apply_segment; everything else applies directly to the MPS.

sample_kraus(state, channel, targets, *, rng=None)

Sample one branch of a Kraus channel against the live state and apply it.

Returns (fidelity, fired_operator). The operator returned is the original one, not a reconstruction, so a LossyOperator branch reaches the driver with its tag intact.

execute(circuit, *, nsamples=1000, seed=None, rng=None, num_qubits=None, passes=None, fuse=None, fuse_threshold=None, canonicaldecompose=None, reorderqubits=None, remove_swaps=None, simplify=None, reorderqubits_seed=None, callback=None, param_grid=None, strict_pass_order=True, stopped=None, progress=False)

Execute circuit and return :class:QCSResults.

The TensorWeaver configuration (bonddim, entdim, sv_cutoff, mpo_cutoff, algorithm, strict_bonddim, post_compress, buffer, traversal, linalg, usegpu) comes from the constructor, not from this call. Circuit preparation, qubit reordering included, comes from this call. Amplitudes and expectation values are requested by putting :class:Amplitude and :class:ExpectationValue operations in the circuit; they write into results.zstates.

Whether the run is a single evolution plus nsamples draws, or nsamples independent trajectories, follows from the circuit; see the class docstring.

Parameters:

Name Type Description Default
circuit Circuit | SweepExperiment | list

One circuit, a list of circuits to run in turn, or a :class:~mimiqcircuits.SweepExperiment, which runs its circuit once per point of its parameter table, as param_grid does.

required
nsamples int

Bitstrings to draw in sampling mode, or trajectories to run in trajectory mode. Default 1000.

1000
seed int | None

Master RNG seed for this run. Overrides the instance seed. Mutually exclusive with rng.

None
rng Random | None

A :class:random.Random to draw the run's seeds from. Mutually exclusive with seed.

None
num_qubits int | None

Width of the simulated register. None (default) infers it from the circuit. This keyword is TensorWeaver-specific.

None
passes PassPipeline | None

Extra circuit-preparation passes, appended after the ones the flags below select, as given.

None
fuse bool | None

Fuse runs of gates into GateCustom blocks before evolution (the client-side FusePass). Default None, which does not fuse. This is distinct from, and usually redundant with, the MPO fusion this simulator does internally while evolving.

None
fuse_threshold int | None

Minimum qubit count for fuse to act. Default None. On its own it asks to fuse; with fuse=False it raises ValueError.

None
canonicaldecompose bool | None

Decompose to the canonical basis before evolution. Default None, which does not decompose.

None
reorderqubits bool | str | None

Relabel the qubits before evolution to bring interacting ones closer together, which lowers bond-dimension growth. None (default) and False leave the layout alone. True runs the whole portfolio of layout searches and keeps the best one scored against bonddim, or the circuit's own layout when none clearly beats it. A method name ("greedy", "sa", "multilevel") pins one search. The pass is :meth:reorder_pass; to tune its alpha or margin, pass a :class:~tensorweaver.TwReorderQubitsPass through passes= instead. Results come back in your qubit numbering, and the search report lands in :attr:last_layout_report.

None
remove_swaps bool | None

Server-side only: raises when set locally.

None
simplify bool | None

Simplify the circuit before evolution with :class:~mimiqcircuits.SimplifyPass, which cancels inverse pairs, merges rotations and resynthesizes one-qubit blocks while keeping the unitary exactly (see :meth:simplify_pass). Default None, which does not simplify. It runs before fuse and reorderqubits, so those see the shorter circuit. To pick the simplification passes, pass a SimplifyPass through passes= instead.

None
reorderqubits_seed int | None

Seed for the layout search alone. None (default) draws it from the run's RNG. On its own it does not ask for reordering.

None
callback object | None

Called as callback(applied, total) as the evolution progresses. total is None because MPOs are fused lazily and the count is not known up front.

None
param_grid list[dict] | None

List of parameter dicts. Each is substituted into the circuit and executed with its own derived seed, and the return value becomes a list of results, one per grid point. A SweepExperiment does the same and checks that its table binds every free symbol of the circuit; pass one or the other.

None
strict_pass_order bool

Enforce the pass pipeline's declared ordering constraints. Default True.

True
stopped object | None

Cooperative cancellation handle checked during evolution.

None
progress bool

Show a progress display. Default False.

False

Returns:

Name Type Description
A QCSResults | list[QCSResults]

class:QCSResults, or a list of them when circuit is a list

QCSResults | list[QCSResults]

or a SweepExperiment, or when param_grid is given.

QCSResults | list[QCSResults]

cstates are always :class:BitString, in both sampling and

QCSResults | list[QCSResults]

trajectory mode.

Raises:

Type Description
TypeError

if both seed and rng are given, or if nsamples is not an integer.

ValueError

if nsamples is negative.

NotImplementedError

if remove_swaps is set on this call.

TwDirectSimulator

A simulator that applies a circuit one operation at a time and builds no MPO. Distant qubits are swapped next to each other and stay where they were moved; every read-out answers in your qubit numbering.

Bases: TwSimulator

MPS simulator that applies a circuit one operation at a time.

Where :class:~tensorweaver.TwSimulator fuses gates into matrix product operators and applies each one in a sweep, this simulator builds no MPO. Each gate updates the state directly:

  • a one-qubit gate acts on the site of its qubit;
  • a two-qubit gate acts on two neighbouring sites, contracted together and split back with one truncated SVD. Qubits that are not neighbours are first brought together with swaps of adjacent sites. Which of the two moves is routing's choice: by default the one that leaves the next gates closer, where its swaps cannot truncate, and otherwise the gate's second qubit, next to its first (for a CX, the target to the control);
  • a gate on three to max_gate_qubits qubits gathers them into one block of sites and acts there in one step. A larger gate is decomposed.

Qubits move and stay moved. Routing swaps are not undone. The state keeps track of where each qubit sits, so a second gate on the same pair needs no routing, and every read-out (sampling, measurement, amplitudes, expectation values) answers in your qubit numbering. :meth:evolvezerostate sorts the chain back at the end and returns the plain MPS, with qubit i on site i. :meth:execute skips that sort, since nothing it reads needs it.

Fidelity. Every swap is a truncated SVD at sv_cutoff and bonddim, like a gate, and what it discards is in the reported fidelity.

Not supported. BondDim, SchmidtRank and VonNeumannEntropy read a bond of the chain, and the chain is in whatever order routing left it, so circuits containing them are refused, as are noise channels on three or more qubits. There is no GPU path.

Which to use. This simulator pays one SVD per two-qubit gate, plus the routing swaps. :class:~tensorweaver.TwSimulator pays one sweep per fused block of gates. Circuits of local gates, or with few gates per qubit between read-outs, favour this one; deep, dense circuits at large bonddim favour MPO fusion.

Parameters:

Name Type Description Default
bonddim int

Maximum MPS bond dimension. Default 256.

256
sv_cutoff float | None

Singular values below this are discarded by every gate and swap. None (default) uses the library default, 1e-7.

None
max_gate_qubits int

The most qubits a gate may act on before it is decomposed. Default 4.

DEFAULT_MAX_GATE_QUBITS
routing Routing

Which qubit a two-qubit gate moves. "lookahead" (default) moves either one: whichever leaves the next 16 two-qubit gates closer, as long as every bond its swaps cross is at most half of bonddim, so that no swap can truncate. Elsewhere it moves the gate's second qubit next to its first. "target" always does that: for a CX, the target to the control. On the MQT Bench u3+cx circuits at 100 qubits the lookahead was 1.1 to 3.6x faster on 11 of 13 inputs, and up to 1.4x slower on the other two (transpiled qft and qftentangled). It is applied by this package, not the engine.

'lookahead'
seed int | None

Default master seed for :meth:execute. None (default) draws fresh entropy per run.

None
linalg Linalg | None

Linear-algebra library, "openblas", "mkl" or "faer", as for :class:~tensorweaver.TwSimulator. None (default) takes the preferred engine this build ships.

None
max_num_threads int | None

The most threads a run uses, as for :class:~tensorweaver.TwSimulator. None (default) leaves it to the process setting.

None
usemkl bool | None

Deprecated, use linalg, as for :class:~tensorweaver.TwSimulator.

None

Example::

import mimiqcircuits as mc
from tensorweaver import TwDirectSimulator

c = mc.Circuit()
c.push(mc.GateH(), 0)
# Qubits 0 and 9 are far apart on the chain: qubit 9 is swapped down
# next to qubit 0, and stays there for the next gate.
c.push(mc.GateCX(), 0, 9)
c.push(mc.GateCZ(), 9, 0)
c.push(mc.Measure(), range(10), range(10))

sim = TwDirectSimulator(bonddim=64)
results = sim.execute(c, nsamples=1000, seed=1)
print(results.histogram())

capabilities()

Capability tokens served: those of TwSimulator without bond_dim and schmidt_rank.

limits()

Resource limits: the configured bonddim as max_bond_dim.

can_handle(circuit)

Refuse what this simulator does not run, before any work is done.

On top of the checks every backend makes, refuses BondDim, SchmidtRank, VonNeumannEntropy and noise channels on three or more qubits.

compile(circuit)

Lower circuit as :meth:TwSimulator.compile does, after refusing what this simulator does not run.

Raises:

Type Description
ValueError

for BondDim, SchmidtRank, VonNeumannEntropy or a noise channel on three or more qubits.

build_state(nq, nb=0, nz=0, **_)

Allocate a fresh |0…0⟩ routed state on nq qubits.

evolvezerostate(circuit, *, seed=None)

Evolve a fresh zero state through circuit and return it.

Returns (state, fidelity). The chain is sorted back at the end, so state.mps is a plain MPS with qubit i on site i, and the fidelity includes the sorting swaps.

Raises:

Type Description
ValueError

if the circuit has an operation this simulator refuses.

apply_instruction(state, inst, *, rng=None)

Apply one instruction the runtime-loss driver issues, directly.

sample_kraus(state, channel, targets, *, rng=None)

Sample one branch of a Kraus channel against the live state and apply it.

TwState

Bases: State

Composite simulation state: MPS, classical bits, and z-vars.

Subclasses :class:mimiqcircuits.backends.State. Build one with :meth:TwSimulator.build_state or :meth:TwSimulator.evolvezerostate; constructing it directly is for advanced use, and requires passing the core the MPS came from.

Instructions mutate all three registers in place as a circuit is evolved: gates and channels act on mps, measurements write into c, and the observable operations write into z.

Attributes:

Name Type Description
mps PyMps

the tensor-network quantum register.

c list[int]

classical-bit register.

z list[complex]

complex z-variable register.

classical_bits property

Live view of the classical-bit register.

complex_values property

Live view of the complex z-register.

get_loss_state()

Per-qubit present/lost register.

Mutated only by the shared runtime-loss driver (:meth:LocalBackend.evolve_with_loss), and left at all-present on circuits without qubit loss.

amplitude(bitstring)

⟨bitstring|ψ⟩. Qubit 0 is the LSB (little-endian).

bitstring is read in the same qubit frame as the underlying MPS. Reorder passes rewrite the embedded bs of every Amplitude op when they fire (see :func:Circuit.reorder_qubits), so a caller issuing Amplitude(bs) from a circuit does not need to permute the bitstring; a caller reaching into this method with a hand-built bitstring on a reordered state does.

sample(nsamples, rng=None, *, seed=None)

Sample nsamples measurement outcomes as :class:BitString\ s.

Sampling does not collapse the state, so the same state can be sampled repeatedly. Either rng (a :class:random.Random) or seed (an int) may be passed, but not both: the native Rng takes a single integer, which is drawn from rng when one is given and from :mod:secrets when neither is.

Raises:

Type Description
TypeError

if both rng and seed are given, or if nsamples is not an integer.

ValueError

if nsamples is negative.

reset()

Always raises: resetting a state in place is not supported.

Rebuilding the MPS needs the bond-dimension cap, which is simulator configuration and not carried on the state. Call sim.build_state(nq, nb, nz) for a fresh state instead.

Raises:

Type Description
NotImplementedError

always.

TensorWeaverBasis

Bases: DecompositionBasis

Decomposition basis targeting TensorWeaver's native gate set.

Terminal operations, executed directly by TensorWeaver: - 1Q parametric: GateRX, GateRY, GateRZ, GateU - 1Q identity: GateID, skipped during execution - 2Q optimized: GateCX, GateCZ, GateISWAP - 2Q generic: any 2-qubit unitary gate, fused into an MPO from its matrix - Multi-controlled: Control(n, 1Q-gate), any number of controls - Measurement/reset: Measure, Reset, MeasureReset - Conditional: IfStatement, terminal when its inner operation is - Z-variable ops: Amplitude, BondDim, SchmidtRank, VonNeumannEntropy, ExpectationValue, all of which observe the state without changing it - Classical: AbstractClassical logic (Not, And, Or, Xor, SetBit0/SetBit1) and the z-register arithmetic Add, Multiply, Pow - Noise channels: any krauschannel subclass, Kraus or mixed unitary - Scheduling: Barrier, a gate-fusion boundary with no effect on the state - Annotations: AbstractAnnotation, which carries no simulation semantics

Anything else is decomposed through the canonical rewrite rule, which delegates to each gate's _decompose(). A gate that neither is terminal nor decomposes raises :class:DecompositionError.

Example

from mimiqcircuits import Circuit, GateCCX, GateH from mimiqcircuits.decomposition import decompose from tensorweaver.basis import TensorWeaverBasis c = Circuit() c.push(GateH(), 0) 1-qubit circuit with 1 instruction: └── H @ q[0] c.push(GateCCX(), 0, 1, 2) 3-qubit circuit with 2 instructions: ├── H @ q[0] └── C₂X @ q[0:1], q[2] decomposed = decompose(c, TensorWeaverBasis())

CCX stays as Control(2, GateX), dispatched to apply_controlled_gate

isterminal(op)

Whether op can be executed natively, without decomposing it.

decompose(op, qubits, bits, zvars)

Decompose a non-terminal operation through the canonical rewrite.

Raises:

Type Description
DecompositionError

if the canonical rewrite has no rule for op, which means the circuit cannot be lowered onto this basis.

TwDirectBasis

Bases: TensorWeaverBasis

Decomposition basis for :class:TwDirectSimulator.

Any unitary gate on at most max_gate_qubits qubits is executed as it is, from its matrix, including controlled gates and custom gates. A larger gate is decomposed through the canonical rewrite until its pieces fit. Everything else is as in :class:~tensorweaver.basis.TensorWeaverBasis, except that BondDim, SchmidtRank and VonNeumannEntropy are not terminal: :class:TwDirectSimulator does not run them.

Parameters:

Name Type Description Default
max_gate_qubits int

The most qubits a gate may act on before it is decomposed. Default :data:DEFAULT_MAX_GATE_QUBITS.

DEFAULT_MAX_GATE_QUBITS

isterminal(op)

Whether op is executed as it is, without decomposing it.

TwReorderQubitsPass

The qubit-reordering pass that execute(reorderqubits=...) runs. Build it yourself and pass it through passes= to set its options; see Qubit ordering.

Bases: AbstractPass

Reorder qubits to reduce MPS bond-dimension growth.

An MPS lays the qubits out on a line, so a gate between two distant qubits pushes entanglement through every site between them. This pass relabels the qubits with a permutation π chosen by the native choose_layout (whose result does not depend on the engine).

Searches minimise the weighted-distance cost Cα(π) = Σ |π(i) − π(j)|^α · w(i, j), which is smooth and cheap to update. What picks between their results is a different cost, one that knows the bond dimension the run will use: per bond, how many truncations happen there times how many bits each one has to discard against it. The circuit's own layout is always a candidate, so a search that finds nothing better leaves it alone. The rewritten circuit is equivalent to the original; the pipeline maps the results back to the user's qubit frame.

Parameters

method: None (default) runs the whole portfolio and keeps whichever result scores best. A single spelling ("greedy", "sa" / "simulated_annealing", "multilevel", or True for "greedy") pins one search, which is still scored the same way. alpha: Exponent in the distance the search minimises. None (default) sweeps it, since every result is scored afterwards and a pointless exponent simply loses. Only 1.0 has a meaning: there the sum is identically the number of bond truncations the layout causes. seed: Seed for the stochastic searches. None (default) draws one from the pass-context RNG. bonddim: The bond dimension the run will use, which is what makes the cost meaningful. None (default) reads it off the backend in the pass context, falling back to 256 when the context has none. margin: How much better than the circuit's own layout a candidate must score before it is applied. A layout that truncates nothing is applied whatever the margin.

See also

:class:AbstractQCSs.ReorderQubitsPass, the Julia analogue used by MPSSim.MPSSimulator, which wraps SOptimizer's search.

spec()

Report the pass identity "reorder_qubits" and its parameters.

seed is part of the spec only when one was given explicitly; a seed drawn from the pass context is not, since it belongs to the run rather than to the pass configuration.

preserves(feature)

Return True for every feature.

Relabelling qubits leaves the circuit's properties intact, and the pipeline's composed_perm step maps the output cstate back to the user's qubit frame.

apply(ctx, circuit)

Search for a layout and return the relabelled circuit.

Returns (circuit, PassResult). The result carries the chosen permutation, or None when nothing beat the circuit's own layout, in which case circuit comes back unchanged. Either way the native report lands in :attr:last_report and is logged at INFO; PassResult has nowhere to carry it.

MockTensorWeaver

MockTensorWeaver and MockTwSimulator are the same class, and MockState is the state it returns. See Without a licence.

Bases: TwSimulator

TensorWeaver's LocalBackend interface, with no simulation behind it.

Same constructor as :class:~tensorweaver.TwSimulator, same execute, same :meth:capabilities, same :meth:limits, same :meth:topology, same refusals. Results are real :class:~mimiqcircuits.QCSResults objects built by the shared LocalBackend driver and carrying all-zero samples, zero amplitudes and expectation values, and fidelity 1.0.

What it is for: exercising the MIMIQ data models end to end (circuit in, passes, compilation, the sampling and trajectory routing, QCSResults, BitString, the protobuf round-trip) on a machine with no licence. It is not an approximation of TensorWeaver; it computes nothing.

Every constructor argument is accepted and validated the same way, so a configuration TensorWeaver refuses is refused here too::

>>> from tensorweaver import MockTensorWeaver
>>> MockTensorWeaver(usegpu=True, algorithm="vmpoa")
Traceback (most recent call last):
...
ValueError: usegpu=True requires algorithm in ('dmpo', 'cmpo'), ...

and one it accepts is accepted::

>>> import mimiqcircuits as mc
>>> sim = MockTensorWeaver(bonddim=64)
>>> c = mc.Circuit()
>>> c.push(mc.GateH(), 0)
...
>>> c.push(mc.Measure(), 0, 0)
...
>>> res = sim.execute(c, nsamples=10)
>>> len(res.cstates)
10

build_state(nq, nb=0, nz=0, **_)

Allocate a :class:MockState of the requested register sizes.

The quantum register is a real |0…0⟩ MPS capped at bonddim; nb and nz size the classical and complex registers. usegpu is honoured as far as accepting it: nothing is uploaded, since nothing runs.

evolve(state, compiled, *, rng=None, callback=None, stopped=None)

Return (state, TruncationLowerBound(1.0)) without touching state.

The TypeError on a non-:class:CompiledCircuit is kept: it is part of the contract a caller may be testing, and it costs one check. callback is invoked once so a progress display sees a run that finished rather than one that never started.

expectation(state, op, *qubits)

Always 0j, for any operator on any qubits.

sample_kraus(state, channel, targets, *, rng=None)

Fire the channel's first branch, without applying it.

The driver reads the returned operator to decide what a loss event did, so it has to be one of the channel's own operators rather than a placeholder: a LossyOperator branch must still arrive tagged.

tensorweaver.MockState

Bases: TwState

A :class:~tensorweaver.TwState that is never evolved.

The MPS underneath is genuine: |0…0⟩ at the simulator's bond dimension, with real site tensors, bond dimensions and canonical form. So anything reading the state back gets the real data model. It simply never has a gate applied to it.

:meth:sample is the one read-out that has to be reimplemented: the native sampler is part of the compute kernels and is absent from the public build.

sample(nsamples, rng=None, *, seed=None)

nsamples all-zero :class:~mimiqcircuits.BitString\ s.

Keeps the real sampler's argument checks, nsamples and the rng/seed exclusivity, so a bad call is refused here exactly as it would be there. rng and seed are otherwise unused: the result does not depend on them.

amplitude(bitstring)

Always 0j.

The untouched state would answer 1 for |0…0⟩ and 0 elsewhere, which reads as a result and is not one. A flat zero cannot be mistaken for a simulation.


Core Classes

MPS, MPO, RoutedMPS and Rng are native classes. The tables below index their methods; each method's own docstring is available with help().

MPS

A quantum state as a chain of tensors with bounded bond dimension.

Constructors

Method Description
MPS.zero_state(num_qubits, max_bond_dim) The \|00...0⟩ state
MPS.product_state(states, max_bond_dim) From a list of 0/1 values
MPS.random_maxent_state(num_qubits, max_bond_dim, rng) Random maximally entangled state
MPS.zero_state_qudits(physical_dims, max_bond_dim) Qudit \|0…0⟩ on sites of the given per-site dimensions (each ≥ 2)
MPS.product_state_qudits(digits, physical_dims, max_bond_dim) Qudit product state from per-site levels (digits[i] < physical_dims[i])
MPS.deserialize(data) From protobuf bytes

Sites carry any physical dimension d ≥ 2, reported per site by mps.physical_dims. A 2×2 or 4×4 qubit gate applied to a d > 2 site acts on the {\|0⟩, \|1⟩} subspace. See example 13.

Gate application (returns fidelity; always 1.0 for single-qubit gates)

Method Description
mps.apply_rx(theta, qubit) RX rotation
mps.apply_ry(theta, qubit) RY rotation
mps.apply_rz(lmbda, qubit) RZ rotation
mps.apply_u(theta, phi, lmbda, gamma, qubit) General U gate
mps.apply_gate_1q(gate, qubit) Arbitrary 1-qubit gate
mps.apply_gate_2q(gate, q1, q2, *, center="right") Arbitrary 2-qubit gate. Skips the post-application compression sweep; pair with mps.compress(). center selects which site holds the orthogonality center afterwards
mps.apply_two_qubit_gate(...) Deprecated alias for apply_gate_2q; emits a DeprecationWarning

MPO application (returns fidelity, below 1.0 when truncation discards weight)

Method Description
mps.apply_mpo(mpo, sv_cutoff=None, algorithm=None, post_compress=True, *, strict_bonddim=None) Apply an operator. algorithm is "dmpo" (the default at this level), "cmpo", "vmpoa", or "vmpob". strict_bonddim says whether intermediate bonds may exceed the state's max_bond_dim; None is strict for all but "cmpo". Strict keeps memory at max_bond_dim and can cost accuracy, which the returned fidelity shows; "cmpo" with True raises BondDimensionError. See Strict and relaxed bond dimension
mps.apply_mpo_dmpo(mpo, sv_cutoff=None, *, strict_bonddim=None) Wrapper for algorithm="dmpo"
mps.apply_mpo_cmpo(mpo, sv_cutoff=None, *, strict_bonddim=None) Wrapper for algorithm="cmpo"
mps.apply_mpo_vmpoa(mpo, sv_cutoff=None, *, strict_bonddim=None) Wrapper for algorithm="vmpoa"
mps.apply_mpo_vmpob(mpo, sv_cutoff=None, *, strict_bonddim=None) Wrapper for algorithm="vmpob"
mps.compress(sv_cutoff=None) Global compression sweep, restoring canonical form. None or non-positive uses the library default 1e-7

The default algorithm differs by level

apply_mpo defaults to "dmpo", the low-level primitive's own default. TwSimulator and execute() default to "vmpoa", which is more accurate at a given bond dimension. Driving the MPS yourself does not inherit the simulator's choice.

Deferred compression

Skipping the per-call compression sweep and running one pass at a quiet point is faster for a batch of two-qubit gates:

import numpy as np
import tensorweaver as tw

mps = tw.MPS.zero_state(64, max_bond_dim=256)
mpo = tw.MPO.identity(64, max_bond_dim=16)

mpo.apply_cx(0, 1)
mpo.apply_cz(2, 3)
mps.apply_mpo(mpo, post_compress=False)

gate = np.eye(4, dtype=np.complex128)      # any 4x4 unitary
mps.apply_gate_2q(gate, 4, 5)

# One sweep at quiescence: before measuring, reading an observable,
# or at the end of the circuit.
mps.compress()

Deferring the sweep leaves the state out of canonical form, so a later truncation compares singular values that are no longer the physical Schmidt weights. Batch only gates you know keep the bond dimension well below the cap, and prefer "cmpo" or "vmpoa" if unsure. See canonical form.

Noise channels (return (fidelity, index), where index is the 0-based sampled Kraus operator or unitary)

Method Description
mps.apply_kraus_1q(kraus_ops, site, rng, *, validate=True) General 1-qubit Kraus channel
mps.apply_kraus_2q(kraus_ops, site1, site2, rng, *, validate=True) General 2-qubit Kraus channel
mps.apply_mixed_unitary_1q(unitaries, probs, site, rng, *, validate=True) 1-qubit mixed-unitary channel
mps.apply_mixed_unitary_2q(unitaries, probs, site1, site2, rng, *, validate=True) 2-qubit mixed-unitary channel

With validate=True (the default), Kraus channels check completeness (‖Σᵢ Kᵢ† Kᵢ − I‖_F ≤ 1e-8) and mixed-unitary channels check that the probabilities sum to 1 within 1e-8. Failures raise KrausCompletenessError. An incomplete operator set produces a non-physical state silently, which is why the check is on by default; validate=False is for post-selection and projective channels, where incompleteness is intended.

Measurement and sampling

Method Returns
mps.measure_qubit(rng, qubit) int, the measured bit; collapses the state
mps.sample_bitstring(rng) list[int], one bitstring
mps.sample(rng, num_samples) list[list[int]]
mps.sample_packed(rng, num_samples) bytes of length num_samples * num_qubits, one byte per site per shot, qubit 0 first
mps.amplitude(bitstring) complex, ⟨bitstring\|ψ⟩
mps.reset_qubit(rng, qubit) int, the pre-reset outcome; leaves the qubit in |0⟩

Observables

Method Returns
mps.expectation_1q(op, site) complex, ⟨ψ\|O\|ψ⟩ for a 1-qubit operator
mps.expectation_2q(op, site1, site2) complex, for a 2-qubit operator
mps.expectation_prod(ops, sites) complex, for a product of single-site operators
mps.schmidt_values(bond) ndarray[float64], singular values at the bond, decreasing
mps.schmidt_rank(bond, cutoff=1e-15) int, singular values above cutoff; minimum 1
mps.von_neumann_entropy(bond) float, S = -Σ pᵢ ln pᵢ in nats
mps.all_entropies() list[float], the entropy of every interior bond

Properties

Property Type
mps.num_qubits int
mps.physical_dims list[int], per-site dimension
mps.max_bond_dim int, the configured cap
mps.max_used_bond_dim int, the largest bond actually in use
mps.orth_center int, current orthogonality center
mps.bond_dim(i) int, bond dimension at cut i
mps.all_bond_dims() list[int]
mps.norm_squared() float
mps.normalize() float, the norm before normalising

Tensor access and copying

Method Description
mps.tensor(site) ndarray[complex128] of shape (left_bond, phys, right_bond), Fortran order
mps.set_tensor(site, arr) Overwrite a site tensor. Can invalidate canonical form; follow with recanonicalize()
mps.tensor_element(site, left, phys, right) complex, one entry
mps.shift_orth_center(target) Move the orthogonality center
mps.recanonicalize(target=0) Restore canonical form with the center at target
mps.clone_mps() Deep copy; copy.copy and copy.deepcopy also work
mps.overlap(other) complex, ⟨ψ\|φ⟩
mps.fidelity(other) float, \|⟨ψ\|φ⟩\|²
mps.serialize() bytes (protobuf)

Device residency

Present on every engine, so a caller asks rather than testing for the attribute. Only to_device() needs the CUDA engine; the other two answer on a CPU engine, where a state is already where they want it. See GPU engine.

Method Description
mps.to_device() Move the state into device memory. Raises GpuError on an engine with no device path, or when no GPU is reachable
mps.to_host() Move it back. A no-op on a host-resident state
mps.is_on_device bool, whether the state currently lives on the device

TwSimulator(usegpu=True) does both moves for you, so these are for drivers built by hand.


MPO

An operator as a chain of tensors, applied to a state with mps.apply_mpo(mpo).

Constructors

Method Description
MPO.identity(num_qubits, max_bond_dim) Identity operator
MPO.identity_with_dims(physical_dims, max_bond_dim) Identity on sites of the given per-site dimensions (each ≥ 2)
MPO.deserialize(data) From protobuf bytes

Gate application (returns True, or False when the gate would push the bond dimension past max_bond_dim)

Every method takes a keyword-only side selecting the physical leg the gate multiplies into: "output" (the default) gives M' = G·M, "input" gives M' = M·G. The two-qubit methods also take sv_cutoff.

Method Description
mpo.apply_gate_1q(gate, site, *, side="output") Arbitrary 1-qubit gate
mpo.apply_gate_2q(gate, q1, q2, *, side="output", sv_cutoff=None) Arbitrary 2-qubit gate
mpo.apply_cx(control, target, *, side="output", sv_cutoff=None) CNOT
mpo.apply_cz(q1, q2, *, side="output", sv_cutoff=None) CZ
mpo.apply_iswap(q1, q2, *, side="output", sv_cutoff=None) iSWAP
mpo.apply_rx(theta, site, *, side="output") RX rotation
mpo.apply_ry(theta, site, *, side="output") RY rotation
mpo.apply_rz(lmbda, site, *, side="output") RZ rotation
mpo.apply_u(theta, phi, lmbda, gamma, site, *, side="output") General U gate
mpo.apply_controlled_gate(unitary, controls, target, *, side="output", sv_cutoff=None) Multi-controlled gate, any number of controls

Normalisation

Method Description
mpo.ensure_operator_norm() Convert to operator norm (identity = δ). No-op if already there
mpo.ensure_trace_norm() Convert to trace norm (Tr(A†A) = 1 per site). No-op if already there

Properties and other methods

Member Description
mpo.num_qubits int
mpo.physical_dims list[int], per-site dimension
mpo.max_bond_dim int, the configured cap
mpo.max_used_bond_dim int, the largest bond actually in use
mpo.bond_dim(i) int, bond dimension at cut i
mpo.all_bond_dims() list[int]
mpo.norm str, 'Operator' or 'Trace'
mpo.tensor(site) ndarray[complex128] of shape (left_bond, d_in, d_out, right_bond), Fortran order
mpo.set_tensor(site, arr) Overwrite a site tensor
mpo.tensor_element(site, left, s_in, s_out, right) complex, one entry
mpo.shift_orth_center(target) Move the orthogonality center
mpo.clone_mpo() Deep copy
mpo.dagger() MPO, the adjoint (the inverse for unitary operators)
mpo.serialize() bytes (protobuf)

RoutedMPS

The state behind TwDirectSimulator: an MPS whose qubits move along the chain as gates are routed, with the map from each qubit to its site. Every method takes 0-based logical qubits and every read-out answers in that numbering. No method builds an MPO.

import numpy as np
from tensorweaver import RoutedMPS

state = RoutedMPS.zero_state(6, 32)
h = np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2)
cx = np.eye(4, dtype=complex)[[0, 1, 3, 2]]
state.apply_gate(h, [0])
state.apply_gate(cx, [0, 5])    # qubit 5 is swapped next to qubit 0, and stays
print(state.layout)             # [0, 2, 3, 4, 5, 1]: qubit 5 sits on site 1
mps, fidelity = state.to_mps()  # sort back: qubit i on site i

Constructors

Method Description
RoutedMPS.zero_state(num_qubits, max_bond_dim) \|0…0⟩, qubit i on site i
RoutedMPS.from_mps(mps) A copy of an MPS, qubit i on site i

Operations (each returns the truncation fidelity, routing swaps included)

Method Description
state.apply_gate(gate, qubits, sv_cutoff=None) D×D gate on any number of qubits, qubits[0] the most significant leg
state.apply_kraus(kraus_ops, qubits, rng, *, validate=True, sv_cutoff=None) One sampled Kraus branch on one or two qubits; returns (fidelity, index)
state.apply_mixed_unitary(unitaries, probs, qubits, rng, *, validate=True, sv_cutoff=None) One sampled unitary on one or two qubits; returns (fidelity, index)
state.measure_qubit(rng, qubit) Measure and collapse; returns the level
state.reset_qubit(rng, qubit) Measure, then reset to \|0⟩; returns the measured level

Read-outs, in logical order

Method Description
state.amplitude(bitstring) complex, ⟨bitstring\|ψ⟩
state.sample_packed(rng, num_samples) bytes, one level per qubit per shot
state.expectation_1q(op, qubit) complex
state.expectation_2q(op, q1, q2) complex, q1 the most significant leg
state.expectation_prod(ops, qubits) complex, product of one-qubit operators

Layout and properties

Member Description
state.layout list[int], entry q is the site holding qubit q
state.is_ordered bool, whether qubit i is on site i
state.restore_order(sv_cutoff=None) Sort back with the fewest adjacent swaps; returns their fidelity
state.to_mps(sv_cutoff=None) Sort back, then return (MPS, fidelity)
state.num_qubits, state.physical_dims Size and per-qubit dimension, in logical order
state.max_bond_dim, state.max_used_bond_dim The cap and the largest bond in use
state.all_bond_dims() list[int], bond dimensions in site order, outer bonds included

Rng

A seeded generator for reproducible measurement and sampling. The same seed always produces the same sequence.

import tensorweaver as tw

mps = tw.MPS.zero_state(4, max_bond_dim=16)
rng = tw.Rng(seed=42)
bits = mps.sample_bitstring(rng)

An Rng belongs to the engine that created it and must be used with states from that same engine.


Utility Functions

Function Description
tensorweaver.optimize_ordering(pairs, num_qubits, method="greedy", alpha=1.0, seed=42, fixed_cuts=None) Qubit layout for a weighted interaction graph, from one search. pairs is a list of (i, j, weight); fixed_cuts lists bonds no qubit may cross. Returns (permutation, cost). reorderqubits= does not call it: it runs choose_layout
tensorweaver.extract_pairs(circuit) The weighted interaction graph of a circuit, as (i, j, weight) triples
tensorweaver.reorder_qubits(circuit, perm) Rewrite a circuit into the layout perm
tensorweaver.choose_layout(pairs, num_qubits, max_bond_dim, fixed_cuts=None, margin=2.0, seed=42, methods=None, alphas=None) Run the portfolio of layout searches and pick a layout, or keep the caller's, scoring against max_bond_dim. pairs is a list of (i, j, events, bits). Returns (perm, report)
tensorweaver.layout_score(perm, pairs, num_qubits, max_bond_dim) Score one layout: a dict with cost, events, max_bits, budget_bits and bonds_over_budget
tensorweaver.operator_entanglement(gate, d1=2, d2=2) Entangling weight of a two-qudit gate, in bits
tensorweaver.examples_dir() Path to the bundled example scripts
tensorweaver.docs_dir() Path to the bundled documentation site
tensorweaver.library_info() Build information string
tensorweaver.rs_version() Version of the native core
tensorweaver.compile_info() Build configuration, formatted for reading
tensorweaver.compile_info_json() The same, as a JSON string
tensorweaver.has_kernels() bool, False only on the unlicensed public wheel, which has no compute kernels. See Without a licence
tensorweaver.__version__ Version of the installed mimiq-tensorweaver package
tensorweaver.thread_info() dict: what each thread pool resolved to, and which variable decided it
tensorweaver.thread_info_json() The same, as the raw JSON string
tensorweaver._engine.available_engines() Engine names this build ships, from "openblas", "mkl", "faer" and "cuda". A name here means the module is present, not that it loads
tensorweaver._engine.get_core(linalg=None, usegpu=False) The engine module for an engine choice. get_core(usegpu=True) raises ImportError, naming the fix, when this build has no CUDA engine or no GPU is reachable. Each engine module also has gpu_available(), which is False on every CPU engine

Instruction Dispatch

Applying one mimiqcircuits instruction at a time, for drivers built by hand.

Function Description
tensorweaver.apply_instruction_mps(mps, rng, inst, cstate, zstate) Apply an instruction to an MPS. Returns the fidelity
tensorweaver.apply_instruction_mpo(mpo, inst, *, mpo_cutoff=None) Accumulate an instruction into an MPO. Returns True, or False when the gate would push the MPO past its max_bond_dim

Plotting

tensorweaver.visualization needs the visualization extra (pip install 'mimiq-tensorweaver[visualization]'); import tensorweaver alone never loads matplotlib. For a guided tour, see Visualization.

tensorweaver.visualization.plotbonds(state, mimiqstyle=True)

Plot the bond dimension and the entanglement entropy along the chain.

The top panel shows the bond dimension χ of every interior bond on a log-2 scale, with max_bond_dim as a dashed line. A bond that has reached that line is drawn red: it holds as many Schmidt values as the simulator allows, so any further weight on that cut was discarded. A state can also fit its cap exactly, a GHZ state at χ = 2 for one, so red marks where truncation can happen; the fidelity of the run and :func:plotschmidt tell how much it cost. Bonds below the line can still be truncated by sv_cutoff. The bottom panel shows the von Neumann entropy of each cut, in nats, which tells how much entanglement the bond carries.

Parameters:

Name Type Description Default
state TwState | MPS

a :class:TwState, such as the first value returned by :meth:TwSimulator.evolvezerostate, or an :class:MPS.

required
mimiqstyle bool

draw with the MIMIQ matplotlib style (default True); False keeps the active style.

True

Returns:

Name Type Description
The Figure

class:matplotlib.figure.Figure.

Example::

import mimiqcircuits as mc
import tensorweaver as tw
from tensorweaver.visualization import plotbonds

# A 12-qubit brickwork of RY rotations and CX bricks.
c = mc.Circuit()
for layer in range(6):
    for q in range(12):
        c.push(mc.GateRY(0.5 + 0.1 * q), q)
    for q in range(layer % 2, 11, 2):
        c.push(mc.GateCX(), q, q + 1)

# χ = 4 is too small for this depth: the middle bonds reach the cap.
state, _ = tw.TwSimulator(bonddim=4, strict_bonddim=False).evolvezerostate(c)
fig = plotbonds(state)
fig.savefig("bonds.png")

tensorweaver.visualization.plotschmidt(state, bond, mimiqstyle=True)

Plot the Schmidt spectrum of one bond on a log scale.

Each bar is a Schmidt weight λᵢ², the probability carried by one Schmidt vector of the cut. A spectrum that is still high where it ends means the bond dimension is cutting off weight that matters; a spectrum that falls off fast means the state is well represented at this bond. Reading the spectrum moves the orthogonality center to bond, which changes no amplitude.

Parameters:

Name Type Description Default
state TwState | MPS

a :class:TwState or an :class:MPS.

required
bond int

the bond, k being the cut between qubits k-1 and k.

required
mimiqstyle bool

draw with the MIMIQ matplotlib style (default True); False keeps the active style.

True

Returns:

Name Type Description
The Figure

class:matplotlib.figure.Figure.

Raises:

Type Description
QubitIndexError

if bond is out of range.

Example::

# The bond plotbonds showed at the cap, in the example above.
fig = plotschmidt(state, 6)

tensorweaver.visualization.BondWindow

A window that follows the bond structure of a :class:TwState as it evolves.

The window shows what :func:plotbonds shows. It is drawn when created and changes only when :meth:update is called: the state is read as it is then, and the picture moves there gradually. Bond dimensions grow on the log-2 axis, bars fade to red as they reach the cap, and the entropies follow. Between two updates the state can be evolved by any number of instructions.

The window needs an interactive matplotlib backend such as QtAgg or TkAgg, which in turn needs PyQt / PySide or Tk installed. In Jupyter, run %matplotlib qt first. With a non-interactive backend nothing opens and :meth:update redraws the figure in one step. In a script, the window only repaints during :meth:update; end with matplotlib.pyplot.show() to keep it open and responsive.

Parameters:

Name Type Description Default
state TwState | MPS

the :class:TwState (or :class:MPS) to follow. Reading it moves the orthogonality center, which changes no amplitude.

required
mimiqstyle bool

draw with the MIMIQ matplotlib style (default True).

True
duration float

default length of an :meth:update animation, in seconds.

1.0

Example::

import matplotlib.pyplot as plt
import mimiqcircuits as mc
import tensorweaver as tw
from tensorweaver.visualization import BondWindow

sim = tw.TwSimulator(bonddim=16, strict_bonddim=False)
state = sim.build_state(20)          # |0...0>: every bond at χ = 1
window = BondWindow(state)

layer = mc.Circuit()
for q in range(20):
    layer.push(mc.GateRY(0.7), q)
for q in range(19):
    layer.push(mc.GateCX(), q, q + 1)
step = sim.compile(layer)

for _ in range(6):
    sim.evolve(state, step)          # as many gates as you like...
    window.update()                  # ...then one animation to the new state

plt.show()                           # keep the window open at the end

update(duration=None)

Move the picture to the watched state as it is now.

Parameters:

Name Type Description Default
duration float | None

length of the animation in seconds; defaults to the window's duration. 0 jumps straight to the new picture.

None

Raises:

Type Description
RuntimeError

if the window has been closed.

Exceptions

All inherit from TensorWeaverError.

Exception Raised when
TensorWeaverError Base class for everything below
QubitIndexError A qubit or site index is out of range. Also a ValueError
BondDimensionError A bond dimension constraint cannot be met
KrausCompletenessError A validated channel's operators do not sum to identity, or its probabilities do not sum to 1
GateShapeError A gate or operator array has the wrong shape or dtype
SerializationError Protobuf encoding or decoding failed
GpuError A GPU operation failed, or the device was asked for and is not there
LicenseError No valid license was found

Qiskit

See the Qiskit page for usage.

Object Description
tensorweaver.qiskit.TensorWeaverBackend(*, name="tensorweaver", num_qubits=64, **tw_options) A Qiskit BackendV2 backed by TwSimulator
tensorweaver.qiskit.TensorWeaverEstimator(*, simulator=None, method="auto", trajectories=None, shots=None, emulate_shot_noise=False, default_precision=0.0, reorderqubits=None, seed=None, **tw_options) An EstimatorV2 reading expectation values from the MPS. trajectories= / shots= set the budget for a circuit that ends in an ensemble