Frames Exactly
To work with frames over the real and complex numbers exactly, we utilize CalciumField (documentation: here) from Oscar.jl. Oscar loads a wrapper, for the C library Calcium, from Nemo.jl. Real and complex numbers can be initialized exactly as follows:
julia> using Oscar;
julia> C = CalciumField();
julia> C(2)
2
julia> sqrt(C(2))
1.41421 {a where a = 1.41421 [a^2-2=0]}
julia exp(C(2)*C(pi)*C(1im)/C(3))
-0.500000 + 0.866025*I {a where a = -0.500000 + 0.866025*I [a^2+a+1=0]}We also expand some of the functionality of Oscar.jl necessary for working with frames, such as defining conjugate_transpose over matrices with elements with type CalciumFieldElem.
Oscar.conjugate_transpose — Method
Examples
@doc raw```jldoctest julia> C = CalciumField();
julia> A = matrix(C, [1 1im; 0 1]);
julia> conjugate_transpose(A) [ 1 0] [-1.00000*I {-a where a = I [a^2+1=0]} 1] ```
Basic Functionality
FrameTheory.is_frame — Method
is_frame(gram) -> Tuple{Bool,Int}Arguments
gram::MatElem{CalciumFieldElem}: A square matrix defined over Oscar.jl's Calcium Field
Returns
frame_bool::Boold::Int
Determines if the matrix gram is the Gram matrix for some frame. Determines if gram is a Gram matrix for some collection of vectors and therefore a hermitian positive semi-definite matrix. Returns a boolean frame_bool if the provided matrix is the Gram matrix of a frame for some d-dimensional space. If frame_bool' is 'false then d would be nothing.
Examples
julia> C = CalciumField();
julia> ζ = exp(C(2)*C(pi)*C(1im)/C(7));
julia> Phi = matrix(C, [1 ζ ζ^2 ζ^3 ζ^4 ζ^5 ζ^6;
1 ζ^2 ζ^4 ζ^6 ζ ζ^3 ζ^5;
1 ζ^4 ζ ζ^5 ζ^2 ζ^6 ζ^3]);
julia> gram = conjugate_transpose(Phi)*Phi;
julia> is_frame(gram)
(true, 3)FrameTheory.is_equiangular — Method
is_equiangular(gram) -> Tuple{Bool,CalciumFieldElem,CalciumFieldElem}Arguments
gram::MatElem{CalciumFieldElem}: A square matrix defined with CalciumField.
Returns
equiangular_bool::Boola::CalciumFieldElemb::CalciumFieldElem
Determines if the matrix gram is the Gram matrix for some collection of equiangular vectors. Checks 3 things: (1) is gram an actual Gram matrix (calls is_frame), (2) are the diagonal entries positive and equal, and (3) do the off diagonals have a common modulus. Returns a boolean equiangular_bool if the provided matrix is the Gram matrix of a collection of equiangular lines. With a being the vectors common 'magnitudes' (the common diagonal entry) and b the 'angle' (the common modulus of the off diagonals). If equiangular_bool is False, then a and b will be nothing.
Examples
julia> C = CalciumField();
julia> ζ = exp(C(2)*C(pi)*C(1im)/C(7));
julia> Phi = matrix(C, [1 ζ ζ^2 ζ^3 ζ^4 ζ^5 ζ^6;
1 ζ^2 ζ^4 ζ^6 ζ ζ^3 ζ^5;
1 ζ^4 ζ ζ^5 ζ^2 ζ^6 ζ^3]);
julia> gram = conjugate_transpose(Phi)*Phi;
julia> (equi_bool, a, b) = is_equiangular(gram)
(true, 3, 2.00000 {a^2+b^2 where a = 1.32288 [Im(-0.500000 + 1.32288*I {c^4+c^2-c})], b = -0.500000 [Re(-0.500000 + 1.32288*I {c^4+c^2-c})], c = 0.900969 + 0.433884*I [c^6-c^5+c^4-c^3+c^2-c+1=0]})
julia> QQ(b)
2FrameTheory.is_frame_tight — Method
is_frame_tight(gram) -> Tuple{Bool,CalciumFieldElem}Arguments
gram::MatElem{CalciumFieldElem}: A square matrix which is the Gram matrix for a frame.
Returns
tight_bool::Boolc::CalciumFieldElem
Determines if the matrix gram is the Gram matrix for a tight frame. If so returns c being the constant such that gram^2=c*gram. One could also determine tightness from the spectrum, but this method matched the method used for frames over finite fields. If tight_bool is False, then c will be nothing.
Examples
julia> C = CalciumField();
julia> ζ = exp(C(2)*C(pi)*C(1im)/C(7));
julia> Phi = matrix(C, [1 ζ ζ^2 ζ^3 ζ^4 ζ^5 ζ^6;
1 ζ^2 ζ^4 ζ^6 ζ ζ^3 ζ^5;
1 ζ^4 ζ ζ^5 ζ^2 ζ^6 ζ^3]);
julia> gram = conjugate_transpose(Phi)*Phi;
julia> is_frame_tight(gram)
(true, 7)FrameTheory.is_ETF — Method
is_ETF(gram) -> Tuple{Bool,CalciumFieldElem,CalciumFieldElem,CalciumFieldElem}Arguments
gram::MatElem{CalciumFieldElem}: A square matrix defined with CalciumField.
Returns
etf_bool::Boola::CalciumFieldElemb::CalciumFieldElemc::CalciumFieldElemd::Int
Determines if the matrix gram is the Gram matrix for some collection of vectors which form an equiangular tight frame (ETF). Calls and returns the same parameters from is_frame, is_equiangular, and is_frame_tight.
Examples
A 3x7 Paley (3,2,7)-ETF.
julia> C = CalciumField();
julia> ζ = exp(C(2)*C(pi)*C(1im)/C(7));
julia> Phi = matrix(C, [1 ζ ζ^2 ζ^3 ζ^4 ζ^5 ζ^6;
1 ζ^2 ζ^4 ζ^6 ζ ζ^3 ζ^5;
1 ζ^4 ζ ζ^5 ζ^2 ζ^6 ζ^3]);
julia> gram = conjugate_transpose(Phi)*Phi;
julia> is_ETF(gram)
(true, 3, 2.00000 {a^2+b^2 where a = 1.32288 [Im(-0.500000 + 1.32288*I {c^4+c^2-c})], b = -0.500000 [Re(-0.500000 + 1.32288*I {c^4+c^2-c})], c = 0.900969 + 0.433884*I [c^6-c^5+c^4-c^3+c^2-c+1=0]}, 7, 3)Constructions
FrameTheory.ExactCons.dx2d_etf_from_prime_power — Method
ExactCons.dx2d_etf_from_prime_power(prime_power) -> MatElem{CalciumFieldElem}Arguments
prime_power::Int: an odd prime power
Returns
gram::MatElem{CalciumFieldElem}the Gram matrix of a(prime_power+1)/2byprime_power+1ETF.
Examples
A real Example
julia> gram = ExactCons.dx2d_etf_from_prime_power(5^2);
julia> is_ETF(gram)
(true, 5,1,10,13)
julia> n = size(gram)[1]
26
julia> all(isreal.(gram))
true
A complex example
julia> gram = ExactCons.dx2d_etf_from_prime_power(3);
julia> is_ETF(gram)
(true, 1.73205 {a where a = 1.73205 [a^2-3=0]}, 1, 3.46410 {2*a where a = 1.73205 [a^2-3=0]}, 2)
julia> n = size(gram)[1]
4
julia> all(isreal.(gram))
false
Binder Finder
FrameTheory.binder_finder — Method
binder_finder(gram) -> Matrix{Int}Arguments
gram::MatElem{CalciumFieldElem}: The Gram matrix of an ETF.
Returns
binder::Matrix{Int}: The Binder of the provided ETF, where each row denotes a simplex.
This function follows the implementation from [6].
Examples
julia> C = CalciumField();
julia> ζ = exp(C(2)*C(pi)*C(1im)/C(3));
julia> sic = matrix(C, [
1 1 1 -1 -1 -1 0 0 0;
0 0 0 1 ζ ζ^2 -1 -1*ζ -1*ζ^2;
-1 -1*ζ^2 -1*ζ 0 0 0 1 ζ^2 ζ
]);
julia> gram = conjugate_transpose(sic)*sic;
julia> is_ETF(gram)
(true, 2, 1.00000 {a^2+b^2 where a = -0.866025 [Im(0.500000 - 0.866025*I {-c})], b = 0.500000 [Re(0.500000 - 0.866025*I {-c})], c = -0.500000 + 0.866025*I [c^2+c+1=0]}, 6, 3)
julia> binder_finder(gram)
12×9 Matrix{Int64}:
1 1 1 0 0 0 0 0 0
1 0 0 1 0 0 1 0 0
1 0 0 0 1 0 0 0 1
1 0 0 0 0 1 0 1 0
0 1 0 1 0 0 0 0 1
0 1 0 0 1 0 0 1 0
0 1 0 0 0 1 1 0 0
0 0 1 1 0 0 0 1 0
0 0 1 0 1 0 1 0 0
0 0 1 0 0 1 0 0 1
0 0 0 1 1 1 0 0 0
0 0 0 0 0 0 1 1 1julia> C = CalciumField();
julia> ζ = exp(C(2)*C(pi)*C(1im)/C(3));
julia> sic = matrix(C, [
1 1 1 1 1 1 0 0 0;
0 0 0 1 ζ ζ^2 1 1*ζ 1*ζ^2;
1 1*ζ^2 1*ζ 0 0 0 1 ζ^2 ζ
]);
julia> gram = conjugate_transpose(sic)*sic;
julia> is_ETF(gram)
(true, 2, 1.00000 {a^2+b^2 where a = -0.866025 [Im(0.500000 - 0.866025*I {-c})], b = 0.500000 [Re(0.500000 - 0.866025*I {-c})], c = -0.500000 + 0.866025*I [c^2+c+1=0]}, 6, 3)
julia> binder_finder(gram)
3×9 Matrix{Int64}:
1 1 1 0 0 0 0 0 0
0 0 0 1 1 1 0 0 0
0 0 0 0 0 0 1 1 1The Projective Symmetry Group of an ETF
FrameTheory.projective_symmetry_group — Method
projective_symmetry_group(gram) -> PermGroupArguments
gram::MatElem{CalciumFieldElem}: A square matrix which is the Gram matrix of some ETF.verbose::Bool: (Defaultfalse).
Returns
G::PermGroup: The group of permutations of the vector of the frame, that correspond to switching equivalences.
This algorithm uses a modified version of the breath first search based Gap code by Joey Iverson: https://github.com/jwiverson/frame-symmetries/blob/main/symp.gap. This implementation instead chooses to use a depth first search approach for better memory management. This algorithm implements the algorithm from section 4 of [7]. Over R or C this algorithm utilizes Proposition 3.3 from the same paper.
Examples
The following is an example of a 2x4 ETF with a 2-transitive symmetry group.
julia> gram = ExactCons.dx2d_etf_from_prime_power(3);
julia> G = projective_symmetry_group(gram)
Permutation group of degree 4
julia> transitivity(G, 1:4)
2
julia> describe(G)
"A4"