Commit cb7c9752 authored by Nguyen, Thien Minh's avatar Nguyen, Thien Minh
Browse files

Work on IQPE



Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>
parent 566223f5
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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) {
+102 −4
Original line number Diff line number Diff line
@@ -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) {
@@ -135,13 +134,112 @@ bool IterativeQpeWorkflow::initialize(const HeterogeneousMap &params) {
  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) {
+7 −0
Original line number Diff line number Diff line
@@ -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 &params) override;
@@ -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;
+10 −3
Original line number Diff line number Diff line
@@ -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);