Quantum Phenomena Explorer 7.9.8
Interactive quantum-phenomena Explorer Tutor — source documentation
Loading...
Searching...
No Matches
quantumloschmidt2d.cpp
Go to the documentation of this file.
1/**
2 * @file quantumloschmidt2d.cpp
3 * @brief Implements the quantumloschmidt2d physics/numerical component used by QPE experiments.
4 * @ingroup qpe_quantum
5 *
6 * Source: @c core/quantum/quantumloschmidt2d.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 "quantumloschmidt2d.h"
13
15
16#include <algorithm>
17#include <cmath>
18#include <complex>
19
20namespace qpe {
21namespace {
22
23/**
24 * @brief Performs finite.
25 * @param value Value to assign or evaluate.
26 */
27bool finite(double value) noexcept
28{
29 return std::isfinite(value);
30}
31
32/**
33 * @brief Performs fail.
34 * @param reason The reason value used by the operation.
35 * @param message The message value used by the operation.
36 */
37void fail(const char **reason, const char *message) noexcept
38{
39 if (reason) *reason = message;
40}
41
42/**
43 * @brief Performs same grid.
44 * @param a The a value used by the operation.
45 * @param b The b value used by the operation.
46 */
47bool sameGrid(const ComplexField2D &a, const ComplexField2D &b) noexcept
48{
49 return !a.empty() && !b.empty() &&
50 a.width() == b.width() && a.height() == b.height() &&
51 a.dx() == b.dx() && a.dy() == b.dy();
52}
53
54} // namespace
55
57 const char **reason) noexcept
58{
59 if (!config.enabled) return true;
60 if (!(config.reversalTime > 0.0) || !finite(config.reversalTime)) {
61 fail(reason, "Loschmidt reversal time must be finite and positive.");
62 return false;
63 }
64 if (!finite(config.momentumErrorX) || !finite(config.momentumErrorY)) {
65 fail(reason, "Loschmidt momentum-error values must be finite.");
66 return false;
67 }
68 if (std::fabs(config.momentumErrorX) > 1000.0 ||
69 std::fabs(config.momentumErrorY) > 1000.0) {
70 fail(reason, "Loschmidt momentum-error values are unreasonably large.");
71 return false;
72 }
73 return true;
74}
75
77 const QuantumLoschmidt2DConfig &config,
78 double hbar) noexcept
79{
80 if (field.empty() || !(hbar > 0.0) || !finite(hbar) ||
81 !validateConfig(config, nullptr)) {
82 return false;
83 }
84
85 const double invHbar = 1.0 / hbar;
86 for (std::size_t y = 0; y < field.height(); ++y) {
87 const double py = GaussianPacket2D::yCoordinate(field, y);
88 for (std::size_t x = 0; x < field.width(); ++x) {
89 const double px = GaussianPacket2D::xCoordinate(field, x);
90 const std::complex<float> reversed = std::conj(field(x, y));
91 const double phase =
92 (config.momentumErrorX * px + config.momentumErrorY * py) * invHbar;
93 const std::complex<float> kick(
94 static_cast<float>(std::cos(phase)),
95 static_cast<float>(std::sin(phase)));
96 field(x, y) = reversed * kick;
97 }
98 }
99 return field.normalize();
100}
101
103 const ComplexField2D &field) noexcept
104{
105 if (!sameGrid(reference, field)) return 0.0;
106
107 // Target = conj(reference). Therefore conj(target) = reference in the
108 // inner product <target|field>.
109 std::complex<double> overlap(0.0, 0.0);
110 double normReference = 0.0;
111 double normField = 0.0;
112 const double cell = static_cast<double>(field.dx()) * static_cast<double>(field.dy());
113
114 for (std::size_t i = 0; i < field.size(); ++i) {
115 const std::complex<double> a(reference.data()[i].real(), reference.data()[i].imag());
116 const std::complex<double> b(field.data()[i].real(), field.data()[i].imag());
117 overlap += a * b;
118 normReference += std::norm(a);
119 normField += std::norm(b);
120 }
121 normReference *= cell;
122 normField *= cell;
123 overlap *= cell;
124 const double denom = normReference * normField;
125 if (!(denom > 0.0) || !finite(denom)) return 0.0;
126 const double f = std::norm(overlap) / denom;
127 return std::max(0.0, std::min(1.0, f));
128}
129
131 const ComplexField2D &field) noexcept
132{
133 if (!sameGrid(reference, field)) return 0.0;
134
135 double bc = 0.0;
136 double normReference = 0.0;
137 double normField = 0.0;
138 const double cell = static_cast<double>(field.dx()) * static_cast<double>(field.dy());
139 for (std::size_t i = 0; i < field.size(); ++i) {
140 const double pa = static_cast<double>(std::norm(reference.data()[i]));
141 const double pb = static_cast<double>(std::norm(field.data()[i]));
142 bc += std::sqrt(std::max(0.0, pa * pb));
143 normReference += pa;
144 normField += pb;
145 }
146 bc *= cell;
147 normReference *= cell;
148 normField *= cell;
149 const double denom = normReference * normField;
150 if (!(denom > 0.0) || !finite(denom)) return 0.0;
151 const double f = (bc * bc) / denom;
152 return std::max(0.0, std::min(1.0, f));
153}
154
156 double &meanX,
157 double &meanY) noexcept
158{
159 meanX = 0.0;
160 meanY = 0.0;
161 if (field.empty()) return false;
162
163 double weight = 0.0;
164 for (std::size_t y = 0; y < field.height(); ++y) {
165 const double py = GaussianPacket2D::yCoordinate(field, y);
166 for (std::size_t x = 0; x < field.width(); ++x) {
167 const double px = GaussianPacket2D::xCoordinate(field, x);
168 const double p = static_cast<double>(std::norm(field(x, y)));
169 weight += p;
170 meanX += p * px;
171 meanY += p * py;
172 }
173 }
174 if (!(weight > 0.0) || !finite(weight)) {
175 meanX = 0.0;
176 meanY = 0.0;
177 return false;
178 }
179 meanX /= weight;
180 meanY /= weight;
181 return finite(meanX) && finite(meanY);
182}
183
185 : maximumSamples_(std::max<std::size_t>(2u, maximumSamples))
186{
187}
188
190{
191 configured_ = false;
194 samples_.clear();
195}
196
198 const QuantumLoschmidt2DConfig &config)
199{
200 clear();
201 if (reference.empty() || !QuantumLoschmidtEcho2D::validateConfig(config, nullptr)) {
202 return false;
203 }
204 reference_ = reference;
205 config_ = config;
207 return configured_;
208}
209
211 const ComplexField2D &field,
212 bool reversalApplied)
213{
214 if (!configured_ || !sameGrid(reference_, field) || !finite(time)) return false;
215
217 sample.time = time;
218 sample.densityEcho = QuantumLoschmidtEcho2D::densityEchoFidelity(reference_, field);
219 sample.stateEcho = QuantumLoschmidtEcho2D::stateEchoFidelity(reference_, field);
220 sample.norm = field.norm2();
221 sample.reversalApplied = reversalApplied;
222 QuantumLoschmidtEcho2D::centroid(field, sample.meanX, sample.meanY);
223
224 // Keep the plot speed-independent: store on a simulation-time cadence
225 // derived from the nominal 0..2T echo window, plus the reversal event.
226 const double totalWindow = std::max(1.0e-9, 2.0 * config_.reversalTime);
227 const double storeInterval = totalWindow /
228 static_cast<double>(std::max<std::size_t>(1u, maximumSamples_ - 1u));
229 const bool first = samples_.empty();
230 const bool reversalEdge = reversalApplied &&
231 (samples_.empty() || !samples_.back().reversalApplied);
232 const bool intervalElapsed = samples_.empty() ||
233 time - samples_.back().time >= std::max(1.0e-9, storeInterval) - 1.0e-12;
234 if (first || reversalEdge || intervalElapsed) {
235 samples_.push_back(sample);
236 trimToLimit();
237 }
238 return true;
239}
240
242{
243 if (samples_.size() <= maximumSamples_) return;
244 const std::size_t excess = samples_.size() - maximumSamples_;
245 samples_.erase(samples_.begin(), samples_.begin() + static_cast<std::ptrdiff_t>(excess));
246}
247
248} // namespace qpe
Owning Cartesian grid of complex scalar samples.
double norm2() const noexcept
Returns the discrete integral of |psi|^2 including dx*dy.
bool empty() const noexcept
Returns whether the field contains no samples.
static double yCoordinate(const ComplexField2D &field, std::size_t y) noexcept
Performs y coordinate.
static double xCoordinate(const ComplexField2D &field, std::size_t x) noexcept
Performs x coordinate.
static double stateEchoFidelity(const ComplexField2D &reference, const ComplexField2D &field) noexcept
Performs state echo fidelity.
static bool validateConfig(const QuantumLoschmidt2DConfig &config, const char **reason=nullptr) noexcept
Validates config.
static bool centroid(const ComplexField2D &field, double &meanX, double &meanY) noexcept
Performs centroid.
static bool applyTimeReversal(ComplexField2D &field, const QuantumLoschmidt2DConfig &config, double hbar) noexcept
Applies time reversal.
static double densityEchoFidelity(const ComplexField2D &reference, const ComplexField2D &field) noexcept
Performs density echo fidelity.
bool append(double time, const ComplexField2D &field, bool reversalApplied)
Carries out append.
std::vector< QuantumLoschmidtSample2D > samples_
bool configure(const ComplexField2D &reference, const QuantumLoschmidt2DConfig &config)
Returns configure.
QuantumLoschmidt2DConfig config_
QuantumLoschmidtHistory2D(std::size_t maximumSamples=1600u)
Constructs a Quantum Loschmidt History 2D instance.
const QuantumLoschmidt2DConfig & config() const noexcept
Returns config.
void trimToLimit()
Carries out trim to limit.
void clear() noexcept
Clears stored state and returns the component to its empty/default state.
Declares the gaussianpacket2d physics/numerical component used by QPE experiments.
Declares the quantumloschmidt2d physics/numerical component used by QPE experiments.
Configuration values for Quantum Loschmidt2D Config.
Data structure used by the Quantum Loschmidt Sample2D component.