Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
bdfd2f5
full IP/EA ADC(0-3) implementation including tests without any proper…
fedy9 Mar 5, 2026
176d9c9
added ChargedExcitation class and updated test setup for energies and…
fedy9 Mar 6, 2026
46b160b
updated printout for ElectronicStates parent class
fedy9 Mar 6, 2026
bf9004d
comment in ip/ea doubles guess setup
fedy9 Mar 6, 2026
be15aa1
linting
fedy9 Mar 6, 2026
65bbd06
linting
fedy9 Mar 6, 2026
db45a07
updated testdata
fedy9 May 4, 2026
5828728
Merge remote-tracking branch 'upstream/master' into ip_ea_impl
fedy9 May 4, 2026
a0ac600
new SHASUMS
fedy9 May 4, 2026
4646a87
updated SHASUMS
fedy9 May 4, 2026
62684e9
code style
fedy9 May 12, 2026
ce7de9d
code style + python3.9 compatibility
fedy9 May 12, 2026
76aa6a1
testdata and small fix
fedy9 May 12, 2026
40c0d34
updated testdata list and c++ code style
fedy9 May 13, 2026
3725db9
updated testdata list and c++ code style
fedy9 May 13, 2026
2abd7b9
c++ code style
fedy9 May 13, 2026
c08e25e
removed spin-flip only testcase HF-631G for IP/EA
fedy9 May 13, 2026
789a1a4
moved hf ip/ea reference generators to potential TODO
fedy9 May 13, 2026
13e8582
updated SHASUMS
fedy9 May 13, 2026
b5ec713
Merged ADC(4) and AdcType changes from master
fedy9 Jun 19, 2026
5460d7a
applied PR reviews
fedy9 Jun 20, 2026
1d3830d
flake8
fedy9 Jun 20, 2026
5deaeda
adapted function rename
fedy9 Jun 20, 2026
5d7d861
updated 1st order double diagonals of IP/EA-ADC
fedy9 Jun 29, 2026
90b9814
reverted doc change
fedy9 Jun 29, 2026
5f783a4
removed describe function test
fedy9 Jun 29, 2026
22a664e
merge master except dump_adcc and ref data
fedy9 Jun 29, 2026
92f3af0
merged upstream changes
fedy9 Jun 29, 2026
165181a
testdata, flake8 and excluded AmplitudeVector from block_apply fun
fedy9 Jun 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 74 additions & 32 deletions adcc/AdcMatrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

from .LazyMp import LazyMp
from .adc_pp import matrix as ppmatrix
from .adc_ip import matrix as ipmatrix
from .adc_ea import matrix as eamatrix
from .timings import Timer, timed_member_call
from .AdcMethod import AdcMethod, Method, AdcType
from .functions import ones_like
Expand Down Expand Up @@ -73,6 +75,8 @@ class AdcMatrixlike:

_special_block_orders = {
"adc2x": {"ph_ph": 2, "ph_pphh": 1, "pphh_ph": 1, "pphh_pphh": 1},
"ip-adc2x": {"h_h": 2, "h_phh": 1, "phh_h": 1, "phh_phh": 1},
"ea-adc2x": {"p_p": 2, "p_pph": 1, "pph_p": 1, "pph_pph": 1},
"isr1s": {"ph_ph": 1, "ph_pphh": None, "pphh_ph": None, "pphh_pphh": None},
"isr2d": {"ph_ph": 2, "ph_pphh": 1, "pphh_ph": 1, "pphh_pphh": 0},
}
Expand Down Expand Up @@ -104,7 +108,9 @@ def _default_block_orders(cls, method: Method,
# - determine which spaces are available in the ADC(n) matrix
# starting from the given minimal space
min_space = {
AdcType.PP: "ph"
AdcType.PP: "ph",
AdcType.IP: "h",
AdcType.EA: "p",
}.get(method.adc_type, None)
if min_space is None:
raise ValueError(f"Unknown adc type {method.adc_type.to_str()} for "
Expand Down Expand Up @@ -196,6 +202,10 @@ def _is_valid_space(cls, space: str, method: Method) -> bool:
# be equal or differ e.g. by +-1 (IP/EA)
if method.adc_type is AdcType.PP:
return n_particle == n_hole
elif method.adc_type is AdcType.IP:
return n_particle == n_hole - 1
elif method.adc_type is AdcType.EA:
return n_particle == n_hole + 1
raise ValueError(f"Unknown adc type {method.adc_type.to_str()} for method "
f"{method.name}. Can not validate space.")

Expand Down Expand Up @@ -268,11 +278,19 @@ def __init__(self, method, hf_or_mp, block_orders=None, intermediates=None,
variant = None
if self.is_core_valence_separated:
variant = "cvs"
# Directly import block dispatch functions?
BLOCK_DISPATCH = {
AdcType.PP: ppmatrix.block,
AdcType.IP: ipmatrix.block,
AdcType.EA: eamatrix.block}
block_dispatch_fun = BLOCK_DISPATCH[self.method.adc_type]
Comment on lines +281 to +286

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
# Directly import block dispatch functions?
BLOCK_DISPATCH = {
AdcType.PP: ppmatrix.block,
AdcType.IP: ipmatrix.block,
AdcType.EA: eamatrix.block}
block_dispatch_fun = BLOCK_DISPATCH[self.method.adc_type]
matrix_block_dispatch = {
AdcType.PP: ppmatrix.block,
AdcType.IP: ipmatrix.block,
AdcType.EA: eamatrix.block
}[self.method.adc_type]

blocks = {
block: ppmatrix.block(self.ground_state, block.split("_"),
order=order, intermediates=self.intermediates,
variant=variant)
for block, order in self.block_orders.items() if order is not None
block: block_dispatch_fun(self.ground_state, block.split("_"),
order=order,
intermediates=self.intermediates,
variant=variant)
for block, order in self.block_orders.items()
if order is not None
}
self.blocks = {bl: blocks[bl].apply for bl in blocks}
if diagonal_precomputed:
Expand Down Expand Up @@ -442,33 +460,57 @@ def construct_symmetrisation_for_blocks(self):
Returns a dictionary block identifier -> function
"""
ret = {}
if self.is_core_valence_separated:
# CVS doubles part is antisymmetric wrt. (i,K,a,b) <-> (i,K,b,a)
ret["pphh"] = lambda v: v.antisymmetrise([(2, 3)])
else:
def symmetrise_generic_adc_doubles(invec):
# doubles part is antisymmetric wrt. (i,j,a,b) <-> (i,j,b,a)
# doubles part is antisymmetric wrt. (i,j,a,b) <-> (j,i,a,b)
scratch = invec.antisymmetrise([(0, 1)]).antisymmetrise([(2, 3)])
# doubles part is symmetric wrt. (i,j,a,b) <-> (j,i,b,a)
return scratch.symmetrise([(0, 1), (2, 3)])
ret["pphh"] = symmetrise_generic_adc_doubles

def symmetrise_generic_adc_triples(invec):
# triples part is antisymmetric wrt. permutations of (i,j,k)
# and wrt. permutations of (a,b,c)
scratch = (
invec.antisymmetrise([(0, 1, 2)]).antisymmetrise([(3, 4, 5)])
)
# triples part is symmetric wrt. permutations of (i,j,k) and
# permutations of (a,b,c)
# NOTE: The doubles fix the (numerical) symmetry with a single
# symmetrise call. This is not possible for triples, since the
# following symmetrise call only covers 6 of the 18
# even permutations generated by the 2 antisymmetrise calls above:
# ijkabc + ikjacb + jikbac + jkibca + kijcab + kjicba
return scratch.symmetrise([(0, 1, 2), (3, 4, 5)])
ret["ppphhh"] = symmetrise_generic_adc_triples
if self.method.adc_type is AdcType.PP:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please split the method into specific PP/IP/EA methods.

Suggested change
if self.method.adc_type is AdcType.PP:
if self.method.adc_type is AdcType.PP:
return self._construct_symmetrisation_for_blocks_pp()

if self.is_core_valence_separated:
# CVS doubles part is antisymmetric wrt. (i,K,a,b) <-> (i,K,b,a)
ret["pphh"] = lambda v: v.antisymmetrise([(2, 3)])
else:
def symmetrise_generic_adc_doubles(invec):
# doubles part is antisymmetric wrt. (i,j,a,b) <-> (i,j,b,a)
# doubles part is antisymmetric wrt. (i,j,a,b) <-> (j,i,a,b)
scratch = invec.antisymmetrise([(0, 1)]).antisymmetrise(
[(2, 3)])
# doubles part is symmetric wrt. (i,j,a,b) <-> (j,i,b,a)
return scratch.symmetrise([(0, 1), (2, 3)])
ret["pphh"] = symmetrise_generic_adc_doubles

def symmetrise_generic_adc_triples(invec):
# triples part is antisymmetric wrt. permutations of (i,j,k)
# and wrt. permutations of (a,b,c)
scratch = (
invec.antisymmetrise([(0, 1, 2)]).antisymmetrise(
[(3, 4, 5)])
)
# triples part is symmetric wrt. permutations of (i,j,k) and
# permutations of (a,b,c)
# NOTE: The doubles fix the (numerical) symmetry with a single
# symmetrise call. This is not possible for triples, since the
# following symmetrise call only covers 6 of the 18
# even permutations generated by the 2 antisymmetrise calls
# above:
# ijkabc + ikjacb + jikbac + jkibca + kijcab + kjicba
return scratch.symmetrise([(0, 1, 2), (3, 4, 5)])
ret["ppphhh"] = symmetrise_generic_adc_triples
elif self.method.adc_type is AdcType.IP:
if not self.is_core_valence_separated:
def symmetrise_generic_adc_doubles(invec):
# doubles part is antisymmetric wrt. (i,j,a) <-> (j,i,a)
return invec.antisymmetrise([(0, 1)])
ret["phh"] = symmetrise_generic_adc_doubles

def symmetrise_generic_adc_triples(invec):
# TODO
pass
Comment on lines +501 to +503

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please remove the IP/EA triples functions, since they are not needed yet

elif self.method.adc_type is AdcType.EA:
if not self.is_core_valence_separated:
def symmetrise_generic_adc_doubles(invec):
# doubles part is antisymmetric wrt. (i,a,b) <-> (i,a,b)
return invec.antisymmetrise([(1, 2)])
ret["pph"] = symmetrise_generic_adc_doubles

def symmetrise_generic_adc_triples(invec):
# TODO
pass
return ret

def dense_basis(self, axis_blocks=None, ordering="adcc"):
Expand Down
38 changes: 36 additions & 2 deletions adcc/AdcMethod.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ def to_int(self) -> int:

class AdcType(Enum):
PP = "pp"
IP = "ip"
EA = "ea"

def to_str(self) -> str:
return self.value
Expand Down Expand Up @@ -253,7 +255,7 @@ def at_level(self: T, newlevel: Union[int, str]) -> T:

def as_method(self, method_cls: type[T]) -> T:
"""
Return a equivalent Method with the method base name replaced
Return an equivalent Method with the method base name replaced
by the provided name.
"""
assert self._method_base_name is not None
Expand Down Expand Up @@ -294,7 +296,23 @@ class AdcMethod(Method):
): LevelSpec(
max_level=3,
special_levels=(MethodLevel.TWO_X,)
)
),
LevelKey(
adc_type=AdcType.IP,
gs_type=GroundStateType.MP,
cvs=False
): LevelSpec(
max_level=3,
special_levels=(MethodLevel.TWO_X,)
),
LevelKey(
adc_type=AdcType.EA,
gs_type=GroundStateType.MP,
cvs=False
): LevelSpec(
max_level=3,
special_levels=(MethodLevel.TWO_X,)
),
}


Expand All @@ -316,5 +334,21 @@ class IsrMethod(Method):
): LevelSpec(
max_level=2,
special_levels=(MethodLevel.ONE_S, MethodLevel.TWO_D)
),
LevelKey(
adc_type=AdcType.IP,
gs_type=GroundStateType.MP,
cvs=False
): LevelSpec(
max_level=2,
special_levels=None
),
LevelKey(
adc_type=AdcType.EA,
gs_type=GroundStateType.MP,
cvs=False
): LevelSpec(
max_level=2,
special_levels=None
Comment on lines +338 to +352

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Strictly this should only be added as part of the second IP/EA property PR, right? However, in order to omit these changes you would need to temporarily set _property_method = None for IP/EA on ElectronicStates. I am not entirely sure if this is a good idea, but it definitely would prevent any calculation of properties :D

Also special levels is defined as tuple[MethodLevel, ...], thus setting it to None is not valid. Please use an empty tuple or simply omit it (tuple() is also the default value)

)
}
4 changes: 3 additions & 1 deletion adcc/AmplitudeVector.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ class AmplitudeVector(dict):
def __init__(self, **kwargs):
"""
Construct an AmplitudeVector. Typical use cases are
``AmplitudeVector(ph=tensor_singles, pphh=tensor_doubles)``.
``AmplitudeVector(ph=tensor_singles, pphh=tensor_doubles)``. For IP-ADC
``AmplitudeVector(h=tensor_singles, phh=tensor_doubles)``, and for
EA-ADC ``AmplitudeVector(p=tensor_singles, pph=tensor_doubles)``
"""
super().__init__(**kwargs)

Expand Down
Loading
Loading