Skip to content

MockTensorWeaver

MockTensorWeaver is TensorWeaver's interface with nothing behind it. It takes the same constructor and the same execute call as TwSimulator, refuses exactly what TwSimulator refuses, and returns a real QCSResults full of nothing: all-zero samples, zero amplitudes, fidelity 1.0.

It is for validating the data models, what a MIMIQ backend accepts and what it hands back, on a machine that has no licence. That is a common shape of acceptance test: a customer's CI checks that its circuits build, that its options are accepted, that its results parse and round-trip, and it has no interest in the physics and no wish to pay for an hour of simulation to get there.

import mimiqcircuits as mc
from tensorweaver import MockTensorWeaver

sim = MockTensorWeaver(bonddim=64, algorithm="cmpo")

c = mc.Circuit()
c.push(mc.GateH(), 0)
c.push(mc.GateCX(), 0, 1)
c.push(mc.Measure(), [0, 1], [0, 1])

res = sim.execute(c, nsamples=1000, seed=42)

res.simulator        # 'MockTensorWeaver'
len(res.cstates)     # 1000, every one a real BitString
res.histogram()      # {bs"00": 1000}
res.saveproto("out.pb")

Swapping it for the real thing is a one-line change, because the two take the same arguments:

from tensorweaver import TwSimulator as Sim      # licensed
from tensorweaver import MockTensorWeaver as Sim # unlicensed CI

What it guarantees

It subclasses TwSimulator and overrides only the places where physics would happen: _evolve_circuit (the chokepoint every evolution route runs through), build_state, evolve, expectation and the three runtime-loss driver hooks. Everything that defines the interface is inherited, so the two cannot drift apart:

  • the constructor, with all its keywords and all its ValueError and TypeError checks: an algorithm TensorWeaver rejects is rejected here;
  • capabilities(), the same seventeen tokens, so the anti-capability sweep every MIMIQ backend runs gives identical verdicts;
  • limits(), topology(), can_handle(), default_passes();
  • execute(), with all its keywords, its seed/rng TypeError, its layout search for reorderqubits, and its NotImplementedError on the server-side remove_swaps;
  • the whole LocalBackend execution driver, which decides between the sampling and trajectory routes from the circuit, runs the pass pipeline, and builds the QCSResults itself.

tests/test_mock.py asserts those methods are not overridden (parity holds because they are inherited, so comparing their signatures would compare a function with itself), and separately records the expected argument names, so a knob added to one and not the other fails in CI rather than on a customer's machine.

What it does not do is compute. It is not a fast approximation, a low-fidelity mode, or a small-bond-dimension shortcut. Nothing is simulated.

The public wheel

MockTensorWeaver is in every build, licensed or not, so one test suite can be run against both. It is also the only backend in the wheel published on public PyPI:

pip install mimiq-tensorweaver

No token, no index URL, no licence file. That wheel is the only one built with --features no-kernels: gate application on the MPS and the MPO, MPO application, compression and its truncation rule, the quantum channels and sampling are not in the binary in any form, having been kept from the compiler by #[cfg] rather than stripped afterwards. TwSimulator refuses to construct there and says so.

What the public wheel does carry is the MPS itself. MPS and MPO are the same native types, with their constructors, site tensors, bond dimensions, canonical form (the QR/LQ sweeps, which truncate nothing), norms, amplitudes, overlaps and serialization:

import tensorweaver as tw

tw.has_kernels()                 # False on the public wheel, True on a licensed one

mps = tw.MPS.zero_state(6, 32)
mps.recanonicalize(0)
mps.all_bond_dims()              # [1, 1, 1, 1, 1, 1, 1]
mps.overlap(mps)                 # 1.0
tw.MPS.deserialize(mps.serialize())

mps.von_neumann_entropy(2)       # entanglement of a cut, in nats

mps.apply_gate_1q(...)           # AttributeError: the method is not there

Measuring entanglement

The Schmidt spectrum of a cut is a property of the state, not a step in evolving one, so the entanglement read-outs are in every build:

mps = tw.MPS.deserialize(open("state.pb", "rb").read())

mps.schmidt_values(2)        # the spectrum of the cut at bond 2
mps.schmidt_rank(2, 1e-12)   # how many values clear a cutoff
mps.von_neumann_entropy(2)   # -Σ p ln p over the squared values, in nats
mps.all_entropies()          # every interior cut at once
tw.operator_entanglement(cx) # and the same measure for a gate, in bits

examples/00_mps_structure prints a state's whole entropy profile cut by cut, against the ceiling each cut's bond dimension allows, which is how you judge whether a run's bonddim was binding. Note the diagnostic is one-sided: a cut below its allocation is definitely not limited, but a cut that fills its allocation may be either truncated or exactly representable: GHZ fills χ=2 at every cut and loses nothing.

They go through a values-only singular-value routine that never returns the factors a truncation would need; choosing what to discard is the compression policy and stays in the licensed build. So an unlicensed install can open a state a licensed run produced and read its entanglement structure (where the correlations are, how much bond dimension each cut is really using) without being able to evolve it.

Saving and loading states

MPS.serialize() and MPS.deserialize() are protobuf, and the conversion module is in every build. So a state evolved on a licensed machine can be opened on an unlicensed one:

# on the licensed machine
state, _ = sim.evolvezerostate(circuit)
open("state.pb", "wb").write(state.mps.serialize())

# anywhere, with a plain pip install and no licence
mps = tw.MPS.deserialize(open("state.pb", "rb").read())
mps.num_qubits, mps.all_bond_dims(), mps.amplitude([0] * mps.num_qubits)

It comes back as a live MPS, not a frozen record: canonicalise it, read its tensors, write to them, save it again. The same holds for MPO. What the unlicensed build cannot do with a loaded state is evolve it further.

The schema is proto/mpssim.proto in tensorweaver-rs, so the bytes are ordinary protobuf and any implementation can read them. tests/test_mock.py keeps fixtures produced by a licensed build and loads them on whatever build is installed, so the wire format cannot change under a customer's archive without a test failing.

The API is bytes in and bytes out; there is no file-level helper, and the MPSSim interop format (serialize_mpssim) is not exposed to Python in either build.

compile_info() reports the enabled build features, kernels among them, so a support report says which wheel is installed without anyone having to guess.

Which wheel am I running?

The two distributions share a name and a version number, which is what lets pip install mimiq-tensorweaver work both with and without the QPerfect registry configured. They are told apart at runtime:

public PyPI QPerfect registry
tw.has_kernels() False True
TwSimulator() raises TensorWeaverError works
MockTensorWeaver() works works
licence required none node-locked or floating

Install the licensed wheel with an exclusive --index-url, as Getting started shows, not --extra-index-url: with both indexes in play, pip is free to prefer either copy of a name it finds in both.