Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Pre- and Post-Mapping Optimizations #56

Merged
merged 10 commits into from
May 8, 2022
2 changes: 1 addition & 1 deletion extern/qfr
Submodule qfr updated from b38fe5 to 22528b
14 changes: 11 additions & 3 deletions include/Mapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ class Mapper {
}
};

qc::QuantumComputation& qc;
Architecture& architecture;
qc::QuantumComputation qc;
Architecture& architecture;

qc::QuantumComputation qcMapped;
std::vector<std::vector<Gate>> layers{};
Expand All @@ -65,8 +65,16 @@ class Mapper {
virtual void placeRemainingArchitectureQubits();
virtual void finalizeMappedCircuit();

virtual void countGates(const qc::QuantumComputation& circuit, MappingResults::CircuitInfo& info) {
countGates(circuit.cbegin(), circuit.cend(), info);
}
virtual void countGates(decltype(qcMapped.cbegin()) it, const decltype(qcMapped.cend())& end, MappingResults::CircuitInfo& info);

virtual void preMappingOptimizations(const Configuration& config);
virtual void postMappingOptimizations(const Configuration& config);

public:
Mapper(qc::QuantumComputation& qc, Architecture& architecture);
Mapper(const qc::QuantumComputation& qc, Architecture& architecture);
virtual ~Mapper() = default;

virtual void map(const Configuration& config) = 0;
Expand Down
24 changes: 12 additions & 12 deletions include/MappingResults.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,31 +64,31 @@ struct MappingResults {
circuit["gates"] = input.gates;
circuit["single_qubit_gates"] = input.singleQubitGates;
circuit["cnots"] = input.cnots;
circuit["layers"] = input.layers;

auto& mapped_circuit = resultJSON["mapped_circuit"];
mapped_circuit["name"] = output.name;
mapped_circuit["qubits"] = output.qubits;
mapped_circuit["gates"] = output.gates;
mapped_circuit["single_qubit_gates"] = output.singleQubitGates;
mapped_circuit["cnots"] = output.cnots;
mapped_circuit["swaps"] = output.swaps;
if (!mappedCircuit.empty()) {
mapped_circuit["qasm"] = mappedCircuit;
}
if (config.method == Method::Exact) {
mapped_circuit["direction_reverse"] = output.directionReverse;
} else if (config.method == Method::Heuristic) {
mapped_circuit["teleportations"] = output.teleportations;
}

resultJSON["config"] = config.json();

auto& stats = resultJSON["statistics"];
stats["timeout"] = timeout;
stats["mapping_time"] = time;
stats["additional_gates"] = output.gates - input.gates;
stats["arch"] = architecture;
auto& stats = resultJSON["statistics"];
stats["timeout"] = timeout;
stats["mapping_time"] = time;
stats["arch"] = architecture;
stats["layers"] = input.layers;
stats["swaps"] = output.swaps;
if (config.method == Method::Exact) {
stats["direction_reverse"] = output.directionReverse;
} else if (config.method == Method::Heuristic) {
stats["teleportations"] = output.teleportations;
}
stats["additional_gates"] = static_cast<long>(output.gates) - input.gates;

return resultJSON;
}
Expand Down
7 changes: 6 additions & 1 deletion include/configuration/Configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ struct Configuration {
// which method to use
Method method = Method::Heuristic;

bool preMappingOptimizations = true;
bool postMappingOptimizations = true;

bool verbose = false;

// map to particular subgraph of architecture (in exact mapper)
Expand Down Expand Up @@ -73,7 +76,9 @@ struct Configuration {
if (!subgraph.empty()) {
config["subgraph"] = subgraph;
}
config["verbose"] = verbose;
config["pre_mapping_optimizations"] = preMappingOptimizations;
config["post_mapping_optimizations"] = postMappingOptimizations;
config["verbose"] = verbose;

if (method == Method::Heuristic) {
auto& heuristic = config["settings"];
Expand Down
2 changes: 2 additions & 0 deletions mqt/qmap/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ PYBIND11_MODULE(pyqmap, m) {
.def_readwrite("swap_limit", &Configuration::swapLimit)
.def_readwrite("use_bdd", &Configuration::useBDD)
.def_readwrite("subgraph", &Configuration::subgraph)
.def_readwrite("pre_mapping_optimizations", &Configuration::preMappingOptimizations)
.def_readwrite("post_mapping_optimizations", &Configuration::postMappingOptimizations)
.def("json", &Configuration::json)
.def("__repr__", &Configuration::toString);

Expand Down
8 changes: 8 additions & 0 deletions mqt/qmap/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ def compile(circ, arch: Union[str, Arch],
include_WCNF: bool = False,
use_subsets: bool = True,
subgraph: Optional[Set[int]] = None,
pre_mapping_optimizations: bool = True,
post_mapping_optimizations: bool = True,
verbose: bool = False
) -> MappingResults:
"""Interface to the MQT QMAP tool for mapping quantum circuits
Expand Down Expand Up @@ -57,6 +59,10 @@ def compile(circ, arch: Union[str, Arch],
:param use_teleportation: Use teleportation in addition to swaps
:param teleportation_fake: Assign qubits as ancillary for teleportation in the initial placement but don't actually use them (used for comparisons)
:param teleportation_seed: Fix a seed for the RNG in the initial ancilla placement (0 means the RNG will be seeded from /dev/urandom/ or similar)
:param pre_mapping_optimizations: Run pre-mapping optimizations (default: True)
:type pre_mapping_optimizations: bool
:param post_mapping_optimizations: Run post-mapping optimizations (default: True)
:type post_mapping_optimizations: bool
:param verbose: Print more detailed information during the mapping process
:type verbose: bool
:return: Object containing all the results
Expand Down Expand Up @@ -84,6 +90,8 @@ def compile(circ, arch: Union[str, Arch],
config.use_teleportation = use_teleportation
config.teleportation_fake = teleportation_fake
config.teleportation_seed = teleportation_seed
config.pre_mapping_optimizations = pre_mapping_optimizations
config.post_mapping_optimizations = post_mapping_optimizations
config.verbose = verbose

return map(circ, arch, config)
76 changes: 64 additions & 12 deletions src/Mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

#include "Mapper.hpp"

#include "CircuitOptimizer.hpp"

void Mapper::initResults() {
countGates(qc, results.input);
results.input.name = qc.getName();
results.input.qubits = qc.getNqubits();
results.architecture = architecture.getArchitectureName();
Expand All @@ -15,11 +18,12 @@ void Mapper::initResults() {
qcMapped.addQubitRegister(architecture.getNqubits());
}

Mapper::Mapper(qc::QuantumComputation& quantumComputation, Architecture& arch):
qc(quantumComputation), architecture(arch) {
Mapper::Mapper(const qc::QuantumComputation& quantumComputation, Architecture& arch):
qc(quantumComputation.clone()), architecture(arch) {
qubits.fill(DEFAULT_POSITION);
locations.fill(DEFAULT_POSITION);
fidelities.fill(INITIAL_FIDELITY);
qc.stripIdleQubits(true, true);
}

void Mapper::createLayers() {
Expand All @@ -29,6 +33,7 @@ void Mapper::createLayers() {

auto qubitsInLayer = std::set<unsigned short>{};

bool even = true;
for (auto& gate: qc) {
// skip over barrier instructions
if (gate->getType() == qc::Barrier || gate->getType() == qc::Measure) {
Expand All @@ -46,9 +51,9 @@ void Mapper::createLayers() {
bool singleQubit = gate->getControls().empty();
short control = -1;
if (!singleQubit) {
control = static_cast<short>((*gate->getControls().begin()).qubit);
control = static_cast<short>(qc.initialLayout.at((*gate->getControls().begin()).qubit));
}
unsigned short target = gate->getTargets().at(0);
unsigned short target = qc.initialLayout.at(gate->getTargets().at(0));
size_t layer = 0;

switch (config.layering) {
Expand All @@ -72,12 +77,13 @@ void Mapper::createLayers() {
layers.at(layer).emplace_back(control, target, gate.get());
break;
case Layering::OddGates:
if (results.input.gates % 2 == 0) {
if (even) {
layers.emplace_back();
layers.back().emplace_back(control, target, gate.get());
} else {
layers.back().emplace_back(control, target, gate.get());
}
even = !even;
break;
case Layering::QubitTriangle:
if (layers.empty()) {
Expand All @@ -103,13 +109,6 @@ void Mapper::createLayers() {
}
break;
}

if (singleQubit) {
results.input.singleQubitGates++;
} else {
results.input.cnots++;
}
results.input.gates++;
}
results.input.layers = layers.size();
}
Expand Down Expand Up @@ -184,3 +183,56 @@ void Mapper::placeRemainingArchitectureQubits() {
}
}
}

void Mapper::preMappingOptimizations(const Configuration& config [[maybe_unused]]) {
if (!config.preMappingOptimizations) {
return;
}

// at the moment there are no pre-mapping optimizations
}

void Mapper::postMappingOptimizations(const Configuration& config) {
if (!config.postMappingOptimizations) {
return;
}

// try to cancel adjacent CNOT gates
qc::CircuitOptimizer::cancelCNOTs(qcMapped);
}

void Mapper::countGates(decltype(qcMapped.cbegin()) it, const decltype(qcMapped.cend())& end, MappingResults::CircuitInfo& info) {
for (; it != end; ++it) {
const auto& g = *it;
if (g->getType() == qc::Teleportation) {
info.gates += GATES_OF_TELEPORTATION;
continue;
}

if (g->isStandardOperation()) {
if (g->getType() == qc::SWAP) {
if (architecture.bidirectional()) {
info.gates += GATES_OF_BIDIRECTIONAL_SWAP;
info.cnots += GATES_OF_BIDIRECTIONAL_SWAP;
} else {
info.gates += GATES_OF_UNIDIRECTIONAL_SWAP;
info.cnots += GATES_OF_BIDIRECTIONAL_SWAP;
info.singleQubitGates += GATES_OF_DIRECTION_REVERSE;
}
} else if (g->getControls().empty()) {
++info.singleQubitGates;
++info.gates;
} else {
assert(g->getType() == qc::X);
++info.cnots;
++info.gates;
}
continue;
}

if (g->isCompoundOperation()) {
const auto& cg = dynamic_cast<const qc::CompoundOperation*>(g.get());
countGates(cg->cbegin(), cg->cend(), info);
}
}
}
64 changes: 34 additions & 30 deletions src/exact/ExactMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ void ExactMapper::map(const Configuration& settings) {
const auto& config = results.config;

std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
qc.stripIdleQubits(true, true);
initResults();

// 0) perform pre-mapping optimizations
preMappingOptimizations(config);

// 1) create layers according to different criteria
createLayers();
if (config.verbose) {
Expand All @@ -35,14 +37,14 @@ void ExactMapper::map(const Configuration& settings) {

// quickly terminate if the circuit only contains single-qubit gates
if (reducedLayerIndices.empty()) {
results.output.gates = results.input.gates;
results.output.cnots = results.input.cnots;
results.output.singleQubitGates = results.input.singleQubitGates;
results.output.layers = results.input.layers;
results.time = static_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start).count();
results.timeout = false;
qcMapped = qc.clone();
qcMapped = qc.clone();
postMappingOptimizations(config);
countGates(qcMapped, results.output);
finalizeMappedCircuit();

results.output.layers = results.input.layers;
results.time = static_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - start).count();
results.timeout = false;
return;
}

Expand Down Expand Up @@ -215,23 +217,15 @@ void ExactMapper::map(const Configuration& settings) {

for (unsigned long i = 0U; i < layers.size(); ++i) {
if (i == 0U) {
// invert the initial layout of the original circuit (most certainly the identity)
// in order to determine the correct qubit mapping
qc::Permutation inverseInitialLayout{};
for (const auto& [physical, logical]: qc.initialLayout) {
inverseInitialLayout.insert({logical, physical});
}

qcMapped.initialLayout.clear();
qcMapped.outputPermutation.clear();

// no swaps but initial permutation
for (const auto& [physical, logical]: *swapsIterator) {
dd::Qubit inverseLogical = inverseInitialLayout.at(static_cast<dd::Qubit>(logical));
locations.at(inverseLogical) = static_cast<short>(physical);
qubits.at(physical) = static_cast<unsigned short>(inverseLogical);
qcMapped.initialLayout[static_cast<dd::Qubit>(physical)] = inverseLogical;
qcMapped.outputPermutation[static_cast<dd::Qubit>(physical)] = inverseLogical;
locations.at(logical) = static_cast<short>(physical);
qubits.at(physical) = static_cast<short>(logical);
qcMapped.initialLayout[static_cast<dd::Qubit>(physical)] = static_cast<dd::Qubit>(logical);
qcMapped.outputPermutation[static_cast<dd::Qubit>(physical)] = static_cast<dd::Qubit>(logical);
}

// place remaining architecture qubits
Expand Down Expand Up @@ -311,6 +305,16 @@ void ExactMapper::map(const Configuration& settings) {
}
}

// 9) apply post mapping optimizations
postMappingOptimizations(config);

// 10) re-count gates
results.output.singleQubitGates = 0U;
results.output.cnots = 0U;
results.output.gates = 0U;
countGates(qcMapped, results.output);

// 11) final post-processing
finalizeMappedCircuit();

auto end = std::chrono::high_resolution_clock::now();
Expand Down Expand Up @@ -508,16 +512,16 @@ void ExactMapper::coreMappingRoutine(const std::set<unsigned short>& qubitChoice
expr coupling = c.bool_val(false);
if (architecture.bidirectional()) {
for (const auto& edge: rcm) {
auto indexFC = x[k][physicalQubitIndex[edge.first]][qc.initialLayout.at(gate.control)];
auto indexST = x[k][physicalQubitIndex[edge.second]][qc.initialLayout.at(gate.target)];
auto indexFC = x[k][physicalQubitIndex[edge.first]][gate.control];
auto indexST = x[k][physicalQubitIndex[edge.second]][gate.target];
coupling = coupling || (indexFC && indexST);
}
} else {
for (const auto& edge: rcm) {
auto indexFC = x[k][physicalQubitIndex[edge.first]][qc.initialLayout.at(gate.control)];
auto indexST = x[k][physicalQubitIndex[edge.second]][qc.initialLayout.at(gate.target)];
auto indexFT = x[k][physicalQubitIndex[edge.first]][qc.initialLayout.at(gate.target)];
auto indexSC = x[k][physicalQubitIndex[edge.second]][qc.initialLayout.at(gate.control)];
auto indexFC = x[k][physicalQubitIndex[edge.first]][gate.control];
auto indexST = x[k][physicalQubitIndex[edge.second]][gate.target];
auto indexFT = x[k][physicalQubitIndex[edge.first]][gate.target];
auto indexSC = x[k][physicalQubitIndex[edge.second]][gate.control];

coupling = coupling || ((indexFC && indexST) || (indexFT && indexSC));
}
Expand Down Expand Up @@ -630,8 +634,8 @@ void ExactMapper::coreMappingRoutine(const std::set<unsigned short>& qubitChoice

expr reverse = c.bool_val(true);
for (const auto& edge: rcm) {
auto indexFT = x[k][physicalQubitIndex[edge.first]][qc.initialLayout.at(gate.target)];
auto indexSC = x[k][physicalQubitIndex[edge.second]][qc.initialLayout.at(gate.control)];
auto indexFT = x[k][physicalQubitIndex[edge.first]][gate.target];
auto indexSC = x[k][physicalQubitIndex[edge.second]][gate.control];
reverse = reverse && (!indexFT || !indexSC);
}
opt.add(reverse.simplify(), GATES_OF_DIRECTION_REVERSE);
Expand Down Expand Up @@ -701,8 +705,8 @@ void ExactMapper::coreMappingRoutine(const std::set<unsigned short>& qubitChoice
if (gate.singleQubit())
continue;
for (const auto& edge: rcm) {
auto indexFT = x[k][physicalQubitIndex[edge.first]][qc.initialLayout.at(gate.target)];
auto indexSC = x[k][physicalQubitIndex[edge.second]][qc.initialLayout.at(gate.control)];
auto indexFT = x[k][physicalQubitIndex[edge.first]][gate.target];
auto indexSC = x[k][physicalQubitIndex[edge.second]][gate.control];
if (eq(m.eval(indexFT && indexSC), c.bool_val(true))) {
choiceResults.output.directionReverse++;
choiceResults.output.gates += GATES_OF_DIRECTION_REVERSE;
Expand Down
Loading