-
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
Delayed Phase Correction #397
base: main
Are you sure you want to change the base?
Conversation
For depth-optimality it is sufficient to impose that at least one gate has to be present on each qubit. The transition relation on the tableau itself constrains the gates enough already. For the maxsat solution some adjustments have to be made because it relies on the exactlyone constraint to be present.
Hey @pehamTom 👋🏼 Haven't looked through this at all yet. Just a brief question: Is this something that's always guaranteed to be faster than the original version? If so, could we just fully replace the old version and avoid adding all those if statements for deciding between both options? Just thinking about maintainability here. |
Ignoring the phase during synthesis does not guarantee depth-optimality. One could always back-propagate Paulis and fuse them with any single-qubit gates as a post-processing step. But I think it makes sense to respect the chosen gateset and leave the rest up to the user. I wanted to compress the settings we have anyway. The maxsat method should be removed entirely for example as it is always worse. But that is something for another PR. |
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #397 +/- ##
=======================================
+ Coverage 92.3% 92.4% +0.1%
=======================================
Files 44 49 +5
Lines 4158 4468 +310
Branches 701 764 +63
=======================================
+ Hits 3839 4132 +293
- Misses 319 336 +17
|
Sorry for responding so late here. For some reason, I missed the notification for the message. I see! In that case, the separate option definitely makes sense. Is this PR ready for review then? |
Now it is ready for review. |
Cpp-Linter Report
|
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.
Many thanks for submitting this PR!
For future reference: It would have been appreciated if, instead of a large PR like this that accomplishes more than a single thing, this were a collection of smaller self-contained PRs. That would make it so much easier to reason about and would considerably speed up the adoption of changes in main
.
I guess it's too late for that now in this case.
That's one of the main reasons why the list of comments below is rather long. Sorry for that.
You should be able to batch the smaller code suggestions together directly on GitHub and commit them in one go. That should get rid of some comments already.
Two things that I couldn't really annotate in the sources themselves:
- Please update the PR description to reflect all the additions, optimisations, changes in this PR
- Is this a feature that should be advertised in the documentation? For example in the existing Clifford Synthesis notebook?
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 revert these unrelated submodule change
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 revert these unrelated submodule change
GateSet gateSet = {qc::OpType::None, qc::OpType::S, qc::OpType::Sdg, | ||
qc::OpType::H, qc::OpType::X, qc::OpType::Y, | ||
qc::OpType::Z}; |
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.
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?
|
||
#include <plog/Log.h> | ||
#include <thread> | ||
#include <unordered_map> | ||
#include <variant> | ||
#include <vector> |
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.
#include <vector> |
Seems unnecessary
// Check if None is already contained and if not, append it | ||
void appendNone() { | ||
if (!containsGate(qc::OpType::None)) { | ||
gates.push_back(qc::OpType::None); |
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.
gates.push_back(qc::OpType::None); | |
gates.emplace_back(qc::OpType::None); |
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.
All of the new options are missing from the Python stubs for QMAP. Please also add any additions to the bindings there.
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 am surprised by the lack of further tests asserting the proper behaviour given the size of the changes in this PR.
Is this really enough to ensure that this works as expected?
For example: you introduce single-qubit gate sets in this PR, but you newer test different gate-sets.
That seems very dangerous to me to introduce regressions.
iChange.emplace_back(logicbase::LogicTerm::ite( | ||
paulis[q][0], LogicTerm(false), LogicTerm(false))); |
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.
Something seems off to me about that constraint no matter the value of the Paulis, this will always be false, right?
Like, this can be way simpler and outside the loop.
void PhaseCorrectionEncoder::splitXorR(const LogicVector& changes) { | ||
auto xorHelper = LogicVector{}; | ||
xorHelper.reserve(S); | ||
for (std::size_t row = 0U; row < S; ++row) { | ||
const std::string hName = | ||
"h_" + std::to_string(row) + "_" + std::to_string(xorHelpers.size()); | ||
DEBUG() << "Creating helper variable for RChange XOR " << hName; | ||
xorHelper.emplace_back(lb->makeVariable(hName)); | ||
} | ||
xorHelpers.emplace_back(xorHelper); | ||
if (xorHelpers.size() == 1) { | ||
for (std::size_t row = 0U; row < S; ++row) { | ||
lb->assertFormula(xorHelpers[0][row] == changes[row]); | ||
} | ||
} else { | ||
for (std::size_t row = 0U; row < S; ++row) { | ||
lb->assertFormula( | ||
xorHelpers.back()[row] == | ||
(changes[row] != xorHelpers[xorHelpers.size() - 2][row])); | ||
} | ||
} | ||
} |
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 method is already implemented elsewhere if I am not mistaken. Can we avoid the code duplication here?
GateSet pauliGates{}; | ||
pauliGates.removePaulis(); |
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 is a truly strange sequence of calls to me that only makes me ask: WHY? 🙃
You create an empty gate set and then you remove Paulis from that?
Description
This PR introduces a feature where stabilizer phases are ignored during Clifford synthesis. If that happens, the phases are corrected during a post-processing step.
Fixes #326
Checklist: