Non-Gaussian states
qkd.fock holds one mode as a density matrix on levels (len(ps), len(xs)) grid layout of qkd.gaussian: the layers compare with no conversion. Scope.
from qkd import fockConstructors
cutoff is keyword-only, at_cutoff(d).
Coherent(11.0, 0.0) at the default cutoff 40 | |
|---|---|
| Norm discarded | 99.79 % |
| Reported Wigner negativity | 2.5072, above every resource state here, on a state whose true negativity is zero |
physical(), purity(), Gaussian shadow | True, |
| Artefact versus discarded population | 1.4 to 6.7 orders of magnitude above it, so trunc_error() does not bound it |
| Parameter | Unit | Default | Description |
|---|---|---|---|
Vacuum(*, cutoff) | levels | 40 | Number(0). |
Number(n, *, cutoff) | — | required | |
Coherent(x, p, *, cutoff) | internal quadratures | required | gaussian.Coherent(x, p). The Poissonian tail reaches every level, so discarded() is non-zero. |
Squeezed(r, *, cutoff) | nepers | required | Squeezed vacuum, gaussian.Squeezed(r). True negativity zero at every |
Thermal(nbar, *, cutoff) | photons | required | |
Cat(x, p, odd=False, *, cutoff) | internal quadratures | required | Even or odd superposition of the coherent states at |
Gkp(logical, delta, *, cutoff) | — / vacuum units | required, cutoff=None | Finite-energy grid state, logical None derives the cutoff as gkp_levels(delta); a pinned value is honoured; a derived cutoff that cannot hold the state raises. |
Density(rho) | — | required | Square complex density matrix; its dimension is the cutoff and discarded() is 0. Validated for shape, finiteness, Hermiticity to |
Module constants
| Name | Value | Bounds |
|---|---|---|
CUTOFF | 40 | Default cutoff for every constructor except Gkp. Not safe for a large displacement. |
MAX_LEVELS | 512 | The native core's cap, so a derived cutoff clamps before the core refuses. |
GKP_SCALE, GKP_TAIL | 8.0, 1e-6 | The numerator of gkp_levels, and the discarded norm a derived-cutoff grid state may reach before its constructor raises. A pinned cutoff is never refused. |
gkp_levels(delta) | ceil(GKP_SCALE / delta**2), capped at MAX_LEVELS | Levels needed for a discarded population below GKP_TAIL track |
EDGE | 3 | Default levels for tail(), and the depth trunc_error() sums over. |
STEP | 0.06 | Default phase-space sample spacing for window(). |
Truncation
| Method | Catches |
|---|---|
discarded() | norm thrown away at construction — a state born too big for its cutoff |
tail(levels=3) | population in the top few levels — a state born inside its cutoff, then pushed against the ceiling by a displacement or a channel |
trunc_error() | the worse of the two, as one number to threshold on |
A truncated matrix is renormalised, so it passes every physicality check. Neither instrument bounds the kernel: both measure population, and the displaced-parity kernel oscillates, so the truncated tail contributes to
| Operation | Truncation cost |
|---|---|
rotate(theta) | none — diagonal in the number basis |
loss(eta) | none — the Kraus operators only move population down, so it is trace-preserving inside the cutoff and shrinks the tail |
displace(x, p) | yes — the truncated displacement is not unitary: it loses norm and pushes population up. The loss is folded into discarded(), not renormalised away |
Phase space
from qkd import fock
grid = [-1e-9, 0.0, 1e-9]
vac = fock.Vacuum(cutoff=24)
vac.wigner(grid, grid)[1, 1] # 0.3183098861837907 == 1/pi
vac.husimi(grid, grid)[1, 1] # 0.15915494309189535 == 1/(2 pi)Wigner negativity as the witness
| Theorem | Statement |
|---|---|
| Spekkens 2008 | negativity of a quasi-probability representation and the failure of generalised non-contextuality are the same statement |
| Mari & Eisert 2012; Veitch et al. 2013 | Wigner-positive states, under Gaussian operations and homodyne readout, are efficiently classically simulable |
The computed quantity is the Kenfack–Życzkowski indicator,
zero iff
The anchor
from qkd import fock
fock.Number(1, cutoff=12).negativity().value # 0.4262150
fock.Vacuum(cutoff=12).negativity().value # -9.1e-07, i.e. zeroFour significant figures; the residual is the default grid's quadrature error — validation quotes the same state on a stated grid. A number state discards nothing at any
The grid is part of the answer
negativity() returns a Volume, not a bare float: the integral runs over a window nobody chose explicitly.
| Member | Description |
|---|---|
value | float(vol) returns it. |
trunc_error | The state's truncation error. |
cutoff | The cutoff the state was held at. |
points | (len(xs), len(ps)). |
span | (x_lo, x_hi, p_lo, p_hi). |
step | (dx, dp). |
window(state, reach=None, step=0.06) | |
|---|---|
reach=None | five standard deviations of the state's second moments plus its displacement, on the wider quadrature, held to |
| Five | a Gaussian leaves |
| The cap | a state too big for its cutoff has the renormalised matrix's moments: Coherent(11, 0) at cutoff 40 asks for reach 25.65, an gkp() sizes its quadrature with. Capping moves volumes by at most |
step | resolves the kink in |
| State | trunc_error | Volume, true value |
|---|---|---|
Squeezed(0.8), cutoff 25 | ||
| the same at cutoff 40 | — | |
| the same at cutoff 60 | — |
Refining the grid does not move those; raising the cutoff does, monotonically. A volume is evidence of negativity when it stops changing as the cutoff grows. Volume over trunc_error is not a screen: a wholly spurious
Two witnesses
non_gaussianity() is the relative-entropy non-Gaussianity shadow(). It returns a Divergence: value, trunc_error, cutoff, and float().
Truncation moves both halves, so a Gaussian state can score large and no cheap bound separates the columns:
| At cutoff 40 | Artefact | Resource |
|---|---|---|
non_gaussianity(), bits | Squeezed(2.0) Coherent(11.0, 0.0) | odd cat Number(10) |
Negativity implies non-Gaussianity; the converse fails. An equal mixture of two coherent states is non-Gaussian with a strictly positive Wigner function: non_gaussianity() sees it, negativity() does not. Loss separates them at a calculable point:
from qkd import fock
cat = fock.Cat(3.0, 0.0, True, cutoff=40)
for eta in (1.0, 0.7, 0.51, 0.5, 0.49):
st = cat.loss(eta)
print(eta, st.negativity().value, float(st.non_gaussianity()))
# 1.00 0.600926 2.5555
# 0.70 0.024912 1.3117
# 0.51 0.000048 1.0931
# 0.50 -0.000000 1.0798
# 0.49 -0.000000 1.0662Negativity reaches zero at
Parity
parity() returns
The Gaussian shadow
moments() returns shadow() is gaussian.Moments(mean, cov) over it.
from qkd import fock
fock.Coherent(1.0, 0.0).shadow().cov
# array([[0.5, 0. ],
# [0. , 0.5]])| What the shadow is used for | |
|---|---|
| Hudson as a live test | a coherent, squeezed or thermal state built in both layers must give pointwise-equal Wigner grids |
| Non-Gaussian states | the shadow stays non-negative where the state it shadows does not |
a Gaussian maximises entropy at fixed second moments, so a negative non_gaussianity() is numerical noise |
Scope
No protocol consumes this layer: Gaussian-modulation CV-QKD is Gaussian end to end, the discrete-modulation analysis works from a covariance matrix, and the click family is written in photons.
| Limit | |
|---|---|
| Single mode | no two-mode gates, no tensor network, no MPS — Architecture |
| GKP is the finite-energy approximation | a Gaussian-enveloped comb of Gaussian peaks in the position wavefunction, at |
| Operations | pure loss, rotation and displacement only. No thermal-loss channel, no cubic-phase gate, no Kerr evolution, no photon-number-resolving measurement |
Read surface
from qkd import fock
odd = fock.Cat(3.0, 0.0, True, cutoff=30)
odd.parity() # -1.0 exactly
odd.negativity().value # 0.600926 at cutoff 30
odd.trunc_error() # 9.1e-13 -- how much the cutoff cost
gkp = fock.Gkp(0, 0.3) # cutoff derived: gkp_levels(0.3) == 89
gkp.cutoff # 89
gkp.negativity().value # 0.926206 at cutoff 89
gkp.trunc_error() # 9.4e-08
thermal = fock.Thermal(0.5, cutoff=30)
thermal.negativity().value # -9.8e-07 -- Gaussian, so Hudson applies
float(thermal.non_gaussianity()) # 4.6e-13 -- and it is Gaussian tooThe cat reads at_cutoff(45). The GKP state:
| Cutoff | negativity().value | trunc_error() |
|---|---|---|
| 60 (the former fixed default) | 0.933283 | |
| 80 | 0.926781 | |
89 (gkp_levels(0.3), the derived default) | 0.926206 | |
| 110 | 0.925878 |
trunc_error improves four orders of magnitude while the volume moves in the third decimal: at 60 the volume's error was trunc_error. The derived 89 cuts the overstatement from 0.8 % to 0.04 %.
| Method | Returns |
|---|---|
cutoff | Levels this state is held on. A property |
at_cutoff(d) | The same state on Volume |
matrix() | The density matrix |
populations() | |
eigenvalues() | Spectrum, ascending |
photons() | Mean photon number |
purity() | A Purity: value is discarded and the certified lower bound least |
entropy() | Von Neumann entropy in bits |
parity() | |
physical(tol=1e-9) | Hermitian, unit trace and positive semidefinite |
moments(), shadow() | The Gaussian shadow, as a (mean, cov) pair of arrays or as a gaussian.State |
wigner(xs, ps), husimi(xs, ps) | Phase-space grids |
negativity(xs=None, ps=None) | A Volume; both axes default to window(self) |
non_gaussianity() | A Divergence: |
discarded(), tail(levels=3), trunc_error() | The truncation instrumentation |
Array dtypes, shapes and aliasing: Bulk returns are numpy arrays. Volume, Divergence and Purity are frozen records carrying __float__.
purity() is the truncated state's purity | |
|---|---|
| Mechanism | the constructors renormalise whatever fits under the cutoff, rescaling every retained element by |
Thermal(200) at cutoff 40 | keeps 18 % of its norm and reports |
| Sign | not one-signed: a state whose discarded tail is purer than its retained head reads too low |
least | the one certified statement, value |
| Above | nothing bounds it usefully — the discarded tail could be pure. entropy() and eigenvalues() carry the same distortion with no bound at all |
References
- Spekkens 2008 — PRL 101, 020401 (2008)
- Genoni and Paris 2010 — PRA 82, 052341 (2010)
- Mari & Eisert 2012 — PRL 109, 230503 (2012)
- Veitch et al. 2013 — NJP 15, 013037 (2013)
- Albarelli et al. 2018 — PRA 98, 052350 (2018)