25constexpr double kPi = 3.141592653589793238462643383279502884;
31bool finite(
double v)
noexcept
33 return std::isfinite(v);
41void fail(std::string *reason,
const char *message)
noexcept
43 if (reason) *reason = message;
53 if (!(period > 0.0))
return 0.0;
54 return x - period * std::floor(x / period + 0.5);
60 std::string *reason)
noexcept
62 if (reason) reason->clear();
63 if (!(config.period > 0.0) || !finite(config.period)) {
64 fail(reason,
"Talbot period must be finite and positive.");
67 if (!(config.toothSigma > 0.0) || !finite(config.toothSigma) ||
68 config.toothSigma >= 0.5 * config.period) {
69 fail(reason,
"Talbot tooth sigma must be positive and smaller than half the period.");
72 if (!(config.sigmaY > 0.0) || !finite(config.sigmaY)) {
73 fail(reason,
"Talbot longitudinal sigma must be finite and positive.");
76 if (!finite(config.offsetX) || !finite(config.centerY) || !finite(config.momentumY)) {
77 fail(reason,
"Talbot initial-state position and momentum values must be finite.");
88 if (reason) reason->clear();
90 fail(reason,
"Talbot initial state requires a non-empty field.");
94 if (!(hbar > 0.0) || !finite(hbar)) {
95 fail(reason,
"Talbot initial state requires positive finite hbar.");
99 const double physicalWidth =
static_cast<double>(field.
width()) * field.
dx();
100 const double periods = physicalWidth / config.
period;
101 const double nearest = std::floor(periods + 0.5);
102 if (nearest < 2.0 || std::fabs(periods - nearest) > 1.0e-5) {
103 fail(reason,
"Talbot comb period must divide the FFT domain width exactly.");
107 const double periodCells = config.
period /
static_cast<double>(field.
dx());
108 const double nearestCells = std::floor(periodCells + 0.5);
109 if (nearestCells < 4.0 || std::fabs(periodCells - nearestCells) > 1.0e-5 ||
110 (
static_cast<long long>(nearestCells) % 2LL) != 0LL) {
111 fail(reason,
"Talbot period must span an even integer number of grid cells.");
116 const double inv4SigmaY2 = 1.0 / (4.0 * config.
sigmaY * config.
sigmaY);
117 const double invHbar = 1.0 / hbar;
119 for (std::size_t y = 0u; y < field.
height(); ++y) {
121 const double dy = py - config.
centerY;
122 const double envelopeY = std::exp(-(dy * dy) * inv4SigmaY2);
123 const double phase = config.
momentumY * py * invHbar;
124 const std::complex<float> phaseY(
125 static_cast<float>(std::cos(phase)),
126 static_cast<float>(std::sin(phase)));
128 for (std::size_t x = 0u; x < field.
width(); ++x) {
130 const double d = wrappedDistance(px - config.
offsetX, config.
period);
131 const double comb = std::exp(-(d * d) * inv4SigmaX2);
132 field(x, y) =
static_cast<float>(comb * envelopeY) * phaseY;
137 fail(reason,
"Talbot initial-state normalization failed.");
145 double hbar)
noexcept
147 if (!(period > 0.0) || !(mass > 0.0) || !(hbar > 0.0) ||
148 !finite(period) || !finite(mass) || !finite(hbar)) {
151 return mass * period * period / (kPi * hbar);
156 std::vector<float> result(field.
width(), 0.0f);
157 if (field.
empty())
return result;
159 const double dy =
static_cast<double>(field.
dy());
160 for (std::size_t x = 0u; x < field.
width(); ++x) {
162 for (std::size_t y = 0u; y < field.
height(); ++y) {
164 sum +=
static_cast<double>(value.real()) * value.real() +
165 static_cast<double>(value.imag()) * value.imag();
167 result[x] =
static_cast<float>(sum * dy);
173 const std::vector<float> &b,
176 if (a.empty() || a.size() != b.size() || !(dx > 0.0) || !finite(dx))
return 0.0;
178 double numerator = 0.0;
181 for (std::size_t i = 0u; i < a.size(); ++i) {
182 const double av = std::max(0.0,
static_cast<double>(a[i]));
183 const double bv = std::max(0.0,
static_cast<double>(b[i]));
184 numerator += av * bv;
188 if (!(normA > 0.0) || !(normB > 0.0))
return 0.0;
189 const double value = numerator / std::sqrt(normA * normB);
190 return std::max(0.0, std::min(1.0, value));
197 if (profile.empty() || !(period > 0.0) || !(dx > 0.0))
return std::vector<float>();
198 const double shiftExact = 0.5 * period / dx;
199 const long long shift =
static_cast<long long>(std::floor(shiftExact + 0.5));
200 if (std::fabs(shiftExact -
static_cast<double>(shift)) > 1.0e-5) {
201 return std::vector<float>();
204 std::vector<float> result(profile.size(), 0.0f);
205 const long long n =
static_cast<long long>(profile.size());
206 for (
long long i = 0; i < n; ++i) {
207 long long source = (i - shift) % n;
208 if (source < 0) source += n;
209 result[
static_cast<std::size_t
>(i)] = profile[
static_cast<std::size_t
>(source)];
215 : maximumSamples_(std::max<std::size_t>(1u, maximumSamples))
262 std::fabs(field.
dy() -
dy_) > 1.0e-9f || !finite(time)) {
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.
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 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 bool initialize(ComplexField2D &field, const QuantumTalbotInitialState2DConfig &config, double hbar=1.0, std::string *reason=nullptr)
Initializes ialize.
static double talbotTime(double period, double mass, double hbar=1.0) noexcept
Performs talbot time.
static double profileCorrelation(const std::vector< float > &a, const std::vector< float > &b, double dx=1.0) noexcept
Performs profile correlation.
static std::vector< float > marginalProbabilityX(const ComplexField2D &field)
Performs marginal probability x.
static bool validateConfig(const QuantumTalbotInitialState2DConfig &config, std::string *reason=nullptr) noexcept
Validates config.
static std::vector< float > circularShiftHalfPeriod(const std::vector< float > &profile, double period, double dx)
Performs circular shift half period.
const QuantumTalbotInitialState2DConfig & config() const noexcept
Returns config.
bool captureReference(const ComplexField2D &field, const QuantumTalbotInitialState2DConfig &config)
Carries out capture reference.
std::vector< QuantumTalbotSample2D > samples_
void trimToLimit()
Carries out trim to limit.
std::vector< float > halfShiftReference_
std::vector< float > referenceMarginal_
QuantumTalbotHistory2D(std::size_t maximumSamples=1000u)
Constructs a Quantum Talbot History 2D instance.
bool append(double time, const ComplexField2D &field)
Carries out append.
void clear() noexcept
Clears stored state and returns the component to its empty/default state.
void setMaximumSamples(std::size_t maximumSamples)
Sets maximum samples.
std::size_t maximumSamples() const noexcept
Carries out maximum samples.
bool hasReference() const noexcept
Returns whether reference.
QuantumTalbotInitialState2DConfig config_
std::size_t maximumSamples_
Declares the gaussianpacket2d physics/numerical component used by QPE experiments.
double wrappedDistance(double x, double period) noexcept
Performs wrapped distance.
Declares the quantumtalbot2d physics/numerical component used by QPE experiments.
Configuration values for Quantum Talbot Initial State2D Config.
Data structure used by the Quantum Talbot Sample2D component.