Skip to content

Commit 804eec0

Browse files
authored
Merge pull request #18 from ACEsuit/v0.1-dev
Cleanup towards v0.1
2 parents 7b79b05 + be61fba commit 804eec0

21 files changed

+1044
-897
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ jobs:
1818
fail-fast: false
1919
matrix:
2020
version:
21-
- '1.9'
2221
- '1.10'
22+
- '1.11'
2323
os:
2424
- ubuntu-latest
2525
arch:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/Manifest.toml
22
/docs/build/
3+
.vscode

Project.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
name = "RepLieGroups"
22
uuid = "f07d36f2-91c4-427a-b67b-965fe5ebe1d2"
3-
authors = ["Christoph Ortner <christophortner@gmail.com> and contributors"]
4-
version = "0.0.4"
3+
version = "0.1.0-dev"
54

65
[deps]
76
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
@@ -13,19 +12,20 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1312

1413
[compat]
1514
Combinatorics = "1.0"
16-
LinearAlgebra = "1.9"
15+
LinearAlgebra = "1.10"
1716
PartialWaveFunctions = "0.2.0"
18-
SparseArrays = "1.9"
17+
SparseArrays = "1.10"
1918
StaticArrays = "1.5"
20-
julia = "1.9, 1.10"
19+
julia = "1.10, 1.11"
2120

2221
[extras]
2322
BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0"
24-
Polynomials4ML = "03c4bcba-a943-47e9-bfa1-b1661fc2974f"
23+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
2524
Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc"
2625
SpheriCart = "5caf2b29-02d9-47a3-9434-5931c85ba645"
2726
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2827
WignerD = "87c4ff3e-34df-11e9-37a7-516cea4e0402"
2928

3029
[targets]
31-
test = ["Test", "Polynomials4ML", "WignerD", "Rotations", "BlockDiagonals", "SpheriCart"]
30+
test = ["Test", "WignerD", "Rotations", "BlockDiagonals",
31+
"SpheriCart", "Random"]

benchmarks/run_benchmark.jl

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
2+
using StaticArrays, LinearAlgebra, RepLieGroups, WignerD, Combinatorics,
3+
Rotations
4+
using WignerD: wignerD
5+
using Test
6+
7+
isdefined(Main, :___UTILS_FOR_TESTS___) || include("../utils/utils_for_tests.jl")
8+
9+
##
10+
11+
# Test the new RPE basis up to L = 4
12+
@info("Testing the new cSH-based RPE basis")
13+
for L = 0:4
14+
@info("Testing L = $L")
15+
16+
# generate a nnll_list for testing
17+
lmax = 4
18+
nmax = 4
19+
nnll_list = []
20+
21+
for ORD = 2:6
22+
for ll in with_replacement_combinations(1:lmax, ORD)
23+
# 0 or 1 above ?
24+
if !iseven(sum(ll)+L); continue; end # This is to ensure the reflection symmetry
25+
if sum(ll) > 2 * lmax; continue; end
26+
for Inn in CartesianIndices( ntuple(_->1:nmax, ORD) )
27+
nn = [ Inn.I[α] for α = 1:ORD ]
28+
if sum(nn) > sum(1:nmax); continue; end
29+
nnll = [ (ll[α], nn[α]) for α = 1:ORD ]
30+
if !issorted(nnll); continue; end
31+
push!(nnll_list, (SVector(nn...), SVector(ll...)))
32+
end
33+
end
34+
end
35+
36+
long_nnll_list = nnll_list
37+
short_nnll_list = nnll_list[1:10:end]
38+
ultra_short_nnll_list = nnll_list[1:100:end]
39+
40+
verbose = true
41+
42+
@info("Using short nnll list for testing")
43+
nnll_list = short_nnll_list
44+
45+
for (itest, (nn, ll)) in enumerate(nnll_list)
46+
N = length(ll)
47+
@assert length(ll) == length(nn)
48+
49+
local Rs, θ, Q, QRs, D, D_r
50+
51+
Rs = rand_config(length(ll))
52+
θ = rand(3) * 2pi
53+
Q = RotZYZ(θ...)
54+
D = transpose(wignerD(L, θ...))
55+
D_r = Ctran(L) * D * Ctran(L)'
56+
QRs = [Q*Rs[i] for i in 1:length(Rs)]
57+
58+
t_rpe = @elapsed coeffs_ind, MM = O3.rpe_basis_new(nn,ll,L)
59+
t_rpe_r = @elapsed coeffs_ind_r, MM_r = O3.rpe_basis_new(nn,ll,L; flag = :SpheriCart)
60+
rk = rank(O3.gram(coeffs_ind),rtol = 1e-12)
61+
rk_r = rank(O3.gram(coeffs_ind_r),rtol = 1e-12)
62+
@test rk == rk_r
63+
64+
fRs1 = eval_basis(Rs; coeffs = coeffs_ind, MM = MM, ll = ll, nn = nn)
65+
fRs1Q = eval_basis(QRs; coeffs = coeffs_ind, MM = MM, ll = ll, nn = nn)
66+
# @info("Testing the equivariance of the old RPE basis")
67+
L == 0 ? (@test norm(fRs1 - fRs1Q) < 1e-14) : (@test norm(fRs1 - Ref(D) .* fRs1Q) < 1e-14)
68+
69+
fRs1_r = eval_basis(Rs; coeffs = coeffs_ind_r, MM = MM_r, ll = ll, nn = nn, Real = true)
70+
fRs1Q_r = eval_basis(QRs; coeffs = coeffs_ind_r, MM = MM_r, ll = ll, nn = nn, Real = true)
71+
# @info("Testing the equivariance of the old RPE basis")
72+
L == 0 ? (@test norm(fRs1_r - fRs1Q_r) < 1e-14) : (@test norm(fRs1_r - Ref(D_r) .* fRs1Q_r) < 1e-14)
73+
74+
ntest = 1000
75+
76+
RR = make_batch(ntest, length(ll))
77+
78+
X = rand_batch(; coeffs=coeffs_ind, MM=MM, ll=ll, nn=nn, batch = RR)
79+
@test rank(O3.gram(X); rtol=1e-12) == size(X,1) == rk
80+
81+
X_r = rand_batch(; coeffs=coeffs_ind_r, MM=MM_r, ll=ll, nn=nn, batch = RR)
82+
@test rank(O3.gram(X_r); rtol=1e-12) == size(X_r,1) == rk_r
83+
84+
Xsym = sym_rand_batch(; coeffs=coeffs_ind, MM=MM, ll=ll, nn=nn, batch = RR)
85+
@test rank(O3.gram(Xsym); rtol=1e-12) == size(Xsym,1) == rk
86+
87+
Xsym_r = sym_rand_batch(; coeffs=coeffs_ind_r, MM=MM_r, ll=ll, nn=nn, batch = RR)
88+
@test rank(O3.gram(Xsym_r); rtol=1e-12) == size(Xsym_r,1) == rk_r
89+
90+
if verbose
91+
@info("Test $itest: t_rpe = $t_rpe, t_rpe_r = $t_rpe_r")
92+
else
93+
print(".")
94+
end
95+
end
96+
end
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ function Ctran(i::Int64,j::Int64;convention = :SpheriCart)
257257
end
258258
end
259259
260-
Ctran(l::Int64;convention = :SpheriCart) = sparse(Matrix{ComplexF64}([ Ctran(m,μ;convention=convention) for m = -l:l, μ = -l:l ])) |> dropzeros
260+
Ctran(l::Int64; convention = :SpheriCart) = sparse(Matrix{ComplexF64}([ Ctran(m,μ;convention=convention) for m = -l:l, μ = -l:l ])) |> dropzeros
261261
262262
## NOTE: Ctran(L) is the transformation matrix from rSH to cSH. More specifically,
263263
# if we write Polynomials4ML rSH as R_{lm} and cSH as Y_{lm} and their corresponding

src/O3.jl

Lines changed: 145 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,54 @@
1-
module O3_new
2-
3-
# Alternative to the computation of rotation equivariant coupling coefficients
1+
module O3
42

53
import RepLieGroups
4+
65
using PartialWaveFunctions
76
using Combinatorics
87
using LinearAlgebra
98
using StaticArrays
9+
using SparseArrays
10+
11+
export coupling_coeffs
12+
13+
14+
# -------------------------------------------------------
15+
16+
## NOTE: Ctran(L) is the transformation matrix from rSH to cSH. More specifically,
17+
# if we write Polynomials4ML rSH as R_{lm} and cSH as Y_{lm} and their corresponding
18+
# vectors of order L as R_L and Y_L, respectively. Then R_L = Ctran(L) * Y_L.
19+
# This suggests that the "D-matrix" for the Polynomials4ML rSH is Ctran(l) * D(l) * Ctran(L)',
20+
# where D, the D-matrix for cSH. This inspires the following new CG recursion.
21+
22+
# transformation matrix from RSH to CSH for different conventions
23+
function Ctran(i::Int64,j::Int64;convention = :SpheriCart)
24+
if convention == :cSH
25+
return i == j
26+
end
27+
28+
order_dict = Dict(:SpheriCart => [1,2,3,4], :CondonShortley => [4,3,2,1], :FHIaims => [4,2,3,1])
29+
val_list = [(-1)^(i), im, (-1)^(i+1)*im, 1] ./ sqrt(2)
30+
if abs(i) != abs(j)
31+
return 0
32+
elseif i == j == 0
33+
return 1
34+
elseif i > 0 && j > 0
35+
return val_list[order_dict[convention][1]]
36+
elseif i < 0 && j < 0
37+
return val_list[order_dict[convention][2]]
38+
elseif i < 0 && j > 0
39+
return val_list[order_dict[convention][3]]
40+
elseif i > 0 && j < 0
41+
return val_list[order_dict[convention][4]]
42+
end
43+
end
44+
45+
Ctran(l::Int64; convention = :SpheriCart) = sparse(Matrix{ComplexF64}([ Ctran(m,μ;convention=convention) for m = -l:l, μ = -l:l ])) |> dropzeros
1046

11-
using RepLieGroups.O3: Ctran
1247

13-
export re_rpe, rpe_basis_new
48+
# -----------------------------------------------------
1449

15-
# The generalized Clebsch Gordan Coefficients; variables of this function are fully inherited from the first ACE paper
50+
# The generalized Clebsch Gordan Coefficients; variables of this function are
51+
# fully inherited from the first ACE paper.
1652
function GCG(l::SVector{N,Int64},m::SVector{N,Int64},L::SVector{N,Int64},M_N::Int64;flag=:cSH) where N
1753
# @assert -L[N] ≤ M_N ≤ L[N]
1854
if m_filter(m, M_N;flag=flag) == false || L[1] < abs(m[1])
@@ -40,7 +76,7 @@ function GCG(l::SVector{N,Int64},m::SVector{N,Int64},L::SVector{N,Int64},M_N::In
4076
mm = SA[mm...]
4177
@assert sum(mm) == M
4278
C_loc = GCG(l,mm,L,M;flag=:cSH)
43-
coeff = Ctran(M_N,M;convention=flag)' * prod( RepLieGroups.O3.Ctran(m[i],mm[i];convention=flag) for i in 1:N )
79+
coeff = Ctran(M_N,M;convention=flag)' * prod( Ctran(m[i],mm[i];convention=flag) for i in 1:N )
4480
C_loc *= coeff
4581
C += C_loc
4682
end
@@ -165,25 +201,113 @@ end
165201
# Function that generates the set of ordered m's given `n` and `l` with the abosolute sum of m's being smaller than L.
166202
m_generate(n,l,L;flag=:cSH) = union([m_generate(n,l,L,k;flag)[1] for k in -L:L]...), sum(length.(union([m_generate(n,l,L,k;flag)[1] for k in -L:L]...))) # orginal version: sum(m_generate(n,l,L,k;flag)[2] for k in -L:L), but this cannot be true anymore b.c. the m_classes can intersect
167203

204+
function gram(X::Matrix{SVector{N,T}}) where {N,T}
205+
G = zeros(T, size(X,1), size(X,1))
206+
for i = 1:size(X,1)
207+
for j = i:size(X,1)
208+
G[i,j] = sum(dot(X[i,t], X[j,t]) for t = 1:size(X,2))
209+
i == j ? nothing : (G[j,i]=G[i,j]')
210+
end
211+
end
212+
return G
213+
end
214+
215+
gram(X::Matrix{<:Number}) = X * X'
216+
217+
function lexi_ord(nn::SVector{N, Int64}, ll::SVector{N, Int64}) where N
218+
pairs = collect(zip(ll, nn)) # create (lᵢ, nᵢ) pairs
219+
sort!(pairs) # sort lexicographically: first by lᵢ, then by nᵢ
220+
return SVector{N}(last.(pairs)), SVector{N}(first.(pairs))
221+
end
222+
223+
"""
224+
O3.coupling_coeffs(L, ll, nn; PI, basis)
225+
O3.coupling_coeffs(L, ll; PI, basis)
226+
227+
Compute coupling coefficients for the spherical harmonics basis, where
228+
- `L` must be an `Integer`;
229+
- `ll, nn` must be vectors or tuples of `Integer` of the same length.
230+
- `PI`: whether or not the coupled basis is permutation-invariant (or the
231+
corresponding tensor symmetric); default is `true` when `nn` is provided
232+
and `false` when `nn` is not provided.
233+
- `basis`: which basis is being coupled, default is `complex`, alternative
234+
choice is `real`, which is compatible with the `SpheriCart.jl` convention.
235+
"""
236+
function coupling_coeffs(L::Integer, ll, nn = nothing;
237+
PI = !(isnothing(nn)),
238+
basis = complex)
239+
240+
# convert L into the format required internally
241+
_L = Int(L)
242+
243+
# convert ll into an SVector{N, Int}, as required internally
244+
N = length(ll)
245+
_ll = try
246+
_ll = SVector{N, Int}(ll...)
247+
catch
248+
error("""coupling_coeffs(L::Integer, ll, ...) requires ll to be
249+
a vector or tuple of integers""")
250+
end
168251
169-
# Function that generates the coupling coefficient of the RE basis given `n` and `l`., the FMatrix for generating the RPE basis is also generated here.
170-
function re_rpe(n::SVector{N,Int64},l::SVector{N,Int64},L::Int64;flag=:cSH) where N
171-
Lset = SetLl(l,L)
252+
# convert nn into an SVector{N, Int}, as required internally
253+
if isnothing(nn)
254+
if PI
255+
_nn = SVector{N, Int}(ntuple(i -> 0, N)...)
256+
else
257+
_nn = SVector{N, Int}((1:N)...)
258+
end
259+
elseif length(nn) != N
260+
error("""coupling_coeffs(L::Integer, ll, nn) requires ll and nn to be
261+
of the same length""")
262+
else
263+
_nn = try
264+
_nn = SVector{N, Int}(nn...)
265+
catch
266+
error("""coupling_coeffs(L::Integer, ll, nn) requires nn to be
267+
a vector or tuple of integers""")
268+
end
269+
end
270+
271+
if basis == complex
272+
flag = :cSH
273+
elseif basis == real
274+
flag = :SpheriCart
275+
elseif basis isa Symbol
276+
flag = basis
277+
else
278+
error("unknown basis type: $basis")
279+
end
280+
281+
return _coupling_coeffs(_L, _ll, _nn; PI = PI, flag = flag)
282+
end
283+
284+
285+
# Function that generates the coupling coefficient of the RE basis (PI = false)
286+
# or RPE basis (PI = true) given `nn` and `ll`.
287+
function _coupling_coeffs(L::Int64, ll::SVector{N, Int64}, nn::SVector{N, Int64};
288+
PI = true, flag = :cSH) where N
289+
290+
# TODO: when PI, (nn, ll) should be ordered
291+
if PI
292+
nn, ll = lexi_ord(nn, ll)
293+
end
294+
295+
Lset = SetLl(ll,L)
172296
r = length(Lset)
173297
T = L == 0 ? Float64 : SVector{2L+1,Float64}
174298
if r == 0
175299
return zeros(T, 1, 1), zeros(T, 1, 1), [zeros(Int,N)], [zeros(Int,N)]
176300
else
177-
MMmat, size_m = m_generate(n,l,L;flag=flag) # classes of m's
301+
MMmat, size_m = m_generate(nn,ll,L;flag=flag) # classes of m's
178302
FMatrix=zeros(T, r, length(MMmat)) # Matrix containing f(m,i)
179303
UMatrix=zeros(T, r, size_m) # Matrix containing the the coupling coefficients D
180304
MM = [] # all possible m's
181305
for i in 1:r
182306
c = 0
183307
for (j,m_class) in enumerate(MMmat)
184-
for m in m_class
308+
for mm in m_class
185309
c += 1
186-
cg_coef = GCG(l,m,Lset[i];vectorize=(L!=0),flag=flag)
310+
cg_coef = GCG(ll,mm,Lset[i];vectorize=(L!=0),flag=flag)
187311
FMatrix[i,j]+= cg_coef
188312
UMatrix[i,c] = cg_coef
189313
end
@@ -196,28 +320,16 @@ function re_rpe(n::SVector{N,Int64},l::SVector{N,Int64},L::Int64;flag=:cSH) wher
196320
end
197321
end
198322
end
199-
return UMatrix, FMatrix, MMmat, MM
200-
end
201323

202-
function gram(X::Matrix{SVector{N,T}}) where {N,T}
203-
G = zeros(T, size(X,1), size(X,1))
204-
for i = 1:size(X,1)
205-
for j = i:size(X,1)
206-
G[i,j] = sum(dot(X[i,t], X[j,t]) for t = 1:size(X,2))
207-
i == j ? nothing : (G[j,i]=G[i,j]')
208-
end
324+
if !PI
325+
# return RE coupling coeffs if the permutation invariance is not needed
326+
return UMatrix, MM
327+
else
328+
U, S, V = svd(gram(FMatrix))
329+
rk = rank(Diagonal(S); rtol = 1e-12)
330+
# return the RE-PI coupling coeffs
331+
return Diagonal(S[1:rk]) * (U[:, 1:rk]' * UMatrix), MM
209332
end
210-
return G
211-
end
212-
213-
gram(X::Matrix{<:Number}) = X * X'
214-
215-
function rpe_basis_new(nn::SVector{N, Int64}, ll::SVector{N, Int64}, L::Int64; flag = :cSH) where N
216-
t_re = @elapsed UMatrix, FMatrix, MMmat, MM = re_rpe(nn, ll, L; flag = flag) # time of constructing the re_basis
217-
# @show t_re # should be removed in the final version
218-
U, S, V = svd(gram(FMatrix))
219-
rk = rank(Diagonal(S); rtol = 1e-12)
220-
return Diagonal(S[1:rk]) * (U[:, 1:rk]' * UMatrix), MM
221333
end
222334

223335
end

src/RepLieGroups.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module RepLieGroups
22

3-
include("yyvector.jl")
3+
export O3
44

5-
include("obsolete/obsolete.jl")
5+
include("yyvector.jl")
66

77
include("O3.jl")
88

0 commit comments

Comments
 (0)