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.

Warning

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 = 77
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 fieldReal quadratic field defined by x^2 - 8
julia> t.h # class number / size of multiplet1
julia> (t.j, t.m) # dimension grid positions(1, 1)
julia> t.D # fundamental discriminant8

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 # formBinary 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_minus7×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
Warning

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}
}