Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
quantumzitterbewegung2d.cpp
Go to the documentation of this file.
1/**
2 * @file quantumzitterbewegung2d.cpp
3 * @brief Implements the quantumzitterbewegung2d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/quantumzitterbewegung2d.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
13
14#include "core/math/fft2d.h"
15
16#include <algorithm>
17#include <cmath>
18#include <complex>
19
20namespace qpe {
21namespace {
22constexpr double kTwoPi = 6.283185307179586476925286766559005768;
23
24/**
25 * @brief Performs finite.
26 * @param x X coordinate/value.
27 */
28bool finite(double x) noexcept { return std::isfinite(x); }
29
30/**
31 * @brief Performs coordinate.
32 * @param i The i value used by the operation.
33 * @param n The n value used by the operation.
34 * @param d The d value used by the operation.
35 */
36double coordinate(std::size_t i, std::size_t n, double d) noexcept
37{
38 return (static_cast<double>(i) - 0.5 * static_cast<double>(n)) * d;
39}
40
41/**
42 * @brief Performs fft k.
43 * @param i The i value used by the operation.
44 * @param n The n value used by the operation.
45 * @param d The d value used by the operation.
46 */
47double fftK(std::size_t i, std::size_t n, double d) noexcept
48{
49 const long long m = (i <= n / 2u) ? static_cast<long long>(i)
50 : static_cast<long long>(i) - static_cast<long long>(n);
51 return kTwoPi * static_cast<double>(m) / (static_cast<double>(n) * d);
52}
53
54/**
55 * @brief Performs mean position.
56 * @param s The s value used by the operation.
57 * @param mx The mx value used by the operation.
58 * @param my The my value used by the operation.
59 */
60void meanPosition(const DiracSpinor2D &s, double &mx, double &my) noexcept
61{
62 mx = my = 0.0;
63 if (s.empty()) return;
64 const double cell = static_cast<double>(s.dx()) * static_cast<double>(s.dy());
65 double n = 0.0;
66 for (std::size_t y = 0; y < s.height(); ++y) {
67 const double yy = coordinate(y, s.height(), s.dy());
68 for (std::size_t x = 0; x < s.width(); ++x) {
69 const double xx = coordinate(x, s.width(), s.dx());
70 const double rho = std::norm(s.upper()(x,y)) + std::norm(s.lower()(x,y));
71 const double w = rho * cell;
72 n += w;
73 mx += xx * w;
74 my += yy * w;
75 }
76 }
77 if (n > 0.0) { mx /= n; my /= n; }
78}
79}
80
82 std::string *reason) noexcept
83{
84 const bool ok = finite(c.lightSpeed) && finite(c.restMass) && finite(c.centerX) &&
85 finite(c.centerY) && finite(c.sigma) && finite(c.momentumX) &&
86 finite(c.momentumY) && finite(c.analysisEndTime) &&
87 c.lightSpeed >= 0.05 && c.lightSpeed <= 20.0 &&
88 c.restMass >= 0.02 && c.restMass <= 20.0 &&
89 c.sigma >= 0.25 && c.sigma <= 20.0 &&
90 std::abs(c.momentumX) <= 20.0 && std::abs(c.momentumY) <= 20.0 &&
91 std::abs(c.centerX) <= 1000.0 && std::abs(c.centerY) <= 1000.0 &&
92 (c.preparationMode == 0 || c.preparationMode == 1) &&
93 c.analysisEndTime >= 0.2 && c.analysisEndTime <= 200.0;
94 if (!ok && reason) *reason = "QuantumZitterbewegung2D: invalid configuration";
95 return ok;
96}
97
100 double hbar,
101 std::string *reason)
102{
103 if (s.empty() || !FFT2D::supportsSize(s.width(), s.height()) ||
104 !(hbar > 0.0) || !finite(hbar) || !validateConfig(c, reason)) {
105 if (reason && reason->empty()) *reason = "QuantumZitterbewegung2D: invalid spinor or hbar";
106 return false;
107 }
108
109 const double inv4Sigma2 = 1.0 / (4.0 * c.sigma * c.sigma);
110 const double kx0 = c.momentumX / hbar;
111 const double ky0 = c.momentumY / hbar;
112 for (std::size_t y = 0; y < s.height(); ++y) {
113 const double yy = coordinate(y, s.height(), s.dy());
114 for (std::size_t x = 0; x < s.width(); ++x) {
115 const double xx = coordinate(x, s.width(), s.dx());
116 const double dx = xx - c.centerX;
117 const double dy = yy - c.centerY;
118 const double amp = std::exp(-(dx*dx + dy*dy) * inv4Sigma2);
119 const double ph = kx0 * dx + ky0 * dy;
120 const std::complex<double> a = amp * std::complex<double>(std::cos(ph), std::sin(ph));
121 s.upper()(x,y) = ComplexField2D::Value(static_cast<float>(a.real()), static_cast<float>(a.imag()));
122 s.lower()(x,y) = ComplexField2D::Value(0.0f, 0.0f);
123 }
124 }
125
126 if (c.preparationMode == 1) {
127 // Project each Fourier component onto the positive-energy eigenspace:
128 // P+ = (I + H/E)/2. Starting from (a,0) gives the two entries below.
131 const double cc = c.lightSpeed;
132 const double rest = c.restMass * cc * cc;
133 for (std::size_t y = 0; y < s.height(); ++y) {
134 const double py = hbar * fftK(y, s.height(), s.dy());
135 for (std::size_t x = 0; x < s.width(); ++x) {
136 const double px = hbar * fftK(x, s.width(), s.dx());
137 const double ax = cc * px;
138 const double ay = cc * py;
139 const double e = std::sqrt(ax*ax + ay*ay + rest*rest);
140 const std::complex<double> a(s.upper()(x,y).real(), s.upper()(x,y).imag());
141 const std::complex<double> up = 0.5 * (1.0 + rest / e) * a;
142 const std::complex<double> lo = 0.5 * std::complex<double>(ax, ay) / e * a;
143 s.upper()(x,y) = ComplexField2D::Value(static_cast<float>(up.real()), static_cast<float>(up.imag()));
144 s.lower()(x,y) = ComplexField2D::Value(static_cast<float>(lo.real()), static_cast<float>(lo.imag()));
145 }
146 }
149 }
150
151 if (!s.normalize()) {
152 if (reason) *reason = "QuantumZitterbewegung2D: normalization failed";
153 return false;
154 }
155 return true;
156}
157
159 const DiracSpinor2D &s, const QuantumZitterbewegung2DConfig &c, double hbar) noexcept
160{
162 if (s.empty() || !(hbar > 0.0) || !finite(hbar)) return out;
163 try {
164 ComplexField2D u = s.upper();
165 ComplexField2D v = s.lower();
168 const double cc = c.lightSpeed;
169 const double rest = c.restMass * cc * cc;
170 double pos = 0.0, neg = 0.0;
171 for (std::size_t y = 0; y < s.height(); ++y) {
172 const double py = hbar * fftK(y, s.height(), s.dy());
173 for (std::size_t x = 0; x < s.width(); ++x) {
174 const double px = hbar * fftK(x, s.width(), s.dx());
175 const double ax = cc * px;
176 const double ay = cc * py;
177 const double e = std::sqrt(ax*ax + ay*ay + rest*rest);
178 const std::complex<double> a(u(x,y).real(), u(x,y).imag());
179 const std::complex<double> b(v(x,y).real(), v(x,y).imag());
180 const double rho = std::norm(a) + std::norm(b);
181 const std::complex<double> h01(ax, -ay);
182 const std::complex<double> h10(ax, ay);
183 const std::complex<double> ha = rest * a + h01 * b;
184 const std::complex<double> hb = h10 * a - rest * b;
185 const double hExp = std::real(std::conj(a)*ha + std::conj(b)*hb);
186 pos += 0.5 * (rho + hExp/e);
187 neg += 0.5 * (rho - hExp/e);
188 }
189 }
190 const double total = pos + neg;
191 if (total > 0.0) { out.positive = pos/total; out.negative = neg/total; }
192 } catch (...) {}
193 return out;
194}
195
197{
198 const double rest = c.restMass * c.lightSpeed * c.lightSpeed;
199 const double cp2 = c.lightSpeed*c.lightSpeed *
200 (c.momentumX*c.momentumX + c.momentumY*c.momentumY);
201 return std::sqrt(cp2 + rest*rest);
202}
203
205 double hbar) noexcept
206{
207 return hbar > 0.0 ? 2.0 * centralEnergy(c) / hbar : 0.0;
208}
209
211 double hbar) noexcept
212{
213 const double w = predictedZitterOmega(c, hbar);
214 return w > 0.0 ? kTwoPi / w : 0.0;
215}
216
218{
219 const double e = centralEnergy(c);
220 if (!(e > 0.0)) return 0.0;
221 if (c.preparationMode == 1)
222 return c.lightSpeed*c.lightSpeed*c.momentumX/e;
223 return c.restMass*std::pow(c.lightSpeed,4.0)*c.momentumX/(e*e);
224}
225
227{
228 const double e = centralEnergy(c);
229 if (!(e > 0.0)) return 0.0;
230 if (c.preparationMode == 1)
231 return c.lightSpeed*c.lightSpeed*c.momentumY/e;
232 return c.restMass*std::pow(c.lightSpeed,4.0)*c.momentumY/(e*e);
233}
234
236 const DiracSpinor2D &s, const QuantumZitterbewegung2DConfig &c, double hbar,
237 double time, double x0, double y0) noexcept
238{
240 out.time = time;
241 if (s.empty()) return out;
242 meanPosition(s, out.meanX, out.meanY);
243 const double cell = static_cast<double>(s.dx()) * static_cast<double>(s.dy());
244 double vx = 0.0, vy = 0.0, n = 0.0;
245 for (std::size_t i = 0; i < s.upper().size(); ++i) {
246 const std::complex<double> a(s.upper().data()[i].real(), s.upper().data()[i].imag());
247 const std::complex<double> b(s.lower().data()[i].real(), s.lower().data()[i].imag());
248 const double w = (std::norm(a)+std::norm(b))*cell;
249 n += w;
250 vx += c.lightSpeed * 2.0 * std::real(std::conj(a)*b) * cell;
251 vy += c.lightSpeed * 2.0 * std::imag(std::conj(a)*b) * cell;
252 }
253 if (n > 0.0) { vx /= n; vy /= n; }
254 out.meanVx = vx;
255 out.meanVy = vy;
256 out.residualX = out.meanX - (x0 + predictedDriftX(c)*time);
257 out.residualY = out.meanY - (y0 + predictedDriftY(c)*time);
258 out.upperPopulation = s.upperPopulation();
259 out.lowerPopulation = s.lowerPopulation();
260 out.norm = s.norm2();
261 (void)hbar;
262 return out;
263}
264
276
279 double hbar)
280{
281 clear();
282 if (s.empty() || !QuantumZitterbewegung2D::validateConfig(c, nullptr) ||
283 !(hbar > 0.0) || !finite(hbar)) return false;
284 config_ = c;
285 hbar_ = hbar;
286 meanPosition(s, initialMeanX_, initialMeanY_);
288 configured_ = true;
289 return true;
290}
291
293{
294 if (!configured_ || s.empty()) return;
297 trim();
298
299 // Detrend against the best linear trajectory actually carried by the
300 // finite-width packet. Using only the central-momentum group velocity
301 // creates a false residual for the positive-energy control because its
302 // momentum distribution has finite width. A least-squares secular line
303 // leaves the genuinely oscillatory Zitterbewegung component while making
304 // the positive-energy control essentially flat.
305 const std::size_t n = samples_.size();
306 if (n < 2u) {
309 if (!samples_.empty()) { samples_.back().residualX = 0.0; samples_.back().residualY = 0.0; }
310 return;
311 }
312
313 double st = 0.0, stt = 0.0, sx = 0.0, sy = 0.0, stx = 0.0, sty = 0.0;
314 for (const auto &a : samples_) {
315 st += a.time; stt += a.time * a.time;
316 sx += a.meanX; sy += a.meanY;
317 stx += a.time * a.meanX; sty += a.time * a.meanY;
318 }
319 const double dn = static_cast<double>(n);
320 const double denominator = dn * stt - st * st;
321 if (!(std::abs(denominator) > 1.0e-18)) return;
322 fittedDriftX_ = (dn * stx - st * sx) / denominator;
323 fittedDriftY_ = (dn * sty - st * sy) / denominator;
324 const double interceptX = (sx - fittedDriftX_ * st) / dn;
325 const double interceptY = (sy - fittedDriftY_ * st) / dn;
327 for (auto &a : samples_) {
328 a.residualX = a.meanX - (interceptX + fittedDriftX_ * a.time);
329 a.residualY = a.meanY - (interceptY + fittedDriftY_ * a.time);
330 maximumAbsResidualX_ = std::max(maximumAbsResidualX_, std::abs(a.residualX));
331 }
332}
333
335{
336 if (maximumSamples_ == 0u) { samples_.clear(); return; }
337 if (samples_.size() > maximumSamples_)
338 samples_.erase(samples_.begin(), samples_.begin() + static_cast<std::ptrdiff_t>(samples_.size()-maximumSamples_));
339}
340
341} // namespace qpe
Owning Cartesian grid of complex scalar samples.
std::complex< float > Value
Two-component complex spinor field used by the QPE Dirac branch.
ComplexField2D & lower() noexcept
Carries out lower.
std::size_t height() const noexcept
Returns height.
ComplexField2D & upper() noexcept
Carries out upper.
bool normalize() noexcept
Returns normalize.
float dx() const noexcept
Returns the X-axis grid spacing.
float dy() const noexcept
Returns the Y-axis grid spacing.
std::size_t width() const noexcept
Returns width.
bool empty() const noexcept
Returns whether the object contains no data.
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 double predictedZitterPeriod(const QuantumZitterbewegung2DConfig &config, double hbar) noexcept
Performs predicted zitter period.
static double predictedDriftX(const QuantumZitterbewegung2DConfig &config) noexcept
Performs predicted drift x.
static double predictedDriftY(const QuantumZitterbewegung2DConfig &config) noexcept
Performs predicted drift y.
static bool validateConfig(const QuantumZitterbewegung2DConfig &config, std::string *reason=nullptr) noexcept
Validates config.
static QuantumZitterbewegungSample2D measure(const DiracSpinor2D &spinor, const QuantumZitterbewegung2DConfig &config, double hbar, double time, double initialMeanX, double initialMeanY) noexcept
Performs measure.
static double centralEnergy(const QuantumZitterbewegung2DConfig &config) noexcept
Performs central energy.
static DiracEnergyFractions2D energyFractions(const DiracSpinor2D &spinor, const QuantumZitterbewegung2DConfig &config, double hbar) noexcept
Returns energy fractions.
static bool prepareState(DiracSpinor2D &spinor, const QuantumZitterbewegung2DConfig &config, double hbar, std::string *reason=nullptr)
Prepares state.
static double predictedZitterOmega(const QuantumZitterbewegung2DConfig &config, double hbar) noexcept
Performs predicted zitter omega.
bool configure(const DiracSpinor2D &spinor, const QuantumZitterbewegung2DConfig &config, double hbar)
Returns configure.
void clear()
Clears stored state and returns the component to its empty/default state.
void append(double time, const DiracSpinor2D &spinor)
Carries out append.
std::vector< QuantumZitterbewegungSample2D > samples_
double hbar() const noexcept
Carries out hbar.
Declares the two-dimensional FFT utility used by spectral quantum propagators and diagnostics.
void meanPosition(const DiracSpinor2D &s, double &mx, double &my) noexcept
Performs mean position.
Declares the quantumzitterbewegung2d physics/numerical component used by QPE experiments.
Data structure used by the Dirac Energy Fractions2D component.
Configuration values for Quantum Zitterbewegung2D Config.
Data structure used by the Quantum Zitterbewegung Sample2D component.