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
ValueErrorandTypeErrorchecks: analgorithmTensorWeaver 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, itsseed/rngTypeError, its layout search forreorderqubits, and itsNotImplementedErroron the server-sideremove_swaps;- the whole
LocalBackendexecution driver, which decides between the sampling and trajectory routes from the circuit, runs the pass pipeline, and builds theQCSResultsitself.
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:
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.