Skip to content

Commit 46047bd

Browse files
feat(Altermagnet): 实现自旋变换和对称性检查的改进
重构自旋变换逻辑,使用向量表示和投影方法替代之前的符号处理 添加xy轴旋转矩阵并更新生成器定义 改进对称性检查,支持更精确的自旋变换验证 新增kx*ky*σz项作为另一种d波反铁磁项
1 parent 36d5528 commit 46047bd

File tree

1 file changed

+81
-43
lines changed

1 file changed

+81
-43
lines changed

Demo/Altermagnet.lean

Lines changed: 81 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import SPG.Algebra.Group
33
import SPG.Geometry.SpatialOps
44
import SPG.Geometry.SpinOps
55
import SPG.Interface.Notation
6+
import Mathlib.LinearAlgebra.Matrix.Determinant.Basic
67

78
namespace Demo.Altermagnet
89

@@ -15,16 +16,18 @@ open SPG.Algebra
1516
-- 1. Generators for D4h altermagnet
1617
def mat_4_z : Matrix (Fin 3) (Fin 3) ℚ := ![![0, -1, 0], ![1, 0, 0], ![0, 0, 1]]
1718
def mat_inv : Matrix (Fin 3) (Fin 3) ℚ := ![![ -1, 0, 0], ![0, -1, 0], ![0, 0, -1]]
19+
-- mat_2_xy defined explicitly for clarity (rotation by 180 degrees about x+y axis)
20+
def mat_2_xy : Matrix (Fin 3) (Fin 3) ℚ := ![![0, 1, 0], ![1, 0, 0], ![0, 0, -1]]
1821

1922
-- Generators
2023
-- C4z * T : The key altermagnetic symmetry
21-
-- C2x : Standard rotation
24+
-- C2xy : Rotation about x+y axis
2225
-- I : Inversion
2326
def gen_C4z_TR : SPGElement := Op[mat_4_z, ^-1]
24-
def gen_C2x : SPGElement := Op[mat_2_x, ^1]
27+
def gen_C2xy : SPGElement := Op[mat_2_xy, ^1]
2528
def gen_Inv : SPGElement := Op[mat_inv, ^1]
2629

27-
def Altermagnet_Group : List SPGElement := generate_group [gen_C4z_TR, gen_C2x, gen_Inv]
30+
def Altermagnet_Group : List SPGElement := generate_group [gen_C4z_TR, gen_C2xy, gen_Inv]
2831

2932
-- 2. Hamiltonian Analysis
3033
-- We want to find H(k) ~ c_ij k_i k_j terms allowed by symmetry.
@@ -72,56 +75,72 @@ def act_on_k (g : SPGElement) (k : Vec3) : Vec3 :=
7275
-- So if g.spin = -I (Time Reversal), the spin vector flips.
7376
-- If g.spin = I, spin vector stays.
7477

75-
def act_on_spin (g : SPGElement) (s : SpinComp) : SpinComp :=
76-
if g.spin == spin_neg_I then -- Time Reversal
77-
match s with
78-
| .I => .I -- Identity matrix is T-even
79-
| _ => s -- This is wrong. T sigma T^-1 = -sigma.
80-
-- But we represent "basis elements". We need a coefficient sign.
81-
-- Let's handle sign separately.
82-
-- s -- Just return component, sign handled in `check_symmetry`
83-
else
84-
s
78+
def act_on_spin (g : SPGElement) (s : SpinComp) : Vec3 :=
79+
let s_vec : Vec3 := match s with
80+
| .x => ![1, 0, 0]
81+
| .y => ![0, 1, 0]
82+
| .z => ![0, 0, 1]
83+
| .I => ![0, 0, 0] -- Handle I separately
8584

86-
def spin_sign (g : SPGElement) (s : SpinComp) : ℚ :=
87-
if g.spin == spin_neg_I then
88-
match s with
89-
| .I => 1
90-
| _ => -1 -- Spin flips under Time Reversal
85+
if s == .I then ![0, 0, 0] -- I transforms to I (scalar)
9186
else
92-
1
93-
94-
-- Check if a term (Quad * Spin) is invariant under the group
95-
-- Term: C * Q(k) * S
96-
-- Transform: C * Q(g^-1 k) * (g S g^-1)
97-
-- We need Q(g^-1 k) * (spin_sign) == Q(k) for all g.
98-
-- Actually, let's just checking specific terms.
87+
-- Apply spatial rotation R to the axial vector sigma
88+
-- sigma' = (det R) * R * sigma
89+
-- If spin part has T (-I), then sigma' = - sigma'
90+
let detR := Matrix.det g.spatial
91+
let rotated := Matrix.mulVec g.spatial s_vec
92+
let axial_rotated := detR • rotated -- Scalar multiplication
93+
94+
if g.spin == spin_neg_I then
95+
-axial_rotated
96+
else
97+
axial_rotated
98+
99+
-- Helper to check if a transformed spin vector matches a target basis component (with sign)
100+
-- Returns 0 if orthogonal, 1 or -1 if parallel/antiparallel
101+
def project_spin (v : Vec3) (target : SpinComp) : ℚ :=
102+
match target with
103+
| .x => v 0
104+
| .y => v 1
105+
| .z => v 2
106+
| .I => 0
99107

100108
def check_invariant (q : QuadTerm) (s : SpinComp) : Bool :=
101109
Altermagnet_Group.all fun g =>
102110
-- Symmetry constraint: H(k) = U H(g^-1 k) U^dagger
103-
-- H(k) = f(k) * sigma
104-
-- U f(g^-1 k) sigma U^dagger = f(g^-1 k) * (U sigma U^dagger)
105-
-- So we need: f(k) * sigma = f(g^-1 k) * (sigma_transformed)
106-
-- Here sigma_transformed = s * spin_sign(g)
107-
-- And f(k) is quadratic form.
111+
-- H(k) = f(k) * sigma_s
112+
-- Transform: f(g^-1 k) * (g sigma_s g^-1)
113+
-- We check: f(g k) * (transformed sigma) == f(k) * sigma_s
114+
-- Note: using g k instead of g^-1 k for group average equivalence.
108115

109-
-- Let's test invariance on a set of random k-points to avoid accidental zeros
110116
let test_ks : List Vec3 := [![1, 0, 0], ![0, 1, 0], ![0, 0, 1], ![1, 1, 0], ![1, 0, 1], ![0, 1, 1], ![1, 2, 3]]
111117

112118
test_ks.all fun k =>
113-
-- Calculate g^-1 k. Since our group is finite and closed, g^-1 is in group.
114-
-- But for checking invariance, checking H(g k) = g H(k) g^-1 is equivalent.
115-
-- Let's check: H(g k) = g H(k) g^-1
116-
-- LHS: f(g k) * sigma
117-
-- RHS: g (f(k) * sigma) g^-1 = f(k) * (g sigma g^-1) = f(k) * sigma * spin_sign(g)
118-
119119
let val_gk := eval_quad q (act_on_k g k)
120120
let val_k := eval_quad q k
121-
let s_sgn := spin_sign g s
122121

123-
-- We need: val_gk == val_k * s_sgn
124-
val_gk == val_k * s_sgn
122+
if s == .I then
123+
-- Scalar term: val_gk == val_k
124+
val_gk == val_k
125+
else
126+
-- Vector term: val_gk * (transformed sigma) == val_k * sigma_s
127+
-- We only support cases where transformed sigma is parallel to sigma_s
128+
-- (i.e. no mixing like x -> y).
129+
-- Let's project transformed sigma onto s.
130+
let s_prime := act_on_spin g s
131+
let coeff := project_spin s_prime s
132+
133+
-- Check if it stays in the same direction (possibly with sign change)
134+
-- And check if orthogonal components are zero (no mixing)
135+
let is_eigen :=
136+
(s == .x && s_prime 1 == 0 && s_prime 2 == 0) ||
137+
(s == .y && s_prime 0 == 0 && s_prime 2 == 0) ||
138+
(s == .z && s_prime 0 == 0 && s_prime 1 == 0)
139+
140+
if is_eigen then
141+
val_gk * coeff == val_k
142+
else
143+
false -- If mixing occurs, this single term is not an invariant by itself.
125144

126145
def find_invariants : List (QuadTerm × SpinComp) :=
127146
let terms := (all_quads.product [.I, .x, .y, .z])
@@ -170,14 +189,33 @@ def main : IO Unit := do
170189
Demo.Altermagnet.Altermagnet_Group.all fun g =>
171190
let val_gk := f (Demo.Altermagnet.act_on_k g k)
172191
let val_k := f k
173-
let s_sgn := Demo.Altermagnet.spin_sign g s
174-
val_gk == val_k * s_sgn
192+
-- Re-use projection logic from check_invariant
193+
if s == .I then
194+
val_gk == val_k
195+
else
196+
let s_prime := Demo.Altermagnet.act_on_spin g s
197+
let coeff := Demo.Altermagnet.project_spin s_prime s
198+
199+
-- Check eigenstate property (no mixing)
200+
let is_eigen :=
201+
(s == .x && s_prime 1 == 0 && s_prime 2 == 0) ||
202+
(s == .y && s_prime 0 == 0 && s_prime 2 == 0) ||
203+
(s == .z && s_prime 0 == 0 && s_prime 1 == 0)
204+
205+
if is_eigen then
206+
val_gk * coeff == val_k
207+
else
208+
false
175209

176210
let kx2_minus_ky2 (k : SPG.Vec3) : ℚ := k 0 * k 0 - k 1 * k 1
177211
let kx2_plus_ky2 (k : SPG.Vec3) : ℚ := k 0 * k 0 + k 1 * k 1
212+
let kx_ky (k : SPG.Vec3) : ℚ := k 0 * k 1
178213

179214
if check_custom kx2_minus_ky2 .z then
180-
IO.println " (kx^2 - ky^2) * σz [Altermagnetic d-wave term!]"
215+
IO.println " (kx^2 - ky^2) * σz [d-wave altermagnetism (x^2-y^2 type)]"
216+
217+
if check_custom kx_ky .z then
218+
IO.println " kx ky * σz [d-wave altermagnetism (xy type)]"
181219

182220
if check_custom kx2_plus_ky2 .I then
183221
IO.println " (kx^2 + ky^2) * I [Standard kinetic term]"

0 commit comments

Comments
 (0)