Unverified Commit 682e3a69 authored by Mccaskey, Alex's avatar Mccaskey, Alex Committed by GitHub
Browse files

Merge pull request #71 from tnguyen-ornl/tnguyen/heisenberg-model

Ability to load parameterized Heisenberg model
parents 98e11c60 771e7c57
Loading
Loading
Loading
Loading
Loading
+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;
}
+1 −1
Original line number Diff line number Diff line
@@ -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);
+3 −5
Original line number Diff line number Diff line
@@ -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);
+104 −0
Original line number Diff line number Diff line
@@ -36,6 +36,85 @@ ModelBuilder::createModel(Observable *obs, const HeterogeneousMap &params) {
  return model;
}

QuantumSimulationModel
ModelBuilder::createModel(ModelType type, const HeterogeneousMap &params) {
  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 &params) {
@@ -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 &params) {
  const auto getKeyIfExists = [&params](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
+45 −1
Original line number Diff line number Diff line
@@ -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 &params = {});

@@ -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 &params);

    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
@@ -121,7 +162,10 @@ public:
  static QuantumSimulationModel createModel(const std::string &format,
                                           const std::string &data,
                                           const HeterogeneousMap &params = {});

  // Predefined model type that we support intrinsically.
  enum class ModelType { Heisenberg };
  static QuantumSimulationModel createModel(ModelType type,
                                           const HeterogeneousMap &params);
  // ========== QuantumSimulationModel with a fixed (pre-defined) ansatz ========
  // The ansatz is provided as a QCOR kernel.
  template <typename... Args>
Loading