From bf3a523700dbd37d71b838b6f0f86ae4e721533f Mon Sep 17 00:00:00 2001 From: zhanglw0521 Date: Fri, 20 Oct 2023 18:04:25 -0700 Subject: [PATCH] Comment out those (L1,L2) block related codes --- src/builder.jl | 374 +++++++++++++++++++------------------- test/test_equivariance.jl | 108 +++++------ 2 files changed, 241 insertions(+), 241 deletions(-) diff --git a/src/builder.jl b/src/builder.jl index 5000dfe..d178cb0 100644 --- a/src/builder.jl +++ b/src/builder.jl @@ -305,191 +305,191 @@ equivariant_SYY_model(totdeg::Int64, ν::Int64, radial::Radial_basis, L::Int64; equivariant_SYY_model(nn::Vector{Int64}, ll::Vector{Int64}, radial::Radial_basis, L::Int64; categories=[], _get_cat = _get_cat_default, d=3, group="O3") = equivariant_SYY_model(_close(nn, ll; filter = RPE_filter_long(L)), radial, L; categories, d, group) -## TODO: The following should eventually go into ACEhamiltonians.jl rather than this package - -# mapping from long vector to spherical matrix -using RepLieGroups.O3: ClebschGordan -cg = ClebschGordan(ComplexF64) -function cgmatrix(L1, L2) - cgm = zeros((2L1+1) * (2L2+1),(L1+L2+1)^2) - for (i,(p,q)) in enumerate(collect(Iterators.product(-L1:L1, L2:-1:-L2))) - ν = p+q - for l = 0:L1+L2 - if abs(ν)<=l - position = ν + l + 1 - cgm[i, l^2+position] = (-1)^q * cg(L1, p, L2, q, l, ν) - # cgm[i, l^2+position] = cg(L1,p,L2,q,l,ν) - # cgm[i, l^2+position] = (-1)^q * sqrt( (2L1+1) * (2L2+1) ) / 2 / sqrt(π * (2l+1)) * cg(L1,0,L2,0,l,0) * cg(L1,p,L2,q,l,ν) - end - end - end - return sparse(cgm) -end - -# This constructor builds a lux chain that maps a configuration to a LONG B^L vector ([B^0, B^1, ... B^L]), -# and then all equivariant basis. -# What can be adjusted in its input are: (1) total polynomial degree; (2) correlation order; (3) largest L -# (4) weight of the order of spherical harmonics; (5) specified radial basis -function equivariant_luxchain_constructor(totdeg, ν, L; wL = 1, Rn = legendre_basis(totdeg)) - - filter = RPE_filter_long(L) - cgen = Rot3DCoeffs_long(L) - - Ylm = CYlmBasis(totdeg) - - spec1p = make_nlms_spec(simple_radial_basis(Rn), Ylm; totaldegree = totdeg, admissible = (br, by) -> br.n + wL * by.l <= totdeg) - spec1p = sort(spec1p, by = (x -> x.n + x.l * wL)) - spec1pidx = getspec1idx(spec1p, simple_radial_basis(Rn).Radialspec, Ylm) - - # define sparse for n-correlations - tup2b = vv -> [ spec1p[v] for v in vv[vv .> 0] ] - default_admissible = bb -> length(bb) == 0 || sum(b.n for b in bb) + wL * sum(b.l for b in bb) <= totdeg - - specAA = gensparse(; NU = ν, tup2b = tup2b, filter = filter, - admissible = default_admissible, - minvv = fill(0, ν), - maxvv = fill(length(spec1p), ν), - ordered = true) - - spec = [ vv[vv .> 0] for vv in specAA if !(isempty(vv[vv .> 0]))] - # map back to nlm - spec_nlm = getspecnlm(spec1p, spec) - - C = _rpi_A2B_matrix(cgen, spec_nlm) - - # acemodel with lux layers - bA = P4ML.PooledSparseProduct(spec1pidx) - bAA = P4ML.SparseSymmProd(spec) - - # wrapping into lux layers - l_Rn = P4ML.lux(Rn) - l_Ylm = P4ML.lux(Ylm) - l_bA = P4ML.lux(bA) - l_bAA = P4ML.lux(bAA) - - # formming model with Lux Chain - _norm(x) = norm.(x) - - l_xnx = Lux.Parallel(nothing; normx = WrappedFunction(_norm), x = WrappedFunction(identity)) - l_embed = Lux.Parallel(nothing; Rn = l_Rn, Ylm = l_Ylm) - - l1l2set = [(l1,l2) for l1 = 0:L for l2 = 0:L-l1] - cgmat_set = [cgmatrix(l1,l2) for (l1,l2) in l1l2set ] - - _block(x,cgmat,l1,l2) = reshape.(Ref(cgmat) .* [ x[j][1:size(cgmat)[2]] for j = 1:length(x) ], 2l1+1, 2l2+1) - _dropzero(x) = x[findall(X -> X>1e-12, norm.(x))] - - l_seperate = Lux.Parallel(nothing, [WrappedFunction(x -> _dropzero(_block(x,cgmat_set[i],l1l2set[i][1],l1l2set[i][2]))) for i = 1:length(cgmat_set)]... ) - - # C - A2Bmap - luxchain = Chain(xnx = l_xnx, embed = l_embed, A = l_bA , AA = l_bAA, BB = WrappedFunction(x -> C * x), blocks = l_seperate)#, rAA = WrappedFunction(ComplexF64)) - ps, st = Lux.setup(MersenneTwister(1234), luxchain) +# ## TODO: The following should eventually go into ACEhamiltonians.jl rather than this package + +# # mapping from long vector to spherical matrix +# using RepLieGroups.O3: ClebschGordan +# cg = ClebschGordan(ComplexF64) +# function cgmatrix(L1, L2) +# cgm = zeros((2L1+1) * (2L2+1),(L1+L2+1)^2) +# for (i,(p,q)) in enumerate(collect(Iterators.product(-L1:L1, L2:-1:-L2))) +# ν = p+q +# for l = 0:L1+L2 +# if abs(ν)<=l +# position = ν + l + 1 +# cgm[i, l^2+position] = (-1)^q * cg(L1, p, L2, q, l, ν) +# # cgm[i, l^2+position] = cg(L1,p,L2,q,l,ν) +# # cgm[i, l^2+position] = (-1)^q * sqrt( (2L1+1) * (2L2+1) ) / 2 / sqrt(π * (2l+1)) * cg(L1,0,L2,0,l,0) * cg(L1,p,L2,q,l,ν) +# end +# end +# end +# return sparse(cgm) +# end + +# # This constructor builds a lux chain that maps a configuration to a LONG B^L vector ([B^0, B^1, ... B^L]), +# # and then all equivariant basis. +# # What can be adjusted in its input are: (1) total polynomial degree; (2) correlation order; (3) largest L +# # (4) weight of the order of spherical harmonics; (5) specified radial basis +# function equivariant_luxchain_constructor(totdeg, ν, L; wL = 1, Rn = legendre_basis(totdeg)) + +# filter = RPE_filter_long(L) +# cgen = Rot3DCoeffs_long(L) + +# Ylm = CYlmBasis(totdeg) + +# spec1p = make_nlms_spec(simple_radial_basis(Rn), Ylm; totaldegree = totdeg, admissible = (br, by) -> br.n + wL * by.l <= totdeg) +# spec1p = sort(spec1p, by = (x -> x.n + x.l * wL)) +# spec1pidx = getspec1idx(spec1p, simple_radial_basis(Rn).Radialspec, Ylm) + +# # define sparse for n-correlations +# tup2b = vv -> [ spec1p[v] for v in vv[vv .> 0] ] +# default_admissible = bb -> length(bb) == 0 || sum(b.n for b in bb) + wL * sum(b.l for b in bb) <= totdeg + +# specAA = gensparse(; NU = ν, tup2b = tup2b, filter = filter, +# admissible = default_admissible, +# minvv = fill(0, ν), +# maxvv = fill(length(spec1p), ν), +# ordered = true) + +# spec = [ vv[vv .> 0] for vv in specAA if !(isempty(vv[vv .> 0]))] +# # map back to nlm +# spec_nlm = getspecnlm(spec1p, spec) + +# C = _rpi_A2B_matrix(cgen, spec_nlm) + +# # acemodel with lux layers +# bA = P4ML.PooledSparseProduct(spec1pidx) +# bAA = P4ML.SparseSymmProd(spec) + +# # wrapping into lux layers +# l_Rn = P4ML.lux(Rn) +# l_Ylm = P4ML.lux(Ylm) +# l_bA = P4ML.lux(bA) +# l_bAA = P4ML.lux(bAA) + +# # formming model with Lux Chain +# _norm(x) = norm.(x) + +# l_xnx = Lux.Parallel(nothing; normx = WrappedFunction(_norm), x = WrappedFunction(identity)) +# l_embed = Lux.Parallel(nothing; Rn = l_Rn, Ylm = l_Ylm) + +# l1l2set = [(l1,l2) for l1 = 0:L for l2 = 0:L-l1] +# cgmat_set = [cgmatrix(l1,l2) for (l1,l2) in l1l2set ] + +# _block(x,cgmat,l1,l2) = reshape.(Ref(cgmat) .* [ x[j][1:size(cgmat)[2]] for j = 1:length(x) ], 2l1+1, 2l2+1) +# _dropzero(x) = x[findall(X -> X>1e-12, norm.(x))] + +# l_seperate = Lux.Parallel(nothing, [WrappedFunction(x -> _dropzero(_block(x,cgmat_set[i],l1l2set[i][1],l1l2set[i][2]))) for i = 1:length(cgmat_set)]... ) + +# # C - A2Bmap +# luxchain = Chain(xnx = l_xnx, embed = l_embed, A = l_bA , AA = l_bAA, BB = WrappedFunction(x -> C * x), blocks = l_seperate)#, rAA = WrappedFunction(ComplexF64)) +# ps, st = Lux.setup(MersenneTwister(1234), luxchain) - return luxchain, ps, st -end - -# This constructor builds a lux chain that maps a configuration to B^0, B^1, ... B^L first, -# and then deduces all equivariant basis. -# What can be adjusted in its input are: (1) total polynomial degree; (2) correlation order; (3) largest L -# (4) weight of the order of spherical harmonics; (5) specified radial basis -function equivariant_luxchain_constructor_new(totdeg, ν, L; wL = 1, Rn = legendre_basis(totdeg)) - Ylm = CYlmBasis(totdeg) - - spec1p = make_nlms_spec(simple_radial_basis(Rn), Ylm; totaldegree = totdeg, admissible = (br, by) -> br.n + wL * by.l <= totdeg) - spec1p = sort(spec1p, by = (x -> x.n + x.l * wL)) - spec1pidx = getspec1idx(spec1p, simple_radial_basis(Rn).Radialspec, Ylm) - - # define sparse for n-correlations - tup2b = vv -> [ spec1p[v] for v in vv[vv .> 0] ] - default_admissible = bb -> length(bb) == 0 || sum(b.n for b in bb) + wL * sum(b.l for b in bb) <= totdeg - - # initialize C and spec_nlm - C = Vector{Any}(undef,L+1) - # spec_nlm = Vector{Any}(undef,L+1) - spec = Vector{Any}(undef,L+1) - - # Spec = [] - - for l = 0:L - filter = RPE_filter(l) - cgen = Rot3DCoeffs(l) - specAA = gensparse(; NU = ν, tup2b = tup2b, filter = filter, - admissible = default_admissible, - minvv = fill(0, ν), - maxvv = fill(length(spec1p), ν), - ordered = true) - - spec[l+1] = [ vv[vv .> 0] for vv in specAA if !(isempty(vv[vv .> 0]))] - # Spec = Spec ∪ spec - - spec_nlm = getspecnlm(spec1p, spec[l+1]) - C[l+1] = _rpi_A2B_matrix(cgen, spec_nlm) - end - # return C, spec, spec_nlm - # acemodel with lux layers - bA = P4ML.PooledSparseProduct(spec1pidx) - - # - filter = RPE_filter_long(L) - specAA = gensparse(; NU = ν, tup2b = tup2b, filter = filter, - admissible = default_admissible, - minvv = fill(0, ν), - maxvv = fill(length(spec1p), ν), - ordered = true) - - Spec = [ vv[vv .> 0] for vv in specAA if !(isempty(vv[vv .> 0]))] - dict = Dict([Spec[i] => i for i = 1:length(Spec)]) - pos = [ [dict[spec[k][j]] for j = 1:length(spec[k])] for k = 1:L+1 ] - # @show typeof(pos) - # return C, spec, Spec - - bAA = P4ML.SparseSymmProd(Spec) - - # wrapping into lux layers - l_Rn = P4ML.lux(Rn) - l_Ylm = P4ML.lux(Ylm) - l_bA = P4ML.lux(bA) - l_bAA = P4ML.lux(bAA) - - # formming model with Lux Chain - _norm(x) = norm.(x) - - l_xnx = Lux.Parallel(nothing; normx = WrappedFunction(_norm), x = WrappedFunction(identity)) - l_embed = Lux.Parallel(nothing; Rn = l_Rn, Ylm = l_Ylm) - l_seperate = Lux.Parallel(nothing, [WrappedFunction(x -> C[i] * x[pos[i]]) for i = 1:L+1]... ) - - function _vectorize(cc) - ccc = [] - for i = 1:length(cc) - ccc = [ccc; cc[i]...] - end - return sparse(complex.(ccc)) - end - - function _block(x,l1,l2) - @assert l1+l2 <= length(x) - 1 - init = iseven(l1+l2) ? 0 : 1 - fea_set = init:2:l1+l2 - A = Vector{Any}(undef,l1+l2+1) - for i = 0:l1+l2 - if i in fea_set - A[i+1] = x[i+1] - else - A[i+1] = [zeros(ComplexF64,2i+1)] - end - end - cc = Iterators.product(A...) |> collect - c = [ _vectorize(cc[i]) for i = 1:length(cc) ] - return reshape.( Ref(cgmatrix(l1,l2)) .* c, 2l1+1, 2l2+1 ) - end - - # l_condensed = WrappedFunction(x -> _block(x,0,1)) - l_condensed = Lux.Parallel(nothing, [WrappedFunction(x -> _block(x,l1,l2)) for l1 in 0:L for l2 in 0:L-l1]... ) - - - # C - A2Bmap - luxchain = Chain(xnx = l_xnx, embed = l_embed, A = l_bA , AA = l_bAA, BB = l_seperate, inter = WrappedFunction(x -> [x[i] for i = 1:length(x)]), B = l_condensed)#, rAA = WrappedFunction(ComplexF64)) - ps, st = Lux.setup(MersenneTwister(1234), luxchain) - - return luxchain, ps, st -end +# return luxchain, ps, st +# end + +# # This constructor builds a lux chain that maps a configuration to B^0, B^1, ... B^L first, +# # and then deduces all equivariant basis. +# # What can be adjusted in its input are: (1) total polynomial degree; (2) correlation order; (3) largest L +# # (4) weight of the order of spherical harmonics; (5) specified radial basis +# function equivariant_luxchain_constructor_new(totdeg, ν, L; wL = 1, Rn = legendre_basis(totdeg)) +# Ylm = CYlmBasis(totdeg) + +# spec1p = make_nlms_spec(simple_radial_basis(Rn), Ylm; totaldegree = totdeg, admissible = (br, by) -> br.n + wL * by.l <= totdeg) +# spec1p = sort(spec1p, by = (x -> x.n + x.l * wL)) +# spec1pidx = getspec1idx(spec1p, simple_radial_basis(Rn).Radialspec, Ylm) + +# # define sparse for n-correlations +# tup2b = vv -> [ spec1p[v] for v in vv[vv .> 0] ] +# default_admissible = bb -> length(bb) == 0 || sum(b.n for b in bb) + wL * sum(b.l for b in bb) <= totdeg + +# # initialize C and spec_nlm +# C = Vector{Any}(undef,L+1) +# # spec_nlm = Vector{Any}(undef,L+1) +# spec = Vector{Any}(undef,L+1) + +# # Spec = [] + +# for l = 0:L +# filter = RPE_filter(l) +# cgen = Rot3DCoeffs(l) +# specAA = gensparse(; NU = ν, tup2b = tup2b, filter = filter, +# admissible = default_admissible, +# minvv = fill(0, ν), +# maxvv = fill(length(spec1p), ν), +# ordered = true) + +# spec[l+1] = [ vv[vv .> 0] for vv in specAA if !(isempty(vv[vv .> 0]))] +# # Spec = Spec ∪ spec + +# spec_nlm = getspecnlm(spec1p, spec[l+1]) +# C[l+1] = _rpi_A2B_matrix(cgen, spec_nlm) +# end +# # return C, spec, spec_nlm +# # acemodel with lux layers +# bA = P4ML.PooledSparseProduct(spec1pidx) + +# # +# filter = RPE_filter_long(L) +# specAA = gensparse(; NU = ν, tup2b = tup2b, filter = filter, +# admissible = default_admissible, +# minvv = fill(0, ν), +# maxvv = fill(length(spec1p), ν), +# ordered = true) + +# Spec = [ vv[vv .> 0] for vv in specAA if !(isempty(vv[vv .> 0]))] +# dict = Dict([Spec[i] => i for i = 1:length(Spec)]) +# pos = [ [dict[spec[k][j]] for j = 1:length(spec[k])] for k = 1:L+1 ] +# # @show typeof(pos) +# # return C, spec, Spec + +# bAA = P4ML.SparseSymmProd(Spec) + +# # wrapping into lux layers +# l_Rn = P4ML.lux(Rn) +# l_Ylm = P4ML.lux(Ylm) +# l_bA = P4ML.lux(bA) +# l_bAA = P4ML.lux(bAA) + +# # formming model with Lux Chain +# _norm(x) = norm.(x) + +# l_xnx = Lux.Parallel(nothing; normx = WrappedFunction(_norm), x = WrappedFunction(identity)) +# l_embed = Lux.Parallel(nothing; Rn = l_Rn, Ylm = l_Ylm) +# l_seperate = Lux.Parallel(nothing, [WrappedFunction(x -> C[i] * x[pos[i]]) for i = 1:L+1]... ) + +# function _vectorize(cc) +# ccc = [] +# for i = 1:length(cc) +# ccc = [ccc; cc[i]...] +# end +# return sparse(complex.(ccc)) +# end + +# function _block(x,l1,l2) +# @assert l1+l2 <= length(x) - 1 +# init = iseven(l1+l2) ? 0 : 1 +# fea_set = init:2:l1+l2 +# A = Vector{Any}(undef,l1+l2+1) +# for i = 0:l1+l2 +# if i in fea_set +# A[i+1] = x[i+1] +# else +# A[i+1] = [zeros(ComplexF64,2i+1)] +# end +# end +# cc = Iterators.product(A...) |> collect +# c = [ _vectorize(cc[i]) for i = 1:length(cc) ] +# return reshape.( Ref(cgmatrix(l1,l2)) .* c, 2l1+1, 2l2+1 ) +# end + +# # l_condensed = WrappedFunction(x -> _block(x,0,1)) +# l_condensed = Lux.Parallel(nothing, [WrappedFunction(x -> _block(x,l1,l2)) for l1 in 0:L for l2 in 0:L-l1]... ) + + +# # C - A2Bmap +# luxchain = Chain(xnx = l_xnx, embed = l_embed, A = l_bA , AA = l_bAA, BB = l_seperate, inter = WrappedFunction(x -> [x[i] for i = 1:length(x)]), B = l_condensed)#, rAA = WrappedFunction(ComplexF64)) +# ps, st = Lux.setup(MersenneTwister(1234), luxchain) + +# return luxchain, ps, st +# end diff --git a/test/test_equivariance.jl b/test/test_equivariance.jl index dccb22b..1a5c4fb 100644 --- a/test/test_equivariance.jl +++ b/test/test_equivariance.jl @@ -223,64 +223,64 @@ for ntest = 1:20 end println() -## TODO: This should eventually go into ACEhamiltonians.jl -# equivariant blocks +# ## TODO: This should eventually go into ACEhamiltonians.jl +# # equivariant blocks -@info("Testing the chain that generates several equivariant blocks from a long vector") -totdeg = 6 -ν = 2 -L = Lmax -luxchain, ps, st = equivariant_luxchain_constructor(totdeg,ν,L) -F(X) = luxchain(X, ps, st)[1] +# @info("Testing the chain that generates several equivariant blocks from a long vector") +# totdeg = 6 +# ν = 2 +# L = Lmax +# luxchain, ps, st = equivariant_luxchain_constructor(totdeg,ν,L) +# F(X) = luxchain(X, ps, st)[1] -# A small comparison - long vector does give us some redundant basis... +# # A small comparison - long vector does give us some redundant basis... -@info("Equivariance test") -l1l2set = [(l1,l2) for l1 = 0:L for l2 = 0:L-l1] -for ntest = 1:10 - local X, θ1, θ2, θ3, Q, QX - X = [ @SVector(rand(3)) for i in 1:10 ] - θ1 = rand() * 2pi - θ2 = rand() * 2pi - θ3 = rand() * 2pi - Q = RotXYZ(θ1, θ2, θ3) - QX = [SVector{3}(x) for x in Ref(Q) .* X] +# @info("Equivariance test") +# l1l2set = [(l1,l2) for l1 = 0:L for l2 = 0:L-l1] +# for ntest = 1:10 +# local X, θ1, θ2, θ3, Q, QX +# X = [ @SVector(rand(3)) for i in 1:10 ] +# θ1 = rand() * 2pi +# θ2 = rand() * 2pi +# θ3 = rand() * 2pi +# Q = RotXYZ(θ1, θ2, θ3) +# QX = [SVector{3}(x) for x in Ref(Q) .* X] - for (i,(l1,l2)) in enumerate(l1l2set) - D1 = wigner_D(l1,Matrix(Q))' - D2 = wigner_D(l2,Matrix(Q))' - # D1 = wignerD(l1, 0, 0, θ) - # D2 = wignerD(l2, 0, 0, θ) - if F(X)[i] |> length ≠ 0 - print_tf(@test norm(Ref(D1') .* F(X)[i] .* Ref(D2) - F(QX)[i]) < 1e-12) - end - end -end -println() +# for (i,(l1,l2)) in enumerate(l1l2set) +# D1 = wigner_D(l1,Matrix(Q))' +# D2 = wigner_D(l2,Matrix(Q))' +# # D1 = wignerD(l1, 0, 0, θ) +# # D2 = wignerD(l2, 0, 0, θ) +# if F(X)[i] |> length ≠ 0 +# print_tf(@test norm(Ref(D1') .* F(X)[i] .* Ref(D2) - F(QX)[i]) < 1e-12) +# end +# end +# end +# println() -@info("Testing the equivariance of the second way of constructing equivariant bases") -totdeg = 6 -ν = 1 -L = Lmax -luxchain, ps, st = EquivariantModels.equivariant_luxchain_constructor_new(totdeg,ν,L); -F(X) = luxchain(X, ps, st)[1] +# @info("Testing the equivariance of the second way of constructing equivariant bases") +# totdeg = 6 +# ν = 1 +# L = Lmax +# luxchain, ps, st = EquivariantModels.equivariant_luxchain_constructor_new(totdeg,ν,L); +# F(X) = luxchain(X, ps, st)[1] -for ntest = 1:10 - local X, θ1, θ2, θ3, Q, QX - X = [ @SVector(rand(3)) for i in 1:10 ] - θ1 = rand() * 2pi - θ2 = rand() * 2pi - θ3 = rand() * 2pi - Q = RotXYZ(θ1, θ2, θ3) - QX = [SVector{3}(x) for x in Ref(Q) .* X] +# for ntest = 1:10 +# local X, θ1, θ2, θ3, Q, QX +# X = [ @SVector(rand(3)) for i in 1:10 ] +# θ1 = rand() * 2pi +# θ2 = rand() * 2pi +# θ3 = rand() * 2pi +# Q = RotXYZ(θ1, θ2, θ3) +# QX = [SVector{3}(x) for x in Ref(Q) .* X] - for i = 1:length(F(X)) - l1,l2 = Int.(size(F(X)[i][1]).-1)./2 - D1 = wigner_D(Int(l1),Matrix(Q))' - D2 = wigner_D(Int(l2),Matrix(Q))' - # D1 = wignerD(l1, 0, 0, θ) - # D2 = wignerD(l2, 0, 0, θ) - print_tf(@test Ref(D1') .* F(X)[i] .* Ref(D2) - F(QX)[i] |> norm < 1e-12) - end -end -println() +# for i = 1:length(F(X)) +# l1,l2 = Int.(size(F(X)[i][1]).-1)./2 +# D1 = wigner_D(Int(l1),Matrix(Q))' +# D2 = wigner_D(Int(l2),Matrix(Q))' +# # D1 = wignerD(l1, 0, 0, θ) +# # D2 = wignerD(l2, 0, 0, θ) +# print_tf(@test Ref(D1') .* F(X)[i] .* Ref(D2) - F(QX)[i] |> norm < 1e-12) +# end +# end +# println()