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

Refactor C++ -> QIR type conversion helpers



Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>
parent 458606fe
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
#include <iostream> 
#include <vector>
#include "qir-types.hpp"
#include "opt_stepper.hpp"
#include "callable_util.hpp"
#include "qir-types-utils.hpp"

// Include the external QSharp function.
qcor_include_qsharp(QCOR__DeuteronVqe__body, double, int64_t shots, Callable* opt_stepper);
@@ -37,7 +36,7 @@ int main() {
  // Q# Callable type.
  const int64_t nb_shots = 2048;
  const double final_energy = QCOR__DeuteronVqe__body(
      nb_shots, qsharp::createCallable(stepper_callable));
      nb_shots, qir::createCallable(stepper_callable));
  std::cout << "Final energy = " << final_energy << "\n";
  return 0;
}
 No newline at end of file
+28 −0
Original line number Diff line number Diff line
namespace QCOR 
{
open QCOR.Intrinsic;
// Estimate energy value in a FTQC manner.
// Passing Array type b/w C++ driver and Q#
operation Ansatz(angles : Double[], shots: Int) : Double {
    mutable numParityOnes = 0;
    use qubits = Qubit[2]
    {
        for test in 1..shots {
            Rx(angles[0], qubits[0]);
            Rx(angles[1], qubits[1]);
            CNOT(qubits[1], qubits[0]);
            // Let's measure <X0X1>
            H(qubits[0]);
            H(qubits[1]);
            if M(qubits[0]) != M(qubits[1]) 
            {
                set numParityOnes += 1;
            }
            Reset(qubits[0]);
            Reset(qubits[1]);
        }
    }
    let res =  IntAsDouble(shots - numParityOnes)/IntAsDouble(shots) - IntAsDouble(numParityOnes)/IntAsDouble(shots);
    return res;
}
}
 No newline at end of file
+20 −0
Original line number Diff line number Diff line
#include <iostream>
#include <vector>
#include "qir-types-utils.hpp"

// Include the external QSharp function.
qcor_include_qsharp(QCOR__Ansatz__body, double, ::Array *, int64_t);

// Compile with:
// Include both the qsharp source and this driver file
// in the command line.
// $ qcor -qrt ftqc vqe_ansatz.qs vqe_driver.cpp
// Run with:
// $ ./a.out
int main() {
  const std::vector<double> angles{1.0, 2.0};
  const double exp_val_xx = QCOR__Ansatz__body(qir::toArray(angles), 1024);
  std::cout << "<X0X1> = " << exp_val_xx << "\n";

  return 0;
}
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
file(GLOB SRC *.cpp)
file(GLOB HEADERS qir-types.hpp qir-qrt.hpp)
file(GLOB HEADERS qir-types.hpp qir-qrt.hpp qir-types-utils.hpp)

add_library(qir-qrt SHARED ${SRC})

+48 −20
Original line number Diff line number Diff line
#pragma once
#include "qir-types.hpp"

#include <typeinfo>
#include <vector>
// Utils for C++ -> QIR type conversion/marshaling
namespace qcor {
namespace qsharp {
// Helper to marshal (pack and unpack) C++ data to Q# QIR data
// e.g. vector is converted to Array type.
template <typename T>
TuplePtr pack(TuplePtr io_tuple, const std::vector<T> &in_vec) {
  static_assert(std::is_same<T, double>::value ||
                    std::is_same<T, int64_t>::value,
                "Only support vector of these types now.");
namespace qir {
/// Array type
// std::vector -> QIR Array
template <typename T> ::Array *toArray(const std::vector<T> &in_vec) {
  // Integral and Floating-point only
  static_assert(std::is_arithmetic_v<T>,
                "Only support vector of arithmetic types.");
  ::Array *qirArray = new ::Array(in_vec.size(), sizeof(T));
  for (size_t i = 0; i < in_vec.size(); ++i) {
    auto dest = qirArray->getItemPointer(i);
    auto src = &in_vec[i];
    memcpy(dest, src, sizeof(T));
  }
  // TODO: QIR Array supports ref-counting, needs to handle lifetime properly.
  return qirArray;
}

template <typename T> 
std::vector<T> fromArray(::Array *in_array) {
  // Integral and Floating-point only
  static_assert(std::is_arithmetic_v<T>,
                "Only support vector of arithmetic types.");
  if (in_array->element_size() != sizeof(T)) {
    throw std::bad_cast();
  }

  std::vector<T> resultArray;
  for (size_t i = 0; i < in_array->size(); ++i) {
    const T el = *(reinterpret_cast<T *>(in_array->getItemPointer(i)));
    resultArray.emplace_back(el);
  }
  return resultArray;
}

/// Tuple type
// Helper to marshal (pack and unpack) C++ data to Q# QIR data
// e.g. vector is converted to Array type.
template <typename T>
TuplePtr tuple_pack(TuplePtr io_tuple, const std::vector<T> &in_vec) {
  ::Array *qirArray = toArray(in_vec);
  TupleHeader *th = ::TupleHeader::getHeader(io_tuple);
  memcpy(io_tuple, &qirArray, sizeof(::Array *));
  return io_tuple + sizeof(::Array *);
}

// Unpack a value from the tuple and move the pointer to the next element.
template <typename T> TuplePtr unpack(TuplePtr in_tuple, T &out_val) {
template <typename T> TuplePtr tuple_unpack(TuplePtr in_tuple, T &out_val) {
  static_assert(std::is_same<T, double>::value ||
                    std::is_same<T, int64_t>::value,
                "Only support these types now.");
@@ -32,29 +59,29 @@ template <typename T> TuplePtr unpack(TuplePtr in_tuple, T &out_val) {
}

template <typename T>
TuplePtr unpack(TuplePtr in_tuple, std::vector<T> &out_val) {
TuplePtr tuple_unpack(TuplePtr in_tuple, std::vector<T> &out_val) {
  static_assert(std::is_same<T, double>::value ||
                    std::is_same<T, int64_t>::value,
                "Only support vector of these types now.");
  out_val.clear();
  ::Array *arrayPtr = *(reinterpret_cast<::Array **>(in_tuple));
  for (size_t i = 0; i < arrayPtr->size(); ++i) {
    const double el = *(reinterpret_cast<T *>(arrayPtr->getItemPointer(i)));
    out_val.emplace_back(el);
  }
  out_val = fromArray<T>(arrayPtr);
  return in_tuple + sizeof(::Array *);
}

template <typename T> T marshal_one(TuplePtr &io_ptr) {
  T t;
  io_ptr = qsharp::unpack(io_ptr, t);
  io_ptr = tuple_unpack(io_ptr, t);
  return t;
}

// Marshal a QIR tuple to std::tuple
template <typename... Args> std::tuple<Args...> marshal(TuplePtr &io_ptr) {
  return std::make_tuple(marshal_one<Args>(io_ptr)...);
}

/// Callable type
// Wrap a std::function in an IFunctor
// which can be used as a Callable.
template <typename ReturnType, typename... Args>
class qs_callback : public qsharp::IFunctor {
public:
@@ -64,18 +91,19 @@ public:
    auto tuple_ptr = args;
    auto args_tuple = marshal<Args...>(tuple_ptr);
    // Pack the result to the return QIR Tuple
    qsharp::pack(result, std::apply(m_functor, args_tuple));
    tuple_pack(result, std::apply(m_functor, args_tuple));
  }

private:
  std::function<ReturnType(Args...)> m_functor;
};

// Create a QIR callable from a std::function.
template <typename ReturnType, typename... Args>
Callable *createCallable(std::function<ReturnType(Args...)> &in_func) {
  auto functor = new qs_callback<ReturnType, Args...>(in_func);
  // Create a QIR callable
  return new Callable(functor);
}
} // namespace qsharp
} // namespace qir
} // namespace qcor
Loading