Skip to content

Getting Started

Install the package, run a circuit, read the results. If Matrix Product State, bond dimension, or truncation are new to you, read Concepts first: this page assumes them.

Installation

Wheels are published on tagged releases to the QPerfect GitLab package registry, which requires authentication:

pip install mimiq-tensorweaver \
  --index-url https://__token__:<your-access-token>@gitlab.qperfect.io/api/v4/projects/29/packages/pypi/simple

To avoid repeating the index URL, configure it in ~/.pip/pip.conf:

[global]
index-url = https://__token__:<your-access-token>@gitlab.qperfect.io/api/v4/projects/29/packages/pypi/simple

then pip install mimiq-tensorweaver.

index-url, not extra-index-url. The same distribution name is published on public PyPI as the unlicensed mock (see Without a licence), and with both indexes in play pip is free to take either copy of a name it finds in both, so pip install -U could replace a licensed install with the mock. An exclusive index-url removes the ambiguity.

The registry does not proxy PyPI, so dependencies have to come from somewhere: pass --extra-index-url https://pypi.org/simple on the install, or keep a second [install] section for it, rather than demoting the QPerfect index back to an extra.

With uv

uv does not read pip.conf. For a one-off install, pass the registry as an index:

uv pip install mimiq-tensorweaver \
  --index https://__token__:<your-access-token>@gitlab.qperfect.io/api/v4/projects/29/packages/pypi/simple

uv takes each package from the first index that has it, so the licensed wheel wins over the PyPI mock and every other dependency still comes from PyPI.

In a uv project, declare the registry in pyproject.toml and pin mimiq-tensorweaver to it:

[[tool.uv.index]]
name = "qperfect"
url = "https://gitlab.qperfect.io/api/v4/projects/29/packages/pypi/simple"
explicit = true  # only packages pinned below are fetched from it

[tool.uv.sources]
mimiq-tensorweaver = { index = "qperfect" }

and keep the token out of the file by passing it through the environment:

export UV_INDEX_QPERFECT_USERNAME=__token__
export UV_INDEX_QPERFECT_PASSWORD=<your-access-token>
uv add mimiq-tensorweaver

To make the registry available everywhere instead, put the same table in ~/.config/uv/uv.toml as [[index]], without explicit = true (uv.toml has no sources table to pin a package with). The first-index rule then keeps the licensed wheel ahead of the mock.

The Qiskit integration is an optional extra:

pip install 'mimiq-tensorweaver[qiskit]'

So are the plots in tensorweaver.visualization, which need matplotlib:

pip install 'mimiq-tensorweaver[visualization]'

Supported platforms

Wheels target Python's stable ABI, so one wheel per platform serves Python 3.11 and later. The free-threaded interpreter is served by a wheel of its own, since it has no stable ABI to target; see Free-threaded Python.

Platform Wheel tag Requires
Linux x86_64 manylinux_2_35_x86_64 glibc 2.35 or newer (Ubuntu 22.04+, Debian 12+); pulls the mkl runtime (~224 MB)
Linux x86_64 manylinux_2_34_x86_64 glibc 2.34 (RHEL 9 and its rebuilds); pulls the mkl runtime (~224 MB)
macOS arm64 macosx_26_0_arm64 macOS 26 or later, Apple Silicon
Windows x86_64 win_amd64 Windows 10/11 x64; pulls the mkl runtime (~180 MB)
Linux x86_64, python3.14t cp314-cp314t-manylinux_2_35_x86_64 glibc 2.35 or newer; free-threaded 3.14 only
Linux x86_64, python3.14t cp314-cp314t-manylinux_2_34_x86_64 glibc 2.34 (RHEL 9 and its rebuilds); free-threaded 3.14 only

The mkl dependency is what makes linalg="mkl" work out of the box. MKL cannot be bundled into a wheel, so the wheels that have an MKL engine depend on it instead; see Linear-algebra engine. The Linux default engine is the vendored OpenBLAS and needs nothing.

Linux has two wheels, for glibc 2.35 and for glibc 2.34, because RHEL 9 ships 2.34. pip selects the one matching your host automatically. Check your version with ldd --version. Hosts below 2.34, such as RHEL 8 and Ubuntu 20.04, are not covered.

There are no wheels for Intel macOS or Linux aarch64. If pip reports no matching distribution, check this table first.

Licensing

The published wheel is licensed, and one binary serves both models. The license file decides at runtime which applies.

Model How it validates
Node-locked Checks ~/.mimiq/license.key against the machine fingerprint, fully offline
Floating Leases a short-lived seat from $QP_LICENSE_SERVER

Activate with the bundled qp-license CLI, also reachable as tensorweaver license:

tensorweaver license activate              # node-locked
tensorweaver license activate --floating   # floating

From source

Source builds need a Rust toolchain, OpenBLAS + LAPACKE development packages, protoc, and SSH access to the QPerfect GitLab: Cargo fetches the Rust core, tensorweaver-rs, at the revision pinned in Cargo.toml.

git clone git@gitlab.qperfect.io:development/tensorweaver/tensorweaver-python.git
cd tensorweaver-python
scripts/dev-build.sh        # builds both BLAS engines into an editable install

To build against a local checkout of tensorweaver-rs instead, clone it next to tensorweaver-python and pass --config 'paths=["../tensorweaver-rs"]' to cargo or maturin.

maturin develop on its own builds only the MKL engine. That still runs, since the default engine follows the build, but it needs the MKL runtime on the system and gives you nothing to compare against, which is why dev-build.sh exists. See Linear-algebra engine for the runtime switch, and the repository README for build variants.

Verify the install

python -c "import tensorweaver; print(tensorweaver.library_info())"

The documentation ships inside the wheel, with nothing loaded from the network, so it works on a machine that has none. Read it with:

tensorweaver docs

That serves the site on a free loopback port and opens a browser on it, and keeps running until you interrupt it. Pass --port to pin the port, or --print-path to print where the files live instead of serving them.

Your first circuit

Build the circuit with mimiqcircuits, run it with execute().

from mimiqcircuits import Circuit, GateH, GateCX, Measure
from tensorweaver import execute

# A Bell pair: H on qubit 0, then CX from 0 to 1.
c = Circuit()
c.push(GateH(), 0)
c.push(GateCX(), 0, 1)
c.push(Measure(), 0, 0)
c.push(Measure(), 1, 1)

results = execute(c, nsamples=1000, bonddim=64, seed=42)

Two keywords carry most of the meaning:

  • nsamples is how many shots you want.
  • bonddim caps the bond dimension, which is the accuracy/cost dial. 64 is ample for a Bell pair; see Concepts for how to choose it, and Circuit Execution for every other keyword.

Reading the results

execute() returns a MIMIQ QCSResults.

print(results.histogram())
# {bs"00": 511, bs"11": 489}

print(results.fidelities[0])
# 1.0 -- nothing was discarded to truncation

print(results.timings)
# {'compile': ..., 'apply': ..., 'sample': ..., 'total': ...}
  • histogram() counts each measured classical state. Roughly half 00 and half 11, with no 01 or 10, is the Bell correlation.
  • fidelities reports how much weight truncation discarded, one entry per evolution. 1.0 means none. Treat it as a screen rather than a certificate: reading the reported fidelity explains what it does and does not cover.
  • timings breaks wall-clock seconds into compile, apply, sample, and total.

Individual shots are in results.cstates:

for bs in results.cstates[:5]:
    print(list(bs))     # [0, 0] or [1, 1]

Asking for amplitudes

Pass the bitstrings you want and read them back from results.amplitudes. The amplitudes are read at the end of the circuit, so leave the measurements out: a measured state has already collapsed.

from mimiqcircuits import BitString

bell = Circuit()
bell.push(GateH(), 0)
bell.push(GateCX(), 0, 1)

bs00, bs11 = BitString([0, 0]), BitString([1, 1])
results = execute(bell, nsamples=1, bonddim=64, bitstrings=[bs00, bs11])

print(results.amplitudes[bs00])   # ~0.7071+0j
print(results.amplitudes[bs11])   # ~0.7071+0j

Qiskit users

TensorWeaver is little-endian by qubit index: bitstring position i is qubit i, so bs[0] is qubit 0. Qiskit puts the highest-indexed qubit on the left, so bitstrings from results.cstates, results.histogram(), and mps.sample_bitstring() appear reversed compared to Qiskit output. The same convention applies to any BitString you pass in.

Plotting the bond structure

With the visualization extra, plotbonds shows the bond dimension and the entanglement entropy of every cut, with the bonds at the cap in red, and plotschmidt the Schmidt spectrum of one cut. BondWindow follows a state as you evolve it.

from tensorweaver.visualization import plotbonds

fig = plotbonds(state)   # state: a TwState, such as TwSimulator.evolvezerostate returns

See Visualization for how to read the plots and use the window.

Where to go next

The examples ship inside the installed package:

import tensorweaver as tw
print(tw.examples_dir())