Frames Numerically
Working with frames exactly is often very slow, and not possible. Instead it may often be benificial to work with frames defined numerically. Here frames are defined with the Julia Matrix type with entries being any subtype of Number. Functionality for computing spectrums over matricies wiht entries of BigFloat is not natively supported by julia but can be provided using a package such as GenericLinearAlgebra.jl.
Basic Functionality
FrameTheory.is_frame — Method
is_frame(gram) -> Tuple{Bool,Int}Arguments
gram::Matrix{Number}: A square matrix defined over any sub type of Number
Returns
frame_bool::Boold::Int
Determines if the matrix gram is the Gram matrix for some frame. 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. To utilize this function for types BigFloat or Complex{BigFloat} you will need to use a package such as GenericLinearAlgebra.
Examples
julia> ζ = exp(2*pi*im/7);
julia> Phi = [1.0 ζ ζ^2 ζ^3 ζ^4 ζ^5 ζ^6;
1 ζ^2 ζ^4 ζ^6 ζ ζ^3 ζ^5;
1 ζ^4 ζ ζ^5 ζ^2 ζ^6 ζ^3];
julia> gram = Phi'*Phi;
julia> is_frame(gram)
(true, 3)FrameTheory.is_equiangular — Method
is_equiangular(gram) -> Tuple{Bool,Number,Number}Arguments
gram::Matrix{Number}: A square matrix.
Returns
equiangular_bool::Boola::Numberb::Number
Determines if the matrix gram is the Gram matrix for some collection of equiangular vectors. 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' and b the 'angle'. If equiangular_bool is False, then a and b will be nothing. To utilize this function for types BigFloat or Complex{BigFloat} you will need to use a package such as GenericLinearAlgebra.
Examples
julia> ζ = exp(2*pi*im/7);
julia> Phi = [1.0 ζ ζ^2 ζ^3 ζ^4 ζ^5 ζ^6;
1 ζ^2 ζ^4 ζ^6 ζ ζ^3 ζ^5;
1 ζ^4 ζ ζ^5 ζ^2 ζ^6 ζ^3];
julia> gram = Phi'*Phi;
julia> is_equiangular(gram)
(true, 3.0, 2.0000000000000004)FrameTheory.is_frame_tight — Method
is_frame_tight(gram) -> Tuple{Bool,Number}Arguments
gram::Matrix{Number}: A square matrix which is the Gram matrix for a frame.
Returns
tight_bool::Boolc::Number
Determines if the matrix gram is the Gram matrix for frame which is tight. If so returns c being the constant such that gram^2=c*gram. If tight_bool is False, then c will be nothing. To utilize this function for types BigFloat or Complex{BigFloat} you will need to use a package such as GenericLinearAlgebra.
Examples
julia> ζ = exp(2*pi*im/7);
julia> Phi = [1.0 ζ ζ^2 ζ^3 ζ^4 ζ^5 ζ^6;
1 ζ^2 ζ^4 ζ^6 ζ ζ^3 ζ^5;
1 ζ^4 ζ ζ^5 ζ^2 ζ^6 ζ^3];
julia> gram = Phi'*Phi;
julia> is_frame_tight(gram)
(true, 7.000000000000001)FrameTheory.is_ETF — Method
is_ETF(gram) -> Tuple{Bool,Number,Number,Number}Arguments
gram::Matrix{Number}: A square matrix defined over the Reals or Complexes
Returns
etf_bool::Boola::Numberb::Numberc::Numberd::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. To utilize this function for types BigFloat or Complex{BigFloat} you will need to use a package such as GenericLinearAlgebra.
Examples
A 3x7 Paley (3,2,7)-ETF.
julia> ζ = exp(2*pi*im/7);
julia> Phi = [1.0 ζ ζ^2 ζ^3 ζ^4 ζ^5 ζ^6;
1 ζ^2 ζ^4 ζ^6 ζ ζ^3 ζ^5;
1 ζ^4 ζ ζ^5 ζ^2 ζ^6 ζ^3];
julia> gram = Phi'*Phi;
julia> is_ETF(gram)
(true, 3.0, 2.0000000000000004, 7.000000000000001, 3)Constructions
A common method for numerically constructing ETFs is by the method of Alternating Projections. This method is not guaranteed to converge but can be used to try and determine if such an ETF exists.
FrameTheory.NumCons.rand_etf_from_alt_proj — Function
NumCons.rand_etf_from_alt_proj(T, d, n[; iters]) -> Matrix{T}Arguments
T::Type: A Type for a number such as Float64, Complex{Float64}d::Int: dimension.n::Int: number of vectors.iters::Int=1000: number of iterations to try and converge.
Returns
gram::Matrix{T}: The Gram matrix, if one exists, of adbynETF. Or non-sense if the algorithm failed to converge.
To utilize this function for types BigFloat or Complex{BigFloat} you will need to use a package such as GenericLinearAlgebra.
Examples
A real 3x6 ETF
julia> gram = NumCons.rand_etf_from_alt_proj(Float64, 3, 6);
julia> is_ETF(gram)
(true, 1.0, 0.19999999999999998, 1.9999999999999998, 3)A complex 2x4 ETF
julia> gram = NumCons.rand_etf_from_alt_proj(Complex{Float64}, 2, 4);
julia> is_ETF(gram)
(true, 1.0, 0.3333333333333331, 1.9999999999999996, 2)No real 2x4 ETF exists A complex 2x4 ETF
julia> gram = NumCons.rand_etf_from_alt_proj(Float64, 2, 4);
julia> is_ETF(gram)
(false, nothing, nothing, nothing, 4)Exactification
In some cases it is possible to exactify a numerically defined matrix. To do this we implement try_exactify using the Oscar.jl function guess.
FrameTheory.try_exactify — Function
try_exactify(A[; maxdeg, maxbits]) -> Union{RealMatrix,ComplexMatrix}Arguments
A::Matrix{BigFloat}: A matrix which is numerically very close to a matrix with algebraic entries.maxdeg: seeOscar.jldocumentation onguess.maxbits: seeOscar.jldocumentation onguess.
Returns
gram::MatElem{CalciumFieldElem}: The exactified matrix
If unable to exactify, an error will be thrown. This function is very finichy, and often fails. It is recommended to use BigFloat or Complex{BigFloat}.
Examples
julia> using GenericLinearAlgebra;
julia> gram = NumCons.rand_etf_from_alt_proj(BigFloat, 3, 6, iters=5000);
julia> exact_gram = try_exactify(gram);
julia> is_ETF(exact_gram)
(true, 1, 0.200000 {1/5}, 2, 3)Binder Finder
FrameTheory.binder_finder — Method
binder_finder(gram) -> Matrix{Int}Arguments
gram::Matrix{Number}: The Gram matrix of an ETF.
Returns
binder::Matrix{Int}: The Binder of the provided frame, where each row denotes a simplex.
This function follows the implementation from [6].
Examples
julia> ζ = exp(2*pi*im/3);
julia> sic = [
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 = sic'*sic;
julia> is_ETF(gram)
(true, 2.0, 0.9999999999999992, 6.0, 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 1The Projective Symmetry Group of an ETF
FrameTheory.projective_symmetry_group — Method
projective_symmetry_group(gram) -> PermGroupArguments
gram::Matrix{Number}: 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 = numericalize(ExactCons.dx2d_etf_from_prime_power(3));
julia> typeof(gram)
Matrix{Complex{BigFloat}}
julia> G = projective_symmetry_group(gram)
Permutation group of degree 4
julia> transitivity(G, 1:4)
2
julia> describe(G)
"A4"