Commit d53571e7 by Nguyen, Thien

### Fixed QITE algorithm impl

```

This mostly completed the impl.
Signed-off-by: Thien Nguyen <nguyentm@ornl.gov>

Also, added python examples```
parent 5698571f
 # Simple 2-qubit demonstration of the Quatum Imaginary Time Evolution algorithm # for Deuteron H2 Hamiltonian # Reference: https://arxiv.org/pdf/1912.06226.pdf # Expected minimum value: -1.74886 import xacc,sys, numpy as np import matplotlib.pyplot as plt # Get access to the desired QPU and # allocate some qubits to run on qpu = xacc.getAccelerator('qpp') # Construct the H2 Hamiltonian ham = xacc.getObservable('pauli', '5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1') xacc.qasm('''.compiler xasm .circuit prep .qbit q X(q[0]); ''') prep_circuit = xacc.getCompiled('prep') # We need 2 qubits for this case (H2) buffer = xacc.qalloc(2) # Horizontal axis: 0 -> 0.3 (step = 0.05) # The number of Trotter steps nbSteps = 6 # The Trotter step size stepSize = 0.05 # Create the QITE algorithm qite = xacc.getAlgorithm('qite', { 'accelerator': qpu, 'observable': ham, 'step-size': stepSize, 'steps': nbSteps, 'ansatz': prep_circuit }) result = qite.execute(buffer) # Expected result: ~ -1.74886 print('Min energy value = ', buffer.getInformation('opt-val')) E = buffer.getInformation('exp-vals') # Reproduce Fig. 3(a) of https://arxiv.org/pdf/1912.06226.pdf plt.plot(np.arange(0, nbSteps + 1) * stepSize, E, 'ro-', label = 'XACC QITE') plt.ylim((-2.0, -0.25)) plt.yticks(np.arange(-2.0, -0.25, step=0.25)) plt.grid() plt.show() \ No newline at end of file
 # Simple 3-qubit demonstration of the Quatum Imaginary Time Evolution algorithm # for Deuteron H3 Hamiltonian # Reference: https://arxiv.org/pdf/1912.06226.pdf # Expected minimum value: -2.04482 import xacc,sys, numpy as np import matplotlib.pyplot as plt # Get access to the desired QPU and # allocate some qubits to run on qpu = xacc.getAccelerator('qpp') # Construct the H3 Hamiltonian ham = xacc.getObservable('pauli', '5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1 + 9.625 - 9.625 Z2 - 3.91 X1 X2 - 3.91 Y1 Y2') xacc.qasm('''.compiler xasm .circuit prep .qbit q X(q[0]); ''') prep_circuit = xacc.getCompiled('prep') # We need 3 qubits for this case (H3) buffer = xacc.qalloc(3) # Horizontal axis: 0 -> 0.3 (step = 0.05) # The number of Trotter steps nbSteps = 6 # The Trotter step size stepSize = 0.05 # Create the QITE algorithm qite = xacc.getAlgorithm('qite', { 'accelerator': qpu, 'observable': ham, 'step-size': stepSize, 'steps': nbSteps, 'ansatz': prep_circuit }) result = qite.execute(buffer) # Expected result: ~ -2.04482 print('Min energy value = ', buffer.getInformation('opt-val')) E = buffer.getInformation('exp-vals') # Reproduce Fig. 3(b) of https://arxiv.org/pdf/1912.06226.pdf plt.plot(np.arange(0, nbSteps + 1) * stepSize, E, 'ro-', label = 'XACC QITE') plt.ylim((-2.5, -0.0)) plt.yticks(np.arange(-2.5, 0.0, step=0.5)) plt.grid() plt.show() \ No newline at end of file
 ... @@ -120,7 +120,6 @@ arma::cx_mat createSMatrix(const std::vector& in_pauliOps, const st ... @@ -120,7 +120,6 @@ arma::cx_mat createSMatrix(const std::vector& in_pauliOps, const st xacc::quantum::PauliOperator left(leftOp); xacc::quantum::PauliOperator left(leftOp); xacc::quantum::PauliOperator right(rightOp); xacc::quantum::PauliOperator right(rightOp); auto product = left * right; auto product = left * right; // std::cout << left.toString() << " * " << right.toString() << " = " << product.toString() << "\n"; const auto index = findMatchingPauliIndex(in_pauliOps, product.toString()); const auto index = findMatchingPauliIndex(in_pauliOps, product.toString()); return in_tomoExp[index]*product.coefficient(); return in_tomoExp[index]*product.coefficient(); }; }; ... @@ -175,10 +174,27 @@ bool QITE::initialize(const HeterogeneousMap ¶meters) ... @@ -175,10 +174,27 @@ bool QITE::initialize(const HeterogeneousMap ¶meters) m_observable = xacc::as_shared_ptr(parameters.getPointerLike("observable")); m_observable = xacc::as_shared_ptr(parameters.getPointerLike("observable")); } } m_analytical = true; m_analytical = false; if (parameters.keyExists("analytical")) if (parameters.keyExists("analytical")) { { m_analytical = parameters.get("analytical"); m_analytical = parameters.get("analytical"); if (m_analytical) { // Default initial state is 0 m_initialState = 0; if (parameters.keyExists("initial-state")) { m_initialState = parameters.get("initial-state"); } } } m_ansatz = nullptr; // Ansatz here is just a state preparation circuit: // e.g. if we want to start in state |01>, not |00> if (parameters.pointerLikeExists("ansatz")) { m_ansatz = parameters.getPointerLike("ansatz"); } } m_approxOps.clear(); m_approxOps.clear(); ... @@ -294,8 +310,6 @@ double QITE::calcCurrentEnergy(int in_nbQubits) const ... @@ -294,8 +310,6 @@ double QITE::calcCurrentEnergy(int in_nbQubits) const std::shared_ptr QITE::calcAOps(const std::shared_ptr& in_buffer, std::shared_ptr in_kernel, std::shared_ptr in_hmTerm) const std::shared_ptr QITE::calcAOps(const std::shared_ptr& in_buffer, std::shared_ptr in_kernel, std::shared_ptr in_hmTerm) const { { const auto pauliOps = generatePauliPermutation(in_buffer->size()); const auto pauliOps = generatePauliPermutation(in_buffer->size()); assert(in_hmTerm->getSubTerms().size() == 1); assert(in_hmTerm->getNonIdentitySubTerms().size() == 1); // Step 1: Observe the kernels using the various Pauli // Step 1: Observe the kernels using the various Pauli // operators to calculate S and b. // operators to calculate S and b. ... @@ -340,18 +354,13 @@ std::shared_ptr QITE::calcAOps(const std::shared_ptr QITE::calcAOps(const std::shared_ptr 1e-12 ? entry : 0.0; } } } } // b vector: // b vector: const auto obsProjCoeffs = observableToVec(in_hmTerm, pauliOps); const auto obsProjCoeffs = observableToVec(in_hmTerm, pauliOps); // std::cout << "Observable Pauli Vec: ["; // for (const auto& elem: obsProjCoeffs) // { // std::cout << elem << ", "; // } // std::cout << "]\n"; // Calculate c: Eq. 3 in https://arxiv.org/pdf/1901.07653.pdf // Calculate c: Eq. 3 in https://arxiv.org/pdf/1901.07653.pdf double c = 1.0; double c = 1.0; ... @@ -360,7 +369,6 @@ std::shared_ptr QITE::calcAOps(const std::shared_ptr QITE::calcAOps(const std::shared_ptr b = (sigmaExpectation[i]/ std::sqrt(c) - sigmaExpectation[i])/ m_dBeta; std::complex b = (sigmaExpectation[i]/ std::sqrt(c) - sigmaExpectation[i])/ m_dBeta; ... @@ -373,22 +381,12 @@ std::shared_ptr QITE::calcAOps(const std::shared_ptr QITE::calcAOps(const std::shared_ptr 1e-12 ? b : 0.0; } } // std::cout << "S Matrix: \n" << S_Mat << "\n"; auto lhs = S_Mat + S_Mat.st(); // std::cout << "B Vector: \n" << b_Vec << "\n"; // Add regularizer arma::cx_mat dalpha(sMatDim, sMatDim, arma::fill::eye); dalpha = 0.1 * dalpha; auto lhs = S_Mat + S_Mat.st() + dalpha; auto rhs = -b_Vec; auto rhs = -b_Vec; // std::cout << "LHS Matrix: \n" << lhs << "\n"; // std::cout << "RHS Vector: \n" << rhs << "\n"; arma::cx_vec a_Vec = arma::solve(lhs, rhs); arma::cx_vec a_Vec = arma::solve(lhs, rhs); // std::cout << "Result A Vector: \n" << a_Vec << "\n"; // Now, we have the decomposition of A observable in the basis of // Now, we have the decomposition of A observable in the basis of // all possible Pauli combinations. // all possible Pauli combinations. ... @@ -421,19 +419,20 @@ void QITE::execute(const std::shared_ptr buffer) const ... @@ -421,19 +419,20 @@ void QITE::execute(const std::shared_ptr buffer) const // Run on hardware/simulator using quantum gates/measure // Run on hardware/simulator using quantum gates/measure // Initial energy // Initial energy m_energyAtStep.emplace_back(calcCurrentEnergy(buffer->size())); m_energyAtStep.emplace_back(calcCurrentEnergy(buffer->size())); auto hamOp = std::make_shared(); for (const auto& hamTerm : m_observable->getNonIdentitySubTerms()) { *hamOp = *hamOp + *(std::dynamic_pointer_cast(hamTerm)); } // Time stepping: // Time stepping: for (int i = 0; i < m_nbSteps; ++i) for (int i = 0; i < m_nbSteps; ++i) { for (const auto& hamTerm : m_observable->getNonIdentitySubTerms()) { { // Propagates the state via Trotter steps: // Propagates the state via Trotter steps: auto kernel = constructPropagateCircuit(); auto kernel = constructPropagateCircuit(); // Optimizes/calculates next A ops // Optimizes/calculates next A ops auto nextAOps = calcAOps(buffer, kernel, hamTerm); auto nextAOps = calcAOps(buffer, kernel, hamOp); m_approxOps.emplace_back(nextAOps); m_approxOps.emplace_back(nextAOps); } m_energyAtStep.emplace_back(calcCurrentEnergy(buffer->size())); m_energyAtStep.emplace_back(calcCurrentEnergy(buffer->size())); } } assert(m_energyAtStep.size() == m_nbSteps + 1); assert(m_energyAtStep.size() == m_nbSteps + 1); ... @@ -496,12 +495,8 @@ void QITE::execute(const std::shared_ptr buffer) const ... @@ -496,12 +495,8 @@ void QITE::execute(const std::shared_ptr buffer) const // Initial state // Initial state arma::cx_vec psiVec(1 << buffer->size(), arma::fill::zeros); arma::cx_vec psiVec(1 << buffer->size(), arma::fill::zeros); psiVec(1) = 1.0; psiVec(m_initialState) = 1.0; // Time stepping: for (int i = 0; i < m_nbSteps; ++i) { arma::cx_mat hMat(1 << buffer->size(), 1 << buffer->size(), arma::fill::zeros); arma::cx_mat hMat(1 << buffer->size(), 1 << buffer->size(), arma::fill::zeros); auto identityTerm = m_observable->getIdentitySubTerm(); auto identityTerm = m_observable->getIdentitySubTerm(); if (identityTerm) if (identityTerm) { { ... @@ -509,7 +504,6 @@ void QITE::execute(const std::shared_ptr buffer) const ... @@ -509,7 +504,6 @@ void QITE::execute(const std::shared_ptr buffer) const hMat += identityTerm->coefficient() * idTerm; hMat += identityTerm->coefficient() * idTerm; } } double stateVecNorm = 1.0; for (const auto& hamTerm : m_observable->getNonIdentitySubTerms()) for (const auto& hamTerm : m_observable->getNonIdentitySubTerms()) { { auto pauliCast = std::dynamic_pointer_cast(hamTerm); auto pauliCast = std::dynamic_pointer_cast(hamTerm); ... @@ -524,10 +518,13 @@ void QITE::execute(const std::shared_ptr buffer) const ... @@ -524,10 +518,13 @@ void QITE::execute(const std::shared_ptr buffer) const } } } } } } // Time stepping: for (int i = 0; i < m_nbSteps; ++i) { double normAfter = 0.0; double normAfter = 0.0; arma::cx_vec dPsiVec(1 << buffer->size(), arma::fill::zeros); arma::cx_vec dPsiVec(1 << buffer->size(), arma::fill::zeros); std::tie(dPsiVec, normAfter) = expMinusHamTerm(hMat, psiVec, m_dBeta); std::tie(dPsiVec, normAfter) = expMinusHamTerm(hMat, psiVec, m_dBeta); stateVecNorm *= normAfter; // Eq. 8, SI of https://arxiv.org/pdf/1901.07653.pdf // Eq. 8, SI of https://arxiv.org/pdf/1901.07653.pdf dPsiVec = dPsiVec - psiVec; dPsiVec = dPsiVec - psiVec; std::vector> pauliExp; std::vector> pauliExp; ... ...
 ... @@ -61,6 +61,10 @@ private: ... @@ -61,6 +61,10 @@ private: mutable std::vector m_energyAtStep; mutable std::vector m_energyAtStep; // If a pure analytical run is requested. // If a pure analytical run is requested. bool m_analytical; bool m_analytical; // For analytical solver only: the initial state // For accelerator-based simulation, the Ansatz is used to // prepare the initial state. int m_initialState; }; }; } // namespace algorithm } // namespace algorithm } // namespace xacc } // namespace xacc
 ... @@ -54,14 +54,18 @@ TEST(QITETester, checkDeuteuronH2) ... @@ -54,14 +54,18 @@ TEST(QITETester, checkDeuteuronH2) std::shared_ptr observable = std::make_shared(); std::shared_ptr observable = std::make_shared(); observable->fromString("5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1"); observable->fromString("5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1"); auto acc = xacc::getAccelerator("qpp"); auto acc = xacc::getAccelerator("qpp"); const int nbSteps = 10; const int nbSteps = 5; const double stepSize = 0.1; const double stepSize = 0.1; auto compiler = xacc::getCompiler("xasm"); auto ir = compiler->compile(R"(__qpu__ void f(qbit q) { X(q[0]); })", nullptr); auto x = ir->getComposite("f"); const bool initOk = qite->initialize({ const bool initOk = qite->initialize({ std::make_pair("accelerator", acc), std::make_pair("accelerator", acc), std::make_pair("steps", nbSteps), std::make_pair("steps", nbSteps), std::make_pair("observable", observable), std::make_pair("observable", observable), std::make_pair("step-size", stepSize) std::make_pair("step-size", stepSize), std::make_pair("ansatz", x), }); }); EXPECT_TRUE(initOk); EXPECT_TRUE(initOk); ... @@ -71,9 +75,56 @@ TEST(QITETester, checkDeuteuronH2) ... @@ -71,9 +75,56 @@ TEST(QITETester, checkDeuteuronH2) const double finalEnergy = (*buffer)["opt-val"].as(); const double finalEnergy = (*buffer)["opt-val"].as(); std::cout << "Final Energy: " << finalEnergy << "\n"; std::cout << "Final Energy: " << finalEnergy << "\n"; EXPECT_NEAR(finalEnergy, -1.74886, 1e-3); EXPECT_NEAR(finalEnergy, -1.74886, 1e-3); } TEST(QITETester, checkDeuteuronH2Analytical) { auto qite = xacc::getService("qite"); std::shared_ptr observable = std::make_shared(); observable->fromString("5.907 - 2.1433 X0X1 - 2.1433 Y0Y1 + .21829 Z0 - 6.125 Z1"); auto acc = xacc::getAccelerator("qpp"); const int nbSteps = 10; const double stepSize = 0.1; const bool initOk = qite->initialize({ std::make_pair("accelerator", acc), std::make_pair("steps", nbSteps), std::make_pair("observable", observable), std::make_pair("step-size", stepSize), std::make_pair("analytical", true), std::make_pair("initial-state", 1) }); EXPECT_TRUE(initOk); auto buffer = xacc::qalloc(2); qite->execute(buffer); const double finalEnergy = (*buffer)["opt-val"].as(); std::cout << "Final Energy: " << finalEnergy << "\n"; EXPECT_NEAR(finalEnergy, -1.74886, 1e-3); buffer->print(); buffer->print(); const std::string Aop_Analytical = (*buffer)["A-op"].as(); std::shared_ptr aOpObservable = std::make_shared(); aOpObservable->fromString(Aop_Analytical); // Expected: recover UCC ansatz: "X0Y1 − X1Y0" EXPECT_EQ(aOpObservable->getSubTerms().size(), 2); auto term1 = aOpObservable->getSubTerms()[0]; auto term2 = aOpObservable->getSubTerms()[1]; const auto term1Coeff = term1->coefficient(); const auto term2Coeff = term2->coefficient(); // Two terms have opposite signs const auto diff = term1Coeff + term2Coeff; EXPECT_NEAR(std::norm(diff), 0.0, 1e-6); const auto endsWith = [](const std::string& in_string, const std::string& in_ending) -> bool { return std::equal(in_ending.rbegin(), in_ending.rend(), in_string.rbegin()); }; // two UCC's terms: "Y0 X1" "X0 Y1" const bool check1 = endsWith(term1->toString(), "Y0 X1") || endsWith(term1->toString(), "X0 Y1"); const bool check2 = endsWith(term2->toString(), "Y0 X1") || endsWith(term2->toString(), "X0 Y1"); EXPECT_TRUE(check1 && check2); } } TEST(QITETester, checkDeuteuronH3) TEST(QITETester, checkDeuteuronH3) { { auto qite = xacc::getService("qite"); auto qite = xacc::getService("qite"); ... @@ -82,12 +133,16 @@ TEST(QITETester, checkDeuteuronH3) ... @@ -82,12 +133,16 @@ TEST(QITETester, checkDeuteuronH3) auto acc = xacc::getAccelerator("qpp"); auto acc = xacc::getAccelerator("qpp"); const int nbSteps = 10; const int nbSteps = 10; const double stepSize = 0.1; const double stepSize = 0.1; auto compiler = xacc::getCompiler("xasm"); auto ir = compiler->compile(R"(__qpu__ void f1(qbit q) { X(q[0]); })", nullptr); auto x = ir->getComposite("f1"); const bool initOk = qite->initialize({ const bool initOk = qite->initialize({ std::make_pair("accelerator", acc), std::make_pair("accelerator", acc), std::make_pair("steps", nbSteps), std::make_pair("steps", nbSteps), std::make_pair("observable", observable), std::make_pair("observable", observable), std::make_pair("step-size", stepSize) std::make_pair("step-size", stepSize), std::make_pair("ansatz", x) }); }); EXPECT_TRUE(initOk); EXPECT_TRUE(initOk); ... @@ -96,7 +151,7 @@ TEST(QITETester, checkDeuteuronH3) ... @@ -96,7 +151,7 @@ TEST(QITETester, checkDeuteuronH3) qite->execute(buffer); qite->execute(buffer); const double finalEnergy = (*buffer)["opt-val"].as(); const double finalEnergy = (*buffer)["opt-val"].as(); std::cout << "Final Energy: " << finalEnergy << "\n"; std::cout << "Final Energy: " << finalEnergy << "\n"; EXPECT_NEAR(finalEnergy, -2.04482, 1e-3); EXPECT_NEAR(finalEnergy, -2.04482, 0.01); } } int main(int argc, char **argv) { int main(int argc, char **argv) { ... ...
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment