Commit d252f2c7 authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

setup createObjectiveFunction in python to take openfermion data structures for observable

parent b3e504c8
Loading
Loading
Loading
Loading
+101 −0
Original line number Diff line number Diff line
@@ -93,6 +93,69 @@ xacc::HeterogeneousMap heterogeneousMapConvert(

  return result;
}

std::shared_ptr<qcor::Observable> convertToPauliOperator(py::object op) {
  if (py::hasattr(op, "terms")) {
    // this is from openfermion
    if (py::hasattr(op, "is_two_body_number_conserving")) {
      // This is a fermion Operator
      auto terms = op.attr("terms");
      // terms is a list of tuples
      std::stringstream ss;
      int i = 0;
      for (auto term : terms) {
        auto term_tuple = term.cast<py::tuple>();
        if (!term_tuple.empty()) {
          ss << terms[term].cast<std::complex<double>>() << " ";
          for (auto element : term_tuple) {
            auto element_pair = element.cast<std::pair<int, int>>();
            ss << element_pair.first << (element_pair.second ? "^" : "") << " ";
          }
        } else {
          // this was identity
          ss << terms[term].cast<double>();
        }
        i++;
        if (i != py::len(terms)) {
          ss << " + ";
        }
      }
      auto obs_tmp = qcor::createOperator("fermion", ss.str());
      return qcor::operatorTransform("jw", obs_tmp);

    } else {
      // this is a qubit  operator
      auto terms = op.attr("terms");
      // terms is a list of tuples
      std::stringstream ss;
      int i = 0;
      for (auto term : terms) {
        auto term_tuple = term.cast<py::tuple>();
        if (!term_tuple.empty()) {
          ss << terms[term].cast<std::complex<double>>() << " ";
          for (auto element : term_tuple) {
            auto element_pair = element.cast<std::pair<int, std::string>>();
            ss << element_pair.second << element_pair.first << " ";
          }
        } else {
          // this was identity
          ss << terms[term].cast<double>();
        }
        i++;
        if (i != py::len(terms)) {
          ss << " + ";
        }
      }
      return qcor::createOperator(ss.str());
    }
  } else {
    // throw an error
    qcor::error(
        "Invalid python object passed as a QCOR Operator/Observable. "
        "Currently, we only accept OpenFermion datastructures.");
  }
}

}  // namespace

namespace qcor {
@@ -143,6 +206,31 @@ class PyObjectiveFunction : public qcor::ObjectiveFunction {
    qjit.write_cache();
  }

  PyObjectiveFunction(py::object q, std::shared_ptr<qcor::Observable> &qq,
                      const int n_dim, const std::string &helper_name,
                      xacc::HeterogeneousMap opts = {})
      : py_kernel(q) {
    // Set the OptFunction dimensions
    _dim = n_dim;

    // Set the helper objective
    helper = xacc::getService<qcor::ObjectiveFunction>(helper_name);

    // Store the observable pointer and give it to the helper
    observable = qq;
    options = opts;
    options.insert("observable", observable);
    helper->set_options(options);
    helper->update_observable(observable);

    // Extract the QJIT source code
    auto src = py_kernel.attr("get_internal_src")().cast<std::string>();

    // QJIT compile
    // this will be fast if already done, and we just do it once
    qjit.jit_compile(src, true);
    qjit.write_cache();
  }
  // Evaluate this ObjectiveFunction at the dictionary of kernel args,
  // return the scalar value
  double operator()(const KernelArgDict args, std::vector<double> &dx) {
@@ -368,6 +456,19 @@ PYBIND11_MODULE(_pyqcor, m) {
        return obj;
      },
      "");
  m.def(
      "createObjectiveFunction",
      [](py::object kernel, py::object &py_obs, const int n_params,
         PyHeterogeneousMap &options) {
        auto nativeHetMap = heterogeneousMapConvert(options);
        auto obs = convertToPauliOperator(py_obs);
        auto q = ::qalloc(obs->nBits());
        std::shared_ptr<qcor::ObjectiveFunction> obj =
            std::make_shared<qcor::PyObjectiveFunction>(kernel, obs, n_params,
                                                        "vqe", nativeHetMap);
        return obj;
      },
      "");

  m.def(
      "createOperator",
+46 −0
Original line number Diff line number Diff line
import unittest
from qcor import *
try:
    from openfermion.ops import FermionOperator as FOp
    from openfermion.ops import QubitOperator as QOp
    from openfermion.transforms import reverse_jordan_wigner, jordan_wigner
    
    class TestOpenFermion(unittest.TestCase):
        def test_simple_fermion(self):
            # Create Operator as OpenFermion FermionOperator
            H = FOp('', 0.0002899) + FOp('0^ 0', -.43658) + \
                FOp('1 0^', 4.2866) + FOp('1^ 0', -4.2866) + FOp('1^ 1', 12.25) 
              
            @qjit
            def ansatz(q: qreg, theta: float):
                X(q[0])
                Ry(q[1], theta)
                CX(q[1], q[0])
        
            n_params = 1
            obj = createObjectiveFunction(ansatz, H, n_params, {'gradient-strategy':'parameter-shift'})
            optimizer = createOptimizer('nlopt', {'nlopt-optimizer':'l-bfgs'})
            results = optimizer.optimize(obj)
            self.assertAlmostEqual(results[0], -1.74, places=1) 

        def test_simple_qubit(self):
            # Create Operator as OpenFermion FermionOperator
            H = QOp('', 5.907) + QOp('Y0 Y1', -2.1433) + \
                QOp('X0 X1', -2.1433) + QOp('Z0', .21829) + QOp('Z1', -6.125) 
              
            @qjit
            def ansatz(q: qreg, theta: float):
                X(q[0])
                Ry(q[1], theta)
                CX(q[1], q[0])
        
            n_params = 1
            obj = createObjectiveFunction(ansatz, H, n_params, {'gradient-strategy':'parameter-shift'})
            optimizer = createOptimizer('nlopt', {'nlopt-optimizer':'l-bfgs'})
            results = optimizer.optimize(obj)
            self.assertAlmostEqual(results[0], -1.74, places=1) 
except:
    pass

if __name__ == '__main__':
  unittest.main()
 No newline at end of file
+4 −5
Original line number Diff line number Diff line
import unittest
import unittest, math
from qcor import *
import numpy as np

class TestWorkflows(unittest.TestCase):
    def test_td_workflow(self):
      # Time-dependent Hamiltonian: 
      # Returns the Pauli operators at a time point.
      def td_hamiltonian(t):
        Jz = 2 * np.pi * 2.86265 * 1e-3
        Jz = 2 * math.pi * 2.86265 * 1e-3
        epsilon = Jz
        omega = 4.8 * 2 * np.pi * 1e-3
        return -Jz * Z(0) * Z(1)  - Jz * Z(1) * Z(2) + (-epsilon * np.cos(omega * t)) * (X(0) + X(1) + X(2)) 
        omega = 4.8 * 2 * math.pi * 1e-3
        return -Jz * Z(0) * Z(1)  - Jz * Z(1) * Z(2) + (-epsilon * math.cos(omega * t)) * (X(0) + X(1) + X(2)) 

      # Observable = average magnetization
      observable = (1.0 / 3.0) * (Z(0) + Z(1) + Z(2))
+11 −0
Original line number Diff line number Diff line
#include "qcor_observable.hpp"

#include "ObservableTransform.hpp"
#include "xacc.hpp"
#include "xacc_quantum_gate_api.hpp"
#include "xacc_service.hpp"
@@ -85,6 +86,16 @@ std::shared_ptr<Observable> createOperator(const std::string &name,
  return createObservable(name, options);
}

std::shared_ptr<Observable> operatorTransform(const std::string &type,
                                              qcor::Observable &op) {
  // return xacc::getService<xacc::ObservableTransform>(type)->transform(
  //     xacc::as_shared_ptr(*&op));
}
std::shared_ptr<Observable> operatorTransform(const std::string &type,
                                              std::shared_ptr<Observable> op) {
  return xacc::getService<xacc::ObservableTransform>(type)->transform(op);
}

namespace __internal__ {
std::vector<std::shared_ptr<xacc::CompositeInstruction>> observe(
    std::shared_ptr<xacc::Observable> obs,
+2 −0
Original line number Diff line number Diff line
@@ -107,5 +107,7 @@ std::shared_ptr<Observable> createOperator(const std::string& name, const std::s
std::shared_ptr<Observable> createOperator(const std::string &name, HeterogeneousMap&& options);
std::shared_ptr<Observable> createOperator(const std::string &name, HeterogeneousMap& options);

std::shared_ptr<Observable> operatorTransform(const std::string& type, qcor::Observable& op);
std::shared_ptr<Observable> operatorTransform(const std::string& type, std::shared_ptr<Observable> op);

} // namespace qcor
 No newline at end of file