17#include <unordered_map>
30 explicit GpuMPO(
const std::shared_ptr<GpuLibrary> &lib,
int device = -1) : lib(lib), obj(
nullptr)
34 auto lock = lib->LockInitialization();
35 if (lib->SetGpuDevice(device == -1 ? lib->GetCreationDevice() : device))
36 obj = lib->CreateMPO();
40 int GetGpuDevice()
const
42 return lib ? lib->MPOGetGpuId(obj) : -1;
45 GpuMPO(
const std::shared_ptr<GpuLibrary> &lib,
void *obj) : lib(lib), obj(obj)
50 GpuMPO(
const GpuMPO &) =
delete;
51 GpuMPO &operator=(
const GpuMPO &) =
delete;
59 bool Create(
unsigned int n)
61 return lib->MPOCreate(obj, n);
64 bool CreateWithState(
unsigned int n,
const double *state)
66 return lib->MPOCreateWithState(obj, n, state);
69 bool CreateWithBasisState(
unsigned int n,
unsigned long long state)
71 return lib->MPOCreateWithBasisState(obj, n, state);
74 bool CreateWithBasisStateBits(
unsigned int n,
const std::vector<unsigned char> &stateBits)
76 return lib->MPOCreateWithBasisStateBits(obj, n, stateBits.data());
79 bool CreateWithMixtureOfBasisStates(
unsigned int n,
const std::vector<std::pair<unsigned long long, double>> &mixture)
81 std::vector<unsigned long long> states;
82 std::vector<double> weights;
83 states.reserve(mixture.size());
84 weights.reserve(mixture.size());
85 for (
const auto &[state, weight] : mixture)
87 states.push_back(state);
88 weights.push_back(weight);
90 return lib->MPOCreateWithMixtureOfBasisStates(obj, n, states.data(), weights.data(),
static_cast<int>(states.size()));
93 bool CreateWithMixtureOfBasisStatesBits(
unsigned int n,
const std::vector<unsigned char> &stateBitsFlat,
const std::vector<double> &weights)
95 return lib->MPOCreateWithMixtureOfBasisStatesBits(obj, n, stateBitsFlat.data(), weights.data(),
static_cast<int>(weights.size()));
100 if (!lib->MPOReset(obj))
101 throw std::runtime_error(
"GPU matrix-product-operator reset failed");
104 bool SetSeed(uint64_t seed)
106 return lib->MPOSetSeed(obj, seed);
109 bool SetInitialQubitsMap(
const std::vector<long long int> &initialMap)
111 return lib->MPOSetInitialQubitsMap(obj, initialMap);
114 bool SetUseOptimalMeetingPosition(
bool useOptimalMeetingPosition)
116 return lib->MPOSetUseOptimalMeetingPosition(obj, useOptimalMeetingPosition);
119 bool GetUseOptimalMeetingPosition()
const
121 return lib->MPOGetUseOptimalMeetingPosition(obj);
124 std::vector<long long> GetQubitsMap()
const
126 std::vector<long long> map(lib->MPOGetNrQubits(obj));
127 if (!lib->MPOGetQubitsMap(obj, map.data(),
static_cast<int>(map.size())))
132 bool SetCallbackContext(
void *context)
134 return lib->MPOSetCallbackContext(obj, context);
137 bool SetMeetingPositionCallback(int64_t (*callback)(
void *,
const int64_t *))
139 return lib->MPOSetMeetingPositionCallback(obj, callback);
142 bool SetBondDimensionsCallback(
void (*callback)(
void *,
const int64_t *))
144 return lib->MPOSetBondDimensionsCallback(obj, callback);
147 void InstallBondSummary(
void (*summary)(
void *, int64_t),
void (*full)(
void *,
const int64_t *))
149 const bool ok = lib->HasMPOBondSummary() ? lib->MPOSetBondDimensionSummaryCallback(obj, summary) : SetBondDimensionsCallback(full);
151 throw std::runtime_error(
"GPU MPO bond callback installation failed");
154 std::vector<double> ExpectationValues(
const std::vector<std::string> &paulis)
const
156 TensorQueries::PauliBatch batch(paulis);
157 std::vector<double> values(paulis.size());
160 if (lib->HasMPOExpectationValues())
162 if (!lib->MPOExpectationValues(obj, TensorQueries::Count(paulis.size()), batch.strings.data(), batch.lengths.data(), values.data()))
163 throw std::runtime_error(
"GPU MPO batch expectation failed");
167 for (
size_t i = 0; i < paulis.size(); ++i)
168 values[i] = ExpectationValueComplex(paulis[i]).real();
173 std::vector<std::complex<double>> ExpectationValuesComplex(
const std::vector<std::string> &paulis,
bool normalized =
true)
const
175 TensorQueries::PauliBatch batch(paulis);
176 std::vector<std::complex<double>> values(paulis.size());
179 if (lib->HasMPOExpectationValuesComplex())
181 std::vector<double> re(paulis.size()), im(paulis.size());
182 if (!lib->MPOExpectationValuesComplex(obj, TensorQueries::Count(paulis.size()), batch.strings.data(), batch.lengths.data(), normalized, re.data(),
184 throw std::runtime_error(
"GPU MPO complex batch expectation failed");
185 for (
size_t i = 0; i < paulis.size(); ++i)
186 values[i] = {re[i], im[i]};
190 for (
size_t i = 0; i < paulis.size(); ++i)
191 values[i] = ExpectationValueComplex(paulis[i], normalized);
196 bool IsCreated()
const
198 return lib->MPOIsCreated(obj);
201 bool IsDoublePrecision()
const
203 return lib->MPOIsDoublePrecision(obj);
206 void SetDataType(
bool useDouble)
208 if (!lib->MPOSetDataType(obj, useDouble))
209 throw std::runtime_error(
"GPU matrix-product-operator precision configuration failed");
212 void SetCutoff(
double singularValueThreshold)
214 if (!lib->MPOSetCutoff(obj, singularValueThreshold))
215 throw std::runtime_error(
"GPU MPO cutoff configuration failed");
218 double GetCutoff()
const
220 return lib->MPOGetCutoff(obj);
225 bool SetTruncationMode(
int mode)
227 return lib->MPOSetTruncationMode(obj, mode);
230 int GetTruncationMode()
const
232 return lib->MPOGetTruncationMode(obj);
235 bool SetGesvdJ(
bool enable)
237 return lib->MPOSetGesvdJ(obj, enable);
240 bool GetGesvdJ()
const
242 return lib->MPOGetGesvdJ(obj);
246 bool SetGesvdP(
bool enable)
248 return obj && lib->MPOSetGesvdP(obj, enable);
251 bool GetGesvdP()
const
253 return lib->MPOGetGesvdP(obj);
256 bool SetGesvdR(
bool enable)
258 return obj && lib->MPOSetGesvdR(obj, enable);
261 bool GetGesvdR()
const
263 return lib->MPOGetGesvdR(obj);
266 int GetLastSvdAlgo()
const
268 return lib->MPOGetLastSvdAlgo(obj);
271 void SetMaxExtent(
long int chi)
273 if (!lib->MPOSetMaxExtent(obj, chi))
274 throw std::runtime_error(
"GPU MPO bond cap configuration failed");
277 long int GetMaxExtent()
const
279 return lib->MPOGetMaxExtent(obj);
282 std::vector<long long int> GetBondDimensions(
size_t nrQubits)
const
286 std::vector<long long int> bondDims(nrQubits - 1);
287 if (!lib->MPOGetBondDimensions(obj, bondDims.data()))
292 void ReCanonicalize()
294 if (!lib->MPOReCanonicalize(obj))
295 throw std::runtime_error(
"GPU MPO canonicalization failed");
298 void Trim(
double cutoff = -1.,
long int maxExtent = -1)
300 if (!lib->MPOTrim(obj, cutoff, maxExtent))
301 throw std::runtime_error(
"GPU MPO trim failed");
306 return lib->MPOMeasureQubitCollapse(obj, q);
311 return lib->MPOMeasureQubitNoCollapse(obj, q);
314 bool MeasureQubits(std::vector<int> &qubits, std::vector<int> &bits,
bool collapse =
true)
316 if (qubits.size() != bits.size())
317 throw std::invalid_argument(
"Measurement vectors must have equal size");
318 return collapse ? lib->MPOMeasureQubitsCollapse(obj, qubits.data(), bits.data(),
static_cast<int>(bits.size()))
319 : lib->MPOMeasureQubitsNoCollapse(obj, qubits.data(), bits.data(),
static_cast<int>(bits.size()));
322 unsigned long long MeasureAll(
bool collapse =
true)
324 return collapse ? lib->MPOMeasureAllQubitsCollapse(obj) : lib->MPOMeasureAllQubitsNoCollapse(obj);
327 bool Sample(
unsigned int nSamples,
long int *samples,
unsigned int nBits,
int *bitOrdering)
329 return lib->MPOSample(obj, nSamples, samples, nBits, bitOrdering);
332 bool SampleAll(
unsigned int shots,
long int *samples)
334 return lib->MPOSampleAll(obj, shots, samples);
339 return lib->MPOBasisStateProbability(obj, outcome);
342 std::complex<double> GetElement(
long long row,
long long col)
const
344 double re = 0., im = 0.;
345 if (!lib->MPOGetElement(obj, row, col, &re, &im))
346 throw std::runtime_error(
"GPU MPO element query failed");
352 if (!lib->MPOAllProbabilities(obj, probabilities))
353 throw std::runtime_error(
"GPU matrix-product-operator probability enumeration failed");
356 double ExpectationValue(
const std::string &pauli)
const
358 return lib->MPOExpectationValue(obj, pauli.c_str(), pauli.size());
361 double QubitProbability0(
unsigned int q)
const
363 return lib->MPOQubitProbability0(obj, q);
368 return lib->MPOTrace(obj);
371 double Purity()
const
373 return lib->MPOPurity(obj);
376 double HermiticityResidual()
const
378 return lib->MPOHermiticityResidual(obj);
381 bool IsHermitian(
double eps = 1e-10)
const
383 return lib->MPOIsHermitian(obj, eps);
386 double TraceOfSquare()
const
388 return lib->MPOTraceOfSquare(obj);
393 if (!lib->MPORestoreTrace(obj))
394 throw std::runtime_error(
"GPU MPO trace restoration failed");
399 if (!lib->MPOHermitize(obj))
400 throw std::runtime_error(
"GPU MPO hermitization failed");
403 bool SetKrausCompletenessCheck(
int mode)
405 return lib->MPOSetKrausCompletenessCheck(obj, mode);
408 int GetKrausCompletenessCheck()
const
410 return lib->MPOGetKrausCompletenessCheck(obj);
413 std::vector<std::complex<double>> PartialTrace(
const std::vector<int> &qubits)
const
415 if (qubits.size() > 13)
416 throw std::invalid_argument(
"GPU MPO partial trace supports at most 13 qubits");
417 const size_t dim =
size_t{1} << qubits.size();
418 std::vector<double> raw(2 * dim * dim);
419 if (!lib->MPOPartialTrace(obj, qubits.data(),
static_cast<int>(qubits.size()), raw.data()))
420 throw std::runtime_error(
"GPU MPO partial trace failed");
421 std::vector<std::complex<double>> result(dim * dim);
422 for (
size_t i = 0; i < result.size(); ++i)
423 result[i] = {raw[2 * i], raw[2 * i + 1]};
427 std::complex<double> HilbertSchmidtOverlap(
const GpuMPO &other)
const
429 double re = 0., im = 0.;
430 if (!lib->MPOHilbertSchmidtOverlap(obj, other.obj, &re, &im))
431 throw std::runtime_error(
"GPU MPO overlap failed");
435 double FidelityWithStatevector(
const double *state)
const
438 if (!lib->MPOFidelityWithStatevector(obj, state, &result))
439 throw std::runtime_error(
"GPU MPO fidelity failed");
445 double ProbabilityBits(
const std::vector<unsigned char> &bits)
const
448 const double probability = lib->MPOBasisStateProbabilityBits(obj, bits.data());
449 if (!std::isfinite(probability))
450 throw std::runtime_error(
"GPU MPO probability requires a safely positive trace");
454 std::complex<double> GetElementBits(
const std::vector<unsigned char> &row,
const std::vector<unsigned char> &col)
const
458 double re = 0., im = 0.;
459 if (!lib->MPOGetElementBits(obj, row.data(), col.data(), &re, &im))
460 throw std::runtime_error(
"GPU MPO element query failed");
464 std::complex<double> TraceComplex()
const
466 double re = 0., im = 0.;
467 if (!lib->MPOTraceComplex(obj, &re, &im))
468 throw std::runtime_error(
"GPU MPO complex trace failed");
472 std::complex<double> ExpectationValueComplex(
const std::string &pauli,
bool normalized =
true)
const
474 double re = 0., im = 0.;
475 const bool ok = normalized ? lib->MPOExpectationValueComplex(obj, pauli.c_str(), pauli.size(), &re, &im)
476 : lib->MPOUnnormalizedExpectationValue(obj, pauli.c_str(), pauli.size(), &re, &im);
478 throw std::runtime_error(
"GPU MPO complex expectation failed");
482 std::vector<std::complex<double>> GetDensityMatrix(
bool normalized =
true)
const
484 const int n = lib->MPOGetNrQubits(obj);
486 throw std::invalid_argument(
"GPU MPO dense output supports at most 13 qubits");
487 const size_t count =
size_t{1} << (2 * n);
488 std::vector<double> raw(2 * count);
489 const bool ok = normalized ? lib->MPOGetDensityMatrix(obj, raw.data()) : lib->MPOGetUnnormalizedDensityMatrix(obj, raw.data());
491 throw std::runtime_error(
"GPU MPO density matrix query failed");
492 std::vector<std::complex<double>> result(count);
493 for (
size_t i = 0; i < count; ++i)
494 result[i] = {raw[2 * i], raw[2 * i + 1]};
498 bool SetRestoreTraceAfterTruncation(
bool enable)
500 return lib->MPOSetRestoreTraceAfterTruncation(obj, enable) == 1;
503 bool GetRestoreTraceAfterTruncation()
const
505 return lib->MPOGetRestoreTraceAfterTruncation(obj) == 1;
508 bool SetHermitizeAfterTruncation(
bool enable)
510 return lib->MPOSetHermitizeAfterTruncation(obj, enable) == 1;
513 bool GetHermitizeAfterTruncation()
const
515 return lib->MPOGetHermitizeAfterTruncation(obj) == 1;
518 void MoveAtBeginningOfChain(
const std::vector<int> &qubits)
520 if (!lib->MPOMoveAtBeginningOfChain(obj, qubits.data(), qubits.size()))
521 throw std::runtime_error(
"GPU MPO chain routing failed");
524 bool ApplyOperator(
const std::vector<int> &qubits,
const double *matrix,
bool normalize =
false)
526 return normalize ? lib->MPOApplyOperatorAndNormalize(obj, qubits.size(), qubits.data(), matrix) == 1
527 : lib->MPOApplyOperator(obj, qubits.size(), qubits.data(), matrix) == 1;
530 std::vector<unsigned char> SampleBits(
unsigned int shots,
const std::vector<int> &qubits)
532 if (!shots || qubits.empty())
534 if (qubits.size() > std::numeric_limits<unsigned int>::max() || qubits.size() > std::numeric_limits<size_t>::max() / shots)
535 throw std::length_error(
"GPU MPO sample buffer too large");
536 std::vector<unsigned char> result(
size_t(shots) * qubits.size());
537 if (!lib->MPOSampleBits(obj, shots, qubits.size(), qubits.data(), result.data()))
538 throw std::runtime_error(
"GPU MPO bit sampling failed");
542 std::unordered_map<std::vector<bool>, int64_t> SampleHistogram(
size_t shots, std::vector<unsigned int> qubits)
544 if (!shots || qubits.empty())
546 if (shots > std::numeric_limits<unsigned int>::max() || shots >
static_cast<size_t>(std::numeric_limits<long int>::max()) ||
547 qubits.size() >
static_cast<size_t>(std::numeric_limits<int>::max()))
548 throw std::invalid_argument(
"GPU MPO sampling size exceeds API limits");
549 using Map = std::unordered_map<std::vector<bool>, int64_t>;
550 auto deleter = [
this](Map *map) { lib->MPOFreeMapForSample(map); };
551 std::unique_ptr<Map,
decltype(deleter)> map(
static_cast<Map *
>(lib->MPOGetMapForSample()), deleter);
552 if (!map || !lib->MPOSampleHistogram(obj, shots, qubits.size(), qubits.data(), map.get()))
553 throw std::runtime_error(
"GPU MPO histogram sampling failed");
559 if (!lib->MPOSaveState(obj))
560 throw std::runtime_error(
"GPU matrix-product-operator state save failed");
565 if (!lib->MPORestoreState(obj))
566 throw std::runtime_error(
"GPU matrix-product-operator state restore failed");
569 void CleanSavedState()
571 if (!lib->MPOCleanSavedState(obj))
572 throw std::runtime_error(
"GPU matrix-product-operator saved-state cleanup failed");
575 std::unique_ptr<GpuMPO> Clone()
const
577 void *cloned = lib->MPOClone(obj);
580 return std::make_unique<GpuMPO>(lib, cloned);
583 bool ApplyKraus(
const std::vector<int> &qubits,
int count,
const double *operators)
585 return lib->MPOApplyKraus(obj, qubits.size(), qubits.data(), count, operators);
588 bool ApplyOneQubitMatrix(
int qubit,
const double *matrixInterleaved)
590 return lib->MPOApplyOneQubitMatrix(obj, qubit, matrixInterleaved);
593 bool ApplyTwoQubitMatrix(
int qubit1,
int qubit2,
const double *matrixInterleaved)
595 return lib->MPOApplyTwoQubitMatrix(obj, qubit1, qubit2, matrixInterleaved);
598#define GPU_MPO_CHECK(call, name) \
602 throw std::runtime_error("GPU matrix-product-operator " name " failed"); \
604#define GPU_MPO_GATE1(name) \
607 GPU_MPO_CHECK(lib->MPO##name(obj, q), #name); \
609#define GPU_MPO_GATE2(name) \
610 void name(int a, int b) \
612 GPU_MPO_CHECK(lib->MPO##name(obj, a, b), #name); \
614#define GPU_MPO_ROT1(name) \
615 void name(int q, double x) \
617 GPU_MPO_CHECK(lib->MPO##name(obj, q, x), #name); \
619#define GPU_MPO_ROT2(name) \
620 void name(int a, int b, double x) \
622 GPU_MPO_CHECK(lib->MPO##name(obj, a, b, x), #name); \
626 GPU_MPO_ROT1(ApplyBitFlipNoise)
628 GPU_MPO_ROT1(ApplyPhaseFlipNoise)
630 GPU_MPO_ROT1(ApplyDepolarizingNoise)
632 GPU_MPO_ROT1(ApplyAmplitudeDamping)
634 GPU_MPO_ROT1(ApplyPhaseDamping)
636 GPU_MPO_GATE1(ApplyNonSelectiveMeasurement)
662 GPU_MPO_CHECK(lib->MPOApplyU(obj, q, a, b, c, d),
"ApplyU");
683 GPU_MPO_CHECK(lib->MPOApplyCU(obj, a, b, c, d, e, f),
"ApplyCU");
693 void ValidateBits(
const std::vector<unsigned char> &bits)
const
695 if (bits.size() !=
static_cast<size_t>(lib->MPOGetNrQubits(obj)))
696 throw std::invalid_argument(
"GPU MPO bit vector width " + std::to_string(bits.size()) +
" does not match register width " +
697 std::to_string(lib->MPOGetNrQubits(obj)));
700 GpuDeviceContext lib;
int ApplyK(void *sim, int qubit)
double Probability(void *sim, unsigned long long int outcome)
int RestoreState(void *sim)
int ApplyRx(void *sim, int qubit, double theta)
int ApplyReset(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyX(void *sim, int qubit)
int ApplyU(void *sim, int qubit, double theta, double phi, double lambda, double gamma)
int ApplyCRy(void *sim, int controlQubit, int targetQubit, double theta)
int ApplyTDG(void *sim, int qubit)
int ApplyCSXDG(void *sim, int controlQubit, int targetQubit)
int ApplyS(void *sim, int qubit)
int ApplyCX(void *sim, int controlQubit, int targetQubit)
int ApplyCRz(void *sim, int controlQubit, int targetQubit, double theta)
double * AllProbabilities(void *sim)
unsigned long long int MeasureNoCollapse(void *sim)
int ApplyCP(void *sim, int controlQubit, int targetQubit, double theta)
int ApplySXDG(void *sim, int qubit)
int ApplySDG(void *sim, int qubit)
unsigned long long int Measure(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyY(void *sim, int qubit)
int ApplyZ(void *sim, int qubit)
int ApplyH(void *sim, int qubit)
int ApplyCY(void *sim, int controlQubit, int targetQubit)
int ApplyCU(void *sim, int controlQubit, int targetQubit, double theta, double phi, double lambda, double gamma)
int ApplySwap(void *sim, int qubit1, int qubit2)
int ApplyRy(void *sim, int qubit, double theta)
int ApplyP(void *sim, int qubit, double theta)
int ApplyCH(void *sim, int controlQubit, int targetQubit)
int ApplySX(void *sim, int qubit)
int ApplyCZ(void *sim, int controlQubit, int targetQubit)
int ApplyRz(void *sim, int qubit, double theta)
int ApplyT(void *sim, int qubit)
int ApplyCRx(void *sim, int controlQubit, int targetQubit, double theta)
int ApplyCSX(void *sim, int controlQubit, int targetQubit)
Bounded online routing based on current physical bond dimensions.