-
Notifications
You must be signed in to change notification settings - Fork 197
/
succd.py
181 lines (151 loc) · 7.06 KB
/
succd.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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# This code is part of Qiskit.
#
# (C) Copyright IBM 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
The SUCCD Ansatz.
"""
from typing import List, Optional, Tuple
import itertools
import logging
from qiskit.circuit import QuantumCircuit
from qiskit_nature import QiskitNatureError
from qiskit_nature.converters.second_quantization import QubitConverter
from .ucc import UCC
from .utils.fermionic_excitation_generator import (
generate_fermionic_excitations,
get_alpha_excitations,
)
logger = logging.getLogger(__name__)
# TODO: figure out how to implement `succ_full`: a variant of this class, which does include also
# the symmetrically mirrored double excitations, but assigns the same circuit parameter to them.
class SUCCD(UCC):
"""The SUCCD Ansatz.
The SUCCD Ansatz (by default) only contains double excitations. Furthermore, it only considers
the set of excitations which is symmetrically invariant with respect to spin-flips of both
particles. For more information see also [1].
Note, that this Ansatz can only work for singlet-spin systems. Therefore, the number of alpha
and beta electrons must be equal.
This is a convenience subclass of the UCC Ansatz. For more information refer to :class:`UCC`.
References:
[1] https://arxiv.org/abs/1911.10864
"""
def __init__(
self,
qubit_converter: Optional[QubitConverter] = None,
num_particles: Optional[Tuple[int, int]] = None,
num_spin_orbitals: Optional[int] = None,
reps: int = 1,
initial_state: Optional[QuantumCircuit] = None,
include_singles: Tuple[bool, bool] = (False, False),
generalized: bool = False,
):
"""
Args:
qubit_converter: the QubitConverter instance which takes care of mapping a
:class:`~.SecondQuantizedOp` to a :class:`PauliSumOp` as well as performing all
configured symmetry reductions on it.
num_particles: the tuple of the number of alpha- and beta-spin particles.
num_spin_orbitals: the number of spin orbitals.
reps: The number of times to repeat the evolved operators.
initial_state: A `QuantumCircuit` object to prepend to the circuit.
include_singles: enables the inclusion of single excitations per spin species.
generalized: boolean flag whether or not to use generalized excitations, which ignore
the occupation of the spin orbitals. As such, the set of generalized excitations is
only determined from the number of spin orbitals and independent from the number of
particles.
Raises:
QiskitNatureError: if the number of alpha and beta electrons is not equal.
"""
self._validate_num_particles(num_particles)
self._include_singles = include_singles
super().__init__(
qubit_converter=qubit_converter,
num_particles=num_particles,
num_spin_orbitals=num_spin_orbitals,
excitations=self.generate_excitations,
alpha_spin=True,
beta_spin=True,
max_spin_excitation=None,
generalized=generalized,
reps=reps,
initial_state=initial_state,
)
@property
def include_singles(self) -> Tuple[bool, bool]:
"""Whether to include single excitations."""
return self._include_singles
@include_singles.setter
def include_singles(self, include_singles: Tuple[bool, bool]) -> None:
"""Sets whether to include single excitations."""
self._include_singles = include_singles
def generate_excitations(
self, num_spin_orbitals: int, num_particles: Tuple[int, int]
) -> List[Tuple[Tuple[int, ...], Tuple[int, ...]]]:
"""Generates the excitations for the SUCCD Ansatz.
Args:
num_spin_orbitals: the number of spin orbitals.
num_particles: the number of alpha and beta electrons. Note, these must be identical for
this class.
Raises:
QiskitNatureError: if the number of alpha and beta electrons is not equal.
Returns:
The list of excitations encoded as tuples of tuples. Each tuple in the list is a pair of
tuples. The first tuple contains the occupied spin orbital indices whereas the second
one contains the indices of the unoccupied spin orbitals.
"""
self._validate_num_particles(num_particles)
excitations: List[Tuple[Tuple[int, ...], Tuple[int, ...]]] = []
excitations.extend(
generate_fermionic_excitations(
1,
num_spin_orbitals,
num_particles,
alpha_spin=self.include_singles[0],
beta_spin=self.include_singles[1],
)
)
num_electrons = num_particles[0]
beta_index_shift = num_spin_orbitals // 2
# generate alpha-spin orbital indices for occupied and unoccupied ones
alpha_excitations = get_alpha_excitations(
num_electrons, num_spin_orbitals, self._generalized
)
logger.debug("Generated list of single alpha excitations: %s", alpha_excitations)
# Find all possible double excitations constructed from the list of single excitations.
# Note, that we use `combinations_with_replacement` here, in order to also get those double
# excitations which excite from the same occupied level twice. We will need those in the
# following post-processing step.
pool = itertools.combinations_with_replacement(alpha_excitations, 2)
for exc in pool:
# find the two excitations (Note: SUCCD only works for double excitations!)
alpha_exc, second_exc = exc[0], exc[1]
# shift the second excitation into the beta-spin orbital index range
beta_exc = (
second_exc[0] + beta_index_shift,
second_exc[1] + beta_index_shift,
)
# add the excitation tuple
occ: Tuple[int, ...]
unocc: Tuple[int, ...]
occ, unocc = zip(alpha_exc, beta_exc)
exc_tuple = (occ, unocc)
excitations.append(exc_tuple)
logger.debug("Added the excitation: %s", exc_tuple)
return excitations
def _validate_num_particles(self, num_particles):
try:
assert num_particles[0] == num_particles[1]
except AssertionError as exc:
raise QiskitNatureError(
"The SUCCD Ansatz only works for singlet-spin systems. However, you specified "
"differing numbers of alpha and beta electrons:",
str(num_particles),
) from exc