Zauner
Zauner
is a Julia module for computing properties of SICs (or SIC-POVMs) and related objects such as rank-$r$ versions of SICs ($r$-SICs), objects that are galois conjugates of these called ghosts, and certain Stark units over real quadratic fields. The properties of ghosts, SICs, and Stark units computed by Zauner
are, in most cases, only known to hold conjecturally. This package is a systematic way to test Zauner's conjecture and certain Stark conjectures, generate new conjectures, and sharpen (or refute!) existing ones.
This package is still in its infancy. Expect breaking changes in upcoming versions.
Getting Started
Here's how to get started.
using Zauner
With Julia 1.10 or later, you should be prompted to install Zauner
if you haven't done so already. If you're running an older version of Julia, you can first install Zauner
by running import Pkg; Pkg.add("Zauner")
.
Admissible Tuples
The ghosts constructed by Zauner
are parameterized by an AdmissibleTuple
. This can be specified in one of several ways. A rank-1 ghost in dimension d > 3
is specified up to equivalence by d
and a suitable quadratic form Q
. Thus, we have
julia> d = 7
7
julia> Q = binary_quadratic_form(2,-4,1)
Binary quadratic form over integer ring with equation 2*x^2 - 4*x*y + y^2
julia> t = AdmissibleTuple(d,Q)
AdmissibleTuple( d = 7, K = ℚ(√8), q = 1, Q = ⟨2,-4,1⟩, h = 1 )
We see that the AdmissibleTuple
type precomputes several associated data from d,Q
.
julia> t.K # associated field
Real quadratic field defined by x^2 - 8
julia> t.h # class number / size of multiplet
1
julia> (t.j, t.m) # dimension grid positions
(1, 1)
julia> t.D # fundamental discriminant
8
The complete set of fields is given in the documentation for AdmissibleTuple
.
If no quadratic form is given, then AdmissibleTuple
defaults to the principal form.
julia> t = AdmissibleTuple(7)
AdmissibleTuple( d = 7, K = ℚ(√8), q = 2, Q = ⟨1,-6,1⟩, h = 1 )
julia> t.Q # form
Binary quadratic form over integer ring with equation x^2 - 6*x*y + y^2
An AdmissibleTuple
can be used to do explicit computations of ghosts. From the previous tuple, we have the following ghost (reduced from default BigFloat
precision):
julia> v = ComplexF64.(ghost(t))
7-element Vector{ComplexF64}: 1.0 + 0.0im -0.14618644568475053 + 0.23112117370673468im -0.18050295608012903 + 0.06772881462683221im -0.25906051639099376 + 0.06620663109494396im -0.43080738577379907 + 0.23025848273105493im -0.4518178172779057 + 0.7896096096684991im 0.46837512120757807 + 1.2608265992365257im
Note that ghosts are normalized so that the first coordinate is 1. To get the properly normalized ghost overlaps and validate the solution, one can do the following to compute ghost overlaps:
julia> u = circshift(reverse(v),1); # parity-reversed ghost
julia> nu_plus = real.( [u' * wh( [ p; q], v) / (u'v) for p=0:d-1, q=0:d-1] )
7×7 Matrix{Float64}: 1.0 0.145506 0.198503 0.289727 0.431441 0.629714 0.859068 0.859068 -0.924012 1.09271 -1.24903 1.25386 -0.924012 0.145506 0.629714 1.25386 1.69491 1.69491 1.09271 0.198503 -0.13528 0.431441 -1.24903 1.69491 -1.24903 0.289727 0.099692 0.114394 0.289727 1.09271 1.25386 0.431441 -0.100078 0.0737504 -0.100078 0.198503 -0.924012 0.629714 0.114394 0.0737504 0.0737504 0.099692 0.145506 0.859068 -0.13528 0.099692 -0.100078 0.114394 -0.13528
julia> nu_minus = real.( [u' * wh( [-p;-q], v) / (u'v) for p=0:d-1, q=0:d-1] )
7×7 Matrix{Float64}: 1.0 0.859068 0.629714 0.431441 0.289727 0.198503 0.145506 0.145506 -0.13528 0.114394 -0.100078 0.099692 -0.13528 0.859068 0.198503 0.099692 0.0737504 0.0737504 0.114394 0.629714 -0.924012 0.289727 -0.100078 0.0737504 -0.100078 0.431441 1.25386 1.09271 0.431441 0.114394 0.099692 0.289727 -1.24903 1.69491 -1.24903 0.629714 -0.13528 0.198503 1.09271 1.69491 1.69491 1.25386 0.859068 0.145506 -0.924012 1.25386 -1.24903 1.09271 -0.924012
julia> (d + 1) .* nu_plus .* nu_minus
7×7 Matrix{Float64}: 8.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
Here wh(p,v)
acts a Weyl-Heisenberg displacement operator indexed by p
onto v
, i.e., in quantum notation it returns $D_{\boldsymbol{p}}|v\rangle$.
It can also be used with necromancy
to attempt to reconstruct a 1-SIC, which we again round down from BigFloat
precision.
julia> ψ = ComplexF64.(necromancy(t))
[ Info: [Convex.jl] Compilation finished: 16.17 seconds, 1.239 GiB of memory allocated 7-element Vector{ComplexF64}: 0.19600171277900127 + 0.0im -0.4427183043594661 - 0.1203704382925133im 0.12118210358198538 + 0.056074038580580944im -0.08024909053562126 - 0.29060718382096185im -0.15206734426395246 - 0.13736890827635656im 0.12696687443048085 + 0.6055399023112957im 0.23088404836757231 + 0.4053043780539029im
Zauner
provides numerical validation by measuring how far the returned objects are from satisfing the overlap conditions or minimizing the "pointwise" frame potential conditions discussed below.
julia> ghost_overlap_test(v)
7.216449660063518e-16
julia> sic_overlap_test(ψ)
8.049116928532385e-16
julia> ghost_frame_test(v)
1.7616627229545797e-16
julia> sic_frame_test(ψ)
2.8583690254042265e-17
Rank $r>1$ has very limited support at the moment and is essentially confined to the definition of an AdmissibleTuple
. Many other functions have limited domains at present. For example, we do not presently support necromancy on $F_a$ orbits. Please file an issue on GitHub if there is something you wish to see supported in future updates.
Citing Zauner
How to cite us:
@misc{AFK2025,
archiveprefix = {arXiv},
author = {Appleby, Marcus and Flammia, Steven T. and Kopp, Gene S.},
eprint = {2501.03970},
howpublished = {arXiv:2501.03970},
primaryclass = {math.NT},
title = {A constructive approach to Zauner's conjecture via the Stark conjectures},
year = {2025}
}