Skip to content

Commit

Permalink
DD synthesis of irreversible functions (Encoded to reversible without…
Browse files Browse the repository at this point in the history
… additional line) (#115)

## Description

This PR addresses the dd synthesis of irreversible functions which are
encoded to reversible without any need of an additional line. For the
synthesis, don't care conditions must be taken into consideration. This
PR introduces the DC node condition based on which the synthesis can be
carried forward. Appropriate tests and benchmarks are included as well.

## Checklist:

<!---
This checklist serves as a reminder of a couple of things that ensure
your pull request will be merged swiftly.
-->

- [x] The pull request only contains commits that are related to it.
- [x] I have added appropriate tests and documentation.
- [x] I have made sure that all CI jobs on GitHub pass.
- [x] The pull request introduces no new warnings and follows the
project's style guidelines.
  • Loading branch information
SmaranTum authored Nov 30, 2022
1 parent 87959a2 commit d0b9631
Show file tree
Hide file tree
Showing 18 changed files with 306 additions and 31 deletions.
10 changes: 10 additions & 0 deletions include/algorithms/simulation/circuit_to_truthtable.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#include "QuantumComputation.hpp"
#include "core/truthTable/truth_table.hpp"

namespace syrec {

auto buildTruthTable(const qc::QuantumComputation& qc, TruthTable& tt) -> void;

} // namespace syrec
2 changes: 2 additions & 0 deletions include/algorithms/synthesis/dd_synthesis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ namespace syrec {
static auto controlRoot(dd::mEdge const& current, dd::Controls& ctrl, TruthTable::Cube const& ctrlCube) -> void;
static auto controlNonRoot(dd::mEdge const& current, dd::Controls& ctrl, TruthTable::Cube const& ctrlCube) -> void;

static auto dcNodeCondition(dd::mEdge const& current) -> bool;

auto swapPaths(dd::mEdge src, dd::mEdge const& current, TruthTable::Cube::Set const& p1SigVec, TruthTable::Cube::Set const& p2SigVec, std::unique_ptr<dd::Package<>>& dd) -> dd::mEdge;

auto shiftUniquePaths(dd::mEdge src, dd::mEdge const& current, TruthTable::Cube::Set const& p1SigVec, TruthTable::Cube::Set const& p2SigVec, std::unique_ptr<dd::Package<>>& dd) -> dd::mEdge;
Expand Down
2 changes: 1 addition & 1 deletion include/algorithms/synthesis/encoding.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace syrec {
data(std::move(data)), freq(freq) {}

auto operator>(const MinHeapNode& other) const -> bool {
return freq > other.freq;
return (freq > other.freq || (freq == other.freq && (data > other.data)));
}

auto traverse(TruthTable::Cube&& encodedCube, TruthTable::CubeMap& encoding) const -> void {
Expand Down
51 changes: 51 additions & 0 deletions include/core/truthTable/truth_table.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,47 @@ namespace syrec {
return result;
}

// return bool vec representation of the cube (order is b0,b1,b2......bn)
[[nodiscard]] auto toBoolVec() const -> std::vector<bool> {
assert(std::all_of(cube.cbegin(), cube.cend(), [](auto const& v) { return v.has_value(); }));
const auto nBits = size();
std::vector<bool> result(nBits);
for (std::size_t i = 0U; i < nBits; ++i) {
result[nBits - 1 - i] = *cube[i];
}
return result;
}

// return string representation of the cube
[[nodiscard]] auto toString() const -> std::string {
std::stringstream ss{};
for (const auto& i: cube) {
if (!i.has_value()) {
ss << '-';
} else {
ss << (*i ? '1' : '0');
}
}
return ss.str();
}

// checks if 2 Cubes are equal irrespective of don't care
[[nodiscard]] static auto checkCubeEquality(const Cube& c1, const Cube& c2, const bool equalityUpToDontCare = true) -> bool {
if (c1.size() != c2.size()) {
return false;
}
const auto nBits = c1.size();
for (auto i = 0U; i < nBits; ++i) {
if (equalityUpToDontCare && (!c1[i].has_value() || !c2[i].has_value())) {
continue;
}
if (*c1[i] != *c2[i]) {
return false;
}
}
return true;
}

[[nodiscard]] auto completeCubes() const -> Vector;

auto insertZero() -> void {
Expand Down Expand Up @@ -136,6 +177,10 @@ namespace syrec {
return (cube < cv.cube);
}

auto operator>(const Cube& cv) const -> bool {
return (cube > cv.cube);
}

auto operator==(const Cube& cv) const -> bool {
return (cube == cv.cube);
}
Expand Down Expand Up @@ -267,6 +312,10 @@ namespace syrec {
return cubeMap.find(Cube::fromString(str));
}

auto find(const Cube& c) -> decltype(cubeMap.cbegin()) {
return cubeMap.find(c);
}

auto try_emplace(const Cube& input, const Cube& output) -> void { // NOLINT(readability-identifier-naming) keeping same Interface as std::vector
assert(cubeMap.empty() || (input.size() == nInputs() && output.size() == nOutputs()));
cubeMap.try_emplace(input, output);
Expand All @@ -280,6 +329,8 @@ namespace syrec {
cubeMap.insert(std::move(nh));
}

static auto equal(TruthTable& tt1, TruthTable& tt2, bool equalityUpToDontCare = true) -> bool;

auto clear() -> void {
cubeMap.clear();
}
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ add_library(
${CMAKE_CURRENT_SOURCE_DIR}/algorithms/synthesis/syrec_cost_aware_synthesis.cpp
${CMAKE_CURRENT_SOURCE_DIR}/algorithms/synthesis/syrec_line_aware_synthesis.cpp
${CMAKE_CURRENT_SOURCE_DIR}/algorithms/simulation/simple_simulation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/algorithms/simulation/circuit_to_truthtable.cpp
${CMAKE_CURRENT_SOURCE_DIR}/algorithms/synthesis/dd_synthesis.cpp
${CMAKE_CURRENT_SOURCE_DIR}/algorithms/synthesis/encoding.cpp
${CMAKE_CURRENT_SOURCE_DIR}/algorithms/optimization/esop_minimization.cpp
${${PROJECT_NAME}_SOURCE_DIR}/include/algorithms/optimization/esop_minimization.hpp
${${PROJECT_NAME}_SOURCE_DIR}/include/algorithms/simulation/simple_simulation.hpp
${${PROJECT_NAME}_SOURCE_DIR}/include/algorithms/simulation/circuit_to_truthtable.hpp
${${PROJECT_NAME}_SOURCE_DIR}/include/algorithms/synthesis/syrec_synthesis.hpp
${${PROJECT_NAME}_SOURCE_DIR}/include/algorithms/synthesis/syrec_cost_aware_synthesis.hpp
${${PROJECT_NAME}_SOURCE_DIR}/include/algorithms/synthesis/syrec_line_aware_synthesis.hpp
Expand Down
29 changes: 29 additions & 0 deletions src/algorithms/simulation/circuit_to_truthtable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "algorithms/simulation/circuit_to_truthtable.hpp"

#include "dd/Simulation.hpp"

namespace syrec {

auto buildTruthTable(const qc::QuantumComputation& qc, TruthTable& tt) -> void {
const auto nBits = qc.getNqubits();
assert(nBits < 65U);

auto dd = std::make_unique<dd::Package<>>(nBits);

const auto totalInputs = 1U << nBits;

std::uint64_t n = 0U;

while (n < totalInputs) {
const auto inCube = TruthTable::Cube::fromInteger(n, nBits);

auto const inEdge = dd->makeBasisState(nBits, inCube.toBoolVec());
const auto out = dd::simulate(&qc, inEdge, dd, 1);
const auto outString = out.begin()->first;

tt.try_emplace(inCube, TruthTable::Cube::fromString(outString));
++n;
}
}

} // namespace syrec
1 change: 0 additions & 1 deletion src/algorithms/simulation/simple_simulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,4 @@ namespace syrec {
t.stop();
}
}

} // namespace syrec
56 changes: 41 additions & 15 deletions src/algorithms/synthesis/dd_synthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,18 @@ namespace syrec {
for (const auto& [input, output]: tt) {
// truth table has to be completely specified
assert(input[0].has_value());
assert(output[0].has_value());
const auto in = *input[0];
const auto out = *output[0];
const auto index = (static_cast<std::size_t>(out) * 2U) + static_cast<std::size_t>(in);
edges.at(index) = dd::mEdge::one;

const auto in = *input[0];
const auto out = *output[0];

if (output[0].has_value()) {
const auto index = (static_cast<std::size_t>(out) * 2U) + static_cast<std::size_t>(in);
edges.at(index) = dd::mEdge::one;
} else {
const auto offset = in ? 1U : 0U;
edges.at(0U + offset) = dd::mEdge::one;
edges.at(2U + offset) = dd::mEdge::one;
}
}
return dd->makeDDNode(0, edges);
}
Expand All @@ -33,15 +40,22 @@ namespace syrec {
for (const auto& [input, output]: tt) {
// truth table has to be completely specified
assert(input[0].has_value());
assert(output[0].has_value());
const auto in = *input[0];
const auto out = *output[0];
const auto index = static_cast<std::size_t>(out) * 2U + static_cast<std::size_t>(in);

const auto in = *input[0];
const auto out = *output[0];

TruthTable::Cube reducedInput(input.begin() + 1, input.end());
TruthTable::Cube reducedOutput(output.begin() + 1, output.end());
subTables.at(index).try_emplace(std::move(reducedInput), std::move(reducedOutput));
}

if (output[0].has_value()) {
const auto index = static_cast<std::size_t>(out) * 2U + static_cast<std::size_t>(in);
subTables.at(index).try_emplace(std::move(reducedInput), std::move(reducedOutput));
} else {
const auto offset = in ? 1U : 0U;
subTables.at(0 + offset).try_emplace(reducedInput, reducedOutput);
subTables.at(2 + offset).try_emplace(reducedInput, reducedOutput);
}
}
// recursively build the DD for each sub-table
for (std::size_t i = 0U; i < 4U; ++i) {
edges.at(i) = buildDD(subTables.at(i), dd);
Expand Down Expand Up @@ -181,6 +195,18 @@ namespace syrec {
}
}

// Check whether all the edges of the current node are pointing to the same node (indicating a don't care node).
auto DDSynthesizer::dcNodeCondition(dd::mEdge const& current) -> bool {
if (!(dd::mNode::isTerminal(current.p))) {
if (current.p->v == 0U) {
return std::all_of(current.p->e.begin(), current.p->e.end(), [](const auto& e) { return e == dd::mEdge::one; });
}

return std::all_of(current.p->e.begin(), current.p->e.end(), [&current](const auto& e) { return e.p == current.p->e[0].p; });
}
return false;
}

// This function performs the multi-control (if any) X operation.
auto DDSynthesizer::applyOperation(dd::QubitCount const& totalBits, dd::Qubit const& targetBit, dd::mEdge& to, dd::Controls const& ctrl, std::unique_ptr<dd::Package<>>& dd) -> void {
// create operation and corresponding decision diagram
Expand Down Expand Up @@ -354,7 +380,7 @@ namespace syrec {
runtime = 0.;
numGates = 0U;

if (src.p == nullptr || src.p->isIdentity()) {
if (src.p == nullptr || src.p->isIdentity() || dcNodeCondition(src)) {
return qc;
}

Expand All @@ -380,7 +406,7 @@ namespace syrec {
const auto current = queue.front();
queue.pop();

if (current.p == nullptr || current.p->isIdentity()) {
if (current.p == nullptr || current.p->isIdentity() || dcNodeCondition(current)) {
continue;
}

Expand All @@ -400,7 +426,7 @@ namespace syrec {

if (pathsShifted) {
// stopping criterion
if (srcShifted.p->isIdentity()) {
if (srcShifted.p->isIdentity() && dcNodeCondition(srcShifted)) {
break;
}

Expand All @@ -414,7 +440,7 @@ namespace syrec {

// if all paths have been shifted, the children of the current node need to be processed.
for (const auto& e: current.p->e) {
if (!e.isTerminal() && visited.find(e) == visited.end()) {
if (!e.isTerminal() && !dcNodeCondition(e) && visited.find(e) == visited.end()) {
queue.emplace(e);
visited.emplace(e);
}
Expand Down
3 changes: 0 additions & 3 deletions src/algorithms/synthesis/encoding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ namespace syrec {
TruthTable newTT{};

for (auto const& [input, output]: tt) {
// ensure that all outputs are complete
assert(std::none_of(output.cbegin(), output.cend(), [](auto const& v) { return !v.has_value(); }));

// compute the complete cubes for the input
auto completeInputs = input.completeCubes();
// move all the complete cubes to the new cube map
Expand Down
34 changes: 34 additions & 0 deletions src/core/truthTable/truth_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,38 @@ namespace syrec {
return result;
}

auto TruthTable::equal(TruthTable& tt1, TruthTable& tt2, bool equalityUpToDontCare) -> bool {
if (!equalityUpToDontCare) {
return (tt1 == tt2);
}

if (tt1.nInputs() != tt2.nInputs()) {
return false;
}

const auto nBits = tt1.nInputs();
assert(nBits < 65U);
const auto totalInputs = 1U << nBits;

std::uint64_t n = 0U;

while (n < totalInputs) {
const auto input = TruthTable::Cube::fromInteger(n, nBits);
++n;

const auto foundtt1 = tt1.find(input) != tt1.end();
const auto foundtt2 = tt2.find(input) != tt2.end();

if (!foundtt1 || !foundtt2) {
continue;
}

if (!Cube::checkCubeEquality(tt1[input], tt2[input])) {
return false;
}
}

return true;
}

} // namespace syrec
3 changes: 2 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ package_add_test(
unittests/test_extend_tt.cpp
unittests/test_huffman.cpp
unittests/test_pla_parser.cpp
unittests/test_dd_synthesis.cpp)
unittests/test_dd_synthesis.cpp
unittests/test_dd_synthesis_dc.cpp)

add_custom_command(
TARGET ${PROJECT_NAME}_test
Expand Down
9 changes: 9 additions & 0 deletions test/circuits/dc3bit.pla
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.i 3
.o 3
00- 0--
01- 10-
100 111
101 0--
110 0--
111 110
.e
11 changes: 11 additions & 0 deletions test/circuits/dc3bitNew.pla
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.i 3
.o 3
000 0--
001 10-
010 0--
011 110
100 10-
101 111
110 0--
111 0--
.e
5 changes: 5 additions & 0 deletions test/circuits/dcX2bit.pla
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.i 2
.o 2
0- 1-
1- 0-
.e
7 changes: 7 additions & 0 deletions test/circuits/dcX4bit.pla
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.i 4
.o 4
00-- 11--
01-- 10--
10-- 01--
11-- 00--
.e
36 changes: 36 additions & 0 deletions test/circuits/sym6_32.pla
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Function: sym6_32
# This file has been taken from RevLib (www.revlib.org).
.i 6
.o 1
000011 1
000101 1
000110 1
000111 1
001001 1
001010 1
001011 1
001100 1
001101 1
001110 1
001111 1
010001 1
010010 1
010011 1
010100 1
010101 1
010110 1
010111 1
011000 1
011001 1
011010 1
011011 1
011100 1
011101 1
011110 1
100001 1
100010 1
100011 1
100100 1
100101 1
100110 1
100111 1
Loading

0 comments on commit d0b9631

Please sign in to comment.