Commit 3f5a7a78 authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

Adding custom objective function taskInitiate and example

parent 1f5a0628
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -86,7 +86,6 @@ bool FuzzyParsingExternalSemaSource::LookupUnqualified(clang::LookupResult &R,
      S->getFlags() != 128 && S->getBlockParent() != nullptr) {

    if (!qbit) {
        std::cout << unknownName <<  ", TRYING TO FIND QBIT\n";
      // Save pointers to xacc::qbit, xacc::HeterogeneousMap&& ParmVarDecl
      qbit = FirstDeclMatcher<ParmVarDecl>().match(
          ci.getASTContext().getTranslationUnitDecl(),
+7 −3
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include "clang/Parse/ParseAST.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/Tooling.h"
#include <type_traits>

#include "xacc.hpp"

@@ -28,14 +29,17 @@ protected:
public:
  make_pair_visitor(std::stringstream &ss, std::vector<std::string> &c)
      : s(ss), captures(c) {}
  template <typename T> void operator()(const std::string &key, const T &t) {
    if (std::find(captures.begin(), captures.end(), key) !=
        std::end(captures)) {

  void operator()(const std::string &key, const std::string &t) {
    if (std::find(captures.begin(), captures.end(), t) != std::end(captures)) {
      s << "std::make_pair(\"" << key << "\"," << t << "),";
    } else {
      s << "std::make_pair(\"" << key << "\",\"" << t << "\"),";
    }
  }
  template <typename T> void operator()(const std::string &key, const T &t) {
    s << "std::make_pair(\"" << key << "\"," << t << "),";
  }
};

class QCORASTVisitor : public RecursiveASTVisitor<QCORASTVisitor> {
+114 −0
Original line number Diff line number Diff line
#include "qcor.hpp"

class QCBM : public qcor::ObjectiveFunction {
protected:
  // Example Bars and Stripes PDF
  std::vector<double> target{
      0.16666667, 0.,         0., 0.16666667, 0., 0.16666667, 0.,        0., 0.,
      0.,         0.16666667, 0., 0.16666667, 0., 0.,         0.16666667};

  // Helper function
  double entropy(const std::vector<double> p, const std::vector<double> q) {
    double sum = 0.0;
    for (int i = 0; i < p.size(); i++) {
      if (std::fabs(p[i]) > 1e-12) {
        sum += p[i] * std::log(p[i] / q[i]);
      }
    }
    return sum;
  }

public:
  // All ObjectiveFunctions must implement this method.
  // QCOR will take your ObjectiveFunction and inject it
  // with the Observable (observable), Kernel (kernel),
  // and Global ResultsBuffer (buffer). You can use those as
  // you wish to construct your function. Also, note you have
  // reference to the backend QPU (backend) that this
  // file was compiled for.
  double operator()(const std::vector<double> &x) override {
    // Evaluate the ansatz at the given parameters.
    auto evaledKernel = kernel->operator()(x);
    // Observe the ansatz (appends measurements). We
    // should only have one measured kernel
    auto observed = observable->observe(evaledKernel)[0];

    // Create a temp buffer to store results
    auto tmpBuffer = qcor::qalloc(buffer->size());
    // Execute
    backend->execute(tmpBuffer, observed);
    // Get the counts
    auto counts = tmpBuffer->getMeasurementCounts();
    int shots = 0;
    for (auto &x : counts) {
      shots += x.second;
    }

    // Compute the probability distribution
    std::vector<double> q(target.size()); // all zeros
    for (auto &x : counts) {
      int idx = std::stoi(x.first, nullptr, 2);
      q[idx] = (double) x.second / shots;
    }
    // get M=1/2(P+Q)
    std::vector<double> m(target.size());
    for (int i = 0; i < m.size(); i++)
      m[i] = .5 * (target[i] + q[i]);

    auto js = 0.5 * (entropy(target, m) + entropy(q, m));
    std::cout << "JS: " << js <<  "\n";
    return js;
  }
};

int main(int argc, char **argv) {

  // Initialize QCOR
  qcor::Initialize(argc, argv);

  // Create our custom ObjectiveFunction
  // as a shared_ptr, this is the QCBM work
  // from https://journals.aps.org/pra/abstract/10.1103/PhysRevA.99.062323
  auto qcbm = std::make_shared<QCBM>();

  // Define a few of the CNOT couplings for the
  // HWE ansatz, the paper defines dc3 and dc4 amongst others
  std::vector<std::pair<int, int>> dc3{{0, 1}, {0,2}, {0, 3}};
  std::vector<std::pair<int, int>> dc4{{0, 1}, {0,2}, {1, 3}, {2,3}};

  // Create the quantum kernel, a standard lambda
  // that uses the HWE circuit generator
  auto ansatz = [&](qbit q, std::vector<double> x) {
    hwe(q, x, {{"nq", 4}, {"layers", 1}, {"coupling", dc4}});
  };

  // Create the NLOpt cobyla optimizer
  // FIXME implement ADAM optimizer from mlpack
  auto optimizer =
      qcor::getOptimizer("nlopt", {std::make_pair("nlopt-optimizer", "cobyla"),
                                   std::make_pair("nlopt-maxeval", 500)});

  // Call taskInitiate with the objective function
  // and optimizer, default initial params (all zeros)
  // No observable implies QCOR will measure in
  // computational basis
  auto handle =
      qcor::taskInitiate(ansatz, qcbm, optimizer, std::vector<double>{});

  // We have the handle, its an async call,
  // maybe go do other work...

  // Get the results
  auto results = qcor::sync(handle);

  auto optJS = results->getInformation("opt-val").as<double>();
  auto params = results->getInformation("opt-params").as<std::vector<double>>();

  std::cout << "Opt JS = " << optJS << "\n";
  std::cout << "At parameters: "
            << params
            << "\n";

  // Finalize the framewokr
  qcor::Finalize();
}
+49 −42
Original line number Diff line number Diff line
@@ -94,28 +94,24 @@ using ResultBuffer = xacc::qbit;
// this handle to query the results using the qcor::sync call
using Handle = std::future<ResultBuffer>;

class ObjectiveFunction : public OptFunction {
class ObjectiveFunction
    : public std::function<double(const std::vector<double> &)> {
protected:
  std::shared_ptr<Observable> observable;
  std::shared_ptr<CompositeInstruction> kernel;
  std::vector<std::shared_ptr<CompositeInstruction>> kernels;
  std::shared_ptr<Accelerator> backend;
  ResultBuffer buffer;
  std::shared_ptr<AcceleratorBuffer> buffer;

public:
  ObjectiveFunction() = default;
  const int dimensions() const override { return kernel->nVariables(); }
  double operator()(const std::vector<double> &params) override {
    xacc::error("[qpu_handler] You must implement operator()() on custom "
                "ObjectiveFunction.");
    return 0.0;
  }
  virtual double operator()(const std::vector<double> &params) = 0;
  void initialize(std::shared_ptr<Observable> obs,
                  std::shared_ptr<CompositeInstruction> k, ResultBuffer b) {
                  std::shared_ptr<CompositeInstruction> k, std::shared_ptr<AcceleratorBuffer> b) {
    observable = obs;
    kernel = k;
    kernels = obs->observe(k);
    buffer = b;
    backend = xacc::getAccelerator(kernel->accelerator_signature());
    buffer = b;
  }
};

@@ -338,6 +334,27 @@ public:

    algo->execute(buffer);
  }

  template <typename QuantumKernel, typename... InitialArgs>
  void execute(QuantumKernel &kernel, std::shared_ptr<ObjectiveFunction> objFunction,
               std::shared_ptr<Optimizer> optimizer,
               std::shared_ptr<Observable> observable, InitialArgs... args) {
    auto function = qcor::__internal::getCompositeInstruction(kernel, args...);
    auto nLogicalBits = function->nLogicalBits();
    if (!buffer) {
      buffer = xacc::qalloc(nLogicalBits);
    }

    objFunction->initialize(observable, function, buffer);

    OptFunction optF(
        [&, objFunction](const std::vector<double> &x) { return objFunction->operator()(x); },
        function->nVariables());
    auto result = optimizer->optimize(optF);

    buffer->addExtraInfo("opt-val", ExtraInfo(result.first));
    buffer->addExtraInfo("opt-params", ExtraInfo(result.second));
  }
};

// Full TaskInitiate, built in objective function (given by its name)
@@ -349,7 +366,6 @@ taskInitiate(QuantumKernel &&kernel, const std::string objectiveFunctionName,
  auto function = qcor::__internal::getCompositeInstruction(kernel, args...);
  auto parameters = __internal::parametersFromVariadic(function, args...);
  return qcor::submit([&, parameters, function](qcor::qpu_handler &q) {

    auto nLogicalBits = function->nLogicalBits();
    auto accelerator = xacc::getAccelerator(function->accelerator_signature());
    auto buffer = xacc::qalloc(nLogicalBits);
@@ -403,40 +419,31 @@ taskInitiate(QuantumKernel &&kernel, const std::string objectiveFunctionName,
  });
}

// FIXME Need to think about this more...
//
// Custom objective function
// Custom Objective Function, no Observable
// template <typename QuantumKernel, typename... InitialArgs>
// Handle taskInitiate(QuantumKernel &&kernel, ObjectiveFunction &&objFunction,
//                     std::shared_ptr<Optimizer> optimizer,
//                     std::shared_ptr<Observable> observable,
//                     InitialArgs... args) {
//   return taskInitiate(kernel, objFunction, optimizer, observable, args...);
// }
// template <typename QuantumKernel, typename... InitialArgs>
// Handle taskInitiate(QuantumKernel &&kernel, ObjectiveFunction &objFunction,
//                     std::shared_ptr<Optimizer> optimizer,
//                     std::shared_ptr<Observable> observable,
//                     InitialArgs... args) {
//   auto function = qcor::__internal::getCompositeInstruction(kernel, args...);
//   auto parameters = __internal::parametersFromVariadic(function, args...);

//   return qcor::submit([&, parameters, function](qcor::qpu_handler &q) {
//     // auto function = qcor::__internal::getCompositeInstruction(kernel,
//     // args...);
//     auto nLogicalBits = function->nLogicalBits();
//     auto accelerator = xacc::getAccelerator(function->accelerator_signature());
//     auto buffer = xacc::qalloc(nLogicalBits);
//     optimizer->appendOption("initial-parameters", parameters);

//     objFunction.initialize(observable, function, buffer);

//     auto result = optimizer->optimize(objFunction);
//     buffer->addExtraInfo("opt-val", ExtraInfo(result.first));
//     buffer->addExtraInfo("opt-params", ExtraInfo(result.second));
//   });
//                     std::shared_ptr<Optimizer> optimizer, InitialArgs... args) {
//   return taskInitiate(kernel, objFunction, optimizer, args...);
// }
template <typename QuantumKernel, typename... InitialArgs>
Handle taskInitiate(QuantumKernel &&kernel, std::shared_ptr<ObjectiveFunction> objFunction,
                    std::shared_ptr<Optimizer> optimizer, InitialArgs... args) {
  auto function = qcor::__internal::getCompositeInstruction(kernel, args...);
  auto parameters = __internal::parametersFromVariadic(function, args...);

  return qcor::submit(
      [&, parameters, function, optimizer](qcor::qpu_handler &qh) {

        optimizer->appendOption("initial-parameters", parameters);

        std::string allZsObsStr = "";
        for (int i = 0; i < function->nLogicalBits(); i++) {
          allZsObsStr += "Z" + std::to_string(i) + " ";
        }
        auto observable = getObservable("pauli", allZsObsStr);
        qh.execute(kernel, objFunction, optimizer, observable, args...);
      });
}
} // namespace qcor

#endif