Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/NumberTheory/ZLattices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,59 @@ function characteristic_vectors(L::ZZLat)
@hassert :Lattice 1 isone(hnf(reduce(vcat, cvL))[1:rank(L),:])
return cvL
end

function _get_canonical_form(A, char_vectors_set, canonical_ordering)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add some types to the signature. This makes it easier to understand what the function is doing

p = length(char_vectors_set)
filter!(e->e!=p+1 && e!=p+2, canonical_ordering)
can_char_vectors_set = transpose(matrix(ZZ, reduce(vcat, char_vectors_set[canonical_ordering])))
H, U = hnf_with_transform(can_char_vectors_set)
U_inv = inv(U)
return transpose(U_inv)*A*U_inv
end

function _get_edge_labeled_graph(cv_set, gram)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here as well

p = length(cv_set)
res_graph = graph(Undirected, p+2)
max_w = QQ(0)
weightDict= Dict{Tuple{Int64, Int64}, QQFieldElem,}()
for i = 1:p
for j = i+1:p
Comment on lines +229 to +230

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the squared length of the vector, i.e. cv_set[i]*gram*transpose(cv_set[i])? This should be relevant as well.


w = (cv_set[i]*gram*transpose(cv_set[j]))[1]
if w>max_w
max_w = w
end
Comment on lines +229 to +235

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be considered a hot loop. Suppose that p=10000. Then the inner part is called p(p-1)/2= 49995000 times.
Some suggestions:

  • move cv_set[i]*gram to the outer loop on i. There is no dependence on j
  • use inplace operations such as transpose! and mul! with preallocated vectors
  • you can detect overflow before hand using the Cauchy-Schwarz inequality. If n= max(v*g*transpose(v)) and n^2 fits in Int, then we are fine. If this is the case (and it will be the case in almost all use cases), convert to Vector{Int} if and the matrix to Matrix{Int}. Then you can use LinearAlgebra.mul! which should be the fastest matrix vector multiplication with overflow that we have in julia. Be careful though, it does not support alialising on the first argument.
  • Is there a way to label the edge directly, instead of collecting all labels beforehand?
    Otherwise we are creating 49995000 dict entries. But I do not know enough of the internals of how labels work to judge if this makes sense.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to label the edge directly, instead of collecting all labels beforehand?
Otherwise we are creating 49995000 dict entries. But I do not know enough of the internals of how labels work to judge if this makes sense.

There is option to use label!() with a newly made Dict with 1 edge. But the problem is, it looks that if you add more edges, the graph object don't merge them inside. Instead, it stores all the dictionaries as is and as Dict uses a lot of memory by default => it results in enourmous memory usage for big graphs. That's why I collect here all edges in one big dictionary.

add_edge!(res_graph, i, j)
weightDict[(i, j)] = w
end
add_edge!(res_graph, i, p+1)
end
a = 1+max_w
b = a + 1
for i = 1:p
add_edge!(res_graph, i, p+2)
merge!(weightDict, Dict((i, p+2) => a))
end
add_edge!(res_graph, p+1, p+2)
merge!(weightDict, Dict((p+1, p+2) => b))
Comment on lines +241 to +248

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what happens here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, here I follows the paper and it looks, that we need 2 more vertices to preserve automorphisms of graphs correctly. So, basically another step to not lose isomorphism/not get "new" isom. of resulting forms

label!(res_graph, weightDict, nothing; name=:edge)
return res_graph
end

"""
Comment thread
Kazak-11 marked this conversation as resolved.
canonical_form(L::ZZLat) -> ZZMatrix
Return the canonical form of ``L``. The form is canonical in the sense, that two isomorphic latticies would have the same canonical form.

We follow ideas of Sikirić, Haensch, Voight and van Woerden [SHVW20](@cite).

!!! note
We do not give any guarantees that the canonical form stays the same
between different versions of Oscar.
"""
function canonical_form(L::ZZLat)
gram = gram_matrix(L)
char_vectors_set = characteristic_vectors(L)
graph = _get_edge_labeled_graph(char_vectors_set, gram) # transform from adjenctcy matrix A to edge-vertex weighted graph Ga, then to edge weighted graph T1(Ga)
can_order = _canonical_perm(graph; label=:edge) #_canonical_perm uses _edge_label_to_vertex_label themselfs
return _get_canonical_form(gram, char_vectors_set, can_order)
end
1 change: 1 addition & 0 deletions src/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ export bump!
export canonical_bundle
export canonical_divisor
export canonical_divisor_class
export canonical_form
export canonical_isomorphism
export canonical_matrix
export cartan_bilinear_form
Expand Down
37 changes: 37 additions & 0 deletions test/NumberTheory/ZLattices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,40 @@ end
@test 7 == length(unique!(Oscar.invariant_function_graph_hash.(L)))
@test 7 == length(unique!(Oscar.oscar_invariant_function.(L)))
end

@testset "Lattice Canonical Form" begin
Comment thread
Kazak-11 marked this conversation as resolved.
function create_gram(data) # helper function to create gram matricies of latticies
U = upper_triangular_matrix(data)
return U + transpose(U) - diagonal_matrix(diagonal(U))
end

n = 10
matrix1 = [2,1,-1,1,1,0,0,0,0,-1,2,-1,0,0,0,0,0,0,-1,2,0,0,0,0,0,0,0,2,1,0,0,0,0,-1,2,0,0,0,0,-1,4,-2,2,2,0,4,-2,-2,0,4,0,0,4,0,4]
matrix2 = [2,1,1,-1,-1,1,-1,1,-1,-1,2,0,-1,-1,1,-1,1,-1,0,4,-2,1,2,-2,-1,-1,0,4,1,-2,2,1,2,1,4,1,-1,0,0,1,4,-2,1,-2,-1,4,-1,2,1,4,0,-1,4,1,4]
G1 = create_gram(matrix1)
G2 = create_gram(matrix2)
U = hnf_with_transform(matrix(ZZ,n,n,rand(0:1,n^2)))[2];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ; here and in the following lines is not necessary

L1 = integer_lattice(gram = G1);
L2 = integer_lattice(gram = G2);
L3 = lattice_in_same_ambient_space(L1,U*basis_matrix(L1));
can_form1 = canonical_form(L1)
can_form2 = canonical_form(L2)
can_form3 = canonical_form(L3)
@test can_form1 != can_form2
@test can_form1 == can_form3

n = 18
matrix1 = [4,0,0,2,1,2,2,1,0,0,0,0,0,0,0,0,0,0,4,0,2,3,1,0,2,0,0,0,0,0,0,0,0,0,0,4,2,3,3,1,1,0,0,0,0,0,0,0,0,0,0,4,4,3,2,2,0,0,0,0,0,0,0,0,0,0,6,4,2,3,0,0,0,0,0,0,0,0,0,0,4,2,2,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,4,0,0,2,1,2,2,1,0,0,4,0,2,3,1,0,2,0,0,4,2,3,3,1,1,0,0,4,4,3,2,2,0,0,6,4,2,3,0,0,4,2,2,0,0,2,1,0,0,2,0,0,4,0,8]
matrix2 = [2,1,1,1,0,0,0,0,1,1,-1,1,-1,0,-1,1,1,1,2,1,0,0,0,0,0,0,0,-1,1,-1,0,-1,1,0,1,2,0,0,0,0,0,1,1,-1,0,0,0,0,0,1,0,2,0,0,0,0,1,1,0,0,0,0,-1,0,0,1,2,0,0,0,-1,-1,-1,-1,1,0,0,0,0,0,2,1,-1,-1,-1,-1,1,-1,1,-1,-1,-1,1,2,-1,-1,-1,-1,1,-1,0,-1,-1,0,1,2,1,1,0,-1,1,0,0,0,1,-1,4,3,0,-1,1,0,1,1,2,0,4,1,-1,1,0,0,0,1,-1,4,0,1,-1,1,0,-1,-2,4,-3,0,-1,1,0,1,4,0,1,-1,0,-2,2,-1,-1,-1,0,4,1,1,-1,4,1,1,4,0,4]
G1 = create_gram(matrix1)
G2 = create_gram(matrix2)
U = hnf_with_transform(matrix(ZZ,n,n,rand(0:1,n^2)))[2];
L1 = integer_lattice(gram = G1);
L2 = integer_lattice(gram = G2);
L3 = lattice_in_same_ambient_space(L1,U*basis_matrix(L1));
can_form1 = canonical_form(L1)
# can_form2 = canonical_form(L2) # too long time, circa 1 hour to calculate can form
can_form3 = canonical_form(L3)
# @test can_form1 != can_form2
@test can_form1 == can_form3
end
Loading