Loading examples/qsim/TdWorkflowHeisenbergModel.cpp 0 → 100644 +50 −0 Original line number Diff line number Diff line #include "qcor_qsim.hpp" // High-level usage of Model Builder for Heisenberg model. // The Heisenberg Hamiltonian is parameterized using ArQTiC scheme. // Compile and run with: /// $ qcor -qpu qpp TdWorkflowHeisenbergModel.cpp /// $ ./a.out int main(int argc, char **argv) { using ModelType = qcor::qsim::ModelBuilder::ModelType; // Example ArQTiC input: // *Jz // 0.01183898 // *h_ext // 0.01183898 // *initial_spins // 0 0 0 // *freq // 0.0048 // *ext_dir // X // *num_spins // 3 auto problemModel = qsim::ModelBuilder::createModel(ModelType::Heisenberg, {{"Jz", 0.01183898}, {"h_ext", 0.01183898}, {"freq", 0.0048}, {"ext_dir", "X"}, {"num_spins", 3}}); // Workflow parameters: // *delta_t // 3 // *steps // 20 auto workflow = qsim::getWorkflow( "td-evolution", {{"dt", 3.0}, {"steps", 20}}); // Result should contain the observable expectation value along Trotter steps. auto result = workflow->execute(problemModel); // Get the observable values (average magnetization) const auto obsVals = result.get<std::vector<double>>("exp-vals"); // Print out for debugging: for (const auto &val : obsVals) { std::cout << "<Magnetization> = " << val << "\n"; } return 0; } examples/qsim/TrotterTdWorkflow.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ int main(int argc, char **argv) { auto problemModel = qsim::ModelBuilder::createModel(observable, H); // Trotter step = 3fs, number of steps = 100 -> end time = 300fs auto workflow = qsim::getWorkflow( "td-evolution", {{"method", "trotter"}, {"dt", 3.0}, {"steps", 100}}); "td-evolution", {{"dt", 3.0}, {"steps", 100}}); // Result should contain the observable expectation value along Trotter steps. auto result = workflow->execute(problemModel); Loading examples/qsim/VerifiedQuantumPhaseEstimation.cpp +3 −5 Original line number Diff line number Diff line Loading @@ -36,11 +36,9 @@ int main(int argc, char **argv) { // Request the Verified QPE observable evaluator: auto vqpeEvaluator = qsim::getObjEvaluator(observable, "qpe", {{"verified", true}}); auto workflow = qsim::getWorkflow("td-evolution", {{"method", "trotter"}, {"dt", 3.0}, {"steps", 100}, {"evaluator", vqpeEvaluator}}); auto workflow = qsim::getWorkflow( "td-evolution", {{"dt", 3.0}, {"steps", 100}, {"evaluator", vqpeEvaluator}}); // Result should contain the observable expectation value along Trotter steps. auto result = workflow->execute(problemModel); Loading lib/qsim/base/qcor_qsim.cpp +104 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,85 @@ ModelBuilder::createModel(Observable *obs, const HeterogeneousMap ¶ms) { return model; } QuantumSimulationModel ModelBuilder::createModel(ModelType type, const HeterogeneousMap ¶ms) { if (type == ModelType::Heisenberg) { // Static internal HeisenbergModel struct // (keep alive to form time-depenedent Hamiltonian operator) static std::unique_ptr<HeisenbergModel> hs_model; hs_model = std::make_unique<HeisenbergModel>(); hs_model->fromDict(params); if (!hs_model->validateModel()) { qcor::error( "Failed to validate the input parameters for the Heisenberg model."); } QuantumSimulationModel model; // Observable = average magnetization auto observable = new qcor::PauliOperator; for (int i = 0; i < hs_model->num_spins; ++i) { (*observable) += ((1.0/hs_model->num_spins) * Z(i)); } model.observable = observable; qsim::TdObservable H = [&](double t) { qcor::PauliOperator tdOp; for (int i = 0; i < hs_model->num_spins - 1; ++i) { if (hs_model->Jx != 0.0) { tdOp += ((hs_model->Jx / hs_model->H_BAR) * (X(i) * X(i + 1))); } if (hs_model->Jy != 0.0) { tdOp += ((hs_model->Jy / hs_model->H_BAR) * (Y(i) * Y(i + 1))); } if (hs_model->Jz != 0.0) { tdOp += ((hs_model->Jz / hs_model->H_BAR) * (Z(i) * Z(i + 1))); } } std::function<double(double)> time_dep_func; if (hs_model->time_func) { time_dep_func = hs_model->time_func; } else { time_dep_func = [&](double time) { return std::cos(hs_model->freq * time); }; } if (hs_model->h_ext != 0.0) { for (int i = 0; i < hs_model->num_spins; ++i) { if (hs_model->ext_dir == "X") { tdOp += ((hs_model->h_ext / hs_model->H_BAR) * time_dep_func(t) * X(i)); } else if (hs_model->ext_dir == "Y") { tdOp += ((hs_model->h_ext / hs_model->H_BAR) * time_dep_func(t) * Y(i)); } else { tdOp += ((hs_model->h_ext / hs_model->H_BAR) * time_dep_func(t) * Z(i)); } } } return tdOp; }; model.hamiltonian = H; // Non-zero initial spin state: if (std::find(hs_model->initial_spins.begin(), hs_model->initial_spins.end(), 1) != hs_model->initial_spins.end()) { auto initialSpinPrep = qcor::__internal__::create_composite("InitialSpin"); auto provider = qcor::__internal__::get_provider(); for (int i = 0; i < hs_model->initial_spins.size(); ++i) { if (hs_model->initial_spins[i] == 1) { initialSpinPrep->addInstruction(provider->createInstruction("X", i)); } } model.user_defined_ansatz = std::make_shared<KernelFunctor>(initialSpinPrep); } return model; } else { qcor::error("Unknown model type."); __builtin_unreachable(); } } QuantumSimulationModel ModelBuilder::createModel(const std::string &format, const std::string &data, const HeterogeneousMap ¶ms) { Loading Loading @@ -64,5 +143,30 @@ getObjEvaluator(Observable *observable, const std::string &name, // ERROR: unknown CostFunctionEvaluator or invalid initialization options. return nullptr; } void ModelBuilder::HeisenbergModel::fromDict(const HeterogeneousMap ¶ms) { const auto getKeyIfExists = [¶ms](auto &modelVar, const std::string &keyName) { using ValType = typename std::remove_reference_t<decltype(modelVar)>; if (params.keyExists<ValType>(keyName)) { modelVar = params.get<ValType>(keyName); } }; // Handle exact types (int, double) getKeyIfExists(Jx, "Jx"); getKeyIfExists(Jy, "Jy"); getKeyIfExists(Jz, "Jz"); getKeyIfExists(h_ext, "h_ext"); getKeyIfExists(H_BAR, "H_BAR"); getKeyIfExists(num_spins, "num_spins"); getKeyIfExists(initial_spins, "initial_spins"); getKeyIfExists(time_func, "time_func"); getKeyIfExists(freq, "freq"); // Handle string-like if (params.stringExists("ext_dir")) { ext_dir = params.getString("ext_dir"); } } } // namespace qsim } // namespace qcor No newline at end of file lib/qsim/base/qcor_qsim.hpp +45 −1 Original line number Diff line number Diff line Loading @@ -49,6 +49,18 @@ class CostFunctionEvaluator : public Identifiable { public: // Evaluate the cost virtual double evaluate(std::shared_ptr<CompositeInstruction> state_prep) = 0; // Batching evaluation: observing multiple kernels in batches. // E.g. for non-vqe cases (Trotter), we have all kernels ready for observable evaluation virtual std::vector<double> evaluate( std::vector<std::shared_ptr<CompositeInstruction>> state_prep_circuits) { // Default is one-by-one, subclass to provide batching if supported. std::vector<double> result; for (auto &circuit : state_prep_circuits) { result.emplace_back(evaluate(circuit)); } return result; } virtual bool initialize(Observable *observable, const HeterogeneousMap ¶ms = {}); Loading Loading @@ -85,6 +97,35 @@ struct QuantumSimulationModel { // Create a model which capture the problem description. class ModelBuilder { public: // Generic Heisenberg model struct HeisenbergModel { double Jx = 0.0; double Jy = 0.0; double Jz = 0.0; double h_ext = 0.0; // Support for H_BAR normalization double H_BAR = 1.0; // "X", "Y", or "Z" std::string ext_dir = "Z"; int num_spins = 2; std::vector<int> initial_spins; // Time-dependent freq. // Default to using the cosine function. double freq = 0.0; // User-provided custom time-dependent function: std::function<double(double)> time_func; // Allows a simple Pythonic kwarg-style initialization. // i.e. all params have preset defaults, only update those that are // specified. void fromDict(const HeterogeneousMap ¶ms); bool validateModel() const { const bool ext_dir_valid = (ext_dir == "X" || ext_dir == "Y" || ext_dir == "Z"); const bool initial_spins_valid = (initial_spins.empty() || (initial_spins.size() == num_spins)); return ext_dir_valid && initial_spins_valid; } }; // ======== Direct model builder ============== // Strongly-typed parameters/argument. // Build a simple Hamiltonian-based model: static Hamiltonian which is also Loading Loading @@ -121,7 +162,10 @@ public: static QuantumSimulationModel createModel(const std::string &format, const std::string &data, const HeterogeneousMap ¶ms = {}); // Predefined model type that we support intrinsically. enum class ModelType { Heisenberg }; static QuantumSimulationModel createModel(ModelType type, const HeterogeneousMap ¶ms); // ========== QuantumSimulationModel with a fixed (pre-defined) ansatz ======== // The ansatz is provided as a QCOR kernel. template <typename... Args> Loading Loading
examples/qsim/TdWorkflowHeisenbergModel.cpp 0 → 100644 +50 −0 Original line number Diff line number Diff line #include "qcor_qsim.hpp" // High-level usage of Model Builder for Heisenberg model. // The Heisenberg Hamiltonian is parameterized using ArQTiC scheme. // Compile and run with: /// $ qcor -qpu qpp TdWorkflowHeisenbergModel.cpp /// $ ./a.out int main(int argc, char **argv) { using ModelType = qcor::qsim::ModelBuilder::ModelType; // Example ArQTiC input: // *Jz // 0.01183898 // *h_ext // 0.01183898 // *initial_spins // 0 0 0 // *freq // 0.0048 // *ext_dir // X // *num_spins // 3 auto problemModel = qsim::ModelBuilder::createModel(ModelType::Heisenberg, {{"Jz", 0.01183898}, {"h_ext", 0.01183898}, {"freq", 0.0048}, {"ext_dir", "X"}, {"num_spins", 3}}); // Workflow parameters: // *delta_t // 3 // *steps // 20 auto workflow = qsim::getWorkflow( "td-evolution", {{"dt", 3.0}, {"steps", 20}}); // Result should contain the observable expectation value along Trotter steps. auto result = workflow->execute(problemModel); // Get the observable values (average magnetization) const auto obsVals = result.get<std::vector<double>>("exp-vals"); // Print out for debugging: for (const auto &val : obsVals) { std::cout << "<Magnetization> = " << val << "\n"; } return 0; }
examples/qsim/TrotterTdWorkflow.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ int main(int argc, char **argv) { auto problemModel = qsim::ModelBuilder::createModel(observable, H); // Trotter step = 3fs, number of steps = 100 -> end time = 300fs auto workflow = qsim::getWorkflow( "td-evolution", {{"method", "trotter"}, {"dt", 3.0}, {"steps", 100}}); "td-evolution", {{"dt", 3.0}, {"steps", 100}}); // Result should contain the observable expectation value along Trotter steps. auto result = workflow->execute(problemModel); Loading
examples/qsim/VerifiedQuantumPhaseEstimation.cpp +3 −5 Original line number Diff line number Diff line Loading @@ -36,11 +36,9 @@ int main(int argc, char **argv) { // Request the Verified QPE observable evaluator: auto vqpeEvaluator = qsim::getObjEvaluator(observable, "qpe", {{"verified", true}}); auto workflow = qsim::getWorkflow("td-evolution", {{"method", "trotter"}, {"dt", 3.0}, {"steps", 100}, {"evaluator", vqpeEvaluator}}); auto workflow = qsim::getWorkflow( "td-evolution", {{"dt", 3.0}, {"steps", 100}, {"evaluator", vqpeEvaluator}}); // Result should contain the observable expectation value along Trotter steps. auto result = workflow->execute(problemModel); Loading
lib/qsim/base/qcor_qsim.cpp +104 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,85 @@ ModelBuilder::createModel(Observable *obs, const HeterogeneousMap ¶ms) { return model; } QuantumSimulationModel ModelBuilder::createModel(ModelType type, const HeterogeneousMap ¶ms) { if (type == ModelType::Heisenberg) { // Static internal HeisenbergModel struct // (keep alive to form time-depenedent Hamiltonian operator) static std::unique_ptr<HeisenbergModel> hs_model; hs_model = std::make_unique<HeisenbergModel>(); hs_model->fromDict(params); if (!hs_model->validateModel()) { qcor::error( "Failed to validate the input parameters for the Heisenberg model."); } QuantumSimulationModel model; // Observable = average magnetization auto observable = new qcor::PauliOperator; for (int i = 0; i < hs_model->num_spins; ++i) { (*observable) += ((1.0/hs_model->num_spins) * Z(i)); } model.observable = observable; qsim::TdObservable H = [&](double t) { qcor::PauliOperator tdOp; for (int i = 0; i < hs_model->num_spins - 1; ++i) { if (hs_model->Jx != 0.0) { tdOp += ((hs_model->Jx / hs_model->H_BAR) * (X(i) * X(i + 1))); } if (hs_model->Jy != 0.0) { tdOp += ((hs_model->Jy / hs_model->H_BAR) * (Y(i) * Y(i + 1))); } if (hs_model->Jz != 0.0) { tdOp += ((hs_model->Jz / hs_model->H_BAR) * (Z(i) * Z(i + 1))); } } std::function<double(double)> time_dep_func; if (hs_model->time_func) { time_dep_func = hs_model->time_func; } else { time_dep_func = [&](double time) { return std::cos(hs_model->freq * time); }; } if (hs_model->h_ext != 0.0) { for (int i = 0; i < hs_model->num_spins; ++i) { if (hs_model->ext_dir == "X") { tdOp += ((hs_model->h_ext / hs_model->H_BAR) * time_dep_func(t) * X(i)); } else if (hs_model->ext_dir == "Y") { tdOp += ((hs_model->h_ext / hs_model->H_BAR) * time_dep_func(t) * Y(i)); } else { tdOp += ((hs_model->h_ext / hs_model->H_BAR) * time_dep_func(t) * Z(i)); } } } return tdOp; }; model.hamiltonian = H; // Non-zero initial spin state: if (std::find(hs_model->initial_spins.begin(), hs_model->initial_spins.end(), 1) != hs_model->initial_spins.end()) { auto initialSpinPrep = qcor::__internal__::create_composite("InitialSpin"); auto provider = qcor::__internal__::get_provider(); for (int i = 0; i < hs_model->initial_spins.size(); ++i) { if (hs_model->initial_spins[i] == 1) { initialSpinPrep->addInstruction(provider->createInstruction("X", i)); } } model.user_defined_ansatz = std::make_shared<KernelFunctor>(initialSpinPrep); } return model; } else { qcor::error("Unknown model type."); __builtin_unreachable(); } } QuantumSimulationModel ModelBuilder::createModel(const std::string &format, const std::string &data, const HeterogeneousMap ¶ms) { Loading Loading @@ -64,5 +143,30 @@ getObjEvaluator(Observable *observable, const std::string &name, // ERROR: unknown CostFunctionEvaluator or invalid initialization options. return nullptr; } void ModelBuilder::HeisenbergModel::fromDict(const HeterogeneousMap ¶ms) { const auto getKeyIfExists = [¶ms](auto &modelVar, const std::string &keyName) { using ValType = typename std::remove_reference_t<decltype(modelVar)>; if (params.keyExists<ValType>(keyName)) { modelVar = params.get<ValType>(keyName); } }; // Handle exact types (int, double) getKeyIfExists(Jx, "Jx"); getKeyIfExists(Jy, "Jy"); getKeyIfExists(Jz, "Jz"); getKeyIfExists(h_ext, "h_ext"); getKeyIfExists(H_BAR, "H_BAR"); getKeyIfExists(num_spins, "num_spins"); getKeyIfExists(initial_spins, "initial_spins"); getKeyIfExists(time_func, "time_func"); getKeyIfExists(freq, "freq"); // Handle string-like if (params.stringExists("ext_dir")) { ext_dir = params.getString("ext_dir"); } } } // namespace qsim } // namespace qcor No newline at end of file
lib/qsim/base/qcor_qsim.hpp +45 −1 Original line number Diff line number Diff line Loading @@ -49,6 +49,18 @@ class CostFunctionEvaluator : public Identifiable { public: // Evaluate the cost virtual double evaluate(std::shared_ptr<CompositeInstruction> state_prep) = 0; // Batching evaluation: observing multiple kernels in batches. // E.g. for non-vqe cases (Trotter), we have all kernels ready for observable evaluation virtual std::vector<double> evaluate( std::vector<std::shared_ptr<CompositeInstruction>> state_prep_circuits) { // Default is one-by-one, subclass to provide batching if supported. std::vector<double> result; for (auto &circuit : state_prep_circuits) { result.emplace_back(evaluate(circuit)); } return result; } virtual bool initialize(Observable *observable, const HeterogeneousMap ¶ms = {}); Loading Loading @@ -85,6 +97,35 @@ struct QuantumSimulationModel { // Create a model which capture the problem description. class ModelBuilder { public: // Generic Heisenberg model struct HeisenbergModel { double Jx = 0.0; double Jy = 0.0; double Jz = 0.0; double h_ext = 0.0; // Support for H_BAR normalization double H_BAR = 1.0; // "X", "Y", or "Z" std::string ext_dir = "Z"; int num_spins = 2; std::vector<int> initial_spins; // Time-dependent freq. // Default to using the cosine function. double freq = 0.0; // User-provided custom time-dependent function: std::function<double(double)> time_func; // Allows a simple Pythonic kwarg-style initialization. // i.e. all params have preset defaults, only update those that are // specified. void fromDict(const HeterogeneousMap ¶ms); bool validateModel() const { const bool ext_dir_valid = (ext_dir == "X" || ext_dir == "Y" || ext_dir == "Z"); const bool initial_spins_valid = (initial_spins.empty() || (initial_spins.size() == num_spins)); return ext_dir_valid && initial_spins_valid; } }; // ======== Direct model builder ============== // Strongly-typed parameters/argument. // Build a simple Hamiltonian-based model: static Hamiltonian which is also Loading Loading @@ -121,7 +162,10 @@ public: static QuantumSimulationModel createModel(const std::string &format, const std::string &data, const HeterogeneousMap ¶ms = {}); // Predefined model type that we support intrinsically. enum class ModelType { Heisenberg }; static QuantumSimulationModel createModel(ModelType type, const HeterogeneousMap ¶ms); // ========== QuantumSimulationModel with a fixed (pre-defined) ansatz ======== // The ansatz is provided as a QCOR kernel. template <typename... Args> Loading