Loading lib/impl/quantum_phase_estimation.hpp +1 −1 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ __qpu__ void pauli_power_trotter_evolution(qreg q, /// omega: the feedback angle. /// pauli_ham: hamiltonian /// num_time_slices: number of trotter steps /// measure: if true, add measure gate on ancilla qubit. /// measure: if true (!= 0), add measure gate on ancilla qubit. __qpu__ void pauli_qpe_iter(qreg q, int k, double omega, qcor::PauliOperator pauli_ham, int num_time_slices, int measure) { Loading lib/qsim/impls/qsim_impl.cpp +102 −4 Original line number Diff line number Diff line Loading @@ -101,7 +101,6 @@ VqeWorkflow::execute(const QuatumSimulationModel &model) { if (model.user_defined_ansatz) { auto nParams = model.user_defined_ansatz->nParams(); evaluator = getObjEvaluator(model.observable); auto qpu = xacc::internal_compiler::get_qpu(); OptFunction f( [&](const std::vector<double> &x, std::vector<double> &dx) { Loading Loading @@ -135,13 +134,112 @@ bool IterativeQpeWorkflow::initialize(const HeterogeneousMap ¶ms) { return (num_steps >= 1) && (num_iters >= 1); } std::shared_ptr<CompositeInstruction> IterativeQpeWorkflow::constructQpeCircuit(const QuatumSimulationModel &model, int k, double omega, bool measure) const { auto provider = xacc::getIRProvider("quantum"); auto kernel = provider->createComposite("__TEMP__QPE__LOOP__"); const auto nbQubits = model.observable->nBits(); const size_t ancBit = nbQubits; // Hadamard on ancilla qubit kernel->addInstruction(provider->createInstruction("H", ancBit)); // Using Trotter evolution method to generate U: // TODO: support other methods (e.g. Suzuki) TrotterEvolution method; const double trotterStepSize = -2 * M_PI / num_steps; auto trotterCir = method.create_ansatz(model.observable, {{"dt", trotterStepSize}}).circuit; // std::cout << "Trotter circ:\n" << trotterCir->toString() << "\n"; // Controlled-U auto ctrlKernel = std::dynamic_pointer_cast<CompositeInstruction>( xacc::getService<xacc::Instruction>("C-U")); ctrlKernel->expand({ std::make_pair("U", trotterCir), std::make_pair("control-idx", static_cast<int>(ancBit)), }); // Apply C-U^n int power = 1 << (k - 1); for (int i = 0; i < power; ++i) { for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { // We need to clone the instruction since it'll be repeated. kernel->addInstruction(ctrlKernel->getInstruction(instId)->clone()); } } // Rz on ancilla qubit kernel->addInstruction(provider->createInstruction("Rz", {ancBit}, {omega})); // Hadamard on ancilla qubit kernel->addInstruction(provider->createInstruction("H", ancBit)); if (measure) { kernel->addInstruction(provider->createInstruction("Measure", ancBit)); } return kernel; } QuatumSimulationResult IterativeQpeWorkflow::execute(const QuatumSimulationModel &model) { // auto provider = xacc::getIRProvider("quantum"); // Iterative Quantum Phase Estimation: // We're using XACC IR construction API here, since using QCOR kernels here // seems to be complicated. double omega_coef = 0.0; // Iterates over the num_iters // k runs from the number of iterations back to 1 for (int iterIdx = 0; iterIdx < num_iters; ++iterIdx) { // State prep: evolves the qubit register to the initial quantum state, i.e. // the eigenvector state to estimate the eigenvalue. auto kernel = provider->createComposite("__TEMP__ITER_QPE__"); if (model.user_defined_ansatz) { kernel->addInstruction(model.user_defined_ansatz->evaluate_kernel({})); } omega_coef = omega_coef/2.0; // Construct the QPE circuit and append to the kernel: auto k = num_iters - iterIdx; auto iterQpe = constructQpeCircuit(model, k, -2 * M_PI * omega_coef); kernel->addInstruction(iterQpe); // Executes the iterative QPE algorithm: auto qpu = xacc::internal_compiler::get_qpu(); auto temp_buffer = xacc::qalloc(model.observable->nBits() + 1); // std::cout << "Kernel: \n" << kernel->toString() << "\n"; return {}; qpu->execute(temp_buffer, kernel); // temp_buffer->print(); const bool bitResult = [&temp_buffer](){ if (!temp_buffer->getMeasurementCounts().empty()) { if (xacc::container::contains(temp_buffer->getMeasurements(), "0")) { if (xacc::container::contains(temp_buffer->getMeasurements(), "1")) { return temp_buffer->computeMeasurementProbability("1") > temp_buffer->computeMeasurementProbability("0"); } else { return false; } } else { assert(xacc::container::contains(temp_buffer->getMeasurements(), "1")); return true; } } else { return temp_buffer->getExpectationValueZ() < 0.0; } }(); std::cout << "Iter " << iterIdx << ": Result = " << bitResult << "\n"; if (bitResult) { omega_coef = omega_coef + 0.5; } } std::cout << "Final phase = " << omega_coef << "\n"; return { {"phase", omega_coef}}; } std::shared_ptr<QuatumSimulationWorkflow> getWorkflow(const std::string &name, const HeterogeneousMap &init_params) { Loading lib/qsim/impls/qsim_impl.hpp +7 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,8 @@ private: }; // Iterative QPE workflow to estimate the energy of a Hamiltonian operator. // For the first pass, we implement this as a workflow. // This can be integrated as a CostFuncEvaluator if needed. class IterativeQpeWorkflow : public QuatumSimulationWorkflow { public: virtual bool initialize(const HeterogeneousMap ¶ms) override; Loading @@ -81,6 +83,11 @@ public: virtual const std::string name() const override { return "iqpe"; } virtual const std::string description() const override { return ""; } private: std::shared_ptr<CompositeInstruction> constructQpeCircuit(const QuatumSimulationModel &model, int k, double omega, bool measure = true) const; private: // Number of time slices (>=1) int num_steps; Loading lib/qsim/impls/tests/IterativeQpeWorkflow.cpp +10 −3 Original line number Diff line number Diff line Loading @@ -7,15 +7,22 @@ /// $ qcor -qpu qpp IterativeQpeWorkflow.cpp /// $ ./a.out __qpu__ void eigen_state_prep(qreg q) { auto theta = 0.297113; X(q[0]); auto exponent_op = X(0) * Y(1) - Y(0) * X(1); exp_i_theta(q, theta, exponent_op); } int main(int argc, char **argv) { // Create the Deuteron Hamiltonian // Create Hamiltonian auto H = 5.907 - 2.1433 * X(0) * X(1) - 2.143 * Y(0) * Y(1) + 0.21829 * Z(0) - 6.125 * Z(1); auto problemModel = qsim::ModelBuilder::createModel(&H); qsim::ModelBuilder::createModel(eigen_state_prep, H, 2, 0); // Instantiate an IQPE workflow. auto workflow = qsim::getWorkflow("iqpe", {}); auto workflow = qsim::getWorkflow("iqpe", {{"iterations", 8}}); // Result should contain the observable expectation value along Trotter steps. auto result = workflow->execute(problemModel); Loading Loading
lib/impl/quantum_phase_estimation.hpp +1 −1 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ __qpu__ void pauli_power_trotter_evolution(qreg q, /// omega: the feedback angle. /// pauli_ham: hamiltonian /// num_time_slices: number of trotter steps /// measure: if true, add measure gate on ancilla qubit. /// measure: if true (!= 0), add measure gate on ancilla qubit. __qpu__ void pauli_qpe_iter(qreg q, int k, double omega, qcor::PauliOperator pauli_ham, int num_time_slices, int measure) { Loading
lib/qsim/impls/qsim_impl.cpp +102 −4 Original line number Diff line number Diff line Loading @@ -101,7 +101,6 @@ VqeWorkflow::execute(const QuatumSimulationModel &model) { if (model.user_defined_ansatz) { auto nParams = model.user_defined_ansatz->nParams(); evaluator = getObjEvaluator(model.observable); auto qpu = xacc::internal_compiler::get_qpu(); OptFunction f( [&](const std::vector<double> &x, std::vector<double> &dx) { Loading Loading @@ -135,13 +134,112 @@ bool IterativeQpeWorkflow::initialize(const HeterogeneousMap ¶ms) { return (num_steps >= 1) && (num_iters >= 1); } std::shared_ptr<CompositeInstruction> IterativeQpeWorkflow::constructQpeCircuit(const QuatumSimulationModel &model, int k, double omega, bool measure) const { auto provider = xacc::getIRProvider("quantum"); auto kernel = provider->createComposite("__TEMP__QPE__LOOP__"); const auto nbQubits = model.observable->nBits(); const size_t ancBit = nbQubits; // Hadamard on ancilla qubit kernel->addInstruction(provider->createInstruction("H", ancBit)); // Using Trotter evolution method to generate U: // TODO: support other methods (e.g. Suzuki) TrotterEvolution method; const double trotterStepSize = -2 * M_PI / num_steps; auto trotterCir = method.create_ansatz(model.observable, {{"dt", trotterStepSize}}).circuit; // std::cout << "Trotter circ:\n" << trotterCir->toString() << "\n"; // Controlled-U auto ctrlKernel = std::dynamic_pointer_cast<CompositeInstruction>( xacc::getService<xacc::Instruction>("C-U")); ctrlKernel->expand({ std::make_pair("U", trotterCir), std::make_pair("control-idx", static_cast<int>(ancBit)), }); // Apply C-U^n int power = 1 << (k - 1); for (int i = 0; i < power; ++i) { for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { // We need to clone the instruction since it'll be repeated. kernel->addInstruction(ctrlKernel->getInstruction(instId)->clone()); } } // Rz on ancilla qubit kernel->addInstruction(provider->createInstruction("Rz", {ancBit}, {omega})); // Hadamard on ancilla qubit kernel->addInstruction(provider->createInstruction("H", ancBit)); if (measure) { kernel->addInstruction(provider->createInstruction("Measure", ancBit)); } return kernel; } QuatumSimulationResult IterativeQpeWorkflow::execute(const QuatumSimulationModel &model) { // auto provider = xacc::getIRProvider("quantum"); // Iterative Quantum Phase Estimation: // We're using XACC IR construction API here, since using QCOR kernels here // seems to be complicated. double omega_coef = 0.0; // Iterates over the num_iters // k runs from the number of iterations back to 1 for (int iterIdx = 0; iterIdx < num_iters; ++iterIdx) { // State prep: evolves the qubit register to the initial quantum state, i.e. // the eigenvector state to estimate the eigenvalue. auto kernel = provider->createComposite("__TEMP__ITER_QPE__"); if (model.user_defined_ansatz) { kernel->addInstruction(model.user_defined_ansatz->evaluate_kernel({})); } omega_coef = omega_coef/2.0; // Construct the QPE circuit and append to the kernel: auto k = num_iters - iterIdx; auto iterQpe = constructQpeCircuit(model, k, -2 * M_PI * omega_coef); kernel->addInstruction(iterQpe); // Executes the iterative QPE algorithm: auto qpu = xacc::internal_compiler::get_qpu(); auto temp_buffer = xacc::qalloc(model.observable->nBits() + 1); // std::cout << "Kernel: \n" << kernel->toString() << "\n"; return {}; qpu->execute(temp_buffer, kernel); // temp_buffer->print(); const bool bitResult = [&temp_buffer](){ if (!temp_buffer->getMeasurementCounts().empty()) { if (xacc::container::contains(temp_buffer->getMeasurements(), "0")) { if (xacc::container::contains(temp_buffer->getMeasurements(), "1")) { return temp_buffer->computeMeasurementProbability("1") > temp_buffer->computeMeasurementProbability("0"); } else { return false; } } else { assert(xacc::container::contains(temp_buffer->getMeasurements(), "1")); return true; } } else { return temp_buffer->getExpectationValueZ() < 0.0; } }(); std::cout << "Iter " << iterIdx << ": Result = " << bitResult << "\n"; if (bitResult) { omega_coef = omega_coef + 0.5; } } std::cout << "Final phase = " << omega_coef << "\n"; return { {"phase", omega_coef}}; } std::shared_ptr<QuatumSimulationWorkflow> getWorkflow(const std::string &name, const HeterogeneousMap &init_params) { Loading
lib/qsim/impls/qsim_impl.hpp +7 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,8 @@ private: }; // Iterative QPE workflow to estimate the energy of a Hamiltonian operator. // For the first pass, we implement this as a workflow. // This can be integrated as a CostFuncEvaluator if needed. class IterativeQpeWorkflow : public QuatumSimulationWorkflow { public: virtual bool initialize(const HeterogeneousMap ¶ms) override; Loading @@ -81,6 +83,11 @@ public: virtual const std::string name() const override { return "iqpe"; } virtual const std::string description() const override { return ""; } private: std::shared_ptr<CompositeInstruction> constructQpeCircuit(const QuatumSimulationModel &model, int k, double omega, bool measure = true) const; private: // Number of time slices (>=1) int num_steps; Loading
lib/qsim/impls/tests/IterativeQpeWorkflow.cpp +10 −3 Original line number Diff line number Diff line Loading @@ -7,15 +7,22 @@ /// $ qcor -qpu qpp IterativeQpeWorkflow.cpp /// $ ./a.out __qpu__ void eigen_state_prep(qreg q) { auto theta = 0.297113; X(q[0]); auto exponent_op = X(0) * Y(1) - Y(0) * X(1); exp_i_theta(q, theta, exponent_op); } int main(int argc, char **argv) { // Create the Deuteron Hamiltonian // Create Hamiltonian auto H = 5.907 - 2.1433 * X(0) * X(1) - 2.143 * Y(0) * Y(1) + 0.21829 * Z(0) - 6.125 * Z(1); auto problemModel = qsim::ModelBuilder::createModel(&H); qsim::ModelBuilder::createModel(eigen_state_prep, H, 2, 0); // Instantiate an IQPE workflow. auto workflow = qsim::getWorkflow("iqpe", {}); auto workflow = qsim::getWorkflow("iqpe", {{"iterations", 8}}); // Result should contain the observable expectation value along Trotter steps. auto result = workflow->execute(problemModel); Loading