5#include <unordered_map>
17 const auto seed =
immediate_->GetConfiguration(
"sampling_seed");
40 void Configure(
const char *key,
const char *value)
override
42 const std::string name(key), setting(value);
46 const auto seed = name ==
"seed" ? std::optional<uint64_t>(std::stoull(setting)) : std::nullopt;
47 if (name ==
"gate_fusion")
49 if (setting !=
"true" && setting !=
"false" && setting !=
"1" && setting !=
"0" && setting !=
"auto")
50 throw std::invalid_argument(
"gate_fusion must be true, false, 1, 0 or auto");
52 if (setting ==
"auto")
55 requested_ = setting ==
"true" || setting ==
"1";
71 if (std::string(key) ==
"gate_fusion")
72 return RequestedSetting();
76 const std::unordered_map<std::string, std::string> &
GetConfigMap()
const override
79 config_[
"gate_fusion"] = RequestedSetting();
92 cache_.Flush([
this](
const auto &block) { Emit(block); });
113 static const std::vector<Operation> empty;
118 if (!planValid_ && sourceIndex_ != 0)
122 if ((!routingPlan_ || routingPlan_->routing.empty()) && !sources_.empty() &&
IsGateFusionEnabled() &&
125 return routingPlan_ ? routingPlan_->routing : empty;
136 throw std::invalid_argument(
"Negative circuit position");
137 if (counter == sourceIndex_)
145 if (counter != sourceIndex_ + 1 || !AtBoundary())
147 sourceIndex_ = counter;
179 routingPlan_.reset();
180 backendRoutingInstalled_ =
false;
182 destructiveSaved_.reset();
195 saved_ = Context{sourceIndex_, sources_};
209 sourceIndex_ = saved_->source;
210 sources_ = saved_->operations;
220 destructiveSaved_ = Context{sourceIndex_, sources_};
225 if (destructiveSaved_)
229 immediate_->RestoreInternalDestructiveSavedState();
230 if (destructiveSaved_)
232 sourceIndex_ = destructiveSaved_->source;
233 sources_ = destructiveSaved_->operations;
234 destructiveSaved_.reset();
276 const auto *adapter =
dynamic_cast<const FusionState *
>(&other);
279 adapter->Synchronize();
280 return immediate_->DensityMatrixOverlap(*adapter->immediate_);
282 return immediate_->DensityMatrixOverlap(other);
288 return immediate_->GetCurrentMaxBondDimension();
293 return immediate_->GetCurrentMaxBondDimension();
328 return immediate_->SupportsMPSSwapOptimization();
338 return immediate_->IsRoutingLookaheadEnabled();
395 return immediate_->ExpectationValueOperators(qubits, matrices);
413 return immediate_->SampleCountsMany(qs, shots);
451 return immediate_->DensityMatrixElementBits(row, col);
457 return immediate_->GetDensityMatrix(normalized);
463 return immediate_->ExpectationValueComplex(pauli, normalized);
466 std::vector<std::complex<double>>
ExpectationValuesComplex(
const std::vector<std::string> &paulis,
bool normalized =
true)
const override
471 return immediate_->ExpectationValuesComplex(paulis, normalized);
477 immediate_->ApplyOperator(qubits, matrix, normalize);
506 return immediate_->DensityMatrixTraceOfSquare();
512 return immediate_->DensityMatrixHermiticityResidual();
518 return immediate_->IsDensityMatrixHermitian(eps);
530 return immediate_->FidelityWithStatevector(psi);
571 UpdateRoutingSetting([&] {
immediate_->SetUseOptimalMeetingPosition(value); });
576 UpdateRoutingSetting([&] {
immediate_->SetLookaheadDepth(value); });
581 UpdateRoutingSetting([&] {
immediate_->SetAutomaticLookaheadDepth(value); });
586 UpdateRoutingSetting([&] {
immediate_->SetLookaheadDepthWithHeuristic(value); });
616 FinishInitialization(n);
628 FinishInitialization(n);
640 FinishInitialization(n);
652 FinishInitialization(n);
664 FinishInitialization(n);
671 immediate_->InitializeToMixtureOfBasisStates(n, value);
675 immediate_->InitializeToMixtureOfBasisStates(n, value);
676 FinishInitialization(n);
683 immediate_->InitializeToMixtureOfBasisStates(n, value);
687 immediate_->InitializeToMixtureOfBasisStates(n, value);
688 FinishInitialization(n);
720 using Operation = std::shared_ptr<Circuits::IOperation<double>>;
727 copy.requested_ = requested_;
728 copy.stats_ = stats_;
729 copy.ready_ = ready_;
730 copy.sources_ = sources_;
731 copy.sourceIndex_ = sourceIndex_;
732 copy.saved_ = saved_;
733 copy.destructiveSaved_ = destructiveSaved_;
735 const auto seed = copy.
immediate_->GetConfiguration(
"seed");
742 const bool cpuChain =
746 copy.routingPlan_ = routingPlan_;
747 copy.planIndex_ = planIndex_;
748 copy.planValid_ = planValid_;
749 copy.backendRoutingInstalled_ = backendRoutingInstalled_;
761 ++stats_.submittedGates;
762 ++stats_.backendGates;
768 throw std::logic_error(
"Initialize the simulator before applying gates");
770 std::sort(qs.begin(), qs.end());
771 if (qs.empty() || qs.back() >=
GetNumberOfQubits() || std::adjacent_find(qs.begin(), qs.end()) != qs.end())
772 throw std::invalid_argument(
"Invalid or duplicate gate target");
773 for (
auto p : gate.
params)
774 if (!std::isfinite(p))
775 throw std::invalid_argument(
"Non-finite gate parameter");
776 if (gate.
kind == FusionGate::Kind::kNone && !gate.
generic.allFinite())
777 throw std::invalid_argument(
"Non-finite gate matrix");
778 ++stats_.submittedGates;
780 if (gate.
kind == FusionGate::Kind::kNone && gate.
qubits.size() > width)
785 ++stats_.backendGates;
791 if (gate.
kind == FusionGate::Kind::kNone &&
797 ++stats_.backendGates;
804 const auto index =
static_cast<size_t>(sourceIndex_);
805 const auto boundary = routingPlan_->boundaries.find(index);
811 immediate_->SetGatesCounter(
static_cast<long long>(boundary->second));
813 ++stats_.backendGates;
818 const auto original = index < sources_.size() ? std::dynamic_pointer_cast<Circuits::IQuantumGate<>>(sources_[index]) :
nullptr;
822 const auto source =
static_cast<uint64_t
>(sourceIndex_++);
824 cache_.SetWidth(width);
825 const auto emit = [
this](
const auto &block) { Emit(block); };
826 if (gate.
qubits.size() <= width)
827 SubmitToCache(cache_, gate, source << 4, emit);
830 const auto primitives = gate.
Expand(width);
831 for (
size_t i = 0; i < primitives.size(); ++i)
832 SubmitToCache(cache_, primitives[i], (source << 4) | i, emit);
839 if (!AtBoundary() || sources_[sourceIndex_]->
GetType() != type)
847 template <
class Setter>
void UpdateRoutingSetting(Setter &&setter)
851 std::forward<Setter>(setter)();
858 const bool cpuChain =
860 if (!cpuChain || sourceIndex_ != 0 || oldLookahead != newLookahead || (newLookahead && !planValid_))
864 template <
class EmitBlock>
void SubmitToCache(
Cache &cache,
const FusionGate &gate, uint64_t source, EmitBlock &&emit)
873 std::vector<Operation> operations;
879 Cache::Sources sources;
885 std::vector<Operation> routing;
886 std::vector<Planned> planned;
888 std::unordered_map<size_t, size_t> boundaries;
891 void Synchronize()
const
896 bool AtBoundary()
const
898 return sourceIndex_ >= 0 &&
static_cast<size_t>(sourceIndex_) < sources_.size() && sources_[sourceIndex_]->GetType() !=
Circuits::OperationType::kGate;
901 void FinishInitialization(
size_t n,
bool invalidateSnapshots =
true)
905 const auto seed =
immediate_->GetConfiguration(
"sampling_seed");
910 if (invalidateSnapshots)
913 destructiveSaved_.reset();
916 backendRoutingInstalled_ =
false;
923 routingPlan_.reset();
927 void InvalidatePlan()
930 routingPlan_.reset();
934 backendRoutingInstalled_ =
false;
940 routingPlan_.reset();
945 if (!sources_.empty() || backendRoutingInstalled_)
947 backendRoutingInstalled_ = !sources_.empty();
953 else if (backendRoutingInstalled_)
956 backendRoutingInstalled_ =
false;
963 routingPlan_.reset();
964 auto prepared = std::make_shared<RoutingPlan>();
968 auto emit = [&prepared](
const auto &block) {
969 prepared->planned.push_back({block.targets, block.sources, prepared->routing.size()});
971 prepared->routing.push_back(std::make_shared<FusionRoutingOperation>(block.targets, block.schmidtRank, swap));
973 for (
size_t i =
static_cast<size_t>(sourceIndex_); i < sources_.size(); ++i)
975 const auto gate = std::dynamic_pointer_cast<Circuits::IQuantumGate<>>(sources_[i]);
979 for (
size_t j = 0; j < primitives.size(); ++j)
980 SubmitToCache(planner, primitives[j], (uint64_t(i) << 4) | j, emit);
985 prepared->boundaries[i] = prepared->routing.size();
986 prepared->routing.push_back(sources_[i]);
990 routingPlan_ = prepared;
991 planValid_ = installRouting;
994 immediate_->SetUpcomingGates(prepared->routing);
995 backendRoutingInstalled_ = !prepared->routing.empty();
999 void Emit(
const Cache::Block &block)
1003 if (planIndex_ >= routingPlan_->planned.size() || routingPlan_->planned[planIndex_].sources != block.sources ||
1004 routingPlan_->planned[planIndex_].targets != block.targets)
1007 immediate_->SetGatesCounter(routingPlan_->planned[planIndex_].routingIndex);
1009 if (block.IsSingle())
1011 else if (block.targets.size() == 1)
1012 immediate_->ApplyGenericOneQubitGate(block.targets[0], block.matrix);
1013 else if (block.targets.size() == 2)
1014 immediate_->ApplyGenericTwoQubitGate(block.targets[0], block.targets[1], block.matrix);
1016 immediate_->ApplyGenericThreeQubitGate(block.targets[0], block.targets[1], block.targets[2], block.matrix);
1017 ++stats_.backendGates;
1018 if (!block.IsSingle())
1019 ++stats_.fusedBlocks;
1024 GateFusionStatistics stats_;
1026 const char *RequestedSetting()
const
1028 return !requested_ ?
"auto" : *requested_ ?
"true" :
"false";
1031 std::optional<bool> requested_;
1032 bool ready_ =
false;
1034 long long sourceIndex_ = 0;
1035 std::vector<Operation> sources_;
1038 std::shared_ptr<const RoutingPlan> routingPlan_;
1039 size_t planIndex_ = 0;
1040 bool planValid_ =
false;
1041 bool backendRoutingInstalled_ =
false;
1042 std::optional<Context> saved_, destructiveSaved_;
1043 mutable std::unordered_map<std::string, std::string> config_;
void SetWidth(unsigned width)
Interface class for a quantum computing simulator.
virtual unsigned GetGateFusionMaxQubits() const
Interface class for a quantum computing simulator state.
void SeedAuxiliaryRng(uint64_t seed)
Shared by SetSeed and backend Configure("seed") implementations.
void NotifyObservers(const Types::qubits_vector &affectedQubits)
Notifies observers.
void Configure(const char *key, const char *value) override
Configures the state.
bool SupportsMPSSwapOptimization() const override
Returns if the simulator supports MPS swap optimization.
void InitializeToBasisState(size_t n, Types::qubit_t value) override
Initializes the state to a computational basis state.
SimulationType GetSimulationType() const override
Returns the type of simulation.
void Trim() override
Compress a matrix-product state or operator to its configured limits.
SimulatorType GetType() const override
Returns the type of simulator.
std::vector< bool > MeasureNoCollapseMany() override
Measures all the qubits without collapsing the state.
void InitializeState(size_t n, AER::Vector< std::complex< double > > &value) override
Initializes the state.
void SetLookaheadDepth(int value) override
Sets the lookahead depth for swap optimization.
void SetGatesCounter(long long counter) override
void SaveStateToInternalDestructive() override
Saves the state to internal storage.
std::complex< double > ExpectationValueComplex(const std::string &pauli, bool normalized=true) const override
virtual bool UsesDestructiveStateStorage() const
std::complex< double > DensityMatrixOverlap(const IState &other) const override
std::shared_ptr< ISimulator > immediate_
void FlushPendingGates() override
bool IsQcsim() const override
Returns if the simulator is a qcsim simulator.
void SetSeed(uint64_t seed) override
Seed every random stream owned by this simulator.
virtual bool InitializationPreservesSnapshots() const
std::complex< double > Amplitude(Types::qubit_t outcome) override
Returns the amplitude of the specified state.
const std::unordered_map< std::string, std::string > & GetConfigMap() const override
void Clear() override
Clears the state.
void MoveAtBeginningOfChain(const Types::qubits_vector &qubits) override
double DensityMatrixPurity() const override
virtual bool PreserveStructuredGates() const
void SetInitialQubitsMap(const std::vector< long long > &value) override
bool SwapGatesRelabelQubits() const override
std::shared_ptr< Circuits::IOperation< double > > Operation
double getGrowthFactorSwap() const override
double Probability(Types::qubit_t outcome) override
Returns the probability of the specified outcome.
void setGrowthFactorSwap(double value) override
Eigen::MatrixXcd PartialTrace(const Types::qubits_vector &qs) const override
void InitializeState(size_t n, std::vector< std::complex< double > > &value) override
Initializes the state.
double ExpectationValue(const std::string &pauli) override
Returns the expected value of a Pauli string.
void InitializeToMixtureOfBasisStates(size_t n, const std::vector< std::pair< Types::qubit_t, double > > &value) override
Initializes the state to a classical mixture of computational basis states.
void ConsumeBoundary(Circuits::OperationType type)
void SetMultithreading(bool value=true) override
Enable/disable multithreading.
size_t GetNumberOfQubits() const override
Returns the number of qubits.
void ApplyReset(const Types::qubits_vector &qs) override
Performs a reset of the specified qubits.
std::vector< bool > MeasureMany(const Types::qubits_vector &qs) override
Performs a measurement on the specified qubits.
std::complex< double > DensityMatrixTrace() const override
Mixed-state diagnostics.
bool SupportsQuantumChannels() const override
Returns whether this state retains channel ensembles directly.
void SetLookaheadDepthWithHeuristic(int value) override
Sets the lookahead depth for swap optimization.
std::complex< double > DensityMatrixTraceOfSquare() const override
GateFusion< FusionGate > Cache
const std::vector< std::shared_ptr< Circuits::IOperation< double > > > & GetUpcomingRoutingOperations() const override
void RestoreInternalDestructiveSavedState() override
Restores the state from the internally saved state.
std::string GetConfiguration(const char *key) const override
Returns configuration value.
void SaveState() override
Saves the state to internal storage.
virtual size_t DefaultGateFusionMinQubits() const
bool IsGateFusionEnabled() const override
double getGrowthFactorGate() const override
std::complex< double > DensityMatrixElementBits(const std::vector< bool > &row, const std::vector< bool > &col) const override
std::vector< double > ExpectationValues(const std::vector< std::string > &paulis) override
void Initialize() override
Initializes the state.
void SetUseOptimalMeetingPosition(bool value) override
Enables or disables optimal meeting position for MPS swaps.
std::complex< double > AmplitudeRaw(Types::qubit_t outcome) override
Gets the amplitude.
double DensityMatrixHermiticityResidual() const override
std::complex< double > ExpectationValueOperators(const Types::qubits_vector &qubits, const std::vector< Eigen::MatrixXcd > &matrices) override
void ApplyQuantumChannel(const Types::qubits_vector &qs, const QuantumChannel &channel) override
Applies a local CPTP quantum channel to one or two qubits.
void HermitizeDensityMatrix() override
double FidelityWithStatevector(const Eigen::VectorXcd &psi) const override
size_t AllocateQubits(size_t n) override
Allocates qubits.
std::vector< std::complex< double > > GetStateVector() override
void SubmitGate(const FusionGate &gate)
void ReCanonicalize() override
Restore the canonical form of a matrix-product state or operator.
void RestoreDensityMatrixTrace() override
int GetGpuDevice() const override
std::vector< std::complex< double > > ExpectationValuesComplex(const std::vector< std::string > &paulis, bool normalized=true) const override
void RestoreState() override
Restores the state from the internally saved state.
bool IsRoutingLookaheadEnabled() const override
std::unordered_map< std::vector< bool >, Types::qubit_t > SampleCountsMany(const Types::qubits_vector &qs, size_t shots=1000) override
Returns the counts of the outcomes of measurement of the specified qubits, for repeated measurements.
double ProbabilityBits(const std::vector< bool > &bits) override
MPO extensions: bit vectors are q0-first; matrices use logical basis order.
GateFusionStatistics GetGateFusionStatistics() const override
void Reset() override
Just resets the state to 0.
Types::qubit_t MeasureNoCollapse() override
Measures all the qubits without collapsing the state.
std::vector< double > Probabilities(const Types::qubits_vector &qs) override
Returns the probabilities of the specified outcomes.
void ApplyOperator(const Types::qubits_vector &qubits, const Eigen::MatrixXcd &matrix, bool normalize=false) override
size_t GetExecutedMaxBondDimension() const override
void InitializeToMixtureOfBasisStates(size_t n, const std::vector< std::pair< std::vector< bool >, double > > &value) override
Initializes the state to a classical mixture of computational basis states.
void SetAutomaticLookaheadDepth(int value) override
std::vector< double > AllProbabilities() override
Returns the probabilities of all possible outcomes.
void Flush() override
Flushes the applied operations.
std::complex< double > ProjectOnZero() override
Projects the state onto the zero state.
Eigen::MatrixXcd GetDensityMatrix(bool normalized=true) const override
bool IsDensityMatrixHermitian(double eps=1e-10) const override
std::unordered_map< Types::qubit_t, Types::qubit_t > SampleCounts(const Types::qubits_vector &qs, size_t shots=1000) override
Returns the counts of the outcomes of measurement of the specified qubits, for repeated measurements.
FusionState(std::shared_ptr< ISimulator > immediate)
void IncrementGatesCounter() override
Increments the gates counter.
void InitializeToBasisState(size_t n, const std::vector< bool > &value) override
Initializes the state to a computational basis state.
void InitializeState(size_t n, Eigen::VectorXcd &value) override
Initializes the state.
long long GetGatesCounter() const override
Returns the gates counter.
void SetUpcomingGates(const std::vector< std::shared_ptr< Circuits::IOperation< double > > > &gates) override
Supplies upcoming gates for lookahead swap optimization.
void CloneInto(FusionState ©, const uint64_t *executionSeed=nullptr)
size_t GetCurrentMaxBondDimension() const override
Returns the maximum bond dimension reached.
void setGrowthFactorGate(double value) override
bool GetMultithreading() const override
Get the multithreading flag.
size_t Measure(const Types::qubits_vector &qs) override
Performs a measurement on the specified qubits.
A local completely-positive, trace-preserving map in Kraus form.
OperationType
The type of operations.
@ kConditionalGate
conditional gate, similar with gate, but conditioned on something from 'OperationState'
@ kMeasurement
measurement, result in 'OperationState'
@ kGate
the usual quantum gate, result stays in simulator's state
@ kReset
reset, no result in 'state', just apply measurement, then apply not on all qubits that were measured ...
@ kQuantumChannel
a non-unitary CPTP operation on the simulator state
SimulationType
The type of simulation.
@ kMatrixProductState
matrix product state simulation type
@ kMatrixProductOperator
Matrix product operator simulation type.
SimulatorType
The type of simulator.
@ kQCSim
qcsim simulator type
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
uint_fast64_t qubit_t
The type of a qubit.
void Apply(ISimulator &sim) const
std::vector< FusionGate > Expand(unsigned width) const
bool IsStructured() const
Types::qubits_vector qubits
static FusionGate FromCircuit(const Circuits::IQuantumGate<> &gate)
std::array< double, 4 > params