Commit 8db62292 authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

Merge branch 'master' of https://github.com/eclipse/xacc

parents 3b9b1b56 52934dbd
import xacc
qpu = xacc.getAccelerator('qpp', {'shots':100})
qpu = xacc.getAccelerator('aer', {'shots':1024})
xacc.qasm('''
.compiler xasm
......
......@@ -17,5 +17,21 @@ bool IfStmt::expand(const HeterogeneousMap &runtimeOptions) {
}
return true;
}
const std::string IfStmt::toString() {
std::stringstream retStr;
retStr << "if (" << bufferName << "[" << bitIdx << "]) {\n";
for (auto i : instructions) {
if (i->isComposite() &&
!std::dynamic_pointer_cast<CompositeInstruction>(i)->hasChildren()) {
retStr << " " << i->name() << "()\n";
} else {
retStr << " " << i->toString() << "\n";
}
}
retStr << "}\n";
return retStr.str();
}
} // namespace quantum
} // namespace xacc
\ No newline at end of file
......@@ -93,6 +93,7 @@ public:
}
}
const std::string toString() override;
DEFINE_CLONE(IfStmt)
DEFINE_VISITABLE()
};
......
......@@ -368,6 +368,11 @@ std::vector<QFAST::Block> QFAST::refine(const std::vector<QFAST::PauliReps>& in_
// Set the tolerance so that it will terminate when the
// trace distance is sufficiently converged.
optimizer->appendOption("mlpack-tolerance", m_distanceLimit/20.0);
if (optimizer->get_algorithm() == "adam")
{
optimizer->appendOption("adam-exact-objective", true);
}
}
if (needGrads)
......@@ -617,6 +622,10 @@ bool QFAST::optimizeAtDepth(std::vector<PauliReps>& io_repsToOpt, double in_targ
optimizer->appendOption("mlpack-max-iter", maxEval);
optimizer->appendOption("mlpack-step-size", 0.01);
optimizer->appendOption("mlpack-tolerance", in_targetDistance/100.0);
if (optimizer->get_algorithm() == "adam")
{
optimizer->appendOption("adam-exact-objective", true);
}
}
if (needGrads)
......
......@@ -26,6 +26,7 @@ TEST(QFastTester, checkSimple)
// Pick 0 or 1
const int randPick = rand() % 2;
const std::string optimizerName = (randPick == 0) ? "nlopt" : "mlpack";
// Randomly pick an optimizer:
// To save test time, we don't want to run many long tests.
auto optimizer = xacc::getOptimizer(optimizerName);
......
......@@ -31,6 +31,7 @@ protected:
std::string experimentName;
int nTotalQubits = 0;
std::vector<int> usedMemorySlots;
std::optional<int64_t> conditionalRegId;
public:
int maxMemorySlots = 0;
......@@ -49,6 +50,7 @@ public:
qubit2MemorySlot.insert({b, counter});
counter++;
}
xacc::ibm::RegisterAllocator::getInstance()->reset();
}
const std::string toString() override {
......@@ -63,7 +65,7 @@ public:
// std::to_string(experiment.get_instructions().size()));
if (experiment.get_instructions().empty()) {
// maxMemorySlots++;
// maxMemorySlots++;
// construct config and header, add
ExperimentHeader header;
......@@ -101,12 +103,18 @@ public:
return experiment;
}
void setConditional(xacc::ibm::Instruction &io_inst) {
if (conditionalRegId.has_value()) {
io_inst.set_condition_reg_id(conditionalRegId.value());
}
}
void visit(Hadamard &h) override {
xacc::ibm::Instruction inst;
inst.get_mutable_qubits().push_back(h.bits()[0]);
inst.get_mutable_name() = "u2";
inst.set_params({0.0, pi});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -179,7 +187,7 @@ public:
inst.get_mutable_qubits().push_back(cn.bits()[0]);
inst.get_mutable_qubits().push_back(cn.bits()[1]);
inst.get_mutable_name() = "cx";
setConditional(inst);
instructions.push_back(inst);
}
......@@ -192,6 +200,7 @@ public:
inst.get_mutable_name() = "u3";
inst.set_params({pi, 0.0, pi});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -202,6 +211,7 @@ public:
inst.get_mutable_name() = "u3";
inst.set_params({pi, pi / 2.0, pi});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -212,6 +222,7 @@ public:
inst.get_mutable_name() = "u1";
inst.set_params({pi});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -223,7 +234,7 @@ public:
inst.set_params({u.getParameter(0).as<double>(),
u.getParameter(1).as<double>(),
u.getParameter(2).as<double>()});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -239,22 +250,20 @@ public:
inst.get_mutable_qubits().push_back(m.bits()[0]);
inst.get_mutable_name() = "measure";
inst.set_memory({maxMemorySlots});
instructions.push_back(inst);
// if (classicalBit > maxMemorySlots) {
maxMemorySlots++;// = qubit2MemorySlot[m.bits()[0]];
maxMemorySlots++; // = qubit2MemorySlot[m.bits()[0]];
// }
}
void visit(Rx &rx) override {
xacc::ibm::Instruction inst;
inst.get_mutable_qubits().push_back(rx.bits()[0]);
inst.get_mutable_name() = "u3";
inst.set_params(
{rx.getParameter(0).as<double>(), -1.0 * pi / 2.0, pi / 2.0});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -263,7 +272,7 @@ public:
inst.get_mutable_qubits().push_back(ry.bits()[0]);
inst.get_mutable_name() = "u3";
inst.set_params({ry.getParameter(0).as<double>(), 0.0, 0.0});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -272,7 +281,7 @@ public:
inst.get_mutable_qubits().push_back(rz.bits()[0]);
inst.get_mutable_name() = "u1";
inst.set_params({rz.getParameter(0).as<double>()});
setConditional(inst);
instructions.push_back(inst);
}
......@@ -291,6 +300,20 @@ public:
visit(u1_3);
}
void visit(IfStmt &ifStmt) override {
const auto cregId = ifStmt.bits()[0];
auto bfuncInst = xacc::ibm::Instruction::createConditionalInst(cregId);
const auto regId = bfuncInst.get_bFunc()->registerId;
instructions.push_back(bfuncInst);
// All the instructions that are scoped inside this block must have a
// `condition` field points to this register Id.
conditionalRegId = regId;
for (auto &i : ifStmt.getInstructions()) {
i->accept(this);
}
conditionalRegId.reset();
}
/**
* The destructor
*/
......
......@@ -289,16 +289,88 @@ public:
}
};
struct Bfunc {
Bfunc(int64_t in_regId, const std::string &in_hexMask,
const std::string &in_relation = "==",
const std::string &in_val = "")
: registerId(in_regId), hex_mask(in_hexMask), relation(in_relation),
hex_val(in_val.empty() ? in_hexMask : in_val) {}
int64_t registerId;
std::string hex_mask;
std::string relation;
std::string hex_val;
};
class RegisterAllocator {
static inline RegisterAllocator *instance = nullptr;
std::unordered_map<int64_t, int64_t> memoryToRegister;
int64_t registerId;
RegisterAllocator() { registerId = 0; }
public:
static RegisterAllocator *getInstance() {
if (!instance) {
instance = new RegisterAllocator;
}
return instance;
}
std::optional<int64_t> getRegisterId(int64_t in_memoryId) {
if (memoryToRegister.find(in_memoryId) != memoryToRegister.end()) {
return memoryToRegister[in_memoryId];
}
return std::nullopt;
}
int64_t mapMemory(int64_t in_memoryId) {
memoryToRegister[in_memoryId] = registerId;
registerId++;
return memoryToRegister[in_memoryId];
}
int64_t getNextRegister() {
const auto result = registerId;
++registerId;
return result;
}
void reset() {
memoryToRegister.clear();
registerId = 0;
}
};
class Instruction {
public:
Instruction() = default;
virtual ~Instruction() = default;
// Construct a binary function instruction
// Returns the Bfunc instruction and the register Id to condition the
// sub-circuit.
static Instruction createConditionalInst(int64_t memoryId) {
// Map the memory Id to a register:
const auto measRegisterId =
RegisterAllocator::getInstance()->mapMemory(memoryId);
const int64_t maskVal = 1ULL << measRegisterId;
const std::string hexMaskStr = "0x" + std::to_string(maskVal);
const int64_t resultRegisterId =
RegisterAllocator::getInstance()->getNextRegister();
Bfunc bFuncObj(resultRegisterId, hexMaskStr);
Instruction newInst;
newInst.bfunc = bFuncObj;
return newInst;
}
private:
std::vector<int64_t> qubits;
std::string name;
std::vector<double> params;
std::vector<int64_t> memory;
// Conditional on a register value
std::optional<int64_t> conditional;
// If this is a Bfunc to compute a register value
std::optional<Bfunc> bfunc;
public:
const std::vector<int64_t> &get_qubits() const { return qubits; }
......@@ -314,6 +386,13 @@ public:
std::vector<int64_t> get_memory() const { return memory; }
void set_memory(std::vector<int64_t> value) { this->memory = value; }
std::optional<Bfunc> get_bFunc() const { return bfunc; }
void set_bFunc(Bfunc value) { this->bfunc = value; }
bool isBfuc() const { return get_bFunc().has_value(); }
std::optional<int64_t> get_condition_reg_id() const { return conditional; }
void set_condition_reg_id(int64_t value) { this->conditional = value; }
};
class Experiment {
......@@ -698,6 +777,15 @@ inline void from_json(const json &j, xacc::ibm::Instruction &x) {
inline void to_json(json &j, const xacc::ibm::Instruction &x) {
j = json::object();
if (x.isBfuc()) {
j["name"] = "bfunc";
j["register"] = x.get_bFunc()->registerId;
j["mask"] = x.get_bFunc()->hex_mask;
j["relation"] = x.get_bFunc()->relation;
j["val"] = x.get_bFunc()->hex_val;
return;
}
j["qubits"] = x.get_qubits();
j["name"] = x.get_name();
if (!x.get_params().empty()) {
......@@ -705,6 +793,20 @@ inline void to_json(json &j, const xacc::ibm::Instruction &x) {
}
if (!x.get_memory().empty()) {
j["memory"] = x.get_memory();
// Technically, we only use one memory slot in each Measure Op:
if (x.get_memory().size() == 1) {
const auto memoryId = x.get_memory()[0];
const auto measRegisterId =
xacc::ibm::RegisterAllocator::getInstance()->getRegisterId(memoryId);
if (measRegisterId.has_value()) {
std::vector<int64_t> registerIds;
registerIds.emplace_back(measRegisterId.value());
j["register"] = registerIds;
}
}
}
if (x.get_condition_reg_id().has_value()) {
j["conditional"] = x.get_condition_reg_id().value();
}
}
......
......@@ -152,7 +152,7 @@ void AerAccelerator::execute(
nlohmann::json j = nlohmann::json::parse(qobj_str)["qObject"];
j["config"]["shots"] = m_shots;
j["config"]["noise_model"] = noise_model;
xacc::info("Qobj:\n" + j.dump(2));
auto results_json = nlohmann::json::parse(
AER::controller_execute_json<AER::Simulator::QasmController>(j.dump()));
......
......@@ -298,6 +298,164 @@ TEST(AerAcceleratorTester, checkApply) {
EXPECT_EQ(resultQ0, resultQ1);
}
TEST(AerAcceleratorTester, checkConditional) {
auto accelerator = xacc::getAccelerator("aer");
xacc::set_verbose(true);
auto xasmCompiler = xacc::getCompiler("xasm");
{
auto ir = xasmCompiler->compile(R"(__qpu__ void conditionalCirc(qbit q) {
X(q[0]);
Measure(q[0]);
Measure(q[1]);
// Should apply
if (q[0]) {
X(q[2]);
}
// Not apply
if (q[1]) {
X(q[3]);
}
// Apply
X(q[4]);
Measure(q[2]);
Measure(q[3]);
Measure(q[4]);
})",
accelerator);
auto program = ir->getComposite("conditionalCirc");
auto buffer = xacc::qalloc(5);
accelerator->execute(buffer, program);
buffer->print();
// Expected: q0 = 1, q1 = 0, q2 = 1, q3 = 0, q4 = 1
EXPECT_EQ(buffer->computeMeasurementProbability("10101"), 1.0);
}
{
// Check Teleport
auto ir = xasmCompiler->compile(R"(__qpu__ void teleportOneState(qbit q) {
// State to be transported
X(q[0]);
// Bell channel setup
H(q[1]);
CX(q[1], q[2]);
// Alice Bell measurement
CX(q[0], q[1]);
H(q[0]);
Measure(q[0]);
Measure(q[1]);
// Correction
if (q[0])
{
Z(q[2]);
}
if (q[1])
{
X(q[2]);
}
// Measure teleported qubit
Measure(q[2]);
})",
accelerator);
auto program = ir->getComposite("teleportOneState");
auto buffer = xacc::qalloc(3);
accelerator->execute(buffer, program);
buffer->print();
for (const auto &bitStr : buffer->getMeasurements()) {
EXPECT_EQ(bitStr.length(), 3);
// q[2] (MSB) must be '1' (teleported)
EXPECT_EQ(bitStr[0], '1');
}
}
{
// Check Teleport
auto ir = xasmCompiler->compile(R"(__qpu__ void teleportZeroState(qbit q) {
// Bell channel setup
H(q[1]);
CX(q[1], q[2]);
// Alice Bell measurement
CX(q[0], q[1]);
H(q[0]);
Measure(q[0]);
Measure(q[1]);
// Correction
if (q[0])
{
Z(q[2]);
}
if (q[1])
{
X(q[2]);
}
// Measure teleported qubit
Measure(q[2]);
})",
accelerator);
auto program = ir->getComposite("teleportZeroState");
auto buffer = xacc::qalloc(3);
accelerator->execute(buffer, program);
buffer->print();
for (const auto &bitStr : buffer->getMeasurements()) {
EXPECT_EQ(bitStr.length(), 3);
// q[2] (MSB) must be '0' (teleported)
EXPECT_EQ(bitStr[0], '0');
}
}
{
// Check Teleport (50-50)
auto ir = xasmCompiler->compile(
R"(__qpu__ void teleportSuperpositionState(qbit q) {
// State to be transported
// superposition of 0 and 1
H(q[0]);
// Bell channel setup
H(q[1]);
CX(q[1], q[2]);
// Alice Bell measurement
CX(q[0], q[1]);
H(q[0]);
Measure(q[0]);
Measure(q[1]);
// Correction
if (q[0])
{
Z(q[2]);
}
if (q[1])
{
X(q[2]);
}
// Measure teleported qubit
Measure(q[2]);
})",
accelerator);
auto program = ir->getComposite("teleportSuperpositionState");
auto buffer = xacc::qalloc(3);
accelerator->execute(buffer, program);
buffer->print();
double zeroProb = 0.0;
double oneProb = 0.0;
for (const auto &bitStr : buffer->getMeasurements()) {
EXPECT_EQ(bitStr.length(), 3);
// q[2] (MSB) is the teleported qubit
if (bitStr[0] == '0') {
zeroProb += buffer->computeMeasurementProbability(bitStr);
} else {
oneProb += buffer->computeMeasurementProbability(bitStr);
}
}
EXPECT_NEAR(zeroProb, 0.5, 0.1);
EXPECT_NEAR(oneProb, 0.5, 0.1);
}
xacc::set_verbose(false);
}
int main(int argc, char **argv) {
xacc::Initialize();
......
......@@ -59,13 +59,6 @@ BackendMachine::BackendMachine(const NoiseModel &backendNoiseModel) {
const auto twoQubitFidelityAvg = [&]() {
std::vector<std::pair<size_t, size_t>> processedPairs;
std::vector<std::tuple<size_t, size_t, double>> avgData;
// TriQ will have convergence problems when there are pairs
// with super-low fidelity or missing some connectivity links
// i.e. create an incomplete connectivity graph.
// To work around this, we just assign the minimum fidelity for those links.
// This is not ideal, but it should be as good as placement based on
// connectivity graph only without fidelity information.
double minFid = 1.0;
for (const auto &[q1, q2, fidelity] : twoQubitFidelity) {
if (!xacc::container::contains(processedPairs, std::make_pair(q1, q2))) {
assert(
......@@ -79,24 +72,11 @@ BackendMachine::BackendMachine(const NoiseModel &backendNoiseModel) {
});
assert(iter != twoQubitFidelity.end());
const double fid2 = std::get<2>(*iter);
const double avdFid = (fid1 + fid2) / 2.0;
// Non-zero fidelity values: we use those to compute the min fidelity.
if (avdFid > 0.01) {
minFid = (avdFid < minFid) ? avdFid : minFid;
}
avgData.emplace_back(std::make_tuple(q1, q2, avdFid));
avgData.emplace_back(std::make_tuple(q1, q2, (fid1 + fid2) / 2.0));
processedPairs.emplace_back(std::make_pair(q1, q2));
processedPairs.emplace_back(std::make_pair(q2, q1));
}
}
for (auto &[q1, q2, fidVal] : avgData) {
if (fidVal < minFid) {
// Hack for now: scale those zero fidelity values to a value smaller
// than the min fidelity of other valid gates.
constexpr double FACTOR = 0.8;
fidVal = minFid * FACTOR;
}
}
return avgData;
}();
assert(twoQubitFidelityAvg.size() * 2 == twoQubitFidelity.size());
......
Subproject commit b1309e8236d0056bad5634501e4b288fd32a14a5
Subproject commit c8c15f28c242857eba654f5357237b5d72f73cfb
......@@ -102,7 +102,14 @@ OptResult MLPACKOptimizer::optimize(OptFunction &function) {
beta2 = options.get<double>("mlpack-beta2");
}
Adam optimizer(stepSize, 1, beta1, beta2, eps, maxiter, tol, false, false);
// Adam's exactObjective property (default = false)
bool exactObjective = false;
if (options.keyExists<bool>("adam-exact-objective")) {
exactObjective = options.get<bool>("adam-exact-objective");
}
Adam optimizer(stepSize, 1, beta1, beta2, eps, maxiter, tol, false, false,
exactObjective);
results = optimizer.Optimize(f, coordinates);
} else if (mlpack_opt_name == "spsa") {
SPSA optimizer(0.1, 0.102, 0.16, 0.3, 100000, 1e-5);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment