-
Notifications
You must be signed in to change notification settings - Fork 26
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
Feature/TwoQubitEncoding #342
base: main
Are you sure you want to change the base?
Changes from 29 commits
5a32776
014f075
9c9256e
a7822e0
59393a8
cdd566e
716b8a6
499b06e
d283ef8
77cc2a5
7c7d542
9b0155a
c9c1a44
0266b24
122dd5d
1c0a309
7ca3ec7
dfea58a
cf12bef
c095997
b6e11dd
b128086
e37653d
8d1da8f
d042b11
92c5df6
727fba8
0425bfc
bd9af05
3a4c206
247e276
1b39f2b
c8bb1d6
a80fd55
098d4d6
caafb46
31090be
292052b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ class Results { | |
return singleQubitGates; | ||
} | ||
[[nodiscard]] std::size_t getDepth() const { return depth; } | ||
[[nodiscard]] std::size_t getTQDepth() const { return tQDepth; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please rename |
||
[[nodiscard]] double getRuntime() const { return runtime; } | ||
[[nodiscard]] logicbase::Result getSolverResult() const { | ||
return solverResult; | ||
|
@@ -52,6 +53,7 @@ class Results { | |
void setSingleQubitGates(const std::size_t g) { singleQubitGates = g; } | ||
void setTwoQubitGates(const std::size_t g) { twoQubitGates = g; } | ||
void setDepth(const std::size_t d) { depth = d; } | ||
void setTQDepth(const std::size_t d) { tQDepth = d; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please rename |
||
void setRuntime(const double t) { runtime = t; } | ||
void setSolverResult(const logicbase::Result r) { solverResult = r; } | ||
void setSolverCalls(const std::size_t c) { solverCalls = c; } | ||
|
@@ -80,6 +82,7 @@ class Results { | |
resultJSON["single_qubit_gates"] = singleQubitGates; | ||
resultJSON["two_qubit_gates"] = twoQubitGates; | ||
resultJSON["depth"] = depth; | ||
resultJSON["tQDepth"] = tQDepth; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please rename |
||
resultJSON["runtime"] = runtime; | ||
resultJSON["solver_calls"] = solverCalls; | ||
|
||
|
@@ -96,8 +99,10 @@ class Results { | |
std::size_t singleQubitGates = std::numeric_limits<std::size_t>::max(); | ||
std::size_t twoQubitGates = std::numeric_limits<std::size_t>::max(); | ||
std::size_t depth = std::numeric_limits<std::size_t>::max(); | ||
double runtime = 0.0; | ||
std::size_t solverCalls = 0U; | ||
std::size_t tQDepth = std::numeric_limits<std::size_t>::max(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please rename |
||
|
||
double runtime = 0.0; | ||
std::size_t solverCalls = 0U; | ||
|
||
std::string resultTableau{}; | ||
std::string resultCircuit{}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ | |
#include <string> | ||
|
||
namespace cs { | ||
enum class TargetMetric { Gates, TwoQubitGates, Depth }; | ||
enum class TargetMetric { Gates, TwoQubitGates, Depth, TQDepth }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please rename |
||
|
||
[[maybe_unused]] static inline std::string toString(const TargetMetric target) { | ||
switch (target) { | ||
|
@@ -19,6 +19,8 @@ enum class TargetMetric { Gates, TwoQubitGates, Depth }; | |
return "two_qubit_gates"; | ||
case TargetMetric::Depth: | ||
return "depth"; | ||
case TargetMetric::TQDepth: | ||
return "tQDepth"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please rename |
||
} | ||
return "Error"; | ||
} | ||
|
@@ -34,6 +36,9 @@ targetMetricFromString(const std::string& target) { | |
if (target == "depth") { | ||
return TargetMetric::Depth; | ||
} | ||
if (target == "tQDepth") { | ||
return TargetMetric::TQDepth; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please rename |
||
return TargetMetric::Gates; | ||
} | ||
} // namespace cs |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,18 +30,24 @@ | |
logicbase::LogicMatrix3D gS{}; | ||
// variables for the two-qubit gates | ||
logicbase::LogicMatrix3D gC{}; | ||
// variables for the pauli-qubit gates | ||
logicbase::LogicMatrix gP{}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These variables are only used by the TwoQubitGateEncoder, please move the method there. |
||
|
||
void | ||
collectSingleQubitGateVariables(std::size_t pos, std::size_t qubit, | ||
logicbase::LogicVector& variables) const; | ||
void collectTwoQubitGateVariables(std::size_t pos, std::size_t qubit, | ||
bool target, | ||
logicbase::LogicVector& variables) const; | ||
void | ||
collectPauliQubitGateVariables(std::size_t qubit, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is only used by the TwoQubitGateEncoder, please move the method there. |
||
logicbase::LogicVector& variables) const; | ||
}; | ||
|
||
// variable creation | ||
void createSingleQubitGateVariables(); | ||
void createTwoQubitGateVariables(); | ||
virtual void createSingleQubitGateVariables(std::size_t tqEncoding); | ||
virtual void createTwoQubitGateVariables(std::size_t tqEncoding); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, please move. |
||
virtual void createPauliQubitGateVariables(); | ||
|
||
// encode the relation between the tableaus and the gates | ||
virtual void encodeGates() { | ||
|
@@ -52,13 +58,20 @@ | |
virtual void encodeSymmetryBreakingConstraints(); | ||
|
||
// extracting the circuit | ||
void extractCircuitFromModel(Results& res, logicbase::Model& model); | ||
virtual void extractCircuitFromModel(Results& res, logicbase::Model& model); | ||
|
||
[[nodiscard]] auto* getVariables() { return &vars; } | ||
|
||
static constexpr std::array<qc::OpType, 7> SINGLE_QUBIT_GATES = { | ||
qc::OpType::None, qc::OpType::X, qc::OpType::Y, qc::OpType::Z, | ||
qc::OpType::H, qc::OpType::S, qc::OpType::Sdag}; | ||
// TODO: set back | ||
static constexpr std::array<qc::OpType, 4> SINGLE_QUBIT_GATES = { | ||
qc::OpType::None, qc::OpType::H, qc::OpType::S, qc::OpType::SX}; | ||
|
||
// static constexpr std::array<qc::OpType, 7> SINGLE_QUBIT_GATES = { | ||
// qc::OpType::None, qc::OpType::X, qc::OpType::Y, qc::OpType::Z, | ||
// qc::OpType::H, qc::OpType::S, qc::OpType::Sdag}; | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think PAULI_GATES is a better name here. |
||
static constexpr std::array<qc::OpType, 4> PAULI_QUBIT_GATES = { | ||
qc::OpType::None, qc::OpType::X, qc::OpType::Y, qc::OpType::Z}; | ||
|
||
[[nodiscard]] static constexpr std::size_t | ||
gateToIndex(const qc::OpType type) { | ||
|
@@ -130,9 +143,9 @@ | |
virtual void assertSingleQubitGateConstraints(std::size_t pos) = 0; | ||
virtual void assertTwoQubitGateConstraints(std::size_t pos) = 0; | ||
[[nodiscard]] static std::vector<TransformationFamily> | ||
collectGateTransformations(std::size_t pos, std::size_t qubit, | ||
const GateToTransformation& gateToTransformation); | ||
void assertGatesImplyTransform( | ||
collectGateTransformations(std::size_t pos, std::size_t qubit, | ||
const GateToTransformation& gateToTransformation); | ||
virtual void assertGatesImplyTransform( | ||
std::size_t pos, std::size_t qubit, | ||
const std::vector<TransformationFamily>& transformations); | ||
virtual void assertZConstraints(std::size_t pos, std::size_t qubit); | ||
|
@@ -142,10 +155,10 @@ | |
createTwoQubitGateConstraint(std::size_t pos, std::size_t ctrl, | ||
std::size_t trgt) = 0; | ||
|
||
void extractSingleQubitGatesFromModel(std::size_t pos, | ||
logicbase::Model& model, | ||
qc::QuantumComputation& qc, | ||
std::size_t& nSingleQubitGates); | ||
virtual void extractSingleQubitGatesFromModel(std::size_t pos, | ||
logicbase::Model& model, | ||
qc::QuantumComputation& qc, | ||
std::size_t& nSingleQubitGates); | ||
void extractTwoQubitGatesFromModel(std::size_t pos, logicbase::Model& model, | ||
qc::QuantumComputation& qc, | ||
std::size_t& nTwoQubitGates); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,12 @@ class SATEncoder { | |
// whether to use symmetry breaking | ||
bool useSymmetryBreaking = false; | ||
|
||
// whether to use SQG-TQG encoding | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please rename |
||
bool useTwoQubitEncoding = false; | ||
|
||
// the number of threads to pass to the SAT solver | ||
std::size_t nThreads = 1U; | ||
|
||
// an optional limit on the total number of gates | ||
std::optional<std::size_t> gateLimit = std::nullopt; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// | ||
// Created by Velsh Aleksei on 16.06.23. | ||
// | ||
|
||
#pragma once | ||
|
||
#include "cliffordsynthesis/encoding/GateEncoder.hpp" | ||
|
||
#include <cstddef> | ||
#include <optional> | ||
|
||
namespace cs::encoding { | ||
|
||
class TwoQubitEncoder : public GateEncoder { | ||
public: | ||
using GateEncoder::GateEncoder; | ||
|
||
protected: | ||
logicbase::LogicTerm rChanges{}; | ||
logicbase::LogicMatrix xorHelpers{}; | ||
|
||
void assertConsistency() const override; | ||
void assertGateConstraints() override; | ||
void assertRConstraints(std::size_t pos, std::size_t qubit) override; | ||
void assertSingleQubitGateConstraints(std::size_t pos) override; | ||
void assertPauliGateConstraints(std::size_t pos); | ||
void assertTwoQubitGateConstraints(std::size_t pos) override; | ||
[[nodiscard]] logicbase::LogicTerm | ||
createTwoQubitGateConstraint(std::size_t pos, std::size_t ctrl, | ||
std::size_t trgt) override; | ||
[[nodiscard]] logicbase::LogicTerm | ||
createIdentityConstraintOnTQG(std::size_t pos, std::size_t ctrl); | ||
|
||
// assert constrains | ||
void assertSingleQubitGateOrderConstraints(std::size_t pos, | ||
std::size_t qubit) override; | ||
void assertTwoQubitGateOrderConstraints(std::size_t pos, std::size_t ctrl, | ||
std::size_t trgt) override; | ||
|
||
// collect TQG variables | ||
void collectTwoQubitGateVariables(std::size_t pos, std::size_t qubit, | ||
bool target, | ||
logicbase::LogicVector& variables) const; | ||
// extracting the circuit | ||
void extractCircuitFromModel(Results& res, logicbase::Model& model) override; | ||
|
||
void splitXorR(const logicbase::LogicTerm& changes, std::size_t pos); | ||
|
||
virtual void assertGatesImplyTransform( | ||
Check warning on line 49 in include/cliffordsynthesis/encoding/TwoQubitEncoder.hpp GitHub Actions / 🇨 Lint / 🚨 Lint/include/cliffordsynthesis/encoding/TwoQubitEncoder.hpp:49:16 [cppcoreguidelines-explicit-virtual-functions
|
||
std::size_t pos, std::size_t qubit, | ||
const std::vector<TransformationFamily>& transformations) override; | ||
}; | ||
|
||
} // namespace cs::encoding |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,14 +5,14 @@ | |
|
||
#include "cliffordsynthesis/CliffordSynthesizer.hpp" | ||
|
||
#include "LogicTerm/Logic.hpp" | ||
#include "QuantumComputation.hpp" | ||
#include "cliffordsynthesis/Tableau.hpp" | ||
#include "utils/logging.hpp" | ||
|
||
#include <chrono> | ||
#include <fstream> | ||
#include <future> | ||
#include <iostream> | ||
#include <memory> | ||
#include <thread> | ||
|
||
|
@@ -44,6 +44,8 @@ void CliffordSynthesizer::synthesize(const Configuration& config) { | |
encoderConfig.solverParameters = configuration.solverParameters; | ||
encoderConfig.useMultiGateEncoding = | ||
requiresMultiGateEncoding(encoderConfig.targetMetric); | ||
encoderConfig.useTwoQubitEncoding = | ||
requiresTwoQubitEncoding(encoderConfig.targetMetric); | ||
|
||
if (configuration.heuristic) { | ||
if (initialCircuit->empty() && !targetTableau.isIdentityTableau()) { | ||
|
@@ -97,6 +99,9 @@ void CliffordSynthesizer::synthesize(const Configuration& config) { | |
case TargetMetric::TwoQubitGates: | ||
twoQubitGateOptimalSynthesis(encoderConfig, 0U, results.getTwoQubitGates()); | ||
break; | ||
case TargetMetric::TQDepth: | ||
twoQubitDepthOptimalSynthesis(encoderConfig, lower, upper); | ||
break; | ||
} | ||
|
||
results.setSolverCalls(solverCalls); | ||
|
@@ -130,6 +135,10 @@ void CliffordSynthesizer::determineInitialTimestepLimit(EncoderConfig& config) { | |
config.timestepLimit = results.getDepth(); | ||
INFO() << "Using initial circuit's depth as initial timestep limit: " | ||
<< config.timestepLimit; | ||
} else if (requiresTwoQubitEncoding(config.targetMetric)) { | ||
config.timestepLimit = results.getTQDepth(); | ||
INFO() << "Using initial circuit's tQDepth as initial timestep limit: " | ||
<< config.timestepLimit; | ||
} else { | ||
config.timestepLimit = results.getGates(); | ||
INFO() << "Using initial circuit's gate count as initial timestep limit: " | ||
|
@@ -159,6 +168,9 @@ CliffordSynthesizer::determineUpperBound(EncoderConfig config) { | |
if (!results.sat()) { | ||
lowerBound = upperBound + 1U; | ||
upperBound *= 2U; | ||
std::cout << "No solution found for " << config.timestepLimit | ||
<< " timestep(s). Doubling timestep limit to " << upperBound | ||
<< std::endl; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leftover Debug print. Please remove. |
||
INFO() << "No solution found for " << config.timestepLimit | ||
<< " timestep(s). Doubling timestep limit to " << upperBound; | ||
config.timestepLimit = upperBound; | ||
|
@@ -170,6 +182,8 @@ CliffordSynthesizer::determineUpperBound(EncoderConfig config) { | |
upperBound = std::min(upperBound, results.getGates()); | ||
} else if (config.targetMetric == TargetMetric::Depth) { | ||
upperBound = std::min(upperBound, results.getDepth()); | ||
} else if (config.targetMetric == TargetMetric::TQDepth) { | ||
upperBound = std::max(upperBound, results.getTQDepth()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As we have discussed, this can't be right. The optimal synthesis result cannot have a higher depth then either of these values. |
||
} | ||
|
||
INFO() << "Found upper bound for the number of timesteps: " << upperBound; | ||
|
@@ -237,6 +251,29 @@ void CliffordSynthesizer::depthOptimalSynthesis( | |
} | ||
} | ||
|
||
void CliffordSynthesizer::twoQubitDepthOptimalSynthesis( | ||
CliffordSynthesizer::EncoderConfig config, const std::size_t lower, | ||
const std::size_t upper) { | ||
// tQDepth-optimal synthesis is achieved by determining a timestep limit T | ||
// such that there exists a solution with tQDepth T, but no solution with | ||
// tQDepth T-1. This procedure uses an encoding (SQG - TQG) where in single | ||
// and two qubit gates are allowed just one gate per timestep. This procedure | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a tiny bit misleading. The number of gates is not restricted per timestep |
||
// is guaranteed to produce a depth-optimal circuit. However, the number of | ||
// gates in the resulting circuit is not necessarily minimal, i.e., there may | ||
// be a solution with fewer gates and the same depth. To this end, an | ||
// optimization pass is provided that additionally minimizes the number of | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This optimization pass is not currently present |
||
// gates. | ||
|
||
if (configuration.linearSearch) { | ||
runLinearSearch(config.timestepLimit, lower, upper, config); | ||
} else { | ||
// The binary search approach calls the SAT solver repeatedly with varying | ||
// timestep (=tQDepth) limits T until a solution with tQDepth T is found, | ||
// but no solution with tQDepth T-1 could be determined. | ||
runBinarySearch(config.timestepLimit, lower, upper, config); | ||
} | ||
} | ||
|
||
void CliffordSynthesizer::minimizeGatesFixedDepth(EncoderConfig config) { | ||
if (results.getDepth() == 0U) { | ||
return; | ||
|
@@ -427,6 +464,13 @@ void CliffordSynthesizer::updateResults(const Configuration& config, | |
currentResults = newResults; | ||
} | ||
break; | ||
case TargetMetric::TQDepth: | ||
if ((newResults.getTQDepth() < currentResults.getTQDepth()) || | ||
((newResults.getTQDepth() == currentResults.getTQDepth()) && | ||
(newResults.getGates() < currentResults.getGates()))) { | ||
currentResults = newResults; | ||
} | ||
break; | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment inconsistent with variable naming