Skip to content

Commit

Permalink
stop using as composition operator and use * instead
Browse files Browse the repository at this point in the history
- completely drop `∘`: breaking change
- bump to v0.4
  • Loading branch information
thchr committed Jun 23, 2021
1 parent 44b2b4e commit fcd4231
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Crystalline"
uuid = "ae5e2be0-a263-11e9-351e-f94dad1eb351"
authors = ["Thomas Christensen <tchr@mit.edu>"]
version = "0.3.10"
version = "0.4.0"

[deps]
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
Expand Down
6 changes: 3 additions & 3 deletions build/setup_2d_band_representations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ function reduce_cosets!(ops::Vector{SymOperation{D}}, wp::WyckPos{D},
while i length(ops) && i length(orbits)
wpᵢ = orbits[i]
opᵢ = ops[i]
if ops[i]vec(wp) wpᵢ
if ops[i]*vec(wp) wpᵢ
i += 1 # then ops[i] is indeed a "generator" of wpᵢ
else
deleteat!(ops, i)
Expand All @@ -145,7 +145,7 @@ function induce_bandrep(siteir::SiteIrrep{D}, h::SymOperation{D}, kv::KVec{D}) w

siteg = group(siteir)
wp = wyck(siteg)
kv′ = constant(hkv) # <-- TODO: Why only constant part?
kv′ = constant(h*kv) # <-- TODO: Why only constant part?
χs = characters(siteir)

# only loop over the cosets/orbits that are not mere centering "copies"
Expand All @@ -158,7 +158,7 @@ function induce_bandrep(siteir::SiteIrrep{D}, h::SymOperation{D}, kv::KVec{D}) w
# sum over all the (non-centering-equivalent) wyckoff positions/cosets in the orbit
χᴳₖ = zero(ComplexF64)
for (wpα′, gα′) in zip(orbits, gαs)
tα′α′ = constant(vec(hwpα′) - vec(wpα′)) # TODO: <-- explain why we only need constant part here?
tα′α′ = constant(vec(h*wpα′) - vec(wpα′)) # TODO: <-- explain why we only need constant part here?
opᵗ = SymOperation(-tα′α′)

gα′⁻¹ = inv(gα′)
Expand Down
7 changes: 4 additions & 3 deletions docs/src/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,16 @@ We can compose two `SymOperation`s in Crystalline via:
```@example operations
op1 = S"z,x,y" # 3₁₁₁⁺
op2 = S"z,y,x" # m₋₁₀₁
op1 op2
op1 * op2
```
which is accessed by an overloaded call to `Base.*`, i.e. the multiplication operator (this enables us to also call derived methods of `*`, such as integer powers (e.g., `S"-y,x-y,z"^3 == S"x,y,z"`).
Note that composition is taken modulo integer lattice translations by default, such that
```@example operations
op2′ = S"z,y,x+1" # {m₋₁₀₁|001}
op1 op2′ # equivalent to compose(op1, op2′, true)
op1 * op2′ # equivalent to compose(op1, op2′, true)
```
rather than `S"x+1,z,y"`, which is the result of direct application of the above composition rule.
To compute "unreduced" composition, the alternative [`compose`](@ref) variant of [``](@ref) can be used with an optional third argument `false`:
To compute "unreduced" composition, the more precise [`compose`](@ref) variant of [`*`](@ref) can be used with an optional third argument `false`:
```@example operations
compose(op1, op2′, false)
```
Expand Down
2 changes: 1 addition & 1 deletion src/Crystalline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import Base: getindex, setindex!, # → iteration/AbstractArray interface
iterate,
string, isapprox, zero,
readuntil, vec, show, summary,
+, -, , ==, ImmutableDict,
*, +, -, ==, ImmutableDict,
isone, one,
convert
import LinearAlgebra: inv
Expand Down
8 changes: 4 additions & 4 deletions src/irreps_reality.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ function realify(lgirs::AbstractVector{LGIrrep{D}}, verbose::Bool=false) where D
# tionally, with I first, this is indeed what the `findfirst(...)`
# bits below will find)
if !k_equiv_kv₋
g₋ = sgops[findfirst(g-> isapprox(gkv, -kv, cntr; atol=DEFAULT_ATOL), sgops)]
g₋ = sgops[findfirst(g-> isapprox(g*kv, -kv, cntr; atol=DEFAULT_ATOL), sgops)]
else
# This is a bit silly: if k_equiv_kv₋ = true, we will never use g₋; but I'm not sure if
# the compiler will figure that out, or if it will needlessly guard against missing g₋?
Expand Down Expand Up @@ -383,8 +383,8 @@ function calc_reality(lgir::LGIrrep{D},

s = zero(ComplexF64)
for op in sgops
if isapprox(opkv, kv₋, cntr, atol=DEFAULT_ATOL) # check if opk == -k; if so, include in sum
op² = compose(op, op, false) # this is op∘op, _including_ trivial lattice translation parts
if isapprox(op*kv, kv₋, cntr, atol=DEFAULT_ATOL) # check if op*k == -k; if so, include in sum
op² = compose(op, op, false) # this is `op*op`, _including_ trivial lattice translation parts
# find the equivalent of `op²` in `lgops`; this may differ by a number of
# primitive lattice vectors `w_op²`; the difference must be included when
# we calculate the trace of the irrep 𝐃: the irrep matrix 𝐃 is ∝exp(2πi𝐤⋅𝐭)
Expand Down Expand Up @@ -419,7 +419,7 @@ function calc_reality(pgir::PGIrrep)

s = zero(eltype(χs))
for op in pg
op² = opop
op² = op*op
idx = findfirst((op²), pg)
idx === nothing && error("unexpectedly did not find point group element matching op²")

Expand Down
4 changes: 2 additions & 2 deletions src/special_representation_domain_kpoints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ function find_map_from_Ω_to_ΦnotΩ(G::SpaceGroup)
# a bit of extra work to make sure we find an operator which
# is not already in the coset P[matchidx]∘F by growing F with
# the "newly added" elements that P[matchidx] produce
F = append!(F, Ref(Rs[i]).∘F)
F = append!(F, Ref(Rs[i]) .* F)
end
end
return Rs
Expand Down Expand Up @@ -442,7 +442,7 @@ function find_new_kvecs(G::SpaceGroup{D}) where D
newklabs = [String[] for _ in OneTo(Nk)]
for (kidx, (kv, klab)) in enumerate(zip(kvs, klabs))
for (Ridx, R) in enumerate(Rs)
newkv = Rkv
newkv = R*kv
# compare newkv to the stars of kv: if it is equivalent to any member of star{kv},
# it is not a "new" k-vector. We do not have to compare to _all_ kv′∈Ω (i.e. to all
# kvs), because they must have inequivalent stars to kv in the first place and also
Expand Down
38 changes: 21 additions & 17 deletions src/symops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -255,21 +255,24 @@ pointgroup(sg::Union{SpaceGroup,LittleGroup}) = pointgroup(operations(sg))

# ----- GROUP ELEMENT COMPOSITION -----
"""
(∘)(op1::T, op2::T, modτ::Bool=true) where T<:SymOperation
compose(op1::T, op2::T, modτ::Bool=true) where T<:SymOperation
Compose two symmetry operations `op1` ``= \\{W₁|w₁\\}`` and `op2` ``= \\{W₂|w₂\\}``
using the composition rule (in Seitz notation)
``\\{W₁|w₁\\}\\{W₂|w₂\\} = \\{W₁W₂|w₁+W₁w₂\\}``
``\\{W₁|w₁\\}\\{W₂|w₂\\} = \\{W₁W₂|w₁+W₁w₂\\}``
By default, the translation part of the ``\\{W₁W₂|w₁+W₁w₂\\}`` is reduced to the range
``[0,1[``, i.e. computed modulo 1. This can be toggled off (or on) by the Boolean flag
`modτ` (enabled, i.e. `true`, by default). Returns another `SymOperation`.
The multiplication operator [`*`](@ref) is overloaded for `SymOperation`s to call `compose`,
in the manner `op1 * op2 = compose(op1, op2, modτ=true)`.
"""
function()(op1::T, op2::T, modτ::Bool=true) where T<:SymOperation
T(()(unpack(op1)..., unpack(op2)..., modτ)...)
function compose(op1::T, op2::T, modτ::Bool=true) where T<:SymOperation
T(compose(unpack(op1)..., unpack(op2)..., modτ)...)
end
function ()(W₁::T, w₁::R, W₂::T, w₂::R, modτ::Bool=true) where T<:SMatrix{D,D,<:Real} where R<:SVector{D,<:Real} where D
function compose(W₁::T, w₁::R, W₂::T, w₂::R, modτ::Bool=true) where T<:SMatrix{D,D,<:Real} where R<:SVector{D,<:Real} where D
W′ = W₁*W₂
w′ = w₁ + W₁*w₂

Expand All @@ -279,7 +282,7 @@ function (∘)(W₁::T, w₁::R, W₂::T, w₂::R, modτ::Bool=true) where T<:SM

return W′, w′
end
const compose =
(*)(op1::T, op2::T) where T<:SymOperation = compose(op1, op2)

function reduce_translation_to_unitrange(w::SVector{D, <:Real}) where D # reduces components to range [0.0, 1.0[
# naïve approach to achieve semi-robust reduction of integer-translation
Expand All @@ -306,8 +309,8 @@ end
Compose two symmetry operations `op1` ``= \\{W₁|w₁\\}`` and `op2` ``= \\{W₂|w₂\\}`` and
return the quotient of ``w₁+W₁w₂`` and 1. This functionality complements
`op1op2`, which yields the translation modulo 1; accordingly,
`translation(op1op2) + op1⊚op2` yields the translation component
`op1*op2`, which yields the translation modulo 1; accordingly,
`translation(op1*op2) + op1⊚op2` yields the translation component
of the composition `op1` and `op2` *without* taking it modulo 1,
i.e. including any "trivial" lattice translation.
Expand Down Expand Up @@ -348,8 +351,8 @@ The `modτ` keyword argument controls whether composition of operations is taken
lattice vectors (`true`, default) or not (`false`).
A `MultTable{D}` is returned, which contains symmetry operations resulting from composition
of `rowcol` operators; the table of indices give the symmetry operators relative to the
ordering of `ops`.
of `row` and `col` operators; the table of indices give the symmetry operators relative to
the ordering of `ops`.
"""
function MultTable(ops::AbstractVector{SymOperation{D}};
modτ::Bool=true, verbose::Bool=false) where D
Expand Down Expand Up @@ -378,7 +381,7 @@ end
function check_multtable_vs_ir(lgir::LGIrrep{D}, αβγ=nothing; verbose::Bool=false) where D
ops = operations(lgir)
sgnum = num(lgir); cntr = centering(sgnum, D)
primitive_ops = primitivize.(ops, cntr) # must do multiplication table in primitive basis, cf. choices for composition/∘
primitive_ops = primitivize.(ops, cntr) # must do multiplication table in primitive basis, cf. choices in `compose`
check_multtable_vs_ir(MultTable(primitive_ops), lgir, αβγ; verbose=verbose)
end
function check_multtable_vs_ir(mt::MultTable, ir::AbstractIrrep, αβγ=nothing; verbose::Bool=false)
Expand Down Expand Up @@ -531,10 +534,10 @@ end
kstar(sg::SpaceGroup, kv::KVec) = kstar(sg, kv, centering(sg))

@doc raw"""
(∘)(op::SymOperation, kv::KVec, checkabc::Bool=true) --> KVec
compose(op::SymOperation, kv::KVec, checkabc::Bool=true) --> KVec
Return the composition (or action) of `op` on a reciprocal-space vector `kv`, i.e.
`kv' = op kv`.
Return the composition (or action) of `op` on a reciprocal-space vector `kv`. Can also be
called as `op * kv`.
`op` and `kv` are assumed to be specified in the direct lattice and reciprocal lattice
bases, respectively (this is the default behavior in Crystalline). As a result, `op` acts
Expand All @@ -543,20 +546,21 @@ on `kv` via the transpose of its matrix form, i.e. the `kv` vector is multiplied
That is, for a given reciprocal vector ``\mathbf{k}`` (i.e. `kv`) and operation ``g`` (i.e.
`op`) with rotation part ``\mathbf{R}`` (i.e. `rotation(op)`), we define
```math
\mathbf{k}' ≡ g\mathbf{k} = \mathbf{R}^{\text{T}}\mathbf{k}
\mathbf{k}' ≡ g\mathbf{k} = \mathbf{R}^{\text{T}}\mathbf{k}
```
The action of ``g`` on ``\mathbf{k}`` is invariant under any translation parts of ``g``,
i.e. translations do not act in reciprocal space.
If `checkabc = false`, the free part of `KVec` is not transformed (can be improve
performance in situations when `kabc` is zero, and several transformations are requested).
"""
@inline function ()(op::SymOperation{D}, kv::KVec{D}, checkabc::Bool=true) where D
@inline function compose(op::SymOperation{D}, kv::KVec{D}, checkabc::Bool=true) where D
k₀, kabc = parts(kv)
k₀′ = rotation(op)'*k₀
kabc′ = checkabc ? rotation(op)'*kabc : kabc
return KVec{D}(k₀′, kabc′)
end
(*)(op::SymOperation{D}, kv::KVec{D}) where D = compose(op, kv)



Expand Down Expand Up @@ -861,7 +865,7 @@ function isnormal(opsᴳ::T, opsᴴ::T; verbose::Bool=false) where T<:AbstractVe
g⁻¹ = inv(g)
for h in opsᴴ
# check if ghg⁻¹ ∉ G
h′ = ghg⁻¹
h′ = g*h*g⁻¹
if !isapproxin(h′, opsᴴ, atol=Crystalline.DEFAULT_ATOL)
if verbose
println("\nNormality-check failure:\n",
Expand Down
26 changes: 16 additions & 10 deletions src/wyckoff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,17 @@ get_wycks(sgnum::Integer, D) = get_wycks(sgnum, Val(D))
# METHODS

"""
(op::SymOperation, qv::RVec) --> RVec
compose(op::SymOperation, qv::RVec) --> RVec
Return the composition of `op` ``= \\{W|w\\}`` with a real-space vector `qv::RVec`.
The operation is taken to act directly, i.e. returns ``\\{W|w\\}```qv` ``= W```qv```+w``
rather than ``\\{W|w\\}^{-1}```qv` ``= W^{-1}```qv` ``- W^{-1}w``, which can instead be
obtained from `inv(op)∘qv`.
obtained from `compose(inv(op), qv)`.
Can also be called via the multipliication operator, i.e. `op * qv = compose(op, qv)`.
"""
function ()(op::SymOperation{D}, qv::RVec{D}) where D
function compose(op::SymOperation{D}, qv::RVec{D}) where D
cnst, free = parts(qv)
W, w = unpack(op)

Expand All @@ -112,8 +114,12 @@ function (∘)(op::SymOperation{D}, qv::RVec{D}) where D

return RVec{D}(cnst′, free′)
end
()(op::SymOperation{D}, wp::WyckPos{D}) where D = WyckPos{D}(multiplicity(wp), wp.letter,
opvec(wp))
function compose(op::SymOperation{D}, wp::WyckPos{D}) where D
WyckPos{D}(multiplicity(wp), wp.letter, compose(op, vec(wp)))
end

(*)(op::SymOperation{D}, qv::RVec{D}) where D = compose(op, qv)
(*)(op::SymOperation{D}, wp::WyckPos{D}) where D = compose(op, wp)


"""
Expand All @@ -125,7 +131,7 @@ Return the site symmetry group `g::SiteGroup` for a Wyckoff position `wp` in spa
`g` is a group of operations that are isomorphic to the those listed in `sg` (in the sense
that they might differ by lattice vectors) and that leave the Wyckoff position `wp`
invariant, such that `all(op -> wp == opwp, g) == true`.
invariant, such that `all(op -> wp == op*wp, g) == true`.
The returned `SiteGroup` also contains the coset representatives of the Wyckoff position
(that are again isomorphic to those featured in `sg`), accessible via [`cosets`](@ref),
Expand Down Expand Up @@ -167,7 +173,7 @@ julia> MultTable(g)
The original space group can be reconstructed from a left-coset decomposition, using the
operations and cosets contained in a `SiteGroup`:
```jldoctest sitegroup
julia> ops = [opʰopᵍ for opʰ in cosets(g) for opᵍ in g];
julia> ops = [opʰ*opᵍ for opʰ in cosets(g) for opᵍ in g];
julia> Set(sg) == Set(ops)
true
Expand Down Expand Up @@ -202,7 +208,7 @@ function SiteGroup(sg::SpaceGroup{D}, wp::WyckPos{D}) where D
isone(op) && continue # already added identity outside loop; avoid adding twice

W, w = unpack(op) # rotation and translation
qv′ = opqv
qv′ = op * qv
Δ = qv′ - qv
Δcnst, Δfree = parts(Δ)

Expand Down Expand Up @@ -256,10 +262,10 @@ function MultTable(g::SiteGroup; verbose::Bool=false)
end

function orbit(g::SiteGroup{D}, wp::WyckPos{D}) where D
qv′s = cosets(g) . Ref(wp) # TODO: Remove this method; superfluous input `wp`
qv′s = cosets(g) .* Ref(wp) # TODO: Remove this method; superfluous input `wp`
end
function orbit(g::SiteGroup{D}) where D
qv′s = cosets(g) . Ref(wyck(g))
qv′s = cosets(g) .* Ref(wyck(g))
end


Expand Down
8 changes: 4 additions & 4 deletions test/kvecs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ using Test, Crystalline, StaticArrays, LinearAlgebra

αβγ = Crystalline.TEST_αβγ
@testset "Composition" begin
@test SymOperation{2}("x,y")KVec{2}("1,α") == KVec{2}("1,α")
@test SymOperation{2}("x,y") * KVec{2}("1,α") == KVec{2}("1,α")

# 4⁺ rotation (rotates _oppositely_ in reciprocal space)
@test SymOperation{2}("-y,x")KVec(.4,.3) == KVec(.3,-.4)
@test SymOperation{2}("-y,x") * KVec(.4,.3) == KVec(.3,-.4)

# inserting g⁻¹g inside a dot product of a KVec and an RVec should do nothing (if
# g doesn't have a translation part; that would shift the RVec also...)
kv = KVec{3}(".4+α,0.3-γ,-.1+β")
qv = RVec{3}(".7-γ,0.6+α,.2-α")
op = SymOperation{3}("-z,-x,-y") # {-3₁₁₁⁺|½,½,½}
@test dot((inv(op)kv)(αβγ), (opqv)(αβγ)) dot(kv(αβγ), qv(αβγ)) # g⁻¹g
@test dot((opkv)(αβγ), (inv(op)qv)(αβγ)) dot(kv(αβγ), qv(αβγ)) # gg⁻¹
@test dot((inv(op)*kv)(αβγ), (op*qv)(αβγ)) dot(kv(αβγ), qv(αβγ)) # g⁻¹g
@test dot((op*kv)(αβγ), (inv(op)*qv)(αβγ)) dot(kv(αβγ), qv(αβγ)) # gg⁻¹
end
end
2 changes: 1 addition & 1 deletion test/symops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ using Crystalline, Test

# test associativity (with and without modular arithmetic)
g₁, g₂, g₃ = sg[5:7]
@test g₁(g₂g₃) == (g₁g₂)g₃
@test g₁*(g₂*g₃) == (g₁*g₂)*g₃
@test compose(compose(g₁, g₂, false), g₃, false) == compose(g₁, compose(g₂, g₃, false), false)
end

Expand Down
4 changes: 2 additions & 2 deletions test/wyckoff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ using Crystalline: constant, free
qv = vec(wp)
# test that ops in `g` leave the Wyckoff position `wp` invariant
for op in g
qv′ = opqv
qv′ = op*qv
@test isapprox(constant(qv), constant(qv′))
@test isapprox(free(qv), free(qv′))
end
Expand All @@ -28,7 +28,7 @@ using Crystalline: constant, free
end

# test that `g` and `cosets(g)` furnishes a left-coset decomposition of `sg`
ops = [opʰopᵍ for opʰ in cosets(g) for opᵍ in g];
ops = [opʰ*opᵍ for opʰ in cosets(g) for opᵍ in g];
@test sort!(seitz.(ops)) == sort!(seitz.(sg))
# (the above test is analogous to checking `Set(ops) == Set(sg)`, but the
# `==`-check is not robust due to rounding errors here: unfortunately,
Expand Down

2 comments on commit fcd4231

@thchr
Copy link
Owner Author

@thchr thchr commented on fcd4231 Jun 23, 2021

Choose a reason for hiding this comment

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

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

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

Registration pull request created: JuliaRegistries/General/39516

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.4.0 -m "<description of version>" fcd4231f632958f5d11a738a0a64d0ff6f08f4e2
git push origin v0.4.0

Please sign in to comment.