Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
boundstates1d.cpp
Go to the documentation of this file.
1/**
2 * @file boundstates1d.cpp
3 * @brief Implements the boundstates1d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/boundstates1d.cpp. This file is part of Quantum Phenomena Explorer (QPE),
7 * maintained by Officina Turini. See LICENSE_IT.txt / LICENSE.txt for the
8 * QPE freeware source-available terms and THIRD_PARTY_NOTICES.txt for
9 * third-party components.
10 */
11
12#include "boundstates1d.h"
13
16
17#include <algorithm>
18#include <cmath>
19#include <complex>
20#include <cstring>
21#include <limits>
22#include <stdexcept>
23
24namespace qpe {
25namespace {
26
27constexpr double kPi = 3.141592653589793238462643383279502884;
28
29/**
30 * @brief Performs finite positive.
31 * @param value Value to assign or evaluate.
32 */
33bool finitePositive(double value) noexcept
34{
35 return std::isfinite(value) && value > 0.0;
36}
37
38/**
39 * @brief Returns whether power of two.
40 * @param value Value to assign or evaluate.
41 */
42bool isPowerOfTwo(std::size_t value) noexcept
43{
44 return value != 0u && (value & (value - 1u)) == 0u;
45}
46
47/**
48 * @brief Performs fft1 d.
49 * @param data The data value used by the operation.
50 * @param inverse The inverse value used by the operation.
51 */
52void fft1D(std::vector<std::complex<double> > &data, bool inverse)
53{
54 const std::size_t count = data.size();
55 if (!isPowerOfTwo(count)) {
56 throw std::invalid_argument("BoundStates1D: X grid must be a power of two");
57 }
58
59 std::size_t j = 0u;
60 for (std::size_t i = 1u; i < count; ++i) {
61 std::size_t bit = count >> 1u;
62 while (j & bit) {
63 j ^= bit;
64 bit >>= 1u;
65 }
66 j ^= bit;
67 if (i < j) {
68 std::swap(data[i], data[j]);
69 }
70 }
71
72 const double sign = inverse ? 1.0 : -1.0;
73 for (std::size_t length = 2u; length <= count; length <<= 1u) {
74 const double angle = sign * 2.0 * kPi / static_cast<double>(length);
75 const std::complex<double> wLength(std::cos(angle), std::sin(angle));
76 const std::size_t half = length >> 1u;
77 for (std::size_t block = 0u; block < count; block += length) {
78 std::complex<double> w(1.0, 0.0);
79 for (std::size_t offset = 0u; offset < half; ++offset) {
80 const std::complex<double> even = data[block + offset];
81 const std::complex<double> odd = data[block + offset + half] * w;
82 data[block + offset] = even + odd;
83 data[block + offset + half] = even - odd;
84 w *= wLength;
85 }
86 }
87 }
88
89 if (inverse) {
90 const double scale = 1.0 / static_cast<double>(count);
91 for (std::complex<double> &value : data) {
92 value *= scale;
93 }
94 }
95}
96
97/**
98 * @brief Performs weighted norm.
99 * @param v The v value used by the operation.
100 * @param dx The dx value used by the operation.
101 */
102double weightedNorm(const std::vector<std::complex<double> > &v, double dx)
103{
104 double sum = 0.0;
105 for (const std::complex<double> &value : v) {
106 sum += std::norm(value);
107 }
108 return std::sqrt(std::max(0.0, sum * dx));
109}
110
111/**
112 * @brief Returns normalize.
113 * @param v The v value used by the operation.
114 * @param dx The dx value used by the operation.
115 */
116bool normalize(std::vector<std::complex<double> > &v, double dx)
117{
118 const double n = weightedNorm(v, dx);
119 if (!(n > 1.0e-30) || !std::isfinite(n)) {
120 return false;
121 }
122 const double inv = 1.0 / n;
123 for (std::complex<double> &value : v) {
124 value *= inv;
125 }
126 return true;
127}
128
129std::complex<double> innerProduct(const std::vector<std::complex<double> > &a,
130 const std::vector<std::complex<double> > &b,
131 double dx)
132{
133 std::complex<double> sum(0.0, 0.0);
134 for (std::size_t i = 0u; i < a.size(); ++i) {
135 sum += std::conj(a[i]) * b[i];
136 }
137 return sum * dx;
138}
139
140/**
141 * @brief Performs orthonormalize.
142 * @param v The v value used by the operation.
143 * @param basis The basis value used by the operation.
144 * @param dx The dx value used by the operation.
145 */
146bool orthonormalize(std::vector<std::complex<double> > &v,
147 const std::vector<std::vector<std::complex<double> > > &basis,
148 double dx)
149{
150 // Two passes make the deflation noticeably more stable for higher modes.
151 for (int pass = 0; pass < 2; ++pass) {
152 for (const std::vector<std::complex<double> > &q : basis) {
153 const std::complex<double> projection = innerProduct(q, v, dx);
154 for (std::size_t i = 0u; i < v.size(); ++i) {
155 v[i] -= projection * q[i];
156 }
157 }
158 }
159 return normalize(v, dx);
160}
161
162std::uint32_t nextRandom(std::uint32_t &state) noexcept
163{
164 // Deterministic xorshift32: enough for a reproducible non-symmetric trial
165 // vector; this is not used for physical measurement sampling.
166 state ^= state << 13;
167 state ^= state >> 17;
168 state ^= state << 5;
169 return state;
170}
171
172std::vector<std::complex<double> > makeTrial(std::size_t count,
173 const std::vector<double> &potential,
174 double vMin,
175 int stateIndex)
176{
177 std::vector<std::complex<double> > trial(count, std::complex<double>(0.0, 0.0));
178 double spread = 0.0;
179 for (double value : potential) {
180 spread = std::max(spread, value - vMin);
181 }
182 const double scale = std::max(1.0, 0.25 * spread);
183 std::uint32_t rng = 0x9E3779B9u ^ static_cast<std::uint32_t>(0x85EBCA6Bu * (stateIndex + 1));
184
185 for (std::size_t i = 0u; i < count; ++i) {
186 const double u = static_cast<double>(nextRandom(rng) & 0x00FFFFFFu) /
187 static_cast<double>(0x01000000u);
188 const double randomValue = 2.0 * u - 1.0;
189 const double weight = std::exp(-0.35 * (potential[i] - vMin) / scale);
190 // Small deterministic harmonic component improves low-state convergence
191 // while the random part prevents parity from hiding odd/even modes.
192 const double harmonic = std::cos(2.0 * kPi * static_cast<double>(stateIndex + 1) *
193 static_cast<double>(i) / static_cast<double>(count));
194 trial[i] = weight * (0.75 * randomValue + 0.25 * harmonic);
195 }
196 return trial;
197}
198
199std::vector<double> spectralKinetic(std::size_t count,
200 double dx,
201 double mass,
202 double hbar)
203{
204 std::vector<double> kinetic(count, 0.0);
205 const double length = static_cast<double>(count) * dx;
206 for (std::size_t k = 0u; k < count; ++k) {
207 const long signedIndex = (k <= count / 2u)
208 ? static_cast<long>(k)
209 : static_cast<long>(k) - static_cast<long>(count);
210 const double waveNumber = 2.0 * kPi * static_cast<double>(signedIndex) / length;
211 kinetic[k] = hbar * hbar * waveNumber * waveNumber / (2.0 * mass);
212 }
213 return kinetic;
214}
215
216/**
217 * @brief Performs expectation energy.
218 * @param psi The psi value used by the operation.
219 * @param potential Potential field used by the operation.
220 * @param kinetic The kinetic value used by the operation.
221 * @param dx The dx value used by the operation.
222 */
223double expectationEnergy(const std::vector<std::complex<double> > &psi,
224 const std::vector<double> &potential,
225 const std::vector<double> &kinetic,
226 double dx)
227{
228 std::vector<std::complex<double> > kPsi = psi;
229 fft1D(kPsi, false);
230 for (std::size_t i = 0u; i < kPsi.size(); ++i) {
231 kPsi[i] *= kinetic[i];
232 }
233 fft1D(kPsi, true);
234
235 std::complex<double> e(0.0, 0.0);
236 for (std::size_t i = 0u; i < psi.size(); ++i) {
237 const std::complex<double> hPsi = kPsi[i] + potential[i] * psi[i];
238 e += std::conj(psi[i]) * hPsi;
239 }
240 return (e * dx).real();
241}
242
243/**
244 * @brief Returns count nodes.
245 * @param amplitude The amplitude value used by the operation.
246 */
247int countNodes(const std::vector<float> &amplitude)
248{
249 float maximum = 0.0f;
250 for (float value : amplitude) {
251 maximum = std::max(maximum, std::abs(value));
252 }
253 if (!(maximum > 0.0f)) {
254 return 0;
255 }
256 const float threshold = maximum * 0.015f;
257 int previousSign = 0;
258 int nodes = 0;
259 for (float value : amplitude) {
260 if (std::abs(value) < threshold) {
261 continue;
262 }
263 const int sign = value >= 0.0f ? 1 : -1;
264 if (previousSign != 0 && sign != previousSign) {
265 ++nodes;
266 }
267 previousSign = sign;
268 }
269 return nodes;
270}
271
272/**
273 * @brief Performs edge mean.
274 * @param values The values value used by the operation.
275 * @param left The left value used by the operation.
276 */
277double edgeMean(const std::vector<double> &values, bool left)
278{
279 if (values.empty()) {
280 return 0.0;
281 }
282 const std::size_t count = std::max<std::size_t>(4u, values.size() / 10u);
283 double sum = 0.0;
284 if (left) {
285 for (std::size_t i = 0u; i < count; ++i) {
286 sum += values[i];
287 }
288 } else {
289 for (std::size_t i = values.size() - count; i < values.size(); ++i) {
290 sum += values[i];
291 }
292 }
293 return sum / static_cast<double>(count);
294}
295
296/**
297 * @brief Performs finite vector.
298 * @param values The values value used by the operation.
299 */
300bool finiteVector(const std::vector<double> &values) noexcept
301{
302 for (double value : values) {
303 if (!std::isfinite(value)) {
304 return false;
305 }
306 }
307 return true;
308}
309
310} // namespace
311
313 double tolerance) noexcept
314{
315 if (potential.empty() || potential.width() == 0u || potential.height() == 0u ||
316 !std::isfinite(tolerance) || tolerance < 0.0) {
317 return false;
318 }
319
320 const std::size_t referenceY = potential.height() / 2u;
321 double scale = 1.0;
322 for (std::size_t x = 0u; x < potential.width(); ++x) {
323 scale = std::max(scale, std::abs(static_cast<double>(potential(x, referenceY))));
324 }
325 const double allowed = tolerance * scale;
326
327 for (std::size_t y = 0u; y < potential.height(); ++y) {
328 for (std::size_t x = 0u; x < potential.width(); ++x) {
329 const double delta = std::abs(static_cast<double>(potential(x, y)) -
330 static_cast<double>(potential(x, referenceY)));
331 if (delta > allowed) {
332 return false;
333 }
334 }
335 }
336 return true;
337}
338
339std::uint64_t BoundStates1D::potentialSignature(const PotentialField2D &potential) noexcept
340{
341 // FNV-1a over grid metadata and all float samples. The signature is only
342 // used to detect a changed preparation, never as a security primitive.
343 std::uint64_t hash = 1469598103934665603ull;
344 const auto mixBytes = [&hash](const void *data, std::size_t size) {
345 const unsigned char *bytes = static_cast<const unsigned char *>(data);
346 for (std::size_t i = 0u; i < size; ++i) {
347 hash ^= static_cast<std::uint64_t>(bytes[i]);
348 hash *= 1099511628211ull;
349 }
350 };
351
352 const std::size_t width = potential.width();
353 const std::size_t height = potential.height();
354 const float dx = potential.dx();
355 const float dy = potential.dy();
356 mixBytes(&width, sizeof(width));
357 mixBytes(&height, sizeof(height));
358 mixBytes(&dx, sizeof(dx));
359 mixBytes(&dy, sizeof(dy));
360 if (!potential.empty()) {
361 mixBytes(potential.data(), potential.size() * sizeof(PotentialField2D::Value));
362 }
363 return hash;
364}
365
367 const PotentialField2D &potential,
368 double mass,
369 double hbar) noexcept
370{
371 return spectrum.valid &&
372 spectrum.sourceSignature == potentialSignature(potential) &&
373 std::abs(spectrum.mass - mass) <= 1.0e-12 * std::max(1.0, std::abs(mass)) &&
374 std::abs(spectrum.hbar - hbar) <= 1.0e-12 * std::max(1.0, std::abs(hbar)) &&
375 spectrum.x.size() == potential.width();
376}
377
379 const BoundStateSolver1DParams &params)
380{
382 result.mass = params.mass;
383 result.hbar = params.hbar;
384 result.dx = potential.dx();
385 result.sourceSignature = potentialSignature(potential);
386
387 if (potential.empty() || potential.width() < 16u || potential.height() < 1u) {
388 result.message = "Potential grid is empty or too small.";
389 return result;
390 }
391 if (!isPowerOfTwo(potential.width())) {
392 result.message = "Bound-state analysis requires a radix-2 X grid.";
393 return result;
394 }
395 if (!finitePositive(params.mass) || !finitePositive(params.hbar) ||
396 !finitePositive(params.imaginaryTimeStep) || params.maxIterations < 50 ||
397 params.maxStates < 1 || params.maxStates > 16 ||
398 !finitePositive(params.energyTolerance)) {
399 result.message = "Invalid bound-state solver parameters.";
400 return result;
401 }
402 if (!isYInvariant(potential, params.yInvarianceTolerance)) {
403 result.message = "Potential is not invariant in Y. Use a well extended across Y for this 1D bound-state analysis.";
404 return result;
405 }
406
407 const std::size_t n = potential.width();
408 const std::size_t referenceY = potential.height() / 2u;
409 result.x.resize(n);
410 result.potential.resize(n);
411 for (std::size_t i = 0u; i < n; ++i) {
412 result.x[i] = potential.xCoordinate(i);
413 result.potential[i] = static_cast<double>(potential(i, referenceY));
414 }
415 if (!finiteVector(result.potential)) {
416 result.message = "Potential contains non-finite values.";
417 return result;
418 }
419
420 const double dx = static_cast<double>(potential.dx());
421 const double vMin = *std::min_element(result.potential.begin(), result.potential.end());
422 result.continuumThreshold = std::min(edgeMean(result.potential, true),
423 edgeMean(result.potential, false));
424 const double potentialSpan = std::max(1.0,
425 *std::max_element(result.potential.begin(), result.potential.end()) - vMin);
426 const double boundMargin = 1.0e-5 * potentialSpan;
427
428 const std::vector<double> kinetic = spectralKinetic(n, dx, params.mass, params.hbar);
429 std::vector<double> halfPotentialDecay(n, 1.0);
430 std::vector<double> kineticDecay(n, 1.0);
431 for (std::size_t i = 0u; i < n; ++i) {
432 halfPotentialDecay[i] = std::exp(-(result.potential[i] - vMin) *
433 params.imaginaryTimeStep /
434 (2.0 * params.hbar));
435 kineticDecay[i] = std::exp(-kinetic[i] * params.imaginaryTimeStep / params.hbar);
436 }
437
438 std::vector<std::vector<std::complex<double> > > basis;
439 basis.reserve(static_cast<std::size_t>(params.maxStates));
440
441 for (int stateIndex = 0; stateIndex < params.maxStates; ++stateIndex) {
442 std::vector<std::complex<double> > psi = makeTrial(n, result.potential, vMin, stateIndex);
443 if (!orthonormalize(psi, basis, dx)) {
444 break;
445 }
446
447 double previousEnergy = std::numeric_limits<double>::infinity();
448 int stableChecks = 0;
449 for (int iteration = 0; iteration < params.maxIterations; ++iteration) {
450 for (std::size_t i = 0u; i < n; ++i) {
451 psi[i] *= halfPotentialDecay[i];
452 }
453 fft1D(psi, false);
454 for (std::size_t i = 0u; i < n; ++i) {
455 psi[i] *= kineticDecay[i];
456 }
457 fft1D(psi, true);
458 for (std::size_t i = 0u; i < n; ++i) {
459 psi[i] *= halfPotentialDecay[i];
460 }
461 if (!orthonormalize(psi, basis, dx)) {
462 break;
463 }
464
465 if ((iteration % 10) == 0) {
466 const double energy = expectationEnergy(psi, result.potential, kinetic, dx);
467 const double scale = std::max(1.0, std::abs(energy));
468 if (std::isfinite(previousEnergy) &&
469 std::abs(energy - previousEnergy) <= params.energyTolerance * scale) {
470 ++stableChecks;
471 } else {
472 stableChecks = 0;
473 }
474 previousEnergy = energy;
475 if (iteration >= 120 && stableChecks >= 4) {
476 break;
477 }
478 }
479 }
480
481 if (!orthonormalize(psi, basis, dx)) {
482 break;
483 }
484 const double energy = expectationEnergy(psi, result.potential, kinetic, dx);
485 if (!std::isfinite(energy)) {
486 break;
487 }
488
489 // Once the next orthogonal eigenmode reaches the edge continuum, stop:
490 // higher modes are not spatially bound to the central well.
491 if (!(energy < result.continuumThreshold - boundMargin)) {
492 break;
493 }
494
495 // Rotate the global phase so the largest sample is real and positive.
496 std::size_t peakIndex = 0u;
497 double peakMagnitude = 0.0;
498 for (std::size_t i = 0u; i < n; ++i) {
499 const double magnitude = std::abs(psi[i]);
500 if (magnitude > peakMagnitude) {
501 peakMagnitude = magnitude;
502 peakIndex = i;
503 }
504 }
505 if (peakMagnitude > 0.0) {
506 const double phase = std::arg(psi[peakIndex]);
507 const std::complex<double> rotation(std::cos(-phase), std::sin(-phase));
508 for (std::complex<double> &value : psi) {
509 value *= rotation;
510 }
511 }
512
513 BoundState1D state;
514 state.quantumNumber = stateIndex + 1;
515 state.energy = energy;
516 state.amplitude.resize(n, 0.0f);
517 for (std::size_t i = 0u; i < n; ++i) {
518 state.amplitude[i] = static_cast<float>(psi[i].real());
519 }
520 state.nodeCount = countNodes(state.amplitude);
521 result.states.push_back(state);
522
523 // Rebuild the deflation basis from the displayed real representative.
524 // For a real Hamiltonian this removes tiny numerical imaginary residue.
525 std::vector<std::complex<double> > realState(n);
526 for (std::size_t i = 0u; i < n; ++i) {
527 realState[i] = static_cast<double>(state.amplitude[i]);
528 }
529 normalize(realState, dx);
530 basis.push_back(realState);
531 }
532
533 result.valid = !result.states.empty();
534 if (result.valid) {
535 result.message = "Bound states computed.";
536 } else {
537 result.message = "No bound eigenstate was found below the edge continuum threshold.";
538 }
539 return result;
540}
541
543 const BoundStateSpectrum1D &spectrum,
544 int stateIndex)
545{
546 if (!spectrum.valid || stateIndex < 0 ||
547 stateIndex >= static_cast<int>(spectrum.states.size()) ||
548 field.empty() || field.width() != spectrum.x.size()) {
549 return false;
550 }
551
552 const BoundState1D &state = spectrum.states[static_cast<std::size_t>(stateIndex)];
553 if (state.amplitude.size() != field.width()) {
554 return false;
555 }
556
557 const double yMeasure = static_cast<double>(field.height()) * static_cast<double>(field.dy());
558 if (!(yMeasure > 0.0)) {
559 return false;
560 }
561 const float yScale = static_cast<float>(1.0 / std::sqrt(yMeasure));
562 for (std::size_t y = 0u; y < field.height(); ++y) {
563 for (std::size_t x = 0u; x < field.width(); ++x) {
564 field(x, y) = ComplexField2D::Value(state.amplitude[x] * yScale, 0.0f);
565 }
566 }
567 return field.normalize();
568}
569
571 const BoundStateSpectrum1D &spectrum,
572 int stateIndexA,
573 int stateIndexB,
574 double relativePhaseRadians)
575{
576 if (!spectrum.valid || stateIndexA < 0 || stateIndexB < 0 ||
577 stateIndexA == stateIndexB ||
578 stateIndexA >= static_cast<int>(spectrum.states.size()) ||
579 stateIndexB >= static_cast<int>(spectrum.states.size()) ||
580 !std::isfinite(relativePhaseRadians) || field.empty() ||
581 field.width() != spectrum.x.size()) {
582 return false;
583 }
584
585 const BoundState1D &a = spectrum.states[static_cast<std::size_t>(stateIndexA)];
586 const BoundState1D &b = spectrum.states[static_cast<std::size_t>(stateIndexB)];
587 if (a.amplitude.size() != field.width() || b.amplitude.size() != field.width()) {
588 return false;
589 }
590
591 const double yMeasure = static_cast<double>(field.height()) * static_cast<double>(field.dy());
592 if (!(yMeasure > 0.0)) {
593 return false;
594 }
595 const double oneOverSqrtTwo = 1.0 / std::sqrt(2.0);
596 const std::complex<double> phase(std::cos(relativePhaseRadians),
597 std::sin(relativePhaseRadians));
598 const double yScale = 1.0 / std::sqrt(yMeasure);
599
600 for (std::size_t y = 0u; y < field.height(); ++y) {
601 for (std::size_t x = 0u; x < field.width(); ++x) {
602 const std::complex<double> value = oneOverSqrtTwo *
603 (static_cast<double>(a.amplitude[x]) +
604 phase * static_cast<double>(b.amplitude[x])) * yScale;
605 field(x, y) = ComplexField2D::Value(static_cast<float>(value.real()),
606 static_cast<float>(value.imag()));
607 }
608 }
609 return field.normalize();
610}
611
612} // namespace qpe
Declares the boundstates1d physics/numerical component used by QPE experiments.
static bool isYInvariant(const PotentialField2D &potential, double tolerance=1.0e-5) noexcept
Returns whether y invariant.
static bool prepareEigenstate(ComplexField2D &field, const BoundStateSpectrum1D &spectrum, int stateIndex)
Prepares eigenstate.
static bool matches(const BoundStateSpectrum1D &spectrum, const PotentialField2D &potential, double mass, double hbar=1.0) noexcept
Performs matches.
static BoundStateSpectrum1D solve(const PotentialField2D &potential, const BoundStateSolver1DParams &params)
Performs solve.
static std::uint64_t potentialSignature(const PotentialField2D &potential) noexcept
Performs potential signature.
static bool prepareSuperposition(ComplexField2D &field, const BoundStateSpectrum1D &spectrum, int stateIndexA, int stateIndexB, double relativePhaseRadians=0.0)
Prepares superposition.
Owning Cartesian grid of complex scalar samples.
bool empty() const noexcept
Returns whether the field contains no samples.
std::size_t height() const noexcept
Returns the number of samples along Y.
bool normalize() noexcept
Scales the field to unit discrete probability norm.
std::complex< float > Value
float dy() const noexcept
Returns the Y grid spacing.
std::size_t width() const noexcept
Returns the number of samples along X.
Implements the Potential Field2D component.
std::size_t height() const noexcept
Returns height.
double xCoordinate(std::size_t x) const noexcept
Performs x coordinate.
bool empty() const noexcept
Performs empty.
std::size_t width() const noexcept
Returns width.
float dx() const noexcept
Performs dx.
Declares the complex scalar field container representing two-dimensional wave functions.
std::vector< std::complex< double > > makeTrial(std::size_t count, const std::vector< double > &potential, double vMin, int stateIndex)
int countNodes(const std::vector< float > &amplitude)
Returns count nodes.
bool isPowerOfTwo(std::size_t value) noexcept
Returns whether power of two.
bool orthonormalize(std::vector< std::complex< double > > &v, const std::vector< std::vector< std::complex< double > > > &basis, double dx)
Performs orthonormalize.
double expectationEnergy(const std::vector< std::complex< double > > &psi, const std::vector< double > &potential, const std::vector< double > &kinetic, double dx)
Performs expectation energy.
std::vector< double > spectralKinetic(std::size_t count, double dx, double mass, double hbar)
bool normalize(std::vector< std::complex< double > > &v, double dx)
Returns normalize.
void fft1D(std::vector< std::complex< double > > &data, bool inverse)
Performs fft1 d.
std::complex< double > innerProduct(const std::vector< std::complex< double > > &a, const std::vector< std::complex< double > > &b, double dx)
bool finiteVector(const std::vector< double > &values) noexcept
Performs finite vector.
double weightedNorm(const std::vector< std::complex< double > > &v, double dx)
Performs weighted norm.
std::uint32_t nextRandom(std::uint32_t &state) noexcept
double edgeMean(const std::vector< double > &values, bool left)
Performs edge mean.
Declares the potentialfield2d physics/numerical component used by QPE experiments.
Data structure used by the Bound State1D component.
std::vector< float > amplitude
Configuration values for Bound State Solver1D Params.
Data structure used by the Bound State Spectrum1D component.
std::vector< double > x
std::vector< BoundState1D > states
std::vector< double > potential
std::uint64_t sourceSignature