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

First pass of generic Quantum Simulation model builder



Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>
parent 030de777
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
add_subdirectory(hybrid)
add_subdirectory(qsim)

# Install QCOR standard library header
file(GLOB LIB_HEADERS qcor_*)
+17 −0
Original line number Diff line number Diff line
set(LIBRARY_NAME qcor-qsim)

file(GLOB SRC *.cpp tests)

add_library(${LIBRARY_NAME} SHARED ${SRC})

target_link_libraries(${LIBRARY_NAME} PUBLIC qcor)

xacc_configure_library_rpath(${LIBRARY_NAME})

file(GLOB HEADERS qcor_qsim.hpp)
install(FILES ${HEADERS} DESTINATION include/qcor)
install(TARGETS ${LIBRARY_NAME} DESTINATION lib)

if (QCOR_BUILD_TESTS)
	add_subdirectory(tests)
endif()

lib/qsim/qcor_qsim.cpp

0 → 100644
+180 −0
Original line number Diff line number Diff line
#include "qcor_qsim.hpp"
#include "Circuit.hpp"
#include "xacc_service.hpp"

namespace qcor {
Ansatz TrotterEvolution::create_ansatz(Observable *obs,
                                       const HeterogeneousMap &params) {
  Ansatz result;
  // This ansatz generator requires an observable.
  assert(obs != nullptr);
  double dt = 1.0;
  if (params.keyExists<double>("dt")) {
    dt = params.get<double>("dt");
  }

  // Just use exp_i_theta for now
  // TODO: formalize a standard library kernel for this.
  auto expCirc = std::dynamic_pointer_cast<xacc::quantum::Circuit>(
      xacc::getService<xacc::Instruction>("exp_i_theta"));
  expCirc->expand({{"pauli", obs->toString()}});
  result.circuit = expCirc->operator()({dt});
  result.nb_qubits = expCirc->nRequiredBits();

  return result;
}

bool CostFunctionEvaluator::initialize(const HeterogeneousMap &params) {
  target_operator = nullptr;
  if (params.pointerLikeExists<Observable>("observable")) {
    target_operator = params.getPointerLike<Observable>("observable");
  }

  // TODO: use qcor data
  quantum_backend = nullptr;
  if (params.pointerLikeExists<Accelerator>("accelerator")) {
    quantum_backend = params.getPointerLike<Accelerator>("accelerator");
  }

  hyperParams = params;
  return target_operator && quantum_backend;
}

CostFunctionEvaluator *CostFunctionEvaluator::getInstance() {
  if (!instance) {
    instance = new CostFunctionEvaluator();
  }
  return instance;
}

double
CostFunctionEvaluator::evaluate(std::shared_ptr<CompositeInstruction> state_prep) {
  
  // Measure the observables:
  // TODO: Port the existing VQE impl. as the default.

  // TODO:
  return 0.0;
}

QsimWorkflow *TimeDependentWorkflow::getInstance() {
  if (!instance) {
    instance = new TimeDependentWorkflow();
  }
  return instance;
}

bool QsimModel::initialize(WorkFlow workflow_type,
                           const HeterogeneousMap &params) {
  type = workflow_type;
  qsim_workflow = nullptr;
  switch (workflow_type) {
  case (WorkFlow::PE):
    // TODO
    break;
  case (WorkFlow::TD):
    qsim_workflow = TimeDependentWorkflow::getInstance();
    break;
  case (WorkFlow::VQE):
    // TODO:
    break;
  }

  if (qsim_workflow) {
    const bool workflowInit = qsim_workflow->initialize(params);
    if (!workflowInit) {
      return false;
    }

    return true;
  }

  return false;
}

QsimModel ModelBuilder::createModel(const HeterogeneousMap &params) {
  // TODO: we need to formalize the input here, e.g.
  // (1) Problem descriptions
  // (2) Workflow + method
  QsimModel model;

  // ==== TEMP CODE ====
  // We need to support problem decomposition as well.
  // i.e. from high-level problem descriptions.
  const std::string protocol = params.getString("protocol");
  const std::string method = params.getString("method");
  // Only have this currently:
  if (protocol == "time-evolution") 
  {
    if (method == "trotter") {
      model.initialize(WorkFlow::TD, params);
    }
  }
  // ==== TEMP CODE ====
  
  return model;
}

bool TimeDependentWorkflow::initialize(const HeterogeneousMap &params) {
  if (params.keyExists<TdObservable>("hamiltonian")) {
    ham_func = params.get<TdObservable>("hamiltonian");
  }

  // TODO: support multiple evaluator
  evaluator = CostFunctionEvaluator::getInstance();
  evaluator->initialize(params);

  // Get parameters (specific to TD workflow):
  t_0 = 0.0;
  dt = 0.1;
  if (params.keyExists<double>("dt")) {
    dt = params.get<double>("dt");
  }
  int nbSteps = 1;
  if (params.keyExists<int>("steps")) {
    nbSteps = params.get<int>("steps");
  }

  t_final = nbSteps * dt;
  return true;
}

QsimResult TimeDependentWorkflow::execute() {
  QsimResult result;
  // A TD workflow: stepping through Trotter steps,
  // compute expectations at each step.
  double currentTime = t_0;
  std::vector<double> resultExpectationValues;
  std::shared_ptr<CompositeInstruction> totalCirc;
  // Just support Trotter for now
  // TODO: support different methods:
  TrotterEvolution method;
  for (;;)
  {
    // Evaluate the time-dependent Hamiltonian:
    auto ham_t = ham_func(currentTime);
    auto stepAnsatz = method.create_ansatz(&ham_t, {{"dt", dt}});
    // First step:
    if (!totalCirc) {
      totalCirc = stepAnsatz.circuit;
    }
    else {
      // Append Trotter steps
      totalCirc->addInstructions(stepAnsatz.circuit->getInstructions());
    }

    // Evaluate the expectation after these Trotter steps:
    const double ham_expect = evaluator->evaluate(totalCirc);
    resultExpectationValues.emplace_back(ham_expect);
    
    currentTime += dt;
    if (currentTime > t_final) {
      break;
    }
  }

  result.insert("exp-vals", resultExpectationValues);
  return result;
}

} // namespace qcor
 No newline at end of file

lib/qsim/qcor_qsim.hpp

0 → 100644
+146 −0
Original line number Diff line number Diff line
#pragma once
#include "Accelerator.hpp"
#include "AcceleratorBuffer.hpp"
#include "Circuit.hpp"
#include "qcor.hpp"
#include "qcor_utils.hpp"
#include "qrt.hpp"
#include <memory>
#include <xacc_internal_compiler.hpp>
using namespace xacc;

namespace qcor {

// Struct captures a state-preparation circuit.
struct Ansatz {
  std::shared_ptr<CompositeInstruction> circuit;
  std::vector<std::string> symbol_names;
  size_t nb_qubits;
};

// State-preparation method (i.e. ansatz):
// For example,
//  - Real time Hamiltonian evolution with Trotter approximation (of various
//  orders)
//  - Real time Hamiltonian evolution with Taylor series/LCU approach
//  - Imaginary time evolution with QITE
//  - Thermo-field doubles state preparation
//  - Two-point correlation function measurements
//  - QFT
class AnsatzGenerator {
public:
  virtual Ansatz create_ansatz(Observable *obs = nullptr,
                               const HeterogeneousMap &params = {}) = 0;
};

// CostFunctionEvaluator take an unknown quantum state (the circuit that
// prepares the unknown state) and a target operator (e.g. Hamiltonian
// Observable) as input and produce a estimation as output.
class CostFunctionEvaluator {
public:
  // Evaluate the cost
  virtual double evaluate(std::shared_ptr<CompositeInstruction> state_prep);
  virtual bool initialize(const HeterogeneousMap &params);
  static CostFunctionEvaluator *getInstance();

protected:
  Observable *target_operator;
  Accelerator *quantum_backend;
  HeterogeneousMap hyperParams;

private:
  static inline CostFunctionEvaluator *instance = nullptr;
};

// Quantum Simulation Workflow (Protocol)
// This can handle both variational workflow (optimization loop)
// as well as simple Trotter evolution workflow.
// Result is stored in a HetMap
using QsimResult = HeterogeneousMap;
// Abstract workflow:
class QsimWorkflow {
public:
  virtual QsimResult execute() = 0;
  virtual bool initialize(const HeterogeneousMap &params) = 0;

protected:
  CostFunctionEvaluator *evaluator;
};

// Supported Workflow: to be defined and implemented
enum class WorkFlow { VQE, TD, PE };
// Quantum Simulation Model: to capture the problem description and the workflow.
// TODO: support high-level problem descriptions.
class QsimModel {
public:
  bool initialize(WorkFlow workflow_type,
                          const HeterogeneousMap &params);
  QsimWorkflow *get_workflow() { return qsim_workflow; }

private:
  WorkFlow type;
  QsimWorkflow *qsim_workflow;
};

// Generic model builder (factory)
// Create a model which capture the problem statement as well as the simulation
// workflow.
class ModelBuilder {
public:
  static QsimModel createModel(const HeterogeneousMap &params);
};


// ========== Prototype Impl. ========================
// Example implementation:
class TrotterEvolution : public AnsatzGenerator {
public:
  Ansatz create_ansatz(Observable *obs,
                       const HeterogeneousMap &params) override;
};

// Estimate the cost function based on bitstring distribution,
// e.g. actual quantum hardware.
// Note: we can sub-class CostFunctionEvaluator to add post-processing or
// analysis of the result.
class BitCountExpectationEstimator : public CostFunctionEvaluator {
public:
  // Evaluate the cost
  virtual double
  evaluate(std::shared_ptr<CompositeInstruction> state_prep) override;

private:
  size_t nb_samples;
};

// VQE-type workflow which involves an optimization loop, i.e. an Optimizer.
class VqeWorkflow : public QsimWorkflow {
public:
  virtual QsimResult execute() override;
  virtual bool initialize(const HeterogeneousMap &params) override;

private:
  Optimizer *optimizer;
};

// Trotter time-dependent simulation workflow
// Time-dependent Hamiltonian is a function mapping from time t to Observable
// operator.
using TdObservable = std::function<PauliOperator(double)>;

// Time-dependent evolution workflow which can handle
// time-dependent Hamiltonian operator.
class TimeDependentWorkflow : public QsimWorkflow {
public:
  virtual QsimResult execute() override;
  virtual bool initialize(const HeterogeneousMap &params) override;
  static QsimWorkflow *getInstance();

private:
  static inline TimeDependentWorkflow *instance = nullptr;
  double t_0;
  double t_final;
  double dt;
  TdObservable ham_func;
};
} // namespace qcor
 No newline at end of file
+5 −0
Original line number Diff line number Diff line
file(GLOB SRC *.cpp)

add_executable(test-qsim ${SRC})
target_include_directories(test-qsim PUBLIC ..)
target_link_libraries(test-qsim PUBLIC qcor qcor-qsim)
Loading