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

Work on a simple resources estimator Accelerator



Using an Accelerator is the least invasive way to do this, hence requires the least code changes.

Usage: -qrt ftqc -qpu tracer

Users can also provide custom measure probability to control measure emulation (default => return 0)

Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>
parent d30f905a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -523,6 +523,9 @@ void __quantum__rt__finalize() {
    xacc::internal_compiler::execute_pass_manager();
    ::quantum::submit(global_qreg.get());
  }
  
  // QRT finalization
  ::quantum::finalize();
}

bool __quantum__rt__result_equal(Result *res, Result *comp) {
+1 −1
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@
# *******************************************************************************/
set(LIBRARY_NAME qcor-ftqc-qrt)

file(GLOB SRC *.cpp)
file(GLOB SRC *.cpp tracer/*.cpp)

usfunctiongetresourcesource(TARGET ${LIBRARY_NAME} OUT SRC)
usfunctiongeneratebundleinit(TARGET ${LIBRARY_NAME} OUT SRC)
+10 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include "xacc.hpp"
#include "xacc_internal_compiler.hpp"
#include "xacc_service.hpp"
#include "tracer/ResourcesTracerAccelerator.hpp"
using namespace cppmicroservices;

namespace {
@@ -279,6 +280,13 @@ private:
    }
  }

  virtual void finalize() override {
    if (std::dynamic_pointer_cast<qcor::TracerAccelerator>(qpu)) {
      std::dynamic_pointer_cast<qcor::TracerAccelerator>(qpu)
          ->printResourcesEstimationReport();
    }
  }

private:
  bool mark_as_compute = false;
  // Are we in a instruction collection mode?
@@ -302,6 +310,8 @@ public:
  void Start(BundleContext context) {
    auto xt = std::make_shared<qcor::FTQC>();
    context.RegisterService<quantum::QuantumRuntime>(xt);
    context.RegisterService<xacc::Accelerator>(
        std::make_shared<qcor::TracerAccelerator>());
  }
  void Stop(BundleContext /*context*/) {}
};
+90 −0
Original line number Diff line number Diff line
#include "ResourcesTracerAccelerator.hpp"
#include <iomanip>
#include <random>
using namespace xacc;
namespace qcor {
void TracerAccelerator::initialize(const HeterogeneousMap &params) {
  qubitIdToMeasureProbs.clear();

  if (params.keyExists<std::vector<double>>("meas1-probs")) {
    const auto meas1Probs = params.get<std::vector<double>>("meas1-probs");
    for (size_t i = 0; i < meas1Probs.size(); ++i) {
      if (meas1Probs[i] < 0.0 || meas1Probs[i] > 1.0) {
        xacc::error("Invalid measure probability setting: " +
                    std::to_string(meas1Probs[i]));
      }
      qubitIdToMeasureProbs[i] = meas1Probs[i];
    }
  }
}

// For NISQ: resource estimation is simply printing out the circuit....
void TracerAccelerator::execute(
    std::shared_ptr<AcceleratorBuffer> buffer,
    const std::shared_ptr<CompositeInstruction> compositeInstruction) {
  xacc::error("Unsupported!!!");
}
void TracerAccelerator::execute(
    std::shared_ptr<AcceleratorBuffer> buffer,
    const std::vector<std::shared_ptr<CompositeInstruction>>
        compositeInstructions) {
  xacc::error("Unsupported!!!");
}
void TracerAccelerator::apply(std::shared_ptr<AcceleratorBuffer> buffer,
                              std::shared_ptr<Instruction> inst) {
  for (const auto &bitId : inst->bits()) {
    qubit_idxs.emplace(bitId);
  }
  if (gateNameToCount.find(inst->name()) == gateNameToCount.end()) {
    gateNameToCount[inst->name()] = 1;
  } else {
    gateNameToCount[inst->name()] += 1;
  }
  // Emulate measure:
  if (inst->name() == "Measure") {
    const double meas1Prob = getMeas1Prob(inst->bits()[0]);
    static const auto generateRan = []() {
      std::random_device rd;
      std::mt19937 gen(rd());
      std::uniform_real_distribution<> dis(0, 1);
      return dis(gen);
    };

    // meas1Prob = 1.0 => always returns true
    // meas1Prob = 0.0 => always returns false
    const auto measRes = generateRan() < meas1Prob;
    buffer->measure(inst->bits()[0], (measRes ? 1 : 0));
  }
}

void TracerAccelerator::printResourcesEstimationReport() {
  // Print resources estimation result:
  // Currently, simply print gate count:
  std::cout << "Resources Estimation Result:\n";
  std::cout << "Number of qubit required: " << qubit_idxs.size() << "\n";
  std::cout << "Gate Count Report: \n";
  size_t totalNumberGates = 0;
  std::stringstream stream;
  const size_t nbColumns = 2;
  const size_t columnWidth = 8;
  const auto totalWidth = nbColumns * columnWidth + 6;
  stream << std::string(totalWidth, '-') << "\n";
  stream << "| " << std::left << std::setw(8) << "GATE"
         << " |";
  stream << std::left << std::setw(8) << "COUNT"

         << " |\n";
  stream << std::string(totalWidth, '-') << "\n";

  const auto printEachRow = [&](const std::string &gateName, int count) {
    stream << "| " << std::setw(8) << gateName << " |";
    stream << std::setw(8) << count << " |\n";
  };

  for (const auto &[gateName, count] : gateNameToCount) {
    printEachRow(gateName, count);
  }
  stream << std::string(totalWidth, '-') << "\n";
  std::cout << stream.str();
}
} // namespace qcor
+60 −0
Original line number Diff line number Diff line
#pragma one
#include "xacc.hpp"

namespace qcor {
// Descriptions:
// The TracerAccelerator estimates resources (gate counts, qubits) of a quantum
// program by emulating execution the quantum program (not static analysis). The
// idea is to collect/trace a set of metrics during the execution. Measurement
// results can be emulated in different modes: (1) Fixed 0 or 1. (2) Random with
// provided probability threshold. i.e., users can run multiple randomized
// tracing runs to compute the upper bound estimate of resources. This provides
// similar functionalities as the QDK resources estimator
// https://docs.microsoft.com/en-us/azure/quantum/user-guide/machines/resources-estimator
class TracerAccelerator : public xacc::Accelerator {
public:
  // Identifiable interface impls
  virtual const std::string name() const override { return "tracer"; }
  virtual const std::string description() const override { return ""; }

  // Accelerator interface impls
  virtual void initialize(const xacc::HeterogeneousMap &params = {}) override;
  virtual void updateConfiguration(const xacc::HeterogeneousMap &config) override {
    initialize(config);
  };
  virtual const std::vector<std::string> configurationKeys() override {
    return {};
  }
  virtual xacc::HeterogeneousMap getProperties() override { return {}; }
  virtual void execute(std::shared_ptr<xacc::AcceleratorBuffer> buffer,
                       const std::shared_ptr<xacc::CompositeInstruction>
                           compositeInstruction) override;
  virtual void execute(std::shared_ptr<xacc::AcceleratorBuffer> buffer,
                       const std::vector<std::shared_ptr<xacc::CompositeInstruction>>
                           compositeInstructions) override;
  virtual void apply(std::shared_ptr<xacc::AcceleratorBuffer> buffer,
                     std::shared_ptr<xacc::Instruction> inst) override;
  void printResourcesEstimationReport();
private:
  // Probability to measure 1:
  std::unordered_map<size_t, double> qubitIdToMeasureProbs;
  double getMeas1Prob(size_t bitIdx) {
    if (qubitIdToMeasureProbs.find(bitIdx) == qubitIdToMeasureProbs.end()) {
      // Default measurement = 0 => prob gets 1 == 0.0
      return 0.0;
    } else {
      // There is custom measurement probability setting for this qubit.
      return qubitIdToMeasureProbs[bitIdx];
    }
  }
  std::set<size_t> qubit_idxs;
  // Resources metric
  // Gate name to count
  std::unordered_map<std::string, size_t> gateNameToCount;
  // TODO:
  // - We could in-principle perform gate layering during tracing
  // i.e., gates on non-overlapping qubits.
  // => add a circuit depth metric
  // - Number of ancilla allocations, etc.
};
} // namespace qcor
 No newline at end of file
Loading