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

Causal flow preserving circuit optimisation #189

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

calumholker
Copy link

@calumholker calumholker commented Dec 4, 2023

Causal flow preserving optimisation of quantum circuits

Implements various functions and changes used for optimising quantum circuits (https://arxiv.org/abs/2312.02793).

circuits/benchmarking_circuits/

Reorganised benchmarking circuits to be clearer which circuit is optimised by which function, and enable easier integration with the new updated Benchmark class.

demos/circuit_optimisation/

Added new demo notebooks demonstrating the use of the new Benchmark class for circuit optimisation (benchmarking.ipynb) as well as effective optimisation of QFT circuits (qft-opt.ipynb). Also includes stored benchmarking metadata demonstrating how it can be saved, rather than having to rerun optimisations each time.

benchmarking.py

Implements the new Benchmark class for comparing circuit optimisation strategies, replacing benchmark.py. This allows you to store the data so that routines don't need to be rerun, automatically verifies circuits, and automatically produces a more visually appealing output. Demonstrated in demos/circuit_optimisation/benchmarking.ipynb.

  • Benchmark.load_circuits enables loading directories containing unoptimised circuits as well as directories containing circuits pre-optimised by a specific routine.
  • Benchmark.add_simplification_func allows you to add any optimisation function which accepts a circuit and outputs an optimised circuit.
  • Benchmark.df generates a dataframe containing desired results, outputting a stylised version of this.
  • Benchmark.Pt_graphs automatically produces graphs comparing results on randomly generated circuits with varying T-gate probability

pyzx/flow.py

Replaced file gflow.py with flow.py, implementing the following functions:

  • gflow is the same function as before (although marginally optimised), implementing the gflow algorithm from dx.doi.org/10.1007/978-3-540-70575-8_70.
  • cflow implements the causal flow algorithm from the same paper. If phase gadgets are desired to be included then this points to full_cflow.
  • full_cflow implements the causal flow algorithm from https://doi.org/10.48550/arXiv.quant-ph/0603072, which calculates the full partial order. It also checks for cflow with phase gadgets (which can then be extracted via phase polynomial synthesis).

pyzx/extract.py

Updated the extract_simple function:

  • Optimised the function for runtime and readability
  • Added the (optional) ability to extract phase gadgets using the new phase_poly_synth function, implementing the algorithm from https://arxiv.org/abs/2004.06052.

func: teleport_reduce

Reimplemented phase teleportation in pyzx/simplify.py, pyzx/graph/base.py and pyzx/graph/graph_s.py in order to track all potential vertices which phases can be fused on, as well as allow phase jumping between these variables throughout simplification. The basic use of teleport_reduce with the parameter store = False (default) is identical to before.

The new objects stored in BaseGraph are as follows:

  • phase_tracking denotes whether the graph is tracking phases, either when the diagram is being simplified initially to find fused phase variables or afterwards when the diagram is being simplified and phases variables are not yet placed on the graph, or phase jumping is allowed.
  • phase_teleporter points to an instance of pyzx/simplify.PhaseTeleporter. This is only used for the initial simplification when finding phase variables which fuse together. In post-simplification all metadata about the variables is then stored on the graph in the following variables, and this is set to None:
  • parent_vertex points a vertex to the original vertex containing the phase variable on the graph
  • vertex_groups indexes a vertex to which vertex group it is in
  • group_data stores the list of vertices remaining (i.e. not placed on the graph yet) in each group of fused variables.
  • phase_sum stores the total phase sum of each group which remains to be added to the graph on one of the variables. If only one variable remains it the phase will be automatically placed and the group removed.
  • phase_mult stores any phase multipliers for vertices (if -1 then the phase sum needs to be subtracted from the graph on that variable rather than added) - i.e. the result of the function phase_negate when gadgets are negated.
  • vertex_rank stores the ordering of how vertices were fused together in the initial simplification process.
  • vertices_to_update stores any vertices which have been modified by fixing other phases (i.e. if it was the last in the group), such that potential matches involving these can be updated/removed - used in flow_2Q_simp.

In pyzx/simplify.PhaseTeleporter fused phase variables are now stored in a disjoint set. This parent function is not the same as the parent_vertex in BaseGraph, but rather the parent in the disjoint set.

pyzx/simplify.py

Modified some functions and added new functions for selective simplification

  • simp now includes an additional parameter num which limits the number of rewrites applied. It also implements rules using apply_rule rather than separately within the function
  • basic_simp iteratively applies just id_simp and spider_simp
  • flow_2Q_simp is the main optimisation function, selectively applying the transformation which reduces the two-qubit gate count the most (if causal flow is chosen to be preserved). selective_simp, match_score_2Q_simp and update_2Q_simp_matches are all helper functions used for this.
  • to_graph_like and is_graph_like were updated, including an optional assert_bound_connections parameter if the input/output connection conditions are not required to be modified.

pyzx/rules.py

The following modifications to existing functions are made:

  • On many functions an optional allow_interacting_matches parameter was added, used when the matches are not all going to be applied at once (such as in flow_2Q_simp).
  • MatchPivotType and MatchLcompType are modified to use tuples rather than lists, so that they are now immutable and can be used as keys in a dictionary.
  • match_pivot_gadget,match_pivot_boundary and match_phase_gadgets were updated such that they no longer modify the graph at all, with all modifications being moved to the respective rewrite functions. The structure of the MatchTypes for each of these are also modified accordingly.
  • pivot_gadget is the new rewrite function for both match_pivot_gadget and match_pivot_boundary, using a helper function gadgetize which infuses the relevant spiders into phase gadgets before, before applying the usual pivot rewrite function.
    Then the following functions were added:
  • match_id_fuse, id_fuse implement identity fusion (i.e. identity removal followed by immediate fusion).
  • match_lcomp_unfuse, lcomp_unfuse implement local complementation including prior applications of neighbour unfusion.
  • match_pivot_unfuse, pivot_unfuse implement local complementation including prior applications of neighbour unfusion.
  • match_2Q_simp, rewrite_2Q_simp finds all matches of id_fuse,lcomp_unfuse and pivot_unfuse.

pyzx/heuristics.py

Module which enables the calculation of statistics and heuristics for various matches, used in flow_2Q_simp

.DS_Store Outdated Show resolved Hide resolved
benchmarking.py Outdated Show resolved Hide resolved
@jvdwetering
Copy link
Collaborator

This looks great Calum. It also makes quite some changes to the API and where the circuits are stored so I need to make sure first this doesn't break anything in some other place, so it might be a while before I can actually approve this.

Copy link
Contributor

@dlyongemallo dlyongemallo left a comment

Choose a reason for hiding this comment

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

Thanks for the improvements, Calum.

It would also be nice to add unit tests for each added/modified functionality, and especially to have an end-to-end regression test, for example using a circuit from your paper, to demonstrate/verify the result of teleport_reduce with store = True.

(The demo notebooks and benchmarks aren't run by GitHub as part of its continuous integration, so it would be good to be able to catch future breaking changes with a test as otherwise the demos and benchmarks may break or change their behaviour without anyone noticing.)

pyzx/simplify.py Outdated Show resolved Hide resolved
pyzx/extract.py Outdated Show resolved Hide resolved
pyzx/graph/base.py Show resolved Hide resolved
pyzx/simplify.py Show resolved Hide resolved
@calumholker
Copy link
Author

calumholker commented Dec 13, 2023

This looks great Calum. It also makes quite some changes to the API and where the circuits are stored so I need to make sure first this doesn't break anything in some other place, so it might be a while before I can actually approve this.

I've realised that some of the other demo notebooks may now not run with the changes to where circuits are stored, I'll go through and update these also.

@calumholker
Copy link
Author

Thanks for the improvements, Calum.

It would also be nice to add unit tests for each added/modified functionality, and especially to have an end-to-end regression test, for example using a circuit from your paper, to demonstrate/verify the result of teleport_reduce with store = True.

(The demo notebooks and benchmarks aren't run by GitHub as part of its continuous integration, so it would be good to be able to catch future breaking changes with a test as otherwise the demos and benchmarks may break or change their behaviour without anyone noticing.)

Sure, I'll add some tests for everything!

@jvdwetering
Copy link
Collaborator

With these new commits, is this now ready for merging?

@dlyongemallo
Copy link
Contributor

With these new commits, is this now ready for merging?

I suppose he's waiting for an answer about testing Poly phases (see above) before adding a test for that.

@calumholker
Copy link
Author

I had already fixed the bug in to_graph_like that you fixed in the recent commit (although have now updated the vertex alignments).

I also just added a check that the graph initially contains no H-boxes - previously it just treated a boundary H-box correctly, but really the graph should contain no H-boxes at all, otherwise to_gh() and spider_simp() won't work properly for removing hadamard pairs etc. Running hsimplify.from_hypergraph_form(g) would fix this, but can't be used in simplify due to circular imports, so I just raise an error. Any H-boxes would return False on is_graph_like anyway so I think this makes sense. Do you agree?

@jvdwetering
Copy link
Collaborator

Yes, I think it is fine to raise an exception in that case. Though the circular import could be prevented by doing a local import inside the function to_graph_like().

@dlyongemallo
Copy link
Contributor

@jvdwetering Can we try to resolve the conflicts and merge this before more PRs are merged which will cause this to diverge further?

@jvdwetering
Copy link
Collaborator

@RazinShaikh is currently in the middle of a PR spree that updates many of the rewrites to work with multigraphs. This is a really big PR and I currently have no overview on all the things it touches. So I think after all the multigraph changes have been made we can revisit how much of this PR still makes sense.

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.

3 participants