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

Feature/TwoQubitEncoding #342

Draft
wants to merge 38 commits into
base: main
Choose a base branch
from
Draft

Feature/TwoQubitEncoding #342

wants to merge 38 commits into from

Conversation

avelsh
Copy link

@avelsh avelsh commented Jul 8, 2023

Added functionality for SQG-TQG shape encoding

Copy link
Member

@pehamTom pehamTom left a 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>
Copy link
Member

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() &&
Copy link
Member

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() {
Copy link
Member

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()) {
Copy link
Member

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);
Copy link
Member

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;
Copy link
Member

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(
Copy link
Member

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,
Copy link
Member

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.

@pehamTom pehamTom marked this pull request as draft July 11, 2023 06:51
@pehamTom pehamTom assigned pehamTom and unassigned pehamTom Jul 11, 2023
@pehamTom pehamTom added feature New feature or request c++ Anything related to C++ code enhancement Anything related to improvements of the existing library minor Changes leading to a minor version increase labels Jul 11, 2023
Aleksei Velsh and others added 6 commits July 13, 2023 16:18
# 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

This comment appears to contain commented-out code.
Comment on lines 85 to 87
if (q != pos) {
// assertRConstraints(pos, q);
}

Check notice

Code scanning / CodeQL

Futile conditional

If-statement with an empty then-branch and no else-branch.
Aleksei Velsh and others added 3 commits August 9, 2023 13:39
: assertTwoQubitGateConstraints(pos);
splitXorR(tvars->r[pos], pos);
TRACE() << "Asserting r changes at time " << t;
// lb->assertFormula(tvars->r[pos + 1] == xorHelpers[pos].back());

Check notice

Code scanning / CodeQL

Commented-out code

This comment appears to contain commented-out code.
@avelsh avelsh changed the title Feature/s_tqg_encoding Feature/TwoQubitEncoding Sep 6, 2023
Copy link
Member

@pehamTom pehamTom left a 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
Copy link
Member

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; }
Copy link
Member

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; }
Copy link
Member

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;
Copy link
Member

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();
Copy link
Member

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() {
Copy link
Member

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);
Copy link
Member

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]);
Copy link
Member

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) {
Copy link
Member

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());
Copy link
Member

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

pehamTom and others added 8 commits September 9, 2023 13:40
# 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Anything related to C++ code enhancement Anything related to improvements of the existing library feature New feature or request minor Changes leading to a minor version increase
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants