26const double Pi = 3.1415926535897932384626433832795;
31 std::complex<double>
a;
32 std::complex<double>
b;
37 std::complex<double>
aa;
38 std::complex<double>
ab;
39 std::complex<double>
ba;
40 std::complex<double>
bb;
47 double coupling = 0.0;
58bool finite(
double v)
noexcept
60 return std::isfinite(v);
71 return spectrum.valid && a >= 0 && b >= 0 && a != b &&
72 a < static_cast<int>(spectrum.states.size()) &&
73 b < static_cast<int>(spectrum.states.size());
94 const double mean = 0.5 * (energyA + energyB);
95 const double hz = 0.5 * (energyA - energyB);
96 const double hx = coupling * driveValue;
97 const double radius = std::sqrt(hx * hx + hz * hz);
98 const std::complex<double> global =
99 std::exp(std::complex<double>(0.0, -mean * dt / hbar));
100 if (!(radius > 1.0e-15)) {
102 out.
a = global * state.a;
103 out.
b = global * state.b;
106 const double angle = radius * dt / hbar;
107 const double c = std::cos(angle);
108 const double s = std::sin(angle) / radius;
109 const std::complex<double> minusI(0.0, -1.0);
111 out.
a = global * (c * state.a + minusI * s * (hz * state.a + hx * state.b));
112 out.
b = global * (c * state.b + minusI * s * (hx * state.a - hz * state.b));
124 out.
a = op.aa * state.a + op.ab * state.b;
125 out.
b = op.ba * state.a + op.bb * state.b;
147 double maximumStep)
noexcept
151 columnA.
a = std::complex<double>(1.0, 0.0);
152 columnA.
b = std::complex<double>(0.0, 0.0);
154 columnB.
a = std::complex<double>(0.0, 0.0);
155 columnB.
b = std::complex<double>(1.0, 0.0);
157 if (!(duration > 0.0)) {
165 const int steps = std::max(80,
static_cast<int>(std::ceil(duration / maximumStep)));
166 const double dt = duration /
static_cast<double>(steps);
167 const double phase0 = phaseDegrees * Pi / 180.0;
168 for (
int i = 0; i < steps; ++i) {
169 const double localTime = (
static_cast<double>(i) + 0.5) * dt;
170 const double phase = sequence.angularFrequency * localTime + phase0;
171 const double drive = sequence.meanValue + sequence.amplitude * std::sin(phase);
172 columnA =
propagateConstant(columnA, energyA, energyB, coupling, drive, dt, hbar);
173 columnB =
propagateConstant(columnB, energyA, energyB, coupling, drive, dt, hbar);
199 double transitionOffset,
201 double maximumPulseStep)
noexcept
204 const double baseA = spectrum.states[
static_cast<std::size_t
>(stateA)].energy;
205 const double baseB = spectrum.states[
static_cast<std::size_t
>(stateB)].energy;
207 const double halfShift = 0.5 * hbar * transitionOffset;
208 member.
energyA = baseA - halfShift;
209 member.
energyB = baseB + halfShift;
213 sequence, sequence.pulseDuration,
214 sequence.firstPulsePhaseDegrees,
215 hbar, maximumPulseStep);
217 sequence, sequence.pulseDuration,
218 sequence.secondPulsePhaseDegrees,
219 hbar, maximumPulseStep);
220 if (sequence.echoEnabled) {
222 sequence, sequence.echoPulseDuration,
223 sequence.echoPulsePhaseDegrees,
224 hbar, maximumPulseStep);
240 double freeEvolutionDuration,
243 double &populationB)
noexcept
246 state.
a = std::complex<double>(1.0, 0.0);
247 state.
b = std::complex<double>(0.0, 0.0);
250 if (sequence.echoEnabled) {
251 const double halfFree = 0.5 * freeEvolutionDuration;
252 if (halfFree > 0.0) {
254 member.coupling, sequence.meanValue,
258 if (halfFree > 0.0) {
260 member.coupling, sequence.meanValue,
263 }
else if (freeEvolutionDuration > 0.0) {
265 member.coupling, sequence.meanValue,
266 freeEvolutionDuration, hbar);
270 populationA = std::norm(state.
a);
271 populationB = std::norm(state.
b);
280 return sequence.enabled &&
282 finite(sequence.meanValue) && finite(sequence.amplitude) &&
283 finite(sequence.angularFrequency) && sequence.angularFrequency > 0.0 &&
284 finite(sequence.pulseDuration) && sequence.pulseDuration > 0.0 &&
285 finite(sequence.freeEvolutionDuration) && sequence.freeEvolutionDuration >= 0.0 &&
286 finite(sequence.echoPulseDuration) &&
287 (!sequence.echoEnabled || sequence.echoPulseDuration > 0.0) &&
288 finite(sequence.firstPulsePhaseDegrees) &&
289 finite(sequence.secondPulsePhaseDegrees) &&
290 finite(sequence.echoPulsePhaseDegrees);
300 std::vector<PreparedMember> members;
307 members.reserve(
static_cast<std::size_t
>(count));
308 double totalWeight = 0.0;
309 for (
int i = 0; i < count; ++i) {
313 static_cast<double>(i) /
static_cast<double>(count - 1);
315 const double weight = std::exp(-0.5 * z * z);
319 members.push_back(
prepareMember(spectrum, sequence, stateA, stateB, hbar,
320 offset, weight, 0.01));
321 totalWeight += weight;
323 if (!(totalWeight > 0.0)) {
328 member.weight /= totalWeight;
346 double freeEvolutionDuration,
351 double *maximumB)
noexcept
355 double minB = std::numeric_limits<double>::infinity();
356 double maxB = -std::numeric_limits<double>::infinity();
361 meanA += member.weight * a;
362 meanB += member.weight * b;
363 minB = std::min(minB, b);
364 maxB = std::max(maxB, b);
366 if (minimumB) *minimumB = std::isfinite(minB) ? minB : 0.0;
367 if (maximumB) *maximumB = std::isfinite(maxB) ? maxB : 0.0;
377 double hbar)
noexcept
380 if (!validPair(spectrum, stateA, stateB) || !(hbar > 0.0) ||
381 !sequenceValidForReference(sequence)) {
387 result.
energyA = spectrum.states[
static_cast<std::size_t
>(stateA)].energy;
388 result.
energyB = spectrum.states[
static_cast<std::size_t
>(stateB)].energy;
398 result.
firstPulseEnd = sequence.startTime + sequence.pulseDuration;
400 if (sequence.echoEnabled) {
412 const PreparedMember nominal = prepareMember(spectrum, sequence, stateA, stateB,
413 hbar, 0.0, 1.0, 0.0025);
414 predictPrepared(nominal, sequence, sequence.freeEvolutionDuration, hbar,
433 double minimumFreeTime,
434 double maximumFreeTime,
440 std::vector<RamseyFringeSample2D> result;
441 if (!validPair(spectrum, stateA, stateB) || !sequenceValidForReference(sequence) ||
442 !finite(minimumFreeTime) || !finite(maximumFreeTime) ||
443 minimumFreeTime < 0.0 || !(maximumFreeTime > minimumFreeTime) ||
444 sampleCount < 2 || !(hbar > 0.0)) {
448 const double omega12 = std::abs(
449 spectrum.
states[
static_cast<std::size_t
>(stateB)].energy -
450 spectrum.
states[
static_cast<std::size_t
>(stateA)].energy) / hbar;
451 const PreparedMember nominal = prepareMember(spectrum, sequence, stateA, stateB,
452 hbar, 0.0, 1.0, 0.0025);
453 result.reserve(
static_cast<std::size_t
>(sampleCount));
454 for (
int i = 0; i < sampleCount; ++i) {
455 const double u =
static_cast<double>(i) /
static_cast<double>(sampleCount - 1);
457 sample.freeEvolutionDuration =
458 minimumFreeTime + (maximumFreeTime - minimumFreeTime) * u;
459 sample.phaseRadians = omega12 * sample.freeEvolutionDuration;
460 predictPrepared(nominal, sequence, sample.freeEvolutionDuration, hbar,
461 sample.populationA, sample.populationB);
462 result.push_back(sample);
470 return finite(config.detuningStdDev) && config.detuningStdDev >= 0.0 &&
471 config.detuningStdDev <= 10.0 && config.memberCount >= 3 &&
472 config.memberCount <= 201;
485 !validPair(spectrum, stateA, stateB) || !sequenceValidForReference(sequence) ||
490 const std::vector<PreparedMember> members =
491 prepareEnsemble(spectrum, sequence, ensemble, stateA, stateB, hbar);
492 if (members.empty())
return result;
499 ?
static_cast<int>(members.size()) : 1;
504std::vector<RamseyEnsembleFringeSample2D>
509 double minimumFreeTime,
510 double maximumFreeTime,
516 std::vector<RamseyEnsembleFringeSample2D> result;
518 !validPair(spectrum, stateA, stateB) || !sequenceValidForReference(sequence) ||
519 !finite(minimumFreeTime) || !finite(maximumFreeTime) ||
520 minimumFreeTime < 0.0 || !(maximumFreeTime > minimumFreeTime) ||
521 sampleCount < 2 || !(hbar > 0.0)) {
525 const std::vector<PreparedMember> members =
526 prepareEnsemble(spectrum, sequence, ensemble, stateA, stateB, hbar);
527 if (members.empty())
return result;
529 result.reserve(
static_cast<std::size_t
>(sampleCount));
530 for (
int i = 0; i < sampleCount; ++i) {
531 const double u =
static_cast<double>(i) /
static_cast<double>(sampleCount - 1);
533 sample.freeEvolutionDuration =
534 minimumFreeTime + (maximumFreeTime - minimumFreeTime) * u;
535 averagePrepared(members, sequence, sample.freeEvolutionDuration, hbar,
536 sample.meanPopulationA, sample.meanPopulationB,
538 result.push_back(sample);
Declares the boundstates1d physics/numerical component used by QPE experiments.
Owning Cartesian grid of complex scalar samples.
static double matrixElementX(const BoundStateSpectrum1D &spectrum, int stateA=0, int stateB=1) noexcept
Performs matrix element x.
static DrivenTwoLevelProjection2D project(const ComplexField2D &field, const BoundStateSpectrum1D &spectrum, int stateA=0, int stateB=1)
Performs project.
static DrivenTwoLevelProjection2D project(const ComplexField2D &field, const BoundStateSpectrum1D &spectrum, int stateA=0, int stateB=1)
Performs project.
static bool validateEnsembleConfig(const RamseyEnsemble2DConfig &config) noexcept
Validates ensemble config.
static RamseyTheory2D analyze(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, int stateA=0, int stateB=1, double hbar=1.0) noexcept
Performs analyze.
static RamseyEnsembleResult2D analyzeStaticDetuningEnsemble(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, const RamseyEnsemble2DConfig &ensemble, int stateA=0, int stateB=1, double hbar=1.0)
Performs analyze static detuning ensemble.
static std::vector< RamseyFringeSample2D > scanFreeEvolution(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, double minimumFreeTime, double maximumFreeTime, int sampleCount, int stateA=0, int stateB=1, double hbar=1.0)
Performs scan free evolution.
static std::vector< RamseyEnsembleFringeSample2D > scanFreeEvolutionEnsemble(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, const RamseyEnsemble2DConfig &ensemble, double minimumFreeTime, double maximumFreeTime, int sampleCount, int stateA=0, int stateB=1, double hbar=1.0)
Performs scan free evolution ensemble.
State2 applyOperator(const Operator2 &op, const State2 &state) noexcept
Applies operator.
bool sequenceValidForReference(const RamseyPulseSequence2DConfig &sequence) noexcept
Performs sequence valid for reference.
void averagePrepared(const std::vector< PreparedMember > &members, const RamseyPulseSequence2DConfig &sequence, double freeEvolutionDuration, double hbar, double &meanA, double &meanB, double *minimumB, double *maximumB) noexcept
Performs average prepared.
void predictPrepared(const PreparedMember &member, const RamseyPulseSequence2DConfig &sequence, double freeEvolutionDuration, double hbar, double &populationA, double &populationB) noexcept
Performs predict prepared.
Operator2 pulseOperator(double energyA, double energyB, double coupling, const RamseyPulseSequence2DConfig &sequence, double duration, double phaseDegrees, double hbar, double maximumStep) noexcept
Performs pulse operator.
std::vector< PreparedMember > prepareEnsemble(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, const RamseyEnsemble2DConfig &ensemble, int stateA, int stateB, double hbar)
PreparedMember prepareMember(const BoundStateSpectrum1D &spectrum, const RamseyPulseSequence2DConfig &sequence, int stateA, int stateB, double hbar, double transitionOffset, double weight, double maximumPulseStep) noexcept
Prepares member.
State2 propagateConstant(const State2 &state, double energyA, double energyB, double coupling, double driveValue, double dt, double hbar) noexcept
Performs propagate constant.
const double EnsembleSpanSigma
Declares the ramseyinterferometry2d physics/numerical component used by QPE experiments.
Data structure used by the Bound State Spectrum1D component.
std::vector< BoundState1D > states
Data structure used by the Driven Two Level Projection2D component.
Configuration values for Ramsey Ensemble2D Config.
Data structure used by the Ramsey Ensemble Fringe Sample2D component.
Data structure used by the Ramsey Ensemble Result2D component.
double maximumPopulationB
double minimumPopulationB
Data structure used by the Ramsey Fringe Sample2D component.
Configuration values for Ramsey Pulse Sequence2D Config.
double freeEvolutionDuration
Data structure used by the Ramsey Theory2D component.
double couplingMatrixElementX
double predictedFinalPopulationB
double driveAngularFrequency
double predictedFinalPopulationA
double transitionAngularFrequency
double idealPiOver2Duration
double resonantRabiAngularFrequency
std::complex< double > bb
std::complex< double > aa
std::complex< double > ba
std::complex< double > ab
Declares the timedependentpotential2d physics/numerical component used by QPE experiments.