excitationsolve 2.1.2__tar.gz → 2.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- excitationsolve-2.2.1/CITATION.bib +12 -0
- {excitationsolve-2.1.2/excitationsolve.egg-info → excitationsolve-2.2.1}/PKG-INFO +19 -12
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/README.md +13 -11
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve/__init__.py +2 -1
- excitationsolve-2.2.1/excitationsolve/excitation_solve.py +189 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve/excitation_solve_scipy.py +40 -14
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve/info.py +1 -1
- excitationsolve-2.2.1/excitationsolve/parameter_prediction.py +352 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1/excitationsolve.egg-info}/PKG-INFO +19 -12
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve.egg-info/SOURCES.txt +5 -1
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve.egg-info/requires.txt +6 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/pyproject.toml +13 -0
- excitationsolve-2.2.1/tests/pyscf_molecules.py +152 -0
- excitationsolve-2.2.1/tests/tcc_helpers.py +149 -0
- excitationsolve-2.2.1/tests/tcc_tests.py +130 -0
- excitationsolve-2.1.2/CITATION.bib +0 -10
- excitationsolve-2.1.2/excitationsolve/excitation_solve.py +0 -121
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/.gitignore +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/LICENSE.txt +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/assets/convergence_plots.png +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/examples/main_pennylane.py +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/examples/main_pennylane_pyscf.py +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/examples/main_qiskit.py +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve/excitation_solve_2d.py +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve/excitation_solve_adapt.py +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve/excitation_solve_pennylane.py +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve/excitation_solve_qiskit.py +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve/trig_poly_utils.py +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve.egg-info/dependency_links.txt +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/excitationsolve.egg-info/top_level.txt +0 -0
- {excitationsolve-2.1.2 → excitationsolve-2.2.1}/setup.cfg +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
@article{Jaeger2024Fast,
|
|
2
|
+
title={Fast gradient-free optimization of excitations in variational quantum eigensolvers},
|
|
3
|
+
author={Jäger, Jonas and Kaldenbach, Thierry N. and Haas, Max and Schultheis, Erik},
|
|
4
|
+
year={2025},
|
|
5
|
+
volume={8},
|
|
6
|
+
number={1},
|
|
7
|
+
journal={Communications Physics},
|
|
8
|
+
publisher={Springer Science and Business Media LLC},
|
|
9
|
+
language={en},
|
|
10
|
+
url={http://dx.doi.org/10.1038/s42005-025-02375-9},
|
|
11
|
+
DOI={10.1038/s42005-025-02375-9},
|
|
12
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: excitationsolve
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: ExcitationSolve: a fast globally-informed gradient-free optimizer for physically-motivated ansätze constructed of excitation operators
|
|
5
5
|
Author-email: Jonas Jäger <jojaeger@cs.ubc.ca>, "Thierry N. Kaldenbach" <Thierry.Kaldenbach@dlr.de>, Max Haas <Max.Haas@dlr.de>, Erik Schultheis <Erik.Schultheis@dlr.de>
|
|
6
6
|
License: Copyright 2025 Jonas Jäger, Thierry N. Kaldenbach, Max Haas, Erik Schultheis,
|
|
@@ -234,9 +234,14 @@ Requires-Dist: openfermionpyscf==0.5; extra == "examples"
|
|
|
234
234
|
Requires-Dist: aiohttp==3.11.15; extra == "examples"
|
|
235
235
|
Requires-Dist: fsspec==2025.2.0; extra == "examples"
|
|
236
236
|
Requires-Dist: h5py==3.13.0; extra == "examples"
|
|
237
|
+
Provides-Extra: test
|
|
238
|
+
Requires-Dist: pytest>=9.0; extra == "test"
|
|
239
|
+
Requires-Dist: matplotlib; extra == "test"
|
|
240
|
+
Requires-Dist: tencirchem-ng==2024.10; extra == "test"
|
|
241
|
+
Requires-Dist: pyscf==2.8.0; extra == "test"
|
|
237
242
|
Dynamic: license-file
|
|
238
243
|
|
|
239
|
-
# ExcitationSolve [](https://www.nature.com/articles/s42005-025-02375-9) [](https://doi.org/10.5281/zenodo.17457122)
|
|
240
245
|
|
|
241
246
|
An optimization algorithm for ansätze consisting of excitation operators in variational quantum eigensolvers (VQEs).
|
|
242
247
|
|
|
@@ -298,17 +303,19 @@ counts = excsolve_obj.nfevs
|
|
|
298
303
|
Feel free to contact [David Melching](mailto:David.Melching@dlr.de) if you have any questions.
|
|
299
304
|
|
|
300
305
|
## Citation
|
|
301
|
-
If you use portions of this code please cite our [paper](https://
|
|
306
|
+
If you use portions of this code please cite our [paper](https://www.nature.com/articles/s42005-025-02375-9):
|
|
302
307
|
```bibtex
|
|
303
|
-
@
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
308
|
+
@article{Jaeger2024Fast,
|
|
309
|
+
title={Fast gradient-free optimization of excitations in variational quantum eigensolvers},
|
|
310
|
+
author={Jäger, Jonas and Kaldenbach, Thierry N. and Haas, Max and Schultheis, Erik},
|
|
311
|
+
year={2025},
|
|
312
|
+
volume={8},
|
|
313
|
+
number={1},
|
|
314
|
+
journal={Communications Physics},
|
|
315
|
+
publisher={Springer Science and Business Media LLC},
|
|
316
|
+
language={en},
|
|
317
|
+
url={http://dx.doi.org/10.1038/s42005-025-02375-9},
|
|
318
|
+
DOI={10.1038/s42005-025-02375-9},
|
|
312
319
|
}
|
|
313
320
|
```
|
|
314
321
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ExcitationSolve [](https://www.nature.com/articles/s42005-025-02375-9) [](https://doi.org/10.5281/zenodo.17457122)
|
|
2
2
|
|
|
3
3
|
An optimization algorithm for ansätze consisting of excitation operators in variational quantum eigensolvers (VQEs).
|
|
4
4
|
|
|
@@ -60,17 +60,19 @@ counts = excsolve_obj.nfevs
|
|
|
60
60
|
Feel free to contact [David Melching](mailto:David.Melching@dlr.de) if you have any questions.
|
|
61
61
|
|
|
62
62
|
## Citation
|
|
63
|
-
If you use portions of this code please cite our [paper](https://
|
|
63
|
+
If you use portions of this code please cite our [paper](https://www.nature.com/articles/s42005-025-02375-9):
|
|
64
64
|
```bibtex
|
|
65
|
-
@
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
@article{Jaeger2024Fast,
|
|
66
|
+
title={Fast gradient-free optimization of excitations in variational quantum eigensolvers},
|
|
67
|
+
author={Jäger, Jonas and Kaldenbach, Thierry N. and Haas, Max and Schultheis, Erik},
|
|
68
|
+
year={2025},
|
|
69
|
+
volume={8},
|
|
70
|
+
number={1},
|
|
71
|
+
journal={Communications Physics},
|
|
72
|
+
publisher={Springer Science and Business Media LLC},
|
|
73
|
+
language={en},
|
|
74
|
+
url={http://dx.doi.org/10.1038/s42005-025-02375-9},
|
|
75
|
+
DOI={10.1038/s42005-025-02375-9},
|
|
74
76
|
}
|
|
75
77
|
```
|
|
76
78
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
from excitationsolve.info import __version__, HOMEPAGE
|
|
2
|
-
from excitationsolve.excitation_solve import excitation_solve_step
|
|
2
|
+
from excitationsolve.excitation_solve import excitation_solve_step, excitation_solve_step_shared_param
|
|
3
3
|
from excitationsolve.excitation_solve_scipy import ExcitationSolveScipy
|
|
4
|
+
from excitationsolve.parameter_prediction import optimal_theta, optimal_theta_pyscf
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import numpy as np
|
|
3
|
+
from excitationsolve.trig_poly_utils import fourier_series_minimum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def excitation_solve_step(
|
|
7
|
+
parameter_variations: list | np.ndarray,
|
|
8
|
+
energy_samples: list | np.ndarray,
|
|
9
|
+
return_coeffs=False,
|
|
10
|
+
non_exact=False,
|
|
11
|
+
) -> tuple[float, float] | tuple[float, float, np.ndarray]:
|
|
12
|
+
"""
|
|
13
|
+
Optimizes a single excitation parameter globally given the energies for five shifts of this parameters.
|
|
14
|
+
Recommended to use in a loop for all excitation parameters and perform multiple sweeps through the parameters (potentially shuffled) until convergence.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
parameter_variations: list or np.ndarray
|
|
18
|
+
Five variations/shifts of a single excitation parameter.
|
|
19
|
+
energy_samples: list or np.ndarray
|
|
20
|
+
Five energy samples for a single excitation parameter varied.
|
|
21
|
+
Returns:
|
|
22
|
+
float, float
|
|
23
|
+
The optimized excitation parameter and the corresponding energy.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
parameter_variations = np.array(parameter_variations).flatten()
|
|
27
|
+
energy_samples = np.array(energy_samples).flatten()
|
|
28
|
+
assert len(parameter_variations) >= 5, f"The number of parameter variations must be at least 5. Got {len(parameter_variations)}."
|
|
29
|
+
assert len(energy_samples) >= 5, f"The number of energy samples must be at least 5. Got {len(energy_samples)}."
|
|
30
|
+
assert len(parameter_variations) == len(energy_samples), (
|
|
31
|
+
f"The number of parameter variations and energy samples must match. Got {len(parameter_variations)} and {len(energy_samples)}."
|
|
32
|
+
)
|
|
33
|
+
assert parameter_variations.max() - parameter_variations.min() <= 4 * np.pi, (
|
|
34
|
+
"The parameter variations must be within one period of the excitation operator (4*pi)."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# ### Fit second-order Fourier series / trigonometric polynomial to the energy samples: ###
|
|
38
|
+
# Determine linear equation system with N equations and 5 unknowns (N number of samples):
|
|
39
|
+
c1 = np.cos(parameter_variations / 2)
|
|
40
|
+
s1 = np.sin(parameter_variations / 2)
|
|
41
|
+
c2 = np.cos(parameter_variations)
|
|
42
|
+
s2 = np.sin(parameter_variations)
|
|
43
|
+
A = np.array([np.ones(len(energy_samples)), c1, s1, c2, s2]).T
|
|
44
|
+
b = np.array(energy_samples)
|
|
45
|
+
logging.debug("Linear equation system: \nA: \n%s \n b: %s", np.around(A, 3), b)
|
|
46
|
+
|
|
47
|
+
if len(parameter_variations) == 5:
|
|
48
|
+
# ## Exact reconstruction: ##
|
|
49
|
+
# solve the equation system:
|
|
50
|
+
coeffs = np.linalg.solve(A, b)
|
|
51
|
+
else:
|
|
52
|
+
# ## Fitted reconstruction (least squares fit) for potentially noisy energy values: ##
|
|
53
|
+
logging.debug(
|
|
54
|
+
"More than 5 parameter variations or energy samples provided. A least squares fit will be performed instead of an exact reconstruction."
|
|
55
|
+
)
|
|
56
|
+
coeffs, residuals, rank_A, s = np.linalg.lstsq(A, b)
|
|
57
|
+
logging.debug(
|
|
58
|
+
"Least-squares fit info:\n\tResiduals: %s \n\tRank of A: %i \n\tSingular values of A: %s",
|
|
59
|
+
residuals,
|
|
60
|
+
rank_A,
|
|
61
|
+
s,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
logging.debug("Solved coefficients: %s", coeffs)
|
|
65
|
+
|
|
66
|
+
if non_exact:
|
|
67
|
+
# reconstructed energy function:
|
|
68
|
+
def energy_function(xs):
|
|
69
|
+
c1 = np.cos(xs / 2)
|
|
70
|
+
s1 = np.sin(xs / 2)
|
|
71
|
+
c2 = np.cos(xs)
|
|
72
|
+
s2 = np.sin(xs)
|
|
73
|
+
return coeffs[0] + coeffs[1] * c1 + coeffs[2] * s1 + coeffs[3] * c2 + coeffs[4] * s2
|
|
74
|
+
|
|
75
|
+
### Brute-force optimization of reconstruction
|
|
76
|
+
n_samples = int(1e3)
|
|
77
|
+
x_min, x_max = -2 * np.pi, 2 * np.pi
|
|
78
|
+
x = np.linspace(x_min, x_max, n_samples)
|
|
79
|
+
y = energy_function(x) # Note, vectorized evaluation must be supported!
|
|
80
|
+
min_idx = np.argmin(y)
|
|
81
|
+
return x[min_idx], y[min_idx]
|
|
82
|
+
|
|
83
|
+
# ### Optimize parameter globally in reconstruction (companion matrix method)
|
|
84
|
+
a = [coeffs[0], *coeffs[1::2]] # cosine coefficients
|
|
85
|
+
b = coeffs[2::2] # sine coefficients
|
|
86
|
+
assert len(a) == len(b) + 1, (
|
|
87
|
+
f"The number of cosine coefficients must be one more than the number of sine coefficients. Got {len(a)} and {len(b)}."
|
|
88
|
+
)
|
|
89
|
+
min_x, min_y = fourier_series_minimum(a, b, return_y=True)
|
|
90
|
+
# rescale to original parameter range since the optimization was performed on the interval [pi, pi] with doubled frequencies instead of [-2*pi, 2*pi]:
|
|
91
|
+
min_x *= 2
|
|
92
|
+
|
|
93
|
+
if return_coeffs:
|
|
94
|
+
return min_x, min_y, coeffs
|
|
95
|
+
return min_x, min_y
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def excitation_solve_step_shared_param(
|
|
99
|
+
parameter_occ: int,
|
|
100
|
+
parameter_variations: list | np.ndarray,
|
|
101
|
+
energy_samples: list | np.ndarray,
|
|
102
|
+
return_coeffs=False,
|
|
103
|
+
) -> tuple[float, float] | tuple[float, float, np.ndarray]:
|
|
104
|
+
"""
|
|
105
|
+
Optimizes a single excitation parameter globally given the energies for 4S+1 shifts of this parameters.
|
|
106
|
+
Recommended to use in a loop for all excitation parameters and perform multiple sweeps through the parameters (potentially shuffled) until convergence.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
parameter_occ: int
|
|
110
|
+
Number of times S the parameter occures in different excitations, i.e., how many excitations share this parameter.
|
|
111
|
+
parameter_variations: list or np.ndarray
|
|
112
|
+
4S+1 variations/shifts of a single excitation parameter.
|
|
113
|
+
energy_samples: list or np.ndarray
|
|
114
|
+
4S+1 energy samples for a single excitation parameter varied.
|
|
115
|
+
Returns:
|
|
116
|
+
float, float
|
|
117
|
+
The optimized excitation parameter and the corresponding energy.
|
|
118
|
+
"""
|
|
119
|
+
assert parameter_occ >= 1, f"The number of parameter occurences S must be at least 1. Got {parameter_occ}."
|
|
120
|
+
parameter_variations = np.array(parameter_variations).flatten()
|
|
121
|
+
energy_samples = np.array(energy_samples).flatten()
|
|
122
|
+
assert len(parameter_variations) >= 4 * parameter_occ + 1, (
|
|
123
|
+
f"The number of parameter variations must be at least 4S+1={4 * parameter_occ + 1}. Got {len(parameter_variations)}."
|
|
124
|
+
)
|
|
125
|
+
assert len(parameter_variations) == len(energy_samples), (
|
|
126
|
+
f"The number of parameter variations and energy samples must match. Got {len(parameter_variations)} and {len(energy_samples)}."
|
|
127
|
+
)
|
|
128
|
+
assert parameter_variations.max() - parameter_variations.min() <= 4 * np.pi, (
|
|
129
|
+
"The parameter variations must be within one period of the excitation operator (4*pi)."
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# ### Fit Fourier series / trigonometric polynomial of order 2S to the energy samples: ###
|
|
133
|
+
# Determine linear equation system with N equations and 2S+1 unknowns (N number of samples):
|
|
134
|
+
c_lst = []
|
|
135
|
+
s_lst = []
|
|
136
|
+
for s in range(2 * parameter_occ):
|
|
137
|
+
s += 1 # to start at 1
|
|
138
|
+
c_lst.append(np.cos(parameter_variations / 2 * s))
|
|
139
|
+
s_lst.append(np.sin(parameter_variations / 2 * s))
|
|
140
|
+
c_s_lst = [val for pair in zip(c_lst, s_lst) for val in pair] # interleave c_lst and s_lst
|
|
141
|
+
|
|
142
|
+
A = np.array([np.ones(len(energy_samples)), *c_s_lst]).T
|
|
143
|
+
b = np.array(energy_samples)
|
|
144
|
+
logging.debug("Linear equation system: \nA: \n%s \n b: %s", np.around(A, 3), b)
|
|
145
|
+
|
|
146
|
+
if len(parameter_variations) == 4 * parameter_occ + 1:
|
|
147
|
+
# ## Exact reconstruction: ##
|
|
148
|
+
# solve the equation system:
|
|
149
|
+
coeffs = np.linalg.solve(A, b)
|
|
150
|
+
else:
|
|
151
|
+
# ## Fitted reconstruction (least squares fit) for potentially noisy energy values: ##
|
|
152
|
+
logging.debug(
|
|
153
|
+
"More than 4S+1 parameter variations or energy samples provided. A least squares fit will be performed instead of an exact reconstruction."
|
|
154
|
+
)
|
|
155
|
+
coeffs, residuals, rank_A, s = np.linalg.lstsq(A, b)
|
|
156
|
+
logging.debug(
|
|
157
|
+
"Least-squares fit info:\n\tResiduals: %s \n\tRank of A: %i \n\tSingular values of A: %s",
|
|
158
|
+
residuals,
|
|
159
|
+
rank_A,
|
|
160
|
+
s,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
logging.debug("Solved coefficients: %s", coeffs)
|
|
164
|
+
|
|
165
|
+
# ### Optimize parameter globally in reconstruction (companion matrix method)
|
|
166
|
+
a = [coeffs[0], *coeffs[1::2]] # cosine coefficients
|
|
167
|
+
b = coeffs[2::2] # sine coefficients
|
|
168
|
+
assert len(a) == len(b) + 1, (
|
|
169
|
+
f"The number of cosine coefficients must be one more than the number of sine coefficients. Got {len(a)} and {len(b)}."
|
|
170
|
+
)
|
|
171
|
+
min_x, min_y = fourier_series_minimum(a, b, return_y=True)
|
|
172
|
+
# rescale to original parameter range since the optimization was performed on the interval [pi, pi] with doubled frequencies instead of [-2*pi, 2*pi]:
|
|
173
|
+
min_x *= 2
|
|
174
|
+
|
|
175
|
+
if return_coeffs:
|
|
176
|
+
return min_x, min_y, coeffs
|
|
177
|
+
return min_x, min_y
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
### Demo: ###
|
|
181
|
+
if __name__ == "__main__":
|
|
182
|
+
np.random.seed(42) # For reproducibility and because 42 is the answer to everything
|
|
183
|
+
logging.basicConfig(level=logging.INFO) # Enable logging
|
|
184
|
+
# Example data:
|
|
185
|
+
parameter_variations_test = np.array([0, np.pi / 2, -np.pi / 2, np.pi, -np.pi])
|
|
186
|
+
energy_samples_test = np.random.rand(5) # random energies for demonstration
|
|
187
|
+
# Optimize excitation parameter:
|
|
188
|
+
optimized_parameter, optimized_energy = excitation_solve_step(parameter_variations_test, energy_samples_test)
|
|
189
|
+
print(f"Optimized excitation parameter: {optimized_parameter} \nOptimized energy: {optimized_energy}")
|
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
from collections.abc import Callable
|
|
4
4
|
import numpy as np
|
|
5
5
|
from scipy.optimize import OptimizeResult
|
|
6
|
-
from excitationsolve import excitation_solve_step
|
|
6
|
+
from excitationsolve import excitation_solve_step, excitation_solve_step_shared_param
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class ExcitationSolveScipy:
|
|
@@ -55,19 +55,39 @@ class ExcitationSolveScipy:
|
|
|
55
55
|
self.save_parameters = save_parameters
|
|
56
56
|
self.param_scaling = param_scaling
|
|
57
57
|
|
|
58
|
-
self.energies = []
|
|
59
|
-
self.energies_after_it = []
|
|
60
|
-
self.nfevs = []
|
|
61
|
-
self.nfevs_after_it = []
|
|
62
|
-
self.params = []
|
|
58
|
+
self.energies = np.array([])
|
|
59
|
+
self.energies_after_it = np.array([])
|
|
60
|
+
self.nfevs = np.array([])
|
|
61
|
+
self.nfevs_after_it = np.array([])
|
|
62
|
+
self.params = np.array([])
|
|
63
63
|
|
|
64
64
|
def minimize(self, fun: Callable[[np.ndarray], float], x0: np.ndarray, args=(), **kwargs) -> OptimizeResult:
|
|
65
|
-
|
|
66
|
-
shifts = (np.arange(self.num_samples) - self.num_samples // 2) * param_dist
|
|
65
|
+
"""Minimize energy function using the ExcitationSolve optimizer
|
|
67
66
|
|
|
67
|
+
Args:
|
|
68
|
+
fun (Callable[[np.ndarray], float]): Function that evaluates the energy given a set of parameters
|
|
69
|
+
x0 (np.ndarray): Initial starting point of the optimizer
|
|
70
|
+
parameter_occ (np.ndarray, optional): Number of times S the parameters occur in different excitations, i.e.,
|
|
71
|
+
how many excitations share each parameter.
|
|
72
|
+
parameter_occ[i] is the number of times the i-th parameter occurs in different excitations.
|
|
73
|
+
Defaults to [1, 1, ...] (all set to 1).
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
OptimizeResult: Scipy OptimizeResult object
|
|
77
|
+
"""
|
|
68
78
|
params_excsolve = np.array(x0.copy())
|
|
69
79
|
num_params = len(params_excsolve)
|
|
70
80
|
|
|
81
|
+
parameter_occ = kwargs.get("parameter_occ")
|
|
82
|
+
if parameter_occ is None:
|
|
83
|
+
parameter_occ = np.ones((num_params,))
|
|
84
|
+
if len(parameter_occ) != num_params:
|
|
85
|
+
raise ValueError(f"Length of parameter_occ ({len(parameter_occ)}) does not equal number of parameters ({num_params})!")
|
|
86
|
+
parameter_occ = parameter_occ.astype(int)
|
|
87
|
+
logging.info(f"{parameter_occ=}")
|
|
88
|
+
for i, val in enumerate(parameter_occ):
|
|
89
|
+
logging.info(f"\t{i}-th parameter appears {val} times")
|
|
90
|
+
|
|
71
91
|
energy_at_zero = None
|
|
72
92
|
if self.hf_energy is not None:
|
|
73
93
|
energy_at_zero = self.hf_energy
|
|
@@ -76,6 +96,10 @@ class ExcitationSolveScipy:
|
|
|
76
96
|
for n_iter in range(self.maxiter):
|
|
77
97
|
for param_to_vary in range(num_params):
|
|
78
98
|
e_shifted = []
|
|
99
|
+
|
|
100
|
+
num_samples_tmp = max(self.num_samples, 4 * parameter_occ[param_to_vary] + 1)
|
|
101
|
+
param_dist = 4 * np.pi / num_samples_tmp
|
|
102
|
+
shifts = (np.arange(num_samples_tmp) - num_samples_tmp // 2) * param_dist
|
|
79
103
|
for shift in shifts:
|
|
80
104
|
if shift == 0.0 and energy_at_zero is not None:
|
|
81
105
|
e_shifted.append(energy_at_zero)
|
|
@@ -92,7 +116,9 @@ class ExcitationSolveScipy:
|
|
|
92
116
|
)
|
|
93
117
|
nfev += 1
|
|
94
118
|
|
|
95
|
-
|
|
119
|
+
logging.debug("Now optimizing parameter number %s which occurs %s times", param_to_vary, parameter_occ[param_to_vary])
|
|
120
|
+
params_excsolve[param_to_vary], current_energy_excsolve = excitation_solve_step_shared_param(
|
|
121
|
+
parameter_occ[param_to_vary],
|
|
96
122
|
(shifts * self.param_scaling + params_excsolve[param_to_vary]) / self.param_scaling,
|
|
97
123
|
e_shifted,
|
|
98
124
|
)
|
|
@@ -106,14 +132,14 @@ class ExcitationSolveScipy:
|
|
|
106
132
|
# about the optimization progress/convergence
|
|
107
133
|
# fun(params_excsolve * param_scaling)
|
|
108
134
|
|
|
109
|
-
self.energies.append(current_energy_excsolve)
|
|
110
|
-
self.nfevs.append(nfev)
|
|
135
|
+
self.energies = np.append(self.energies, current_energy_excsolve)
|
|
136
|
+
self.nfevs = np.append(self.nfevs, nfev)
|
|
111
137
|
if self.save_parameters:
|
|
112
|
-
self.params.append(params_excsolve.copy())
|
|
138
|
+
self.params = np.append(self.params, params_excsolve.copy())
|
|
113
139
|
logging.debug("Current ExcitationSolve optimum energy: %s", current_energy_excsolve)
|
|
114
140
|
|
|
115
|
-
self.energies_after_it.append(current_energy_excsolve)
|
|
116
|
-
self.nfevs_after_it.append(nfev)
|
|
141
|
+
self.energies_after_it = np.append(self.energies_after_it, current_energy_excsolve)
|
|
142
|
+
self.nfevs_after_it = np.append(self.nfevs_after_it, nfev)
|
|
117
143
|
if n_iter > 0:
|
|
118
144
|
msg = "Current ExcitationSolve optimum energy after %s iterations: %s | Diff. to prev.: %s" % (
|
|
119
145
|
n_iter + 1,
|