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] ```

source

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::Bool
  • d::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)
source
FrameTheory.is_equiangular — Method
is_equiangular(gram) -> Tuple{Bool,CalciumFieldElem,CalciumFieldElem}

Arguments

  • gram::MatElem{CalciumFieldElem}: A square matrix defined with CalciumField.

Returns

  • equiangular_bool::Bool
  • a::CalciumFieldElem
  • b::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)
2
source
FrameTheory.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::Bool
  • c::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)
source
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::Bool
  • a::CalciumFieldElem
  • b::CalciumFieldElem
  • c::CalciumFieldElem
  • d::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)
source

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)/2 by prime_power+1 ETF.

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
source

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  1
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)
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  1
source

The Projective Symmetry Group of an ETF

FrameTheory.projective_symmetry_group — Method
projective_symmetry_group(gram) -> PermGroup

Arguments

  • gram::MatElem{CalciumFieldElem}: A square matrix which is the Gram matrix of some ETF.
  • verbose::Bool: (Default false).

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"
source