Skip to content

✨ Convenience features #226

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

Merged
merged 7 commits into from
Dec 2, 2022
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
36 changes: 36 additions & 0 deletions include/QuantumComputation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ namespace qc {

[[nodiscard]] std::size_t getNindividualOps() const;
[[nodiscard]] std::size_t getNsingleQubitOps() const;
[[nodiscard]] std::size_t getDepth() const;

[[nodiscard]] std::string getQubitRegister(dd::Qubit physicalQubitIndex) const;
[[nodiscard]] std::string getClassicalRegister(std::size_t classicalIndex) const;
Expand Down Expand Up @@ -633,6 +634,18 @@ namespace qc {
emplace_back<NonUnitaryOperation>(getNqubits(), targets, qc::Barrier);
}

void classicControlled(const OpType op, const dd::Qubit target, const std::pair<dd::Qubit, dd::QubitCount>& controlRegister, const unsigned int expectedValue = 1U, const dd::fp lambda = 0., const dd::fp phi = 0., const dd::fp theta = 0.) {
classicControlled(op, target, dd::Controls{}, controlRegister, expectedValue, lambda, phi, theta);
}
void classicControlled(const OpType op, const dd::Qubit target, const dd::Control control, const std::pair<dd::Qubit, dd::QubitCount>& controlRegister, const unsigned int expectedValue = 1U, const dd::fp lambda = 0., const dd::fp phi = 0., const dd::fp theta = 0.) {
classicControlled(op, target, dd::Controls{control}, controlRegister, expectedValue, lambda, phi, theta);
}
void classicControlled(const OpType op, const dd::Qubit target, const dd::Controls& controls, const std::pair<dd::Qubit, dd::QubitCount>& controlRegister, const unsigned int expectedValue = 1U, const dd::fp lambda = 0., const dd::fp phi = 0., const dd::fp theta = 0.) {
checkQubitRange(target, controls);
std::unique_ptr<Operation> gate = std::make_unique<StandardOperation>(getNqubits(), controls, target, op, lambda, phi, theta);
emplace_back<ClassicControlledOperation>(std::move(gate), controlRegister, expectedValue);
}

/// strip away qubits with no operations applied to them and which do not pop up in the output permutation
/// \param force if true, also strip away idle qubits occurring in the output permutation
void stripIdleQubits(bool force = false, bool reduceIOpermutations = true);
Expand Down Expand Up @@ -718,6 +731,24 @@ namespace qc {
virtual void dumpOpenQASM(std::ostream& of);
virtual void dumpTensorNetwork(std::ostream& of) const;

// this convenience method allows to turn a circuit into a compound operation.
std::unique_ptr<CompoundOperation> asCompoundOperation() {
return std::make_unique<CompoundOperation>(getNqubits(), std::move(ops));
}

// this convenience method allows to turn a circuit into an operation.
std::unique_ptr<Operation> asOperation() {
if (ops.empty()) {
return {};
}
if (ops.size() == 1) {
auto op = std::move(ops.front());
ops.clear();
return op;
}
return asCompoundOperation();
}

virtual void reset() {
ops.clear();
nqubits = 0;
Expand Down Expand Up @@ -783,6 +814,11 @@ namespace qc {
ops.emplace_back(std::move(op));
}

template<class T>
void emplace_back(std::unique_ptr<T>&& op) {
ops.emplace_back(std::move(op));
}

template<class T>
iterator insert(const_iterator pos, T&& op) { return ops.insert(pos, std::forward<T>(op)); }

Expand Down
11 changes: 11 additions & 0 deletions include/operations/CompoundOperation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ namespace qc {
type = Compound;
}

explicit CompoundOperation(dd::QubitCount nq, std::vector<std::unique_ptr<Operation>>&& operations):
CompoundOperation(nq) {
ops = std::move(operations);
}

[[nodiscard]] std::unique_ptr<Operation> clone() const override {
auto cloned_co = std::make_unique<CompoundOperation>(nqubits);
cloned_co->reserve(ops.size());
Expand Down Expand Up @@ -97,6 +102,12 @@ namespace qc {
return std::any_of(ops.cbegin(), ops.cend(), [&i](const auto& op) { return op->actsOn(i); });
}

void addDepthContribution(std::vector<std::size_t>& depths) const override {
for (const auto& op: ops) {
op->addDepthContribution(depths);
}
}

void dumpOpenQASM(std::ostream& of, const RegisterNames& qreg, const RegisterNames& creg) const override {
for (const auto& op: ops) {
op->dumpOpenQASM(of, qreg, creg);
Expand Down
2 changes: 2 additions & 0 deletions include/operations/NonUnitaryOperation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ namespace qc {

[[nodiscard]] bool actsOn(dd::Qubit i) const override;

void addDepthContribution(std::vector<std::size_t>& depths) const override;

[[nodiscard]] bool equals(const Operation& op, const Permutation& perm1, const Permutation& perm2) const override;
[[nodiscard]] bool equals(const Operation& operation) const override {
return equals(operation, {}, {});
Expand Down
2 changes: 2 additions & 0 deletions include/operations/Operation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ namespace qc {
return false;
}

virtual void addDepthContribution(std::vector<std::size_t>& depths) const;

[[nodiscard]] virtual bool equals(const Operation& op, const Permutation& perm1, const Permutation& perm2) const;
[[nodiscard]] virtual bool equals(const Operation& op) const {
return equals(op, {}, {});
Expand Down
13 changes: 13 additions & 0 deletions src/QuantumComputation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ namespace qc {
return nops;
}

std::size_t QuantumComputation::getDepth() const {
if (empty()) {
return 0U;
}

std::vector<std::size_t> depths(getNqubits(), 0U);
for (const auto& op: ops) {
op->addDepthContribution(depths);
}

return *std::max_element(depths.begin(), depths.end());
}

void QuantumComputation::import(const std::string& filename) {
size_t dot = filename.find_last_of('.');
std::string extension = filename.substr(dot + 1);
Expand Down
13 changes: 5 additions & 8 deletions src/algorithms/QFT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,13 @@ namespace qc {
for (dd::QubitCount j = 1; j <= i; ++j) {
const auto d = static_cast<dd::Qubit>(precision - j);
if (j == i) {
std::unique_ptr<qc::Operation> op = std::make_unique<StandardOperation>(nqubits, 0, S);
emplace_back<ClassicControlledOperation>(op, std::pair{static_cast<dd::Qubit>(d), 1U}, 1);
classicControlled(S, 0, {d, 1U}, 1U);
} else if (j == i - 1) {
std::unique_ptr<qc::Operation> op = std::make_unique<StandardOperation>(nqubits, 0, T);
emplace_back<ClassicControlledOperation>(op, std::pair{static_cast<dd::Qubit>(d), 1U}, 1);
classicControlled(T, 0, {d, 1U}, 1U);
} else {
auto powerOfTwo = std::pow(2.L, i - j + 1);
auto lambda = static_cast<dd::fp>(dd::PI / powerOfTwo);
std::unique_ptr<qc::Operation> op = std::make_unique<StandardOperation>(nqubits, 0, Phase, lambda);
emplace_back<ClassicControlledOperation>(op, std::pair{static_cast<dd::Qubit>(d), 1U}, 1);
auto powerOfTwo = std::pow(2.L, i - j + 1);
auto lambda = static_cast<dd::fp>(dd::PI / powerOfTwo);
classicControlled(Phase, 0, {d, 1U}, 1U, lambda);
}
}

Expand Down
5 changes: 2 additions & 3 deletions src/algorithms/QPE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,8 @@ namespace qc {

// hybrid quantum-classical inverse QFT
for (dd::QubitCount j = 0; j < i; j++) {
auto iQFT_lambda = -dd::PI / static_cast<double>(1ULL << (i - j));
std::unique_ptr<qc::Operation> op = std::make_unique<StandardOperation>(nqubits, 1, Phase, iQFT_lambda);
emplace_back<ClassicControlledOperation>(op, std::pair{static_cast<dd::Qubit>(j), 1U}, 1);
auto iQFT_lambda = -dd::PI / static_cast<double>(1ULL << (i - j));
classicControlled(Phase, 1, {j, 1U}, 1U, iQFT_lambda);
}
h(1);

Expand Down
5 changes: 5 additions & 0 deletions src/operations/NonUnitaryOperation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,9 @@ namespace qc {
return false;
}
}
void NonUnitaryOperation::addDepthContribution(std::vector<std::size_t>& depths) const {
if (type == Measure || type == Reset) {
Operation::addDepthContribution(depths);
}
}
} // namespace qc
17 changes: 17 additions & 0 deletions src/operations/Operation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,4 +303,21 @@ namespace qc {
return true;
}

void Operation::addDepthContribution(std::vector<std::size_t>& depths) const {
std::size_t maxDepth = 0;
for (const auto& target: getTargets()) {
maxDepth = std::max(maxDepth, depths[target]);
}
for (const auto& control: getControls()) {
maxDepth = std::max(maxDepth, depths[control.qubit]);
}
maxDepth += 1;
for (const auto& target: getTargets()) {
depths[target] = maxDepth;
}
for (const auto& control: getControls()) {
depths[control.qubit] = maxDepth;
}
}

} // namespace qc
Loading