Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
quantumbackflow2d.cpp
Go to the documentation of this file.
1/**
2 * @file quantumbackflow2d.cpp
3 * @brief Implements the quantumbackflow2d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/quantumbackflow2d.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 "quantumbackflow2d.h"
13
14#include "core/math/fft2d.h"
15
16#include <algorithm>
17#include <cmath>
18#include <complex>
19#include <limits>
20
21namespace qpe {
22namespace {
23
24constexpr double kPi = 3.141592653589793238462643383279502884;
25constexpr double kTwoPi = 2.0 * kPi;
26
27/**
28 * @brief Performs finite.
29 * @param v The v value used by the operation.
30 */
31bool finite(double v) noexcept { return std::isfinite(v); }
32
33/**
34 * @brief Performs fft wave number.
35 * @param index Zero-based index of the selected item.
36 * @param count The count value used by the operation.
37 * @param spacing The spacing value used by the operation.
38 */
39double fftWaveNumber(std::size_t index, std::size_t count, double spacing) noexcept
40{
41 const long long n = (index <= count / 2u)
42 ? static_cast<long long>(index)
43 : static_cast<long long>(index) - static_cast<long long>(count);
44 return kTwoPi * static_cast<double>(n) /
45 (static_cast<double>(count) * spacing);
46}
47
48/**
49 * @brief Performs coordinate.
50 * @param index Zero-based index of the selected item.
51 * @param count The count value used by the operation.
52 * @param spacing The spacing value used by the operation.
53 */
54double coordinate(std::size_t index, std::size_t count, double spacing) noexcept
55{
56 return (static_cast<double>(index) - 0.5 * static_cast<double>(count)) * spacing;
57}
58
59std::size_t nearestColumn(const ComplexField2D &field, double x) noexcept
60{
61 const double f = x / static_cast<double>(field.dx()) +
62 0.5 * static_cast<double>(field.width());
63 long long i = static_cast<long long>(std::llround(f));
64 if (i < 1) i = 1;
65 if (i > static_cast<long long>(field.width()) - 2)
66 i = static_cast<long long>(field.width()) - 2;
67 return static_cast<std::size_t>(i);
68}
69
70} // namespace
71
73 std::string *reason) noexcept
74{
75 const bool ok = finite(c.detectorX) && finite(c.slowWaveNumber) &&
76 finite(c.fastWaveNumber) && finite(c.momentumSigma) &&
77 finite(c.fastAmplitudeRatio) && finite(c.relativePhaseDegrees) &&
78 finite(c.initialCenterX) && finite(c.transverseSigma) &&
79 finite(c.analysisEndTime) &&
80 c.slowWaveNumber > 0.0 && c.slowWaveNumber <= 8.0 &&
81 c.fastWaveNumber > c.slowWaveNumber && c.fastWaveNumber <= 12.0 &&
82 c.momentumSigma >= 0.03 && c.momentumSigma <= 3.0 &&
83 c.fastAmplitudeRatio > 0.0 && c.fastAmplitudeRatio <= 2.0 &&
84 c.transverseSigma >= 0.2 && c.transverseSigma <= 12.0 &&
85 c.analysisEndTime >= 0.1 && c.analysisEndTime <= 100.0 &&
86 std::abs(c.detectorX) <= 1000.0 &&
87 std::abs(c.initialCenterX) <= 1000.0;
88 if (!ok && reason)
89 *reason = "QuantumBackflow2D: invalid configuration";
90 return ok;
91}
92
95 double hbar,
96 std::string *reason)
97{
98 if (field.empty() || !FFT2D::supportsSize(field.width(), field.height()) ||
99 !finite(hbar) || hbar <= 0.0 || !validateConfig(c, reason)) {
100 if (reason && reason->empty()) *reason = "QuantumBackflow2D: invalid field or hbar";
101 return false;
102 }
103
104 ComplexField2D spectrum(field.width(), field.height(), field.dx(), field.dy());
105 spectrum.fill(ComplexField2D::Value(0.0f, 0.0f));
106
107 const double lx = static_cast<double>(field.width()) * static_cast<double>(field.dx());
108 const double ly = static_cast<double>(field.height()) * static_cast<double>(field.dy());
109 const double x0IndexCoordinates = c.initialCenterX + 0.5 * lx;
110 const double y0IndexCoordinates = 0.5 * ly;
111 const double phase = c.relativePhaseDegrees * kPi / 180.0;
112 const std::complex<double> relative(std::cos(phase), std::sin(phase));
113 const double invTwoSigma2 = 0.5 / (c.momentumSigma * c.momentumSigma);
114 const double transverseKsigma = 1.0 / c.transverseSigma;
115 const double invTwoKySigma2 = 0.5 / (transverseKsigma * transverseKsigma);
116
117 for (std::size_t y = 0; y < field.height(); ++y) {
118 const double ky = fftWaveNumber(y, field.height(), static_cast<double>(field.dy()));
119 const double gy = std::exp(-ky * ky * invTwoKySigma2);
120 const std::complex<double> shiftY = std::exp(std::complex<double>(0.0, -ky * y0IndexCoordinates));
121 for (std::size_t x = 0; x < field.width(); ++x) {
122 const double kx = fftWaveNumber(x, field.width(), static_cast<double>(field.dx()));
123 if (kx <= 0.0) continue; // exact positive-momentum support by construction
124
125 const double d1 = kx - c.slowWaveNumber;
126 const double d2 = kx - c.fastWaveNumber;
127 const double g1 = std::exp(-d1 * d1 * invTwoSigma2);
128 const double g2 = std::exp(-d2 * d2 * invTwoSigma2);
129 const std::complex<double> ax = g1 + c.fastAmplitudeRatio * relative * g2;
130 const std::complex<double> shiftX = std::exp(std::complex<double>(0.0, -kx * x0IndexCoordinates));
131 const std::complex<double> a = ax * gy * shiftX * shiftY;
132 spectrum(x, y) = ComplexField2D::Value(static_cast<float>(a.real()), static_cast<float>(a.imag()));
133 }
134 }
135
136 FFT2D::inverse(spectrum);
137 field = spectrum;
138 if (!field.normalize()) {
139 if (reason) *reason = "QuantumBackflow2D: state normalization failed";
140 return false;
141 }
142 return true;
143}
144
146{
147 if (field.empty() || !FFT2D::supportsSize(field.width(), field.height())) return 0.0;
148 try {
149 ComplexField2D spectrum = field;
150 FFT2D::forward(spectrum);
151 double positive = 0.0;
152 double total = 0.0;
153 for (std::size_t y = 0; y < field.height(); ++y) {
154 for (std::size_t x = 0; x < field.width(); ++x) {
155 const double w = std::norm(spectrum(x, y));
156 total += w;
157 if (fftWaveNumber(x, field.width(), static_cast<double>(field.dx())) > 0.0)
158 positive += w;
159 }
160 }
161 return total > 0.0 ? positive / total : 0.0;
162 } catch (...) {
163 return 0.0;
164 }
165}
166
169 double mass,
170 double hbar,
171 double time) noexcept
172{
174 out.time = time;
175 if (field.empty() || !finite(mass) || mass <= 0.0 || !finite(hbar) || hbar <= 0.0)
176 return out;
177
178 const std::size_t ix = nearestColumn(field, c.detectorX);
179 const double dx = static_cast<double>(field.dx());
180 const double dy = static_cast<double>(field.dy());
181 const double currentScale = hbar / mass;
182
183 double left = 0.0;
184 double right = 0.0;
185 double current = 0.0;
186 for (std::size_t y = 0; y < field.height(); ++y) {
187 for (std::size_t x = 0; x < field.width(); ++x) {
188 const double p = std::norm(field(x, y)) * dx * dy;
189 if (coordinate(x, field.width(), dx) < c.detectorX) left += p;
190 else right += p;
191 }
192 const std::complex<double> psi(field(ix, y).real(), field(ix, y).imag());
193 const std::complex<double> plus(field(ix + 1u, y).real(), field(ix + 1u, y).imag());
194 const std::complex<double> minus(field(ix - 1u, y).real(), field(ix - 1u, y).imag());
195 const std::complex<double> dpsi = (plus - minus) / (2.0 * dx);
196 current += currentScale * std::imag(std::conj(psi) * dpsi) * dy;
197 }
198 out.detectorCurrent = current;
199 out.probabilityLeft = left;
200 out.probabilityRight = right;
201 return out;
202}
203
205{
206 configured_ = false;
208 mass_ = 1.0;
209 hbar_ = 1.0;
211 minimumCurrent_ = 0.0;
213 samples_.clear();
214}
215
217 const QuantumBackflow2DConfig &config,
218 double mass,
219 double hbar)
220{
221 clear();
222 if (field.empty() || !QuantumBackflow2D::validateConfig(config, nullptr) ||
223 !finite(mass) || mass <= 0.0 || !finite(hbar) || hbar <= 0.0)
224 return false;
225 config_ = config;
226 mass_ = mass;
227 hbar_ = hbar;
229 configured_ = true;
230 return true;
231}
232
234{
235 if (!configured_ || field.empty()) return;
237 if (!samples_.empty()) {
238 const QuantumBackflowSample2D &p = samples_.back();
239 const double dt = std::max(0.0, s.time - p.time);
240 const double backward0 = std::max(0.0, -p.detectorCurrent);
241 const double backward1 = std::max(0.0, -s.detectorCurrent);
242 s.cumulativeBackwardFlux = p.cumulativeBackwardFlux + 0.5 * dt * (backward0 + backward1);
243 }
246 samples_.push_back(s);
247 trim();
248}
249
251{
252 if (maximumSamples_ == 0u) { samples_.clear(); return; }
253 if (samples_.size() > maximumSamples_)
254 samples_.erase(samples_.begin(), samples_.begin() + static_cast<std::ptrdiff_t>(samples_.size() - maximumSamples_));
255}
256
257} // namespace qpe
Owning Cartesian grid of complex scalar samples.
bool empty() const noexcept
Returns whether the field contains no samples.
float dx() const noexcept
Returns the X grid spacing.
std::size_t height() const noexcept
Returns the number of samples along Y.
void fill(const Value &value)
Assigns one complex value to every grid sample.
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.
static bool supportsSize(std::size_t width, std::size_t height) noexcept
Returns whether both grid dimensions are supported by the radix-2 backend.
Definition fft2d.cpp:30
static void forward(ComplexField2D &field)
Convenience wrapper for an in-place forward transform.
Definition fft2d.cpp:59
static void inverse(ComplexField2D &field)
Convenience wrapper for an in-place normalized inverse transform.
Definition fft2d.cpp:64
static bool prepareState(ComplexField2D &field, const QuantumBackflow2DConfig &config, double hbar, std::string *reason=nullptr)
Prepares state.
static bool validateConfig(const QuantumBackflow2DConfig &config, std::string *reason=nullptr) noexcept
Validates config.
static double positiveMomentumFraction(const ComplexField2D &field) noexcept
Performs positive momentum fraction.
static QuantumBackflowSample2D measure(const ComplexField2D &field, const QuantumBackflow2DConfig &config, double mass, double hbar, double time) noexcept
Performs measure.
const QuantumBackflow2DConfig & config() const noexcept
Returns config.
std::vector< QuantumBackflowSample2D > samples_
void append(double time, const ComplexField2D &field)
Carries out append.
void clear()
Clears stored state and returns the component to its empty/default state.
bool configure(const ComplexField2D &field, const QuantumBackflow2DConfig &config, double mass, double hbar)
Returns configure.
QuantumBackflow2DConfig config_
Declares the two-dimensional FFT utility used by spectral quantum propagators and diagnostics.
std::size_t nearestColumn(const ComplexField2D &field, double x) noexcept
Declares the quantumbackflow2d physics/numerical component used by QPE experiments.
Configuration values for Quantum Backflow2D Config.
Data structure used by the Quantum Backflow Sample2D component.