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

Delayed Phase Correction #397

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c81eb40
Removed fixed gateset and replaced with parameter.
pehamTom Nov 10, 2023
5eb3747
Check if gateset is Clifford.
pehamTom Nov 10, 2023
8c9d539
Added completens check for single-qubit Gatesets.
pehamTom Nov 10, 2023
1cd55fd
Extracted Gateset in separate file and pass gateset through config.
pehamTom Nov 10, 2023
256f588
Bindings for Gateset.
pehamTom Nov 10, 2023
d218f42
Commit missing file.
pehamTom Nov 13, 2023
af8ee8f
Relax ExactlyOne constraint for depth-optimal synthesis.
pehamTom Nov 13, 2023
eb35989
Always add the none gate.
pehamTom Nov 13, 2023
b2e3a92
Merge branch 'tom-dev' into relax-exactly-one-constraint
pehamTom Nov 13, 2023
848e9d7
Removed templates.
pehamTom Nov 13, 2023
7690afa
Merge branch 'tom-dev' into relax-exactly-one-constraint
pehamTom Nov 13, 2023
d334f58
Made GateSet into an actual set.
pehamTom Nov 13, 2023
267c2b1
Merge branch 'tom-dev' into relax-exactly-one-constraint
pehamTom Nov 13, 2023
f58a738
Can only relax constraints if no Paulis are in the target Gateset.
pehamTom Nov 13, 2023
3b3f028
added setting for delaying paulis.
pehamTom Nov 13, 2023
e6c3348
Extract common functionality of different encoders.
pehamTom Nov 13, 2023
f45ac80
Added new class for handling the encoding for phase correction.
pehamTom Nov 14, 2023
cad2a04
Added files for encoder.
pehamTom Nov 14, 2023
c02c8ed
Encoding and tests for delayed phase correction.
pehamTom Nov 14, 2023
0608ae9
Fixed tests.
pehamTom Nov 14, 2023
e8d4794
🎨 pre-commit fixes
pre-commit-ci[bot] Nov 14, 2023
b8423cc
Fix constexpr error.
pehamTom Nov 14, 2023
995ed9d
Merge remote-tracking branch 'origin/postpone-paulis' into postpone-p…
pehamTom Nov 14, 2023
6a3bafa
🎨 pre-commit fixes
pre-commit-ci[bot] Nov 14, 2023
dac77bb
missing header?
pehamTom Nov 15, 2023
60f64ca
Merge remote-tracking branch 'origin/postpone-paulis' into postpone-p…
pehamTom Nov 15, 2023
4291548
Missing include.
pehamTom Nov 15, 2023
245f55c
🎨 pre-commit fixes
pre-commit-ci[bot] Nov 15, 2023
a0652f0
Linter warnings.
pehamTom Nov 15, 2023
aaeb22f
Merge remote-tracking branch 'origin/postpone-paulis' into postpone-p…
pehamTom Nov 15, 2023
4b48b48
Fixed relation of consistency constraints.
pehamTom Nov 17, 2023
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
2 changes: 1 addition & 1 deletion extern/LogicBlocks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert these unrelated submodule change

2 changes: 1 addition & 1 deletion extern/mqt-core
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert these unrelated submodule change

11 changes: 11 additions & 0 deletions include/cliffordsynthesis/Configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@

#pragma once

#include "GateSet.hpp"
#include "TargetMetric.hpp"
#include "nlohmann/json.hpp"
#include "operations/OpType.hpp"

#include <plog/Log.h>
#include <thread>
#include <unordered_map>
#include <variant>
#include <vector>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <vector>

Seems unnecessary


namespace cs {

Expand Down Expand Up @@ -48,6 +51,12 @@ struct Configuration {
std::size_t splitSize = 5U;
std::size_t nThreadsHeuristic = std::thread::hardware_concurrency();

GateSet gateSet = {qc::OpType::None, qc::OpType::S, qc::OpType::Sdg,
qc::OpType::H, qc::OpType::X, qc::OpType::Y,
qc::OpType::Z};
Comment on lines +54 to +56
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I am missing something, but I believe this is missing the SX and SXdg gates. (and potentially the I identity gate; although I guess that is not really necessary). Couldn't you just publicly expose the SINGLE_QUBIT_CLIFFORDS member from the GateSet class and use it here?


bool delayPaulis = false;

[[nodiscard]] nlohmann::json json() const {
nlohmann::json j;
j["initial_timestep_limit"] = initialTimestepLimit;
Expand All @@ -66,6 +75,8 @@ struct Configuration {
j["heuristic"] = heuristic;
j["split_size"] = splitSize;
j["n_threads_heuristic"] = nThreadsHeuristic;
j["gate_set"] = gateSet.toString();
j["delay_paulis"] = delayPaulis;
if (!solverParameters.empty()) {
nlohmann::json solverParametersJson;
for (const auto& entry : solverParameters) {
Expand Down
237 changes: 237 additions & 0 deletions include/cliffordsynthesis/GateSet.hpp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have two general points here:

  • This only captures single-qubit gates as part of a gate-set. However, in general, there are also many variations for two-qubit Clifford gates (CX, CY, CZ, SWAP, DCX, ECR). So I believe this class should either have SingleQubit in its name or it should address the fact that there are also different two-qubit gates. I assume the first solution is easy and fast while the second one would be nice long-term.
  • Basically the entirety of this file could be constexpr and compile time computed. You know the full list of gates (SINGLE_QUBIT_CLIFFORDS) at compile time. So any gate-set is merely a bitset that has a 1 for any gate that is enabled. I would imagine that that is quite a cleaner and more performant solution. If you agree, but do not feel like implementing that, I am happy to try and contribute such an implementation.

Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
//
// This file is part of the MQT QMAP library released under the MIT license.
// See README.md or go to https://github.com/cda-tum/qmap for more information.
//

#pragma once

#include "operations/OpType.hpp"

#include <algorithm>
#include <array>
#include <initializer_list>
#include <vector>

namespace cs {
class GateSet {
static constexpr std::array<qc::OpType, 10> SINGLE_QUBIT_CLIFFORDS = {
qc::OpType::I, qc::OpType::H, qc::OpType::X, qc::OpType::Y,
qc::OpType::Z, qc::OpType::S, qc::OpType::Sdg, qc::OpType::SX,
qc::OpType::SXdg, qc::OpType::None};

using iterator = typename std::vector<qc::OpType>::iterator;
using const_iterator = typename std::vector<qc::OpType>::const_iterator;

private:
// Check if None is already contained and if not, append it
void appendNone() {
if (!containsGate(qc::OpType::None)) {
gates.push_back(qc::OpType::None);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
gates.push_back(qc::OpType::None);
gates.emplace_back(qc::OpType::None);

}
}
std::vector<qc::OpType> gates{};

public:
GateSet() { appendNone(); };

explicit GateSet(std::vector<qc::OpType> gateSet)
: gates(std::move(gateSet)) {
appendNone();
};

GateSet(const GateSet& gateSet) : gates(gateSet.gates) { appendNone(); };

GateSet(GateSet&& gateSet) noexcept : gates(std::move(gateSet.gates)) {
appendNone();
};

GateSet(std::initializer_list<qc::OpType> gateSet) : gates(gateSet) {
appendNone();
}

GateSet& operator=(const GateSet& other) {
gates = other.gates;
appendNone();
return *this;
}

GateSet& operator=(GateSet&& other) noexcept {
gates = std::move(other.gates);
appendNone();
return *this;
}

GateSet& operator=(std::initializer_list<qc::OpType> other) {
gates = other;
appendNone();
return *this;
}
Comment on lines +42 to +68
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a strong feeling that all of these methods could be removed.
Any GateSet that is being constructed will contain None. Thus, the implicitly defined copy&move constructors as well as copy and move assignments work right away. Furthermore, the initialiser_list constructor should implicitly work as well because std::vector has such a constructor directly defined.


void removeGate(const qc::OpType& gate) {
gates.erase(std::remove(gates.begin(), gates.end(), gate), gates.end());
}

void removePaulis() {
gates.erase(std::remove_if(gates.begin(), gates.end(),
[](qc::OpType gate) {
return gate == qc::OpType::X ||
gate == qc::OpType::Y ||
gate == qc::OpType::Z ||
gate == qc::OpType::None;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just for my understanding: Do you really want to remove None here?

}),
gates.end());
}
[[nodiscard]] bool containsGate(qc::OpType gate) const {
for (const auto& g : // NOLINT(readability-use-anyofallof)
gates) {
if (g == gate) {
return true;
}
}
return false;
}
[[nodiscard]] bool containsX() const { return containsGate(qc::OpType::X); }
[[nodiscard]] bool containsY() const { return containsGate(qc::OpType::Y); }
[[nodiscard]] bool containsZ() const { return containsGate(qc::OpType::Z); }
[[nodiscard]] bool containsH() const { return containsGate(qc::OpType::H); }
[[nodiscard]] bool containsS() const { return containsGate(qc::OpType::S); }
[[nodiscard]] bool containsSdg() const {
return containsGate(qc::OpType::Sdg);
}
[[nodiscard]] bool containsSX() const { return containsGate(qc::OpType::SX); }
[[nodiscard]] bool containsSXdg() const {
return containsGate(qc::OpType::SXdg);
}
[[nodiscard]] std::size_t gateToIndex(const qc::OpType type) const {
for (std::size_t i = 0; i < gates.size(); ++i) {
if (gates.at(i) == type) {
return i;
}
}
return 0;
}

[[nodiscard]] GateSet paulis() const {
std::vector<qc::OpType> result;
for (const auto& g : gates) {
if (g == qc::OpType::X || g == qc::OpType::Y || g == qc::OpType::Z ||
g == qc::OpType::I) {
result.push_back(g);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
result.push_back(g);
result.emplace_back(g);

}
}
return GateSet(result);
}

[[nodiscard]] bool isValidGateSet() const {
return std::all_of(gates.begin(), gates.end(), [](const auto& g) {
return std::find(SINGLE_QUBIT_CLIFFORDS.begin(),
SINGLE_QUBIT_CLIFFORDS.end(),
g) != SINGLE_QUBIT_CLIFFORDS.end();
});
}

// Any single-qubit Clifford gate can be obtained from a product of PI/2
// rotations around different axes.
[[nodiscard]] bool isComplete() const {
if (containsS() || containsSdg()) {
if (containsSX() || containsSXdg() || containsH()) {
return true;
}
}

if (containsSX() || containsSXdg()) {
if (containsH()) {
return true;
}
}
return false;
}

[[nodiscard]] std::string toString() const {
std::string result = "{";
for (const auto& g : gates) {
result += qc::toString(g) + ", ";
}
result += "}";
return result;
}

const qc::OpType& operator[](std::size_t index) const { return gates[index]; }
/**
* Pass-Through
*/

// Iterators (pass-through)
auto begin() noexcept { return gates.begin(); }
[[nodiscard]] auto begin() const noexcept { return gates.begin(); }
[[nodiscard]] auto cbegin() const noexcept { return gates.cbegin(); }
auto end() noexcept { return gates.end(); }
[[nodiscard]] auto end() const noexcept { return gates.end(); }
[[nodiscard]] auto cend() const noexcept { return gates.cend(); }
auto rbegin() noexcept { return gates.rbegin(); }
[[nodiscard]] auto rbegin() const noexcept { return gates.rbegin(); }
[[nodiscard]] auto crbegin() const noexcept { return gates.crbegin(); }
auto rend() noexcept { return gates.rend(); }
[[nodiscard]] auto rend() const noexcept { return gates.rend(); }
[[nodiscard]] auto crend() const noexcept { return gates.crend(); }

// Capacity (pass-through)
[[nodiscard]] bool empty() const noexcept { return gates.empty(); }
[[nodiscard]] std::size_t size() const noexcept { return gates.size(); }
// NOLINTNEXTLINE(readability-identifier-naming)
[[nodiscard]] std::size_t max_size() const noexcept {
return gates.max_size();
}
[[nodiscard]] std::size_t capacity() const noexcept {
return gates.capacity();
}

void reserve(const std::size_t newCap) { gates.reserve(newCap); }
// NOLINTNEXTLINE(readability-identifier-naming)
void shrink_to_fit() { gates.shrink_to_fit(); }

// Modifiers (pass-through)
void clear() noexcept { gates.clear(); }
// NOLINTNEXTLINE(readability-identifier-naming)
void pop_back() { return gates.pop_back(); }
void resize(std::size_t count) { gates.resize(count); }
iterator erase(const_iterator pos) { return gates.erase(pos); }
iterator erase(const_iterator first, const_iterator last) {
return gates.erase(first, last);
}

// NOLINTNEXTLINE(readability-identifier-naming)
void push_back(const qc::OpType& gate) {
if (!containsGate(gate)) {
gates.push_back(gate);
}
}

// NOLINTNEXTLINE(readability-identifier-naming)
template <class T, class... Args> void emplace_back(Args&&... args) {
gates.emplace_back(args...);
}

// NOLINTNEXTLINE(readability-identifier-naming)
void emplace_back(qc::OpType& gate) {
if (!containsGate(gate)) {
gates.emplace_back(gate);
}
}

// NOLINTNEXTLINE(readability-identifier-naming)
void emplace_back(qc::OpType&& gate) {
if (!containsGate(gate)) {
gates.emplace_back(gate);
}
}

[[nodiscard]] const auto& at(const std::size_t i) const {
return gates.at(i);
}
[[nodiscard]] auto& at(const std::size_t i) { return gates.at(i); }
[[nodiscard]] const auto& front() const { return gates.front(); }
[[nodiscard]] const auto& back() const { return gates.back(); }
};
Comment on lines +160 to +235
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's quite a long list of pass-through methods. Are all of these really necessary/helpful?


} // namespace cs
17 changes: 16 additions & 1 deletion include/cliffordsynthesis/Tableau.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#pragma once

#include "QuantumComputation.hpp"
#include "operations/OpType.hpp"
#include "utils/logging.hpp"

#include <cstdint>
Expand Down Expand Up @@ -50,7 +51,7 @@ class Tableau {
nQubits = tableau.size() / 2U;
}

[[nodiscard]] RowType operator[](const std::size_t index) {
[[nodiscard]] RowType& operator[](const std::size_t index) {
return tableau[index];
}
[[nodiscard]] RowType operator[](const std::size_t index) const {
Expand Down Expand Up @@ -98,6 +99,7 @@ class Tableau {
}

void applyGate(const qc::Operation* gate);
void applySingleQGate(const qc::OpType& gate, std::size_t target);
void applyH(std::size_t target);
void applyS(std::size_t target);
void applySdag(std::size_t target);
Expand All @@ -121,6 +123,19 @@ class Tableau {
return !(lhs == rhs);
}

[[nodiscard]] bool equalUpToPhase(const Tableau& rhs) const {
const auto& lhsTab = this->getTableau();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const auto& lhsTab = this->getTableau();
const auto& lhsTab = getTableau();

const auto& rhsTab = rhs.getTableau();
for (std::size_t i = 0; i < lhsTab.size(); ++i) {
for (std::size_t j = 0; j < lhsTab[i].size() - 1; ++j) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be stupid, but what is the -1 in the inner loop here for? Seems like a mistake to me.

if (lhsTab[i][j] != rhsTab[i][j]) {
return false;
}
}
}
return true;
}

[[nodiscard]] bool isIdentityTableau() const;

void createDiagonalTableau(std::size_t nQ, bool includeDestabilizers = false);
Expand Down
26 changes: 26 additions & 0 deletions include/cliffordsynthesis/Utils.hpp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heads-up: I personally despise Utils files, so please feel free to disagree on this, but I do not think the functionality here should be in a separate file.
All of this can simply go into the GateSet class/file. The isPauli method can even be used there to simplify some if condition.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// This file is part of the MQT QMAP library released under the MIT license.
// See README.md or go to https://github.com/cda-tum/qmap for more information.
//

#pragma once

#include "cliffordsynthesis/GateSet.hpp"
#include "operations/OpType.hpp"

namespace cs {

const GateSet PAULIS{
qc::OpType::None,
qc::OpType::X,
qc::OpType::Y,
qc::OpType::Z,
};

inline bool isPauli(const qc::OpType& gate) {
return PAULIS.containsGate(gate);
}

qc::OpType multiplyPaulis(const GateSet& paulis);

} // namespace cs
Loading
Loading