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

Fixing minor bug in disjoint_2q_block Layering and adding option add_barriers_between_layers #405

Merged
merged 6 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion include/Mapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,22 @@ class Mapper {
void processDisjointQubitLayer(
std::array<std::optional<std::size_t>, MAX_DEVICE_QUBITS>& lastLayer,
const std::optional<std::uint16_t>& control, std::uint16_t target,
qc::Operation* gate, bool collect2qBlocks);
qc::Operation* gate);

/**
* Similar to processDisjointQubitLayer, but instead of treating each gate
* individually, gates are collected in 2Q-blocks, which are layered
* disjointly to each other
*
* @param lastLayer the array storing the last layer each qubit is used in
* @param control the (potential) control qubit of the gate
* @param target the target qubit of the gate
* @param gate the gate to be added to the layer
*/
void processDisjoint2qBlockLayer(
std::array<std::optional<std::size_t>, MAX_DEVICE_QUBITS>& lastLayer,
const std::optional<std::uint16_t>& control, std::uint16_t target,
qc::Operation* gate);

/**
* @brief Get the index of the next layer after the given index containing a
Expand Down
1 change: 1 addition & 0 deletions include/configuration/Configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct Configuration {

bool addMeasurementsToMappedCircuit = true;
bool swapOnFirstLayer = false;
bool addBarriersBetweenLayers = false;

bool verbose = false;
bool debug = false;
Expand Down
56 changes: 42 additions & 14 deletions src/Mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,49 @@ Mapper::Mapper(qc::QuantumComputation quantumComputation, Architecture& arch)
void Mapper::processDisjointQubitLayer(
std::array<std::optional<std::size_t>, MAX_DEVICE_QUBITS>& lastLayer,
const std::optional<std::uint16_t>& control, const std::uint16_t target,
qc::Operation* gate, bool collect2qBlocks) {
qc::Operation* gate) {
std::size_t layer = 0;
if (!control.has_value()) {
if (lastLayer.at(target).has_value()) {
layer = *lastLayer.at(target);
// single qubit gates can always be added to the last 2Q block
if (!collect2qBlocks) {
layer++;
}
}
if (!collect2qBlocks) {
lastLayer.at(target) = layer;
layer = *lastLayer.at(target) + 1;
}
lastLayer.at(target) = layer;
} else {
if (!lastLayer.at(*control).has_value() &&
!lastLayer.at(target).has_value()) {
layer = 0;
} else if (!lastLayer.at(*control).has_value()) {
layer = *lastLayer.at(target) + 1;
} else if (!lastLayer.at(target).has_value()) {
layer = *lastLayer.at(*control) + 1;
} else {
layer = std::max(*lastLayer.at(*control), *lastLayer.at(target)) + 1;
}
lastLayer.at(*control) = layer;
lastLayer.at(target) = layer;
}

if (layers.size() <= layer) {
layers.emplace_back();
}
if (control.has_value()) {
layers.at(layer).emplace_back(*control, target, gate);
} else {
layers.at(layer).emplace_back(-1, target, gate);
}
}

void Mapper::processDisjoint2qBlockLayer(
std::array<std::optional<std::size_t>, MAX_DEVICE_QUBITS>& lastLayer,
const std::optional<std::uint16_t>& control, const std::uint16_t target,
qc::Operation* gate) {
std::size_t layer = 0;
if (!control.has_value()) {
// single qubit gates can always be added to the last 2Q block and should
// not affect placings of future 2Q blocks
if (lastLayer.at(target).has_value()) {
layer = *lastLayer.at(target);
}
} else {
if (!lastLayer.at(*control).has_value() &&
!lastLayer.at(target).has_value()) {
Expand All @@ -60,14 +89,13 @@ void Mapper::processDisjointQubitLayer(
} else {
layer = std::max(*lastLayer.at(*control), *lastLayer.at(target)) + 1;

if (collect2qBlocks &&
(*lastLayer.at(*control) == *lastLayer.at(target))) {
if (*lastLayer.at(*control) == *lastLayer.at(target)) {
for (auto& g : layers.at(layer - 1)) {
if ((g.control == *control && g.target == target) ||
(g.control == target && g.target == *control)) {
// if last layer contained gate with equivalent qubit set, use that
// layer
layer--;
--layer;
break;
}
}
Expand Down Expand Up @@ -134,10 +162,10 @@ void Mapper::createLayers() {
}
break;
case Layering::DisjointQubits:
processDisjointQubitLayer(lastLayer, control, target, gate.get(), false);
processDisjointQubitLayer(lastLayer, control, target, gate.get());
break;
case Layering::Disjoint2qBlocks:
processDisjointQubitLayer(lastLayer, control, target, gate.get(), true);
processDisjoint2qBlockLayer(lastLayer, control, target, gate.get());
break;
case Layering::OddGates:
// every other gate is put in a new layer
Expand Down
5 changes: 5 additions & 0 deletions src/heuristic/HeuristicMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,11 @@ void HeuristicMapper::routeCircuit() {
printQubits(std::clog);
}

if (layerIndex != 0 && config.addBarriersBetweenLayers) {
qcMapped.barrier();
gateidx++;
}

// initial layer needs no swaps
if (layerIndex != 0 || config.swapOnFirstLayer) {
for (const auto& swaps : result.swaps) {
Expand Down
3 changes: 3 additions & 0 deletions src/mqt/qmap/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def compile( # noqa: A001
pre_mapping_optimizations: bool = True,
post_mapping_optimizations: bool = True,
add_measurements_to_mapped_circuit: bool = True,
add_barriers_between_layers: bool = False,
verbose: bool = False,
debug: bool = False,
visualizer: SearchVisualizer | None = None,
Expand Down Expand Up @@ -113,6 +114,7 @@ def compile( # noqa: A001
pre_mapping_optimizations: Run pre-mapping optimizations. Defaults to True.
post_mapping_optimizations: Run post-mapping optimizations. Defaults to True.
add_measurements_to_mapped_circuit: Whether to add measurements at the end of the mapped circuit. Defaults to True.
add_barriers_between_layers: Whether to add barriers between layers to make them apparent after mapping. Defaults to False.
verbose: Print more detailed information during the mapping process. Defaults to False.
debug: Gather additional information during the mapping process (e.g. number of generated nodes, branching factors, ...). Defaults to False.
visualizer: A SearchVisualizer object to log the search process to. Defaults to None.
Expand Down Expand Up @@ -159,6 +161,7 @@ def compile( # noqa: A001
config.pre_mapping_optimizations = pre_mapping_optimizations
config.post_mapping_optimizations = post_mapping_optimizations
config.add_measurements_to_mapped_circuit = add_measurements_to_mapped_circuit
config.add_barriers_between_layers = add_barriers_between_layers
config.verbose = verbose
config.debug = debug
if visualizer is not None and visualizer.data_logging_path is not None:
Expand Down
1 change: 1 addition & 0 deletions src/mqt/qmap/pyqmap.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class CommanderGrouping:

class Configuration:
add_measurements_to_mapped_circuit: bool
add_barriers_between_layers: bool
admissible_heuristic: bool
consider_fidelity: bool
commander_grouping: CommanderGrouping
Expand Down
2 changes: 2 additions & 0 deletions src/python/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ PYBIND11_MODULE(pyqmap, m) {
&Configuration::postMappingOptimizations)
.def_readwrite("add_measurements_to_mapped_circuit",
&Configuration::addMeasurementsToMappedCircuit)
.def_readwrite("add_barriers_between_layers",
&Configuration::addBarriersBetweenLayers)
.def("json", &Configuration::json)
.def("__repr__", &Configuration::toString);

Expand Down
93 changes: 93 additions & 0 deletions test/test_heuristic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,99 @@ TEST(Functionality, DataLogger) {
}
}

class LayeringTest : public testing::Test {
protected:
qc::QuantumComputation qc{};
Architecture arch{};
std::unique_ptr<HeuristicMapper> mapper;
Configuration settings{};

void SetUp() override {
qc = qc::QuantumComputation{4, 4};
qc.x(0);
qc.x(1);
qc.cx(qc::Control{0}, 1);
qc.cx(qc::Control{2}, 3);
qc.cx(qc::Control{1}, 2);
qc.x(3);
qc.barrier({0, 1, 2});
for (size_t i = 0; i < 3; ++i) {
qc.measure(static_cast<qc::Qubit>(i), i);
}

arch = Architecture{4, {{0, 1}, {1, 2}, {2, 3}}};

settings.initialLayout = InitialLayout::Dynamic;
settings.preMappingOptimizations = false;
settings.postMappingOptimizations = false;
settings.addMeasurementsToMappedCircuit = true;
settings.addBarriersBetweenLayers = true;
settings.automaticLayerSplits = false;

mapper = std::make_unique<HeuristicMapper>(qc, arch);
}
};

TEST_F(LayeringTest, Disjoint2qBlocks) {
settings.layering = Layering::Disjoint2qBlocks;
mapper->map(settings);
auto result = mapper->getResults();
EXPECT_EQ(result.input.layers, 2);
// get mapped circuit
auto qcMapped = qc::QuantumComputation();
std::stringstream qasm{};
mapper->dumpResult(qasm, qc::Format::OpenQASM);
qcMapped.import(qasm, qc::Format::OpenQASM);
// check barrier count
std::size_t barriers = 0;
for (const auto& op : qcMapped) {
if (op->getType() == qc::Barrier) {
++barriers;
}
}
EXPECT_EQ(barriers, result.input.layers);
}

TEST_F(LayeringTest, DisjointQubits) {
settings.layering = Layering::DisjointQubits;
mapper->map(settings);
auto result = mapper->getResults();
EXPECT_EQ(result.input.layers, 3);
// get mapped circuit
auto qcMapped = qc::QuantumComputation();
std::stringstream qasm{};
mapper->dumpResult(qasm, qc::Format::OpenQASM);
qcMapped.import(qasm, qc::Format::OpenQASM);
// check barrier count
std::size_t barriers = 0;
for (const auto& op : qcMapped) {
if (op->getType() == qc::Barrier) {
++barriers;
}
}
EXPECT_EQ(barriers, result.input.layers);
}

TEST_F(LayeringTest, IndividualGates) {
settings.layering = Layering::IndividualGates;
mapper->map(settings);
auto result = mapper->getResults();
EXPECT_EQ(result.input.layers, 6);
// get mapped circuit
auto qcMapped = qc::QuantumComputation();
std::stringstream qasm{};
mapper->dumpResult(qasm, qc::Format::OpenQASM);
qcMapped.import(qasm, qc::Format::OpenQASM);
// check barrier count
std::size_t barriers = 0;
for (const auto& op : qcMapped) {
if (op->getType() == qc::Barrier) {
++barriers;
}
}
EXPECT_EQ(barriers, result.input.layers);
}

class HeuristicTest5Q : public testing::TestWithParam<std::string> {
protected:
std::string testExampleDir = "../examples/";
Expand Down