|
5 | 5 |
|
6 | 6 | # TODO: Move to ITensors.jl |
7 | 7 | function Base.:/(qnval::ITensors.QNVal, n::Int) |
8 | | - div_val = ITensors.val(qnval) / n |
9 | | - if !isinteger(div_val) |
10 | | - error("Dividing $qnval by $n, the resulting QN value is not an integer") |
| 8 | + if abs(qnval.modulus) <= 1 |
| 9 | + div_val = ITensors.val(qnval) / n |
| 10 | + if !isinteger(div_val) |
| 11 | + error("Dividing $qnval by $n, the resulting QN value is not an integer") |
| 12 | + end |
| 13 | + return setval(qnval, Int(div_val)) |
| 14 | + else |
| 15 | + if mod(qnval.val, gcd(qnval.modulus, n)) != 0 |
| 16 | + error("Dividing $qnval by $n, no solution to the Chinese remainder theorem") |
| 17 | + end |
| 18 | + #We look for the inverse of n in the equation n x = qnval.val mod qn.modulus. |
| 19 | + #The Chinese remainder theorem guarantees it exists |
| 20 | + #We perform a brute force sieving solution -> should be ok for all reasonnable n |
| 21 | + sol = qnval.val |
| 22 | + for x in 1:n |
| 23 | + if mod(sol, n) != 0 |
| 24 | + sol += qnval.modulus |
| 25 | + else |
| 26 | + break |
| 27 | + end |
| 28 | + x == n && error("Failed to find solution") #Bezut identity would be a better solver here |
| 29 | + end |
| 30 | + modulus = lcm(qnval.modulus, n) |
| 31 | + sol = mod(sol, modulus) |
| 32 | + sol = sol < abs(sol - modulus) ? sol : sol - modulus |
| 33 | + return setval(qnval, Int(sol / n)) |
11 | 34 | end |
12 | | - return setval(qnval, Int(div_val)) |
| 35 | +end |
| 36 | + |
| 37 | +function Base.:*(qnval::ITensors.QNVal, n::Int) |
| 38 | + prod_val = ITensors.val(qnval) * n |
| 39 | + return setval(qnval, Int(prod_val)) |
13 | 40 | end |
14 | 41 |
|
15 | 42 | # TODO: Move to ITensors.jl |
16 | 43 | function Base.:/(qn::QN, n::Int) |
17 | 44 | return QN(map(qnval -> qnval / n, qn.data)) |
18 | 45 | end |
19 | 46 |
|
| 47 | +function Base.:*(qn::QN, n::Int) |
| 48 | + return QN(map(qnval -> qnval * n, qn.data)) |
| 49 | +end |
| 50 | + |
20 | 51 | # of Index (Tuple, Vector, ITensor, etc.) |
21 | 52 | indtype(i::Index) = typeof(i) |
22 | 53 | indtype(T::Type{<:Index}) = T |
@@ -48,11 +79,81 @@ function shift_flux(i::Index, flux_density::QN) |
48 | 79 | return ITensors.setspace(i, shift_flux(space(i), flux_density)) |
49 | 80 | end |
50 | 81 |
|
| 82 | +function scale_flux(qn::ITensors.QNVal, flux_factor::Int) |
| 83 | + flux_factor == 1 && return qn #This test could occur earlier |
| 84 | + if -1 <= qn.modulus <= 1 #U(1) symmetry, should remain a U(1) symmetry, if qn.modulus = 0, it is a dummy term and the code does nothing |
| 85 | + return qn * flux_factor |
| 86 | + else #To get the same algebra, multiplying the modulus by flux_factor works |
| 87 | + return ITensors.QNVal(qn.name, qn.val * flux_factor, qn.modulus * flux_factor) |
| 88 | + end |
| 89 | +end |
| 90 | + |
| 91 | +function scale_flux(qn::ITensors.QNVal, flux_factor::Dict{ITensors.SmallString,Int}) |
| 92 | + !haskey(flux_factor, qn.name) && return qn #Ensure that we do not touch empty QNs or QNs that we do not need to rescale because the shift is 0 |
| 93 | + return scale_flux(qn, flux_factor[qn.name]) |
| 94 | +end |
| 95 | + |
| 96 | +function scale_flux(qn::QN, flux_factor::Union{Int,Dict{ITensors.SmallString,Int}}) |
| 97 | + return QN(map(qnval -> scale_flux(qnval, flux_factor), qn.data)) |
| 98 | +end |
| 99 | + |
| 100 | +function scale_flux( |
| 101 | + qnblock::Pair{QN,Int}, flux_factor::Union{Int,Dict{ITensors.SmallString,Int}} |
| 102 | +) |
| 103 | + return (scale_flux(ITensors.qn(qnblock), flux_factor) => ITensors.blockdim(qnblock)) |
| 104 | +end |
| 105 | + |
| 106 | +function scale_flux( |
| 107 | + space::Vector{Pair{QN,Int}}, flux_factor::Union{Int,Dict{ITensors.SmallString,Int}} |
| 108 | +) |
| 109 | + return map(qnblock -> scale_flux(qnblock, flux_factor), space) |
| 110 | +end |
| 111 | + |
| 112 | +function scale_flux(i::Index, flux_factor::Union{Int,Dict{ITensors.SmallString,Int}}) |
| 113 | + return ITensors.setspace(i, scale_flux(space(i), flux_factor)) |
| 114 | +end |
| 115 | + |
51 | 116 | function shift_flux_to_zero(s::Vector{<:Index}, flux::QN) |
52 | 117 | if iszero(flux) |
53 | 118 | return s |
54 | 119 | end |
| 120 | + #We are introducing a factor per QN |
55 | 121 | n = length(s) |
| 122 | + multipliers = Dict{ITensors.SmallString,Int}() #multipliers is assigned using the names |
| 123 | + for qn in flux.data |
| 124 | + if qn.val == 0 #We do not need to multiply in this case. It also takes into account the empty QNs which have val and modulus 0 |
| 125 | + continue |
| 126 | + end |
| 127 | + multipliers[qn.name] = abs(lcm(qn.val, n) ÷ qn.val)#default solution with positive multiplier |
| 128 | + if abs(qn.modulus) > 1 |
| 129 | + #= |
| 130 | + for Zp symmetries, we use the Chinese remainder theorem to find an optimal solution. |
| 131 | + We try to find y such that y = 0 mod n and y = qn.val mod qn.modulus. Then we just need to find x such that y = n x. |
| 132 | +
|
| 133 | + This system admits a solution iff qn.val = 0 mod( gcd(qn.modulus, n)), which is not necessarily true. |
| 134 | + In order to avoid this problem, we introduce a global multiplier b: qn.val -> b qn.val, qn.modulus -> b qn.modulus. |
| 135 | + If b = n, we always have n qn.val = 0 mod( gcd(n qn.modulus, n) = n ) |
| 136 | + We can do better: I try to find the smallest b such that: |
| 137 | + b qn.val = 0 mod gcd(n, qn.modulus b). |
| 138 | +
|
| 139 | + Case when things changed: n = 3, initstate = +--, flux = 1 mod 2. |
| 140 | + Previous version: 1 is not divisible by 3, so we mutiply everything by 3. Gives QN ( 4 mod 6, 1 mod 6) |
| 141 | + Current version: gcd(2, 3) = 1, so a solution actually exists: 3 x 1 = 1 mod 2 => work with QN (0 mod 2, 1 mod 2) |
| 142 | + =# |
| 143 | + multipliers[qn.name] = abs(lcm(qn.val, n) ÷ qn.val) |
| 144 | + for b in 1:(multipliers[qn.name] - 1) |
| 145 | + if mod(n, b) != 0 #It is easy to show that the optimal b divides n |
| 146 | + continue |
| 147 | + end |
| 148 | + if mod(qn.val * b, gcd(n, b * qn.modulus)) == 0 |
| 149 | + multipliers[qn.name] = b |
| 150 | + break |
| 151 | + end |
| 152 | + end |
| 153 | + end |
| 154 | + end |
| 155 | + s = map(sₙ -> scale_flux(sₙ, multipliers), s) |
| 156 | + flux = scale_flux(flux, multipliers) |
56 | 157 | flux_density = flux / n |
57 | 158 | return map(sₙ -> shift_flux(sₙ, flux_density), s) |
58 | 159 | end |
|
0 commit comments