-
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?
Conversation
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.
This already looks really good. I have given you some pointers in my review that should help identify problematic sections of the code.
In general, I would like to change the name from STQGatesEncoder
to simply TwoQubitEncoder
. In contrast to the MultiGateEncoder
this should give an idea that this class is used for solving problems wrt. two-qubit gates.
@@ -129,7 +129,7 @@ class GateEncoder { | |||
virtual void assertGateConstraints() = 0; | |||
virtual void assertSingleQubitGateConstraints(std::size_t pos) = 0; | |||
virtual void assertTwoQubitGateConstraints(std::size_t pos) = 0; | |||
[[nodiscard]] static std::vector<TransformationFamily> | |||
[[nodiscard]] virtual std::vector<TransformationFamily> |
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.
I don't think this has to be virtual
necessarily. You could shadow the definition from GateEncoder
since the method is only used in the derived class.
config.initialTableau->hasDestabilizers() | ||
? 2U * N | ||
: N; | ||
std::size_t s = config.targetTableau->hasDestabilizers() && |
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.
Is there a reason that this is not const
anymore?
} | ||
} | ||
|
||
void encoding::STQGatesEncoder::createTwoQubitGateVariables() { |
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.
As it stands, much code is repeated from GateEncoder.cpp
. You could call the base class method and then loop over the timesteps to insert the identity variables separately. Or don't overwrite the method and use g_i_i
as the variable for the identity gate.
const auto n = twoQubitGates.size(); | ||
for (std::size_t q = 0; q < n; ++q) { | ||
if (q == qubit) { | ||
if (N > variables.size()) { |
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.
I think I don't get this check. You could check for the target
flag. This way you would also only add the variable for the identity once.
for (std::size_t t = 0U; t < T; ++t) { | ||
TRACE() << "Asserting gate constraints at time " << t; | ||
|
||
const std::size_t pos = t < T / 2 ? t : t % (T / 2); |
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.
As the Linter says, this is a possible division by Zero if T
is 0. Also, I think it would improve clarity to add a class member that is essentially just T/2
that is set in the constructor. Then T
would be the number of tableaus and T/2
would be the number of layers.
for (std::size_t ctrl = 0U; ctrl < N; ++ctrl) { | ||
for (std::size_t trgt = 0U; trgt < N; ++trgt) { | ||
if (ctrl == trgt) { | ||
continue; |
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.
There is a constraint that needs to be added here. You also have to assert that when the None
gate is set in the two-qubit layer there are no changes. At the moment, when the solver decides to set the identity the tableau variables are unconstrained and can be anything.
} | ||
} | ||
|
||
LogicTerm encoding::STQGatesEncoder::createTwoQubitGateConstraint( |
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.
If I'm not missing something here, this method can be removed as it is just a duplicated from the base class.
const GateToTransformation& gateToTransformation) override; | ||
|
||
void | ||
extractSingleQubitGatesFromModel(std::size_t pos, logicbase::Model& model, |
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.
You need to overwrite the GateEncoder::extractCircuitFromModel
method as well. At the moment this is
for (std::size_t t = 0; t < T; ++t) {
extractSingleQubitGatesFromModel(t, model, qc, nSingleQubitGates);
extractTwoQubitGatesFromModel(t, model, qc, nTwoQubitGates);
}```
The variables are extracted for all `t` up to `T`. But the variables only make sense for even or odd `t,` respectively.
# Conflicts: # include/cliffordsynthesis/encoding/STQGatesEncoder.hpp # src/cliffordsynthesis/encoding/STQGatesEncoder.cpp
# Conflicts: # include/cliffordsynthesis/encoding/TwoQubitEncoder.hpp # src/cliffordsynthesis/encoding/SATEncoder.cpp # src/cliffordsynthesis/encoding/TwoQubitEncoder.cpp
assertZConstraints(pos, q); | ||
assertXConstraints(pos, q); | ||
if (q != pos) { | ||
// assertRConstraints(pos, q); |
Check notice
Code scanning / CodeQL
Commented-out code
if (q != pos) { | ||
// assertRConstraints(pos, q); | ||
} |
Check notice
Code scanning / CodeQL
Futile conditional
# Conflicts: # src/cliffordsynthesis/encoding/TwoQubitEncoder.cpp
# Conflicts: # src/cliffordsynthesis/CliffordSynthesizer.cpp # src/cliffordsynthesis/encoding/SATEncoder.cpp
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.
Thank you for your work. I have reviewed your code and pointed out changes I'd like to see.
As stated in the comments you will most certainly need another single-qubit gate layer after the last two-qubit gate layer.
The naming TQDepth is not very self-explainatory. Please rename the respective variables to twoQubitDepth...
@@ -38,6 +38,9 @@ struct Configuration { | |||
/// Settings for depth-optimal synthesis | |||
bool minimizeGatesAfterDepthOptimization = false; | |||
|
|||
/// Settings for STDepth-optimal synthesis |
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
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
please rename
@@ -52,6 +53,7 @@ | |||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
please rename
@@ -80,6 +82,7 @@ | |||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
please rename
@@ -96,8 +99,10 @@ | |||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
please rename
} | ||
} | ||
|
||
void encoding::TwoQubitEncoder::assertGateConstraints() { |
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.
After playing around with the code a bit I am now 100% convinced that a last single-qubit gate layer is need after the last two-qubit gate layer. Otherwise depth-optimality cannot be guaranteed.
TRACE() << "Asserting gate constraints at time " << t; | ||
splitXorR(tvars->r[t], t); | ||
if (t == T) { | ||
assertPauliGateConstraints(t); |
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.
Again, this doesn't need to be in the loop.
splitXorR(change, pos); | ||
} | ||
lb->assertFormula(tvars->x[pos][q] == tvars->x[pos + 1][q]); | ||
lb->assertFormula(tvars->z[pos][q] == tvars->z[pos + 1][q]); |
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.
Not necessary, but since only the phase is adjusted here, one could save on Tableau Variables.
|
||
LogicTerm | ||
encoding::TwoQubitEncoder::createIdentityConstraintOnTQG(std::size_t pos, | ||
std::size_t ctrl) { |
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.
This does not have to be a method. It is simple enough to be inlined and is only called once.
|
||
res.setSingleQubitGates(nSingleQubitGates); | ||
res.setTwoQubitGates(nTwoQubitGates); | ||
res.setTQDepth(qc.getTQDepth()); |
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.
This should be for the other encodings as well so you can compare against them. Add this computation to GateEncoder::extractCircuitFromModel
# Conflicts: # include/cliffordsynthesis/encoding/GateEncoder.hpp # src/cliffordsynthesis/encoding/GateEncoder.cpp # src/cliffordsynthesis/encoding/TwoQubitEncoder.cpp # test/cliffordsynthesis/test_synthesis.cpp
# Conflicts: # src/cliffordsynthesis/encoding/TwoQubitEncoder.cpp
Added functionality for SQG-TQG shape encoding