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

[refactor] new SingleQubitSquash class for squashing #168

Merged
merged 29 commits into from
Jan 24, 2022

Conversation

lmondada
Copy link
Contributor

@lmondada lmondada commented Jan 12, 2022

This refactor separates single-qubit squashing into two independent component:

  • the squashing logic (ie given a chain of single-qubit gates, what it should squash to)
  • the piping: how to pass through the circuit and substitute circuits

In so doing, it merges most of the implementations of standard_squash and pqp_squash into one, where before both where independent (with a lot of deduplication). This should allow us to add more squashing methods easily in the future.

As an added bonus, smart squashing now supports conditional operations!

@lmondada lmondada requested a review from cqc-alec January 12, 2022 18:32
@lmondada
Copy link
Contributor Author

The commit history isn't particularly tidy or relevant, so you probably want to review everything in one block.

Copy link
Collaborator

@cqc-alec cqc-alec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this refactor adds more lines (865) than it removes (388), I assume the main motivation is that it will save adding new code in the GlobalisePhasedX pass?

Also nice that we can drop the NoClassicalControlPredicate in the squashing pass. Are you confident there is enough testing of squashing with classical control?

const Circuit& c = cu.get_circ_ref();
c.assert_valid();
REQUIRE(c.n_gates() == 3);
auto cmds = c.get_commands();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unused.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the squash_between method is making my life a lot easier. Although I think most of the code size increase is from comments, classes definition and white space -- the code before was very terse and there definitely isn't a 2x blow-up in the amount of code.

I'll add some testing.

@@ -32,8 +32,6 @@ static bool remove_redundancy(
Circuit &circ, const Vertex &vert, VertexList &bin,
std::set<IVertex> &new_affected_verts, IndexMap &im);
static bool commute_singles_to_front(Circuit &circ);
static bool squash_to_pqp(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you've made a new subdirectory for single-qubit squashing code we should probably also move Transform::squash_1qb_to_tk1() there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return SymEngine::cos(e * SymEngine::pi / 2);
if (is_simpler(e + 2, e)) {
// use cos(x + pi) = -cos(x)
return SymEngine::mul(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised symengine doesn't do this simplification. The machinery for it must be there -- see e.g. https://github.com/symengine/symengine/blob/5c47488c5d1df382d36959b0dad14916dd745f54/symengine/tests/basic/test_functions.cpp#L406

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works if I SymEngine::expand before the cos computation. Would you prefer that?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in just return SymEngine::cos(Symengine::expand(e * SymEngine::pi / 2)); ? That seems better, yes!

@@ -92,12 +92,30 @@ static Expr cos_pi_by_12_times(double x) {
}
}

static bool is_simpler(const Expr& e1, const Expr& e2) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worry a bit about the performance impact of this. At least we could save one call to expand (plus json conversion) and one call to dump by only evaluating them once for e (rather than repeating in the two calls to this function).

auto it1 = circ.begin();
auto it2 = gates.cbegin();

while (it1 != circ.end() && it2 != gates.end()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use Gate::operator== for most of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely!

*
* The squash is made backwards, so that rotations get pushed towards
* the front, see the design choice notes in confluence
* https://cqc.atlassian.net/l/c/17xm5hvp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CQC confluence isn't public so let's not link to it here. Instead summarize the reasons here if possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 63 to 64
"Requires two different bases to perform single qubit "
"rotations");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Requires two different bases to perform single qubit "
"rotations");
"Requires two different bases to perform single qubit rotations");

}

// squash everything between edges in and out
bool squash_between(const Edge &in, const Edge &out) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a lot of implementation in this header file. Is it worth moving some it out into non-templated "helper" source files, or does it mostly need to be templated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm refactoring it to use polymorphism instead of templating. This makes the abstract interface more explicit and allows me to move all the implementation into a separate cpp file

bool z_followed_by_measures = true;
for (port_t port = 0; port < kids.size() && z_followed_by_measures;
port++) {
if (circ.get_OpType_from_Vertex(kids[port]) == OpType::Measure) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the test coverage report (which is generally very good), I see a few lines here that are never executed. Can we add one or two more tests to execute lines 298, 310 and 316?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, these lines would only be used if a squasher returned circuits with OpTypes Measure and noop, which currently they aren't (and arguably never should).

This part of the code was just copied over from before. I would suggest leaving everything as is for now, but I can add a task to revisit and simplify that remove_redundancy code, as most of it is probably unnecessary.

@lmondada lmondada requested a review from cqc-alec January 19, 2022 11:43
@cqc-alec
Copy link
Collaborator

Running the test program after building tket seems to take forever! (I reproduced this locally as well.)

Copy link
Collaborator

@cqc-alec cqc-alec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to cancel the build because it was stuck running test after building tket...

@lmondada lmondada requested a review from cqc-alec January 20, 2022 11:26
Copy link
Collaborator

@cqc-alec cqc-alec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just a couple of pedantic corrections to the doxygen and one question.

const Circuit &sub, const std::vector<Gate_ptr> chain) const {
const unsigned n_gates = sub.n_gates();
return n_gates < chain.size() ||
(n_gates == chain.size() && !is_equal(sub, chain, reversed_));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this ever lead to infinite sequences of squashes where the chain changes each time (cycling between values but staying the same length)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It all depends on the squasher. As long as you have a "normal form" which squashing leaves unchanged (which all well-behaved squasher should have) you'll be fine. In our case this is definitely the case. I can add a line in the docs saying that.

@cqc-alec
Copy link
Collaborator

cqc-alec commented Jan 20, 2022

The test failure in the coverage build is puzzling...

Copy link
Collaborator

@cqc-alec cqc-alec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still failing :-(

@cqc-alec cqc-alec merged commit 12478bd into develop Jan 24, 2022
@cqc-alec cqc-alec deleted the refactor/squashing branch January 24, 2022 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants