/
guess_zero.py
164 lines (147 loc) · 7.56 KB
/
guess_zero.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/usr/bin/env python3
## vi: tabstop=4 shiftwidth=4 softtabstop=4 expandtab
## ---------------------------------------------------------------------
##
## Copyright (C) 2020 by the adcc authors
##
## This file is part of adcc.
##
## adcc is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## adcc is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with adcc. If not, see <http://www.gnu.org/licenses/>.
##
## ---------------------------------------------------------------------
from adcc import AmplitudeVector, Symmetry, Tensor
from ..AdcMatrix import AdcMatrixlike
def guess_zero(matrix, spin_change=0, spin_block_symmetrisation="none"):
"""
Return an AmplitudeVector object filled with zeros, but where the symmetry
has been properly set up to meet the specified requirements on the guesses.
matrix The matrix for which guesses are to be constructed
spin_change The spin change to enforce in an excitation.
Typical values are 0 (singlet/triplet/any) and -1 (spin-flip).
spin_block_symmetrisation
Symmetrisation to enforce between equivalent spin blocks, which
all yield the desired spin_change. E.g. if spin_change == 0,
then both the alpha->alpha and beta->beta blocks of the singles
part of the excitation vector achieve a spin change of 0.
The symmetry specified with this parameter will then be imposed
between the a-a and b-b blocks. Valid values are "none",
"symmetric" and "antisymmetric", where "none" enforces
no particular symmetry.
"""
return AmplitudeVector(**{
block: Tensor(sym) for block, sym in guess_symmetries(
matrix, spin_change=spin_change,
spin_block_symmetrisation=spin_block_symmetrisation
).items()
})
def guess_symmetries(matrix, spin_change=0, spin_block_symmetrisation="none"):
"""
Return guess symmetry objects (one for each AmplitudeVector block) such
that the specified requirements on the guesses are satisfied.
matrix The matrix for which guesses are to be constructed
spin_change The spin change to enforce in an excitation.
Typical values are 0 (singlet/triplet/any) and -1 (spin-flip).
spin_block_symmetrisation
Symmetrisation to enforce between equivalent spin blocks, which
all yield the desired spin_change. E.g. if spin_change == 0,
then both the alpha->alpha and beta->beta blocks of the singles
part of the excitation vector achieve a spin change of 0.
The symmetry specified with this parameter will then be imposed
between the a-a and b-b blocks. Valid values are "none",
"symmetric" and "antisymmetric", where "none" enforces
no particular symmetry.
"""
if not isinstance(matrix, AdcMatrixlike):
raise TypeError("matrix needs to be of type AdcMatrixlike")
if spin_block_symmetrisation not in ["none", "symmetric", "antisymmetric"]:
raise ValueError("Invalid value for spin_block_symmetrisation: "
"{}".format(spin_block_symmetrisation))
if spin_block_symmetrisation != "none" and \
not matrix.reference_state.restricted:
raise ValueError("spin_block_symmetrisation != none is only valid for "
"ADC calculations on top of restricted reference "
"states.")
if int(spin_change * 2) / 2 != spin_change:
raise ValueError("Only integer or half-integer spin_change is allowed. "
"You passed {}".format(spin_change))
max_spin_change = 0
if "ph" in matrix.axis_blocks:
max_spin_change = 1
if "pphh" in matrix.axis_blocks:
max_spin_change = 2
if spin_change > max_spin_change:
raise ValueError("spin_change for singles guesses may only be in the "
f"range [{-max_spin_change}, {max_spin_change}] and "
f"not {spin_change}.")
symmetries = {}
if "ph" in matrix.axis_blocks:
symmetries["ph"] = guess_symmetry_singles(
matrix, spin_change=spin_change,
spin_block_symmetrisation=spin_block_symmetrisation
)
if "pphh" in matrix.axis_blocks:
symmetries["pphh"] = guess_symmetry_doubles(
matrix, spin_change=spin_change,
spin_block_symmetrisation=spin_block_symmetrisation
)
return symmetries
def guess_symmetry_singles(matrix, spin_change=0,
spin_block_symmetrisation="none"):
symmetry = Symmetry(matrix.mospaces, "".join(matrix.axis_spaces["ph"]))
symmetry.irreps_allowed = ["A"]
if spin_change != 0 and spin_block_symmetrisation != "none":
raise NotImplementedError("spin_symmetrisation != 'none' only "
"implemented for spin_change == 0")
elif spin_block_symmetrisation == "symmetric":
symmetry.spin_block_maps = [("aa", "bb", 1)]
symmetry.spin_blocks_forbidden = ["ab", "ba"]
elif spin_block_symmetrisation == "antisymmetric":
symmetry.spin_block_maps = [("aa", "bb", -1)]
symmetry.spin_blocks_forbidden = ["ab", "ba"]
return symmetry
def guess_symmetry_doubles(matrix, spin_change=0,
spin_block_symmetrisation="none"):
spaces_d = matrix.axis_spaces["pphh"]
symmetry = Symmetry(matrix.mospaces, "".join(spaces_d))
symmetry.irreps_allowed = ["A"]
if spin_change != 0 and spin_block_symmetrisation != "none":
raise NotImplementedError("spin_symmetrisation != 'none' only "
"implemented for spin_change == 0")
if spin_change == 0 \
and spin_block_symmetrisation in ("symmetric", "antisymmetric"):
fac = 1 if spin_block_symmetrisation == "symmetric" else -1
# Spin mapping between blocks where alpha and beta are just mirrored
symmetry.spin_block_maps = [("aaaa", "bbbb", fac),
("abab", "baba", fac),
("abba", "baab", fac)]
# Mark blocks which change spin as forbidden
symmetry.spin_blocks_forbidden = ["aabb", # spin_change +2
"bbaa", # spin_change -2
"aaab", # spin_change +1
"aaba", # spin_change +1
"abaa", # spin_change -1
"baaa", # spin_change -1
"abbb", # spin_change +1
"babb", # spin_change +1
"bbab", # spin_change -1
"bbba"] # spin_change -1
# Add index permutation symmetry:
permutations = ["ijab"]
if spaces_d[0] == spaces_d[1]:
permutations.append("-jiab")
if spaces_d[2] == spaces_d[3]:
permutations.append("-ijba")
if len(permutations) > 1:
symmetry.permutations = permutations
return symmetry