diff --git a/demos/Time Benchmark.ipynb b/demos/Time Benchmark.ipynb index 6f278dc3..c6464f38 100644 --- a/demos/Time Benchmark.ipynb +++ b/demos/Time Benchmark.ipynb @@ -1269,7 +1269,7 @@ "plt.scatter(x, y,color='grey') # Plot all the datapoints\n", "c = np.polyfit(np.log(x),np.log(y),1, w=np.sqrt(y)) # Generate power law fit\n", "a = np.linspace(0,max(x),100); b = (math.e**c[1])*a**c[0]\n", - "plt.plot(a,b,'-',color='red',label=\"$y = a\\cdot x^{{{:.2f}}}$\".format(c[0])) # Plot the fit\n", + "plt.plot(a,b,'-',color='red',label=r\"$y = a\\cdot x^{{{:.2f}}}$\".format(c[0])) # Plot the fit\n", "plt.ylim(ymin=0); plt.xlim(xmin=0,xmax=max(x)+50); \n", "plt.xlabel(\"Gate-count\"); plt.ylabel(\"Time [seconds]\"); plt.legend(loc='upper left');" ] diff --git a/pyzx/generate.py b/pyzx/generate.py index d06acfd7..32dbd652 100644 --- a/pyzx/generate.py +++ b/pyzx/generate.py @@ -130,7 +130,7 @@ def CNOT_HAD_PHASE_circuit( clifford:bool=False ) -> Circuit: """Construct a Circuit consisting of CNOT, HAD and phase gates. - The default phase gate is the T gate, but if ``clifford=True``, then + The default phase gate is the T gate, but if ``clifford=True``\\ , then this is replaced by the S gate. Args: diff --git a/pyzx/gflow.py b/pyzx/gflow.py index 46fdd733..c57916e7 100644 --- a/pyzx/gflow.py +++ b/pyzx/gflow.py @@ -33,8 +33,8 @@ gFlowaux (V,Gamma,In,Out,k) = begin C := {} - for all u in V \ Out do - Solve in F2 : Gamma[V \ Out, Out \ In] * I[X] = I[{u}] + for all u in V \\ Out do + Solve in F2 : Gamma[V \\ Out, Out \\ In] * I[X] = I[{u}] if there is a solution X0 then C := C union {u} g(u) := X0 diff --git a/pyzx/graph/base.py b/pyzx/graph/base.py index 7420213f..f966c303 100644 --- a/pyzx/graph/base.py +++ b/pyzx/graph/base.py @@ -480,6 +480,12 @@ def to_matrix(self,preserve_scalar:bool=True) -> np.ndarray: """Returns a representation of the graph as a matrix using :func:`~pyzx.tensor.tensorfy`""" return tensor_to_matrix(tensorfy(self, preserve_scalar), self.num_inputs(), self.num_outputs()) + def to_dict(self, include_scalar:bool=True) -> Dict[str, Any]: + """Returns a json representation of the graph that follows the Quantomatic .qgraph format. + Convert back into a graph using :meth:`from_json`.""" + from .jsonparser import graph_to_dict + return graph_to_dict(self, include_scalar) + def to_json(self, include_scalar:bool=True) -> str: """Returns a json representation of the graph that follows the Quantomatic .qgraph format. Convert back into a graph using :meth:`from_json`.""" @@ -497,7 +503,7 @@ def to_tikz(self,draw_scalar:bool=False) -> str: return to_tikz(self,draw_scalar) @classmethod - def from_json(cls, js) -> 'BaseGraph': + def from_json(cls, js:Union[str,Dict[str,Any]]) -> 'BaseGraph': """Converts the given .qgraph json string into a Graph. Works with the output of :meth:`to_json`.""" from .jsonparser import json_to_graph @@ -990,3 +996,27 @@ def is_well_formed(self) -> bool: if self.vertex_degree(v) < 2: return False return True + + def get_auto_simplify(self) -> bool: + """Returns whether this graph auto-simplifies parallel edges + + For multigraphs, this parameter might change, but simple graphs should always return True.""" + return True + + def set_auto_simplify(self, s: bool) -> None: + """Set whether this graph auto-simplifies parallel edges + + Simple graphs should always auto-simplify, so this method is a no-op.""" + pass + + def is_phase_gadget(self, v: VT) -> bool: + """Returns True if the vertex is the 'hub' of a phase gadget""" + if self.phase(v) != 0 or self.vertex_degree(v) < 2: + return False + for w in self.neighbors(v): + if self.vertex_degree(w) == 1: + return True + return False + + + diff --git a/pyzx/graph/diff.py b/pyzx/graph/diff.py index 66f2e640..f68713f8 100644 --- a/pyzx/graph/diff.py +++ b/pyzx/graph/diff.py @@ -22,147 +22,150 @@ from ..utils import VertexType, EdgeType, FractionLike, FloatInt, phase_to_s from .base import BaseGraph, VT, ET from .graph_s import GraphS -from .jsonparser import ComplexDecoder, ComplexEncoder, string_to_phase +from .jsonparser import string_to_phase class GraphDiff(Generic[VT, ET]): - removed_verts: List[VT] - new_verts: List[VT] - removed_edges: List[ET] - new_edges: List[Tuple[Tuple[VT,VT],EdgeType]] - changed_vertex_types: Dict[VT,VertexType] - changed_edge_types: Dict[ET, EdgeType] - changed_phases: Dict[VT, FractionLike] - changed_pos: Dict[VT, Tuple[FloatInt,FloatInt]] - changed_vdata: Dict[VT, Any] - variable_types: Dict[str,bool] - - def __init__(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None: - self.calculate_diff(g1,g2) - - def calculate_diff(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None: - self.changed_vertex_types = {} - self.changed_edge_types = {} - self.changed_phases = {} - self.changed_pos = {} - self.changed_vdata = {} - self.variable_types = g1.variable_types.copy() - self.variable_types.update(g2.variable_types) - - old_verts = g1.vertex_set() - new_verts = g2.vertex_set() - self.removed_verts = list(old_verts - new_verts) - self.new_verts = list(new_verts - old_verts) - old_edges = g1.edge_set() - new_edges = g2.edge_set() - self.new_edges = [] - self.removed_edges = [] - - for e in Counter(new_edges - old_edges).elements(): - self.new_edges.append((g2.edge_st(e), g2.edge_type(e))) - - for e in Counter(old_edges - new_edges).elements(): - s,t = g1.edge_st(e) - self.removed_edges.append(e) - - for v in new_verts: - if v in old_verts: - if g1.type(v) != g2.type(v): - self.changed_vertex_types[v] = g2.type(v) - if g1.phase(v) != g2.phase(v): - self.changed_phases[v] = g2.phase(v) - if g1._vdata.get(v, None) != g2._vdata.get(v, None): - self.changed_vdata[v] = g2._vdata.get(v, None) - pos1 = g1.qubit(v), g1.row(v) - pos2 = g2.qubit(v), g2.row(v) - if pos1 != pos2: - self.changed_pos[v] = pos2 - else: # It is a new vertex - if g2.type(v) != VertexType.Z: # We are taking the Z type to be the default - self.changed_vertex_types[v] = g2.type(v) - if g2.phase(v) != 0: - self.changed_phases[v] = g2.phase(v) - pos2 = g2.qubit(v), g2.row(v) - self.changed_pos[v] = pos2 - - for e in new_edges: - if e in old_edges: - if g1.edge_type(e) != g2.edge_type(e): - self.changed_edge_types[e] = g2.edge_type(e) - else: - if g2.edge_type(e) != EdgeType.HADAMARD: # We take Hadamard edges to be the default - self.changed_edge_types[e] = g2.edge_type(e) - - def apply_diff(self,g: BaseGraph[VT,ET]) -> BaseGraph[VT,ET]: - g = copy.deepcopy(g) - g.remove_edges(self.removed_edges) - g.remove_vertices(self.removed_verts) - for v in self.new_verts: - g.add_vertex_indexed(v) - g.set_position(v,*self.changed_pos[v]) - if v in self.changed_vertex_types: - g.set_type(v,self.changed_vertex_types[v]) - else: - g.set_type(v,VertexType.Z) - if v in self.changed_phases: - g.set_phase(v,self.changed_phases[v]) - if v in self.changed_vdata: - g._vdata[v] = self.changed_vdata[v] - for st, ty in self.new_edges: - g.add_edge(st,ty) - - for v in self.changed_pos: - if v in self.new_verts: continue - g.set_position(v,*self.changed_pos[v]) - - for v in self.changed_vertex_types: - if v in self.new_verts: continue - g.set_type(v,self.changed_vertex_types[v]) - - for v in self.changed_phases: - if v in self.new_verts: continue - g.set_phase(v,self.changed_phases[v]) - - for v in self.changed_vdata: - if v in self.new_verts: continue - g._vdata[v] = self.changed_vdata[v] - - for e in self.changed_edge_types: - g.set_edge_type(e,self.changed_edge_types[e]) - - return g - - def to_json(self) -> str: - changed_edge_types_str_dict = {} - for key, value in self.changed_edge_types.items(): - changed_edge_types_str_dict[f"{key[0]},{key[1]}"] = value # type: ignore - changed_phases_str = {k: phase_to_s(v) for k, v in self.changed_phases.items()} - return json.dumps({ - "removed_verts": self.removed_verts, - "new_verts": self.new_verts, - "removed_edges": self.removed_edges, - "new_edges": self.new_edges, - "changed_vertex_types": self.changed_vertex_types, - "changed_edge_types": changed_edge_types_str_dict, - "changed_phases": changed_phases_str, - "changed_pos": self.changed_pos, - "changed_vdata": self.changed_vdata, - "variable_types": self.variable_types - }, cls=ComplexEncoder) - - @staticmethod - def from_json(json_str: str) -> "GraphDiff": - d = json.loads(json_str, cls=ComplexDecoder) - gd = GraphDiff(GraphS(),GraphS()) - gd.removed_verts = d["removed_verts"] - gd.new_verts = d["new_verts"] - gd.removed_edges = list(map(tuple, d["removed_edges"])) # type: ignore - gd.new_edges = list(map(tuple, d["new_edges"])) # type: ignore - gd.changed_vertex_types = map_dict_keys(d["changed_vertex_types"], int) - gd.changed_edge_types = map_dict_keys(d["changed_edge_types"], lambda x: tuple(map(int, x.split(",")))) - gd.changed_phases = {int(k): string_to_phase(v,gd) for k, v in d["changed_phases"].items()} - gd.changed_pos = map_dict_keys(d["changed_pos"], int) - gd.changed_vdata = map_dict_keys(d["changed_vdata"], int) - return gd + removed_verts: List[VT] + new_verts: List[VT] + removed_edges: List[ET] + new_edges: List[Tuple[Tuple[VT,VT],EdgeType]] + changed_vertex_types: Dict[VT,VertexType] + changed_edge_types: Dict[ET, EdgeType] + changed_phases: Dict[VT, FractionLike] + changed_pos: Dict[VT, Tuple[FloatInt,FloatInt]] + changed_vdata: Dict[VT, Any] + variable_types: Dict[str,bool] + + def __init__(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None: + self.calculate_diff(g1,g2) + + def calculate_diff(self, g1: BaseGraph[VT,ET], g2: BaseGraph[VT,ET]) -> None: + self.changed_vertex_types = {} + self.changed_edge_types = {} + self.changed_phases = {} + self.changed_pos = {} + self.changed_vdata = {} + self.variable_types = g1.variable_types.copy() + self.variable_types.update(g2.variable_types) + + old_verts = g1.vertex_set() + new_verts = g2.vertex_set() + self.removed_verts = list(old_verts - new_verts) + self.new_verts = list(new_verts - old_verts) + old_edges = g1.edge_set() + new_edges = g2.edge_set() + self.new_edges = [] + self.removed_edges = [] + + for e in Counter(new_edges - old_edges).elements(): + self.new_edges.append((g2.edge_st(e), g2.edge_type(e))) + + for e in Counter(old_edges - new_edges).elements(): + s,t = g1.edge_st(e) + self.removed_edges.append(e) + + for v in new_verts: + if v in old_verts: + if g1.type(v) != g2.type(v): + self.changed_vertex_types[v] = g2.type(v) + if g1.phase(v) != g2.phase(v): + self.changed_phases[v] = g2.phase(v) + if g1._vdata.get(v, None) != g2._vdata.get(v, None): + self.changed_vdata[v] = g2._vdata.get(v, None) + pos1 = g1.qubit(v), g1.row(v) + pos2 = g2.qubit(v), g2.row(v) + if pos1 != pos2: + self.changed_pos[v] = pos2 + else: # It is a new vertex + if g2.type(v) != VertexType.Z: # We are taking the Z type to be the default + self.changed_vertex_types[v] = g2.type(v) + if g2.phase(v) != 0: + self.changed_phases[v] = g2.phase(v) + pos2 = g2.qubit(v), g2.row(v) + self.changed_pos[v] = pos2 + + for e in new_edges: + if e in old_edges: + if g1.edge_type(e) != g2.edge_type(e): + self.changed_edge_types[e] = g2.edge_type(e) + else: + if g2.edge_type(e) != EdgeType.HADAMARD: # We take Hadamard edges to be the default + self.changed_edge_types[e] = g2.edge_type(e) + + def apply_diff(self,g: BaseGraph[VT,ET]) -> BaseGraph[VT,ET]: + g = copy.deepcopy(g) + g.remove_edges(self.removed_edges) + g.remove_vertices(self.removed_verts) + for v in self.new_verts: + g.add_vertex_indexed(v) + g.set_position(v,*self.changed_pos[v]) + if v in self.changed_vertex_types: + g.set_type(v,self.changed_vertex_types[v]) + else: + g.set_type(v,VertexType.Z) + if v in self.changed_phases: + g.set_phase(v,self.changed_phases[v]) + if v in self.changed_vdata: + g._vdata[v] = self.changed_vdata[v] + for st, ty in self.new_edges: + g.add_edge(st,ty) + + for v in self.changed_pos: + if v in self.new_verts: continue + g.set_position(v,*self.changed_pos[v]) + + for v in self.changed_vertex_types: + if v in self.new_verts: continue + g.set_type(v,self.changed_vertex_types[v]) + + for v in self.changed_phases: + if v in self.new_verts: continue + g.set_phase(v,self.changed_phases[v]) + + for v in self.changed_vdata: + if v in self.new_verts: continue + g._vdata[v] = self.changed_vdata[v] + + for e in self.changed_edge_types: + g.set_edge_type(e,self.changed_edge_types[e]) + + return g + + def to_dict(self) -> Dict[str, Any]: + changed_edge_types_str_dict = {} + for key, value in self.changed_edge_types.items(): + changed_edge_types_str_dict[f"{key[0]},{key[1]}"] = value # type: ignore + changed_phases_str = {k: phase_to_s(v) for k, v in self.changed_phases.items()} + return { + "removed_verts": self.removed_verts, + "new_verts": self.new_verts, + "removed_edges": self.removed_edges, + "new_edges": self.new_edges, + "changed_vertex_types": self.changed_vertex_types, + "changed_edge_types": changed_edge_types_str_dict, + "changed_phases": changed_phases_str, + "changed_pos": self.changed_pos, + "changed_vdata": self.changed_vdata, + "variable_types": self.variable_types + } + + def to_json(self) -> str: + return json.dumps(self.to_dict()) + + @staticmethod + def from_json(json_str: str) -> "GraphDiff": + d = json.loads(json_str) + gd = GraphDiff(GraphS(),GraphS()) + gd.removed_verts = d["removed_verts"] + gd.new_verts = d["new_verts"] + gd.removed_edges = list(map(tuple, d["removed_edges"])) # type: ignore + gd.new_edges = list(map(tuple, d["new_edges"])) # type: ignore + gd.changed_vertex_types = map_dict_keys(d["changed_vertex_types"], int) + gd.changed_edge_types = map_dict_keys(d["changed_edge_types"], lambda x: tuple(map(int, x.split(",")))) + gd.changed_phases = {int(k): string_to_phase(v,gd) for k, v in d["changed_phases"].items()} + gd.changed_pos = map_dict_keys(d["changed_pos"], int) + gd.changed_vdata = map_dict_keys(d["changed_vdata"], int) + return gd def map_dict_keys(d: Dict[str, Any], f: Callable[[str], Any]) -> Dict[Any, Any]: - return {f(k): v for k, v in d.items()} + return {f(k): v for k, v in d.items()} diff --git a/pyzx/graph/jsonparser.py b/pyzx/graph/jsonparser.py index 6a03f816..79c0fe95 100644 --- a/pyzx/graph/jsonparser.py +++ b/pyzx/graph/jsonparser.py @@ -71,10 +71,13 @@ def _new_var(name: str) -> Poly: except Exception as e: raise ValueError(e) -def json_to_graph(js: str, backend:Optional[str]=None) -> BaseGraph: +def json_to_graph(js: Union[str,Dict[str,Any]], backend:Optional[str]=None) -> BaseGraph: """Converts the json representation of a .qgraph Quantomatic graph into - a pyzx graph.""" - j = json.loads(js, cls=ComplexDecoder) + a pyzx graph. If JSON is given as a string, parse it first.""" + if isinstance(js, str): + j = json.loads(js) + else: + j = js g = Graph(backend) g.variable_types = j.get('variable_types',{}) @@ -178,8 +181,8 @@ def json_to_graph(js: str, backend:Optional[str]=None) -> BaseGraph: return g -def graph_to_json(g: BaseGraph[VT,ET], include_scalar: bool=True) -> str: - """Converts a PyZX graph into JSON output compatible with Quantomatic. +def graph_to_dict(g: BaseGraph[VT,ET], include_scalar: bool=True) -> Dict[str, Any]: + """Converts a PyZX graph into Python dict for JSON output. If include_scalar is set to True (the default), then this includes the value of g.scalar with the json, which will also be loaded by the ``from_json`` method.""" node_vs: Dict[str, Dict[str, Any]] = {} @@ -275,9 +278,15 @@ def graph_to_json(g: BaseGraph[VT,ET], include_scalar: bool=True) -> str: "variable_types": g.variable_types, } if include_scalar: - d["scalar"] = g.scalar.to_json() + d["scalar"] = g.scalar.to_dict() - return json.dumps(d, cls=ComplexEncoder) + return d + +def graph_to_json(g: BaseGraph[VT,ET], include_scalar: bool=True) -> str: + """Converts a PyZX graph into JSON output compatible with Quantomatic. + If include_scalar is set to True (the default), then this includes the value + of g.scalar with the json, which will also be loaded by the ``from_json`` method.""" + return json.dumps(graph_to_dict(g, include_scalar)) def to_graphml(g: BaseGraph[VT,ET]) -> str: gml = """ @@ -326,22 +335,22 @@ def to_graphml(g: BaseGraph[VT,ET]) -> str: return gml -class ComplexEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, complex): - return str(obj) - return super().default(obj) - -class ComplexDecoder(json.JSONDecoder): - def __init__(self, *args, **kwargs): - json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs) - - def object_hook(self, dct): - for k, v in dct.items(): - if isinstance(v, str): - try: - dct[k] = complex(v) - except ValueError: - pass - return dct +# class ComplexEncoder(json.JSONEncoder): +# def default(self, obj): +# if isinstance(obj, complex): +# return str(obj) +# return super().default(obj) + +# class ComplexDecoder(json.JSONDecoder): +# def __init__(self, *args, **kwargs): +# json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs) + +# def object_hook(self, dct): +# for k, v in dct.items(): +# if isinstance(v, str): +# try: +# dct[k] = complex(v) +# except ValueError: +# pass +# return dct diff --git a/pyzx/graph/multigraph.py b/pyzx/graph/multigraph.py index 37e14817..b8e17179 100644 --- a/pyzx/graph/multigraph.py +++ b/pyzx/graph/multigraph.py @@ -69,7 +69,7 @@ def __init__(self) -> None: self._auto_simplify: bool = True self._vindex: int = 0 self.nedges: int = 0 - self.ty: Dict[int,VertexType] = dict() + self.ty: Dict[int,VertexType] = dict() self._phase: Dict[int, FractionLike] = dict() self._qindex: Dict[int, FloatInt] = dict() self._maxq: FloatInt = -1 @@ -108,6 +108,9 @@ def set_auto_simplify(self, s: bool): """Automatically remove parallel edges as edges are added""" self._auto_simplify = s + def get_auto_simplify(self): + return self._auto_simplify + def multigraph(self): return False diff --git a/pyzx/graph/scalar.py b/pyzx/graph/scalar.py index 7d9e2a9c..ca51837f 100644 --- a/pyzx/graph/scalar.py +++ b/pyzx/graph/scalar.py @@ -20,7 +20,7 @@ import cmath import copy from fractions import Fraction -from typing import List +from typing import Dict, List, Any, Union import json from ..utils import FloatInt, FractionLike @@ -178,26 +178,39 @@ def to_unicode(self) -> str: s += "{:d}/{:d}π)".format(phase.numerator,phase.denominator) return s - def to_json(self) -> str: + def to_dict(self) -> Dict[str, Any]: d = {"power2": self.power2, "phase": str(self.phase)} if abs(self.floatfactor - 1) > 0.00001: - d["floatfactor"] = self.floatfactor + d["floatfactor"] = str(self.floatfactor) if self.phasenodes: d["phasenodes"] = [str(p) for p in self.phasenodes] if self.is_zero: d["is_zero"] = self.is_zero if self.is_unknown: d["is_unknown"] = self.is_unknown, - return json.dumps(d) + return d + + def to_json(self) -> str: + return json.dumps(self.to_dict()) @classmethod - def from_json(cls, s: str) -> 'Scalar': - d = json.loads(s) - d["phase"] = Fraction(d["phase"]) - if "phasenodes" in d: - d["phasenodes"] = [Fraction(p) for p in d["phasenodes"]] + def from_json(cls, s: Union[str,Dict[str,Any]]) -> 'Scalar': + if isinstance(s, str): + d = json.loads(s) + else: + d = s + # print('scalar from json', repr(d)) scalar = Scalar() - scalar.__dict__.update(d) + scalar.phase = Fraction(d["phase"]) # TODO support parameters + scalar.power2 = int(d["power2"]) + if "floatfactor" in d: + scalar.floatfactor = complex(d["floatfactor"]) + if "phasenodes" in d: + scalar.phasenodes = [Fraction(p) for p in d["phasenodes"]] + if "is_zero" in d: + scalar.is_zero = bool(d["is_zero"]) + if "is_unknown" in d: + scalar.is_unknown = bool(d["is_unknown"]) return scalar def set_unknown(self) -> None: diff --git a/pyzx/hrules.py b/pyzx/hrules.py index f362d2fc..26ed83fa 100644 --- a/pyzx/hrules.py +++ b/pyzx/hrules.py @@ -22,6 +22,54 @@ from .graph.base import BaseGraph, ET, VT, upair from . import rules +def is_hadamard(g: BaseGraph[VT,ET], v: VT) -> bool: + """Returns whether the vertex v in graph g is a Hadamard gate.""" + if g.type(v) != VertexType.H_BOX: return False + if g.phase(v) != 1: return False + if g.vertex_degree(v) != 2: return False + return True + +def replace_hadamard(g: BaseGraph[VT,ET], v: VT) -> bool: + """Replaces a Hadamard gate with a Hadamard edge.""" + if not is_hadamard(g, v): return False + n1,n2 = g.neighbors(v) + et1 = g.edge_type(g.edge(v,n1)) + et2 = g.edge_type(g.edge(v,n2)) + if et1 == et2: # both connecting edges are HADAMARD or SIMPLE + g.add_edge((n1,n2), EdgeType.HADAMARD) + else: + g.add_edge((n1,n2), EdgeType.SIMPLE) + g.remove_vertex(v) + g.scalar.add_power(1) # Correct for the sqrt(2) difference in H-boxes and H-edges + return True + +def had_edge_to_hbox(g: BaseGraph[VT,ET], e: ET) -> bool: + """Converts a Hadamard edge to a Hadamard gate. + Note that while this works with multigraphs, it will put the new H-box in the middle of the vertices, + so that the diagram might look wrong. + """ + et = g.edge_type(e) + if et != EdgeType.HADAMARD: return False + s,t = g.edge_st(e) + rs = g.row(s) + rt = g.row(t) + qs = g.qubit(s) + qt = g.qubit(t) + g.remove_edge(e) + h = g.add_vertex(VertexType.H_BOX) + g.scalar.add_power(-1) # Correct for sqrt(2) scalar difference in H-edge and H-box. + g.add_edge((s, h),EdgeType.SIMPLE) + g.add_edge((h, t),EdgeType.SIMPLE) + + if qs == qt: + g.set_qubit(h, qs) + else: + q = (qs + qt) / 2 + if round(q) == q: q += 0.5 + g.set_qubit(h, q) + g.set_row(h, (rs + rt) / 2) + return True + def match_hadamards(g: BaseGraph[VT,ET], vertexf: Optional[Callable[[VT],bool]] = None diff --git a/pyzx/optimize.py b/pyzx/optimize.py index 8eed0cf4..39c5bd45 100644 --- a/pyzx/optimize.py +++ b/pyzx/optimize.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""This module implements several optimization methods on ``Circuit`` s. +"""This module implements several optimization methods on ``Circuit``\\ s. The function :func:`basic_optimization` runs a set of back-and-forth gate commutation and cancellation routines. :func:`phase_block_optimize` does phase polynomial optimization using the TODD algorithm, and :func:`full_optimize` combines these two methods.""" diff --git a/pyzx/simplify.py b/pyzx/simplify.py index e25b45aa..582580ae 100644 --- a/pyzx/simplify.py +++ b/pyzx/simplify.py @@ -27,12 +27,15 @@ 'full_reduce', 'teleport_reduce', 'reduce_scalar', 'supplementarity_simp', 'to_clifford_normal_form_graph', 'to_graph_like', 'is_graph_like'] +from ast import Mult +from functools import reduce from optparse import Option from typing import List, Callable, Optional, Union, Generic, Tuple, Dict, Iterator, cast from .utils import EdgeType, VertexType, toggle_edge, vertex_is_zx, toggle_vertex from .rules import * from .graph.base import BaseGraph, VT, ET +from .graph.multigraph import Multigraph from .circuit import Circuit class Stats(object): @@ -58,6 +61,7 @@ def simp( name: str, match: Callable[..., List[MatchObject]], rewrite: Callable[[BaseGraph[VT,ET],List[MatchObject]],RewriteOutputType[VT,ET]], + auto_simplify_parallel_edges: bool = False, matchf:Optional[Union[Callable[[ET],bool], Callable[[VT],bool]]]=None, quiet:bool=False, stats:Optional[Stats]=None) -> int: @@ -73,6 +77,7 @@ def simp( str name: The name to display if ``quiet`` is set to False. match: One of the ``match_*`` functions of rules_. rewrite: One of the rewrite functions of rules_. + auto_simplify_parallel_edges: whether to automatically combine parallel edges between vertices if the graph is a Multigraph matchf: An optional filtering function on candidate vertices or edges, which is passed as the second argument to the match function. quiet: Suppress output on numbers of matches found during simplification. @@ -80,6 +85,9 @@ def simp( Returns: Number of iterations of ``rewrite`` that had to be applied before no more matches were found.""" + if auto_simplify_parallel_edges: + auto_simp_value = g.get_auto_simplify() + g.set_auto_simplify(True) i = 0 new_matches = True while new_matches: @@ -103,19 +111,25 @@ def simp( new_matches = True if stats is not None: stats.count_rewrites(name, len(m)) if not quiet and i>0: print(' {!s} iterations'.format(i)) + if auto_simplify_parallel_edges: + g.set_auto_simplify(auto_simp_value) return i def pivot_simp(g: BaseGraph[VT,ET], matchf:Optional[Callable[[ET],bool]]=None, quiet:bool=False, stats:Optional[Stats]=None) -> int: - return simp(g, 'pivot_simp', match_pivot_parallel, pivot, matchf=matchf, quiet=quiet, stats=stats) + return simp(g, 'pivot_simp', match_pivot_parallel, pivot, + auto_simplify_parallel_edges=True, matchf=matchf, quiet=quiet, stats=stats) def pivot_gadget_simp(g: BaseGraph[VT,ET], matchf:Optional[Callable[[ET],bool]]=None, quiet:bool=False, stats:Optional[Stats]=None) -> int: - return simp(g, 'pivot_gadget_simp', match_pivot_gadget, pivot, matchf=matchf, quiet=quiet, stats=stats) + return simp(g, 'pivot_gadget_simp', match_pivot_gadget, pivot, + auto_simplify_parallel_edges=True, matchf=matchf, quiet=quiet, stats=stats) def pivot_boundary_simp(g: BaseGraph[VT,ET], matchf:Optional[Callable[[ET],bool]]=None, quiet:bool=False, stats:Optional[Stats]=None) -> int: - return simp(g, 'pivot_boundary_simp', match_pivot_boundary, pivot, matchf=matchf, quiet=quiet, stats=stats) + return simp(g, 'pivot_boundary_simp', match_pivot_boundary, pivot, + auto_simplify_parallel_edges=True, matchf=matchf, quiet=quiet, stats=stats) def lcomp_simp(g: BaseGraph[VT,ET], matchf:Optional[Callable[[VT],bool]]=None, quiet:bool=False, stats:Optional[Stats]=None) -> int: - return simp(g, 'lcomp_simp', match_lcomp_parallel, lcomp, matchf=matchf, quiet=quiet, stats=stats) + return simp(g, 'lcomp_simp', match_lcomp_parallel, lcomp, + auto_simplify_parallel_edges=True, matchf=matchf, quiet=quiet, stats=stats) def bialg_simp(g: BaseGraph[VT,ET], quiet:bool=False, stats: Optional[Stats]=None) -> int: return simp(g, 'bialg_simp', match_bialg_parallel, bialg, quiet=quiet, stats=stats) @@ -127,10 +141,12 @@ def id_simp(g: BaseGraph[VT,ET], matchf:Optional[Callable[[VT],bool]]=None, quie return simp(g, 'id_simp', match_ids_parallel, remove_ids, matchf=matchf, quiet=quiet, stats=stats) def gadget_simp(g: BaseGraph[VT,ET], matchf: Optional[Callable[[VT],bool]]=None, quiet:bool=False, stats:Optional[Stats]=None) -> int: - return simp(g, 'gadget_simp', match_phase_gadgets, merge_phase_gadgets, matchf=matchf, quiet=quiet, stats=stats) + return simp(g, 'gadget_simp', match_phase_gadgets, merge_phase_gadgets, + auto_simplify_parallel_edges=True, matchf=matchf, quiet=quiet, stats=stats) def supplementarity_simp(g: BaseGraph[VT,ET], quiet:bool=False, stats:Optional[Stats]=None) -> int: - return simp(g, 'supplementarity_simp', match_supplementarity, apply_supplementarity, quiet=quiet, stats=stats) + return simp(g, 'supplementarity_simp', match_supplementarity, apply_supplementarity, + auto_simplify_parallel_edges=True, quiet=quiet, stats=stats) def copy_simp(g: BaseGraph[VT,ET], quiet:bool=False, stats:Optional[Stats]=None) -> int: """Copies 1-ary spiders with 0/pi phase through neighbors. @@ -144,6 +160,19 @@ def phase_free_simp(g: BaseGraph[VT,ET], quiet:bool=False, stats:Optional[Stats] i2 = bialg_simp(g, quiet=quiet, stats=stats) return i1+i2 +def basic_simp(g: BaseGraph[VT,ET], matchf: Optional[Callable[[Union[VT, ET]],bool]]=None, quiet:bool=False, stats:Optional[Stats]=None) -> int: + """Keeps doing the simplifications ``id_simp`` and ``spider_simp`` until none of them can be applied anymore. If + starting from a circuit, the result should still have causal flow.""" + spider_simp(g, matchf=matchf, quiet=quiet, stats=stats) + to_gh(g) + i = 0 + while True: + i1 = id_simp(g, matchf=matchf, quiet=quiet, stats=stats) + i2 = spider_simp(g, matchf=matchf, quiet=quiet, stats=stats) + if i1+i2==0: break + i += 1 + return i + def interior_clifford_simp(g: BaseGraph[VT,ET], matchf: Optional[Callable[[Union[VT, ET]],bool]]=None, quiet:bool=False, stats:Optional[Stats]=None) -> int: """Keeps doing the simplifications ``id_simp``, ``spider_simp``, ``pivot_simp`` and ``lcomp_simp`` until none of them can be applied anymore.""" @@ -283,23 +312,55 @@ def to_gh(g: BaseGraph[VT,ET],quiet:bool=True) -> None: et = g.edge_type(e) g.set_edge_type(e, toggle_edge(et)) -def to_rg(g: BaseGraph[VT,ET], select:Optional[Callable[[VT],bool]]=None) -> None: - """Turn green nodes into red nodes by color-changing vertices which satisfy the predicate ``select``. - By default, the predicate is set to greedily reducing the number of Hadamard-edges. +def to_rg(g: BaseGraph[VT,ET], select:Optional[Callable[[VT],bool]]=None, change_gadgets: bool=True) -> None: + """Try to eliminate H-edges by turning green nodes red + + By default, this does a breadth-first search starting at an arbitrary node, flipping the + color of alternating layers. For a ZX-diagram that is graph-like and 2-colorable, this will + eliminate all of the interior H-edges. + + Alternatively, the function `select` can be provided instructing the method where to flip colors. + :param g: A ZX-graph. - :param select: A function taking in vertices and returning ``True`` or ``False``.""" - if select is None: - select = lambda v: ( - len([e for e in g.incident_edges(v) if g.edge_type(e) == EdgeType.SIMPLE]) < - len([e for e in g.incident_edges(v) if g.edge_type(e) == EdgeType.HADAMARD]) - ) + :param select: A function taking in vertices and returning ``True`` or ``False``. + :param change_gadgets: A flag saying always change gadgets to X.""" ty = g.types() - for v in g.vertices(): - if select(v) and vertex_is_zx(ty[v]): - g.set_type(v, toggle_vertex(ty[v])) - for e in g.incident_edges(v): - g.set_edge_type(e, toggle_edge(g.edge_type(e))) + if select is None: + remaining = set() + for w in g.vertices(): + if change_gadgets and g.is_phase_gadget(w): + if vertex_is_zx(ty[w]): + g.set_type(w, toggle_vertex(ty[w])) + for e in g.incident_edges(w): + g.set_edge_type(e, toggle_edge(g.edge_type(e))) + else: + remaining.add(w) + while len(remaining) > 0: + v = next(iter(remaining)) + # if v is a boundary, set `flip` such that its adjacent edge will not be an H-edge afterwards + if ty[v] == VertexType.BOUNDARY and g.incident_edges(v)[0] == EdgeType.SIMPLE: + flip = True + else: + flip = False + nhd = set([v]) + while len(nhd) > 0: + for w in nhd: + if flip and vertex_is_zx(ty[w]): + g.set_type(w, toggle_vertex(ty[w])) + for e in g.incident_edges(w): + g.set_edge_type(e, toggle_edge(g.edge_type(e))) + flip = not flip + remaining -= nhd + nhd = set.union(*(set(g.neighbors(w)) for w in nhd)).intersection(remaining) + + else: + for v in g.vertices(): + if g.is_phase_gadget(v) and select(v) and vertex_is_zx(ty[v]): + g.set_type(v, toggle_vertex(ty[v])) + for e in g.incident_edges(v): + g.set_edge_type(e, toggle_edge(g.edge_type(e))) + def tcount(g: Union[BaseGraph[VT,ET], Circuit]) -> int: """Returns the amount of nodes in g that have a non-Clifford phase.""" diff --git a/pyzx/todd.py b/pyzx/todd.py index e665b8df..798cf3e3 100644 --- a/pyzx/todd.py +++ b/pyzx/todd.py @@ -207,7 +207,7 @@ def phase_gates_to_poly(gates: List[Gate], qubits: int) -> Tuple[ParityPolynomia def xi(m: Mat2, z: List[Z2]) -> Mat2: - r"""Constructs the \chi matrix from the TOpt paper.""" + """Constructs the \\chi matrix from the TOpt paper.""" arr = np.asarray(m.data) rows = m.rows() data = [] diff --git a/scratchpads/JohnDoodle.ipynb b/scratchpads/JohnDoodle.ipynb index d21fda45..4123f11b 100644 --- a/scratchpads/JohnDoodle.ipynb +++ b/scratchpads/JohnDoodle.ipynb @@ -6167,7 +6167,7 @@ "\n", "c = np.polyfit(np.log(x),np.log(y),1, w=np.sqrt(y)) # Generate power law fit\n", "a = np.linspace(0,max(x),100); b = (math.e**c[1])*a**c[0]\n", - "ax1.plot(a,b,'-',color='red',label=\"$y = a\\cdot x^{{{:.2f}}}$\".format(c[0])) # Plot the fit\n", + "ax1.plot(a,b,'-',color='red',label=r\"$y = a\\cdot x^{{{:.2f}}}$\".format(c[0])) # Plot the fit\n", "\n", "ax1.set_ylabel(\"time [s]\")\n", "ax1.set_xlabel(\"qubits\")\n", diff --git a/scratchpads/ak.ipynb b/scratchpads/ak.ipynb index 5831c475..e6c73f05 100644 --- a/scratchpads/ak.ipynb +++ b/scratchpads/ak.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -26,13 +26,13 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], "text/plain": [ @@ -350,3964 +375,35 @@ "metadata": {}, "output_type": "display_data" }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[T(0)]\n", - "OPENQASM 2.0;\n", - "include \"qelib1.inc\";\n", - "qreg q[1];\n", - "t q[0];\n", - "\n" - ] - } - ], - "source": [ - "c = zx.qasm(\"\"\"\n", - "qreg q[1];\n", - "t q[0];\n", - "t q[0];\n", - "t q[0];\n", - "t q[0];\n", - "t q[0];\n", - "\"\"\")\n", - "c = zx.basic_optimization(c)\n", - "zx.draw(c)\n", - "print(c.gates)\n", - "print(c.to_qasm())" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ { "data": { "text/plain": [ - "Circuit(1 qubits, 0 bits, 1 gates)" + "Graph(62 vertices, 69 edges)" ] }, - "execution_count": 5, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "zx.basic_optimization(c)" + "random.seed(1339)\n", + "g = zx.generate.cliffordT(5, 30, p_t=0.1)\n", + "zx.draw(g)\n", + "g" ] }, { "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g = zx.Graph()\n", - "g.add_vertex(qubit=0,row=0)\n", - "g.add_vertex(qubit=1,row=0)\n", - "g.add_vertex(ty=Z, qubit=0,row=1)\n", - "g.add_vertex(ty=Z, qubit=1,row=1)\n", - "g.add_vertex(ty=X, qubit=0.5,row=1.5)\n", - "g.add_vertex(qubit=0,row=2)\n", - "g.add_vertex(qubit=1,row=2)\n", - "g.add_edges([(0,2), (2,5), (1,3), (3,6), (2,4), (3,4)])\n", - "zx.draw(g,labels=True)\n", - "zx.clifford_simp(g)\n", - "zx.draw(g,labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " 2023-04-06T14:49:25.086169\n", - " image/svg+xml\n", - " \n", - " \n", - " Matplotlib v3.5.1, https://matplotlib.org/\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "\n" - ], - "text/plain": [ - "
" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "random.seed(1339)\n", - "g = zx.generate.cliffordT(3, 20, p_t=0.05)\n", - "zx.draw(g)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "zx.quimb.to_quimb_tensor(g)" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g1 = g.copy()\n", - "zx.full_reduce(g1)\n", - "g1.normalize()\n", - "zx.draw(g1)" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "c = zx.extract_circuit(g1.copy(), up_to_perm=True).to_basic_gates()\n", - "zx.optimize.basic_optimization(c)\n", - "g2 = c.to_graph()\n", - "zx.draw(g2)" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": {}, - "outputs": [], - "source": [ - "for h,fname in [(g, \"g.tikz\"), (g1, \"g1.tikz\"), (g2, \"g2.tikz\")]:\n", - " with open(fname, \"w\") as f:\n", - " f.write(h.to_tikz())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "c = zx.qasm(\"\"\"\n", - "qreg q[8];\n", - "rx(0.5*pi) q[4];\n", - "h q[7];\n", - "cx q[4], q[7];\n", - "rz(0.75*pi) q[7];\n", - "cx q[4], q[7];\n", - "h q[7];\n", - "rx(-0.5*pi) q[4];\n", - "rx(0.5*pi) q[2];\n", - "h q[7];\n", - "cx q[2], q[7];\n", - "rz(0.25*pi) q[7];\n", - "cx q[2], q[7];\n", - "h q[7];\n", - "rx(-0.5*pi) q[2];\n", - "h q[4];\n", - "rx(0.5*pi) q[7];\n", - "cx q[4], q[7];\n", - "rz(1.75*pi) q[7];\n", - "cx q[4], q[7];\n", - "rx(-0.5*pi) q[7];\n", - "h q[4];\n", - "h q[0];\n", - "cx q[0], q[5];\n", - "rz(0.25*pi) q[5];\n", - "cx q[0], q[5];\n", - "h q[0];\n", - "rx(0.5*pi) q[2];\n", - "cx q[2], q[7];\n", - "rz(0.75*pi) q[7];\n", - "cx q[2], q[7];\n", - "rx(-0.5*pi) q[2];\n", - "h q[7];\n", - "cx q[5], q[7];\n", - "rz(1.75*pi) q[7];\n", - "cx q[5], q[7];\n", - "h q[7];\n", - "h q[3];\n", - "h q[7];\n", - "cx q[3], q[7];\n", - "rz(1.75*pi) q[7];\n", - "cx q[3], q[7];\n", - "h q[7];\n", - "h q[3];\n", - "h q[1];\n", - "h q[3];\n", - "cx q[1], q[3];\n", - "rz(1.75*pi) q[3];\n", - "cx q[1], q[3];\n", - "h q[3];\n", - "h q[1];\n", - "h q[2];\n", - "rx(0.5*pi) q[4];\n", - "cx q[2], q[4];\n", - "rz(1.75*pi) q[4];\n", - "cx q[2], q[4];\n", - "rx(-0.5*pi) q[4];\n", - "h q[2];\n", - "h q[0];\n", - "h q[4];\n", - "cx q[0], q[4];\n", - "rz(1.25*pi) q[4];\n", - "cx q[0], q[4];\n", - "h q[4];\n", - "h q[0];\n", - "h q[1];\n", - "h q[6];\n", - "cx q[1], q[6];\n", - "rz(1.75*pi) q[6];\n", - "cx q[1], q[6];\n", - "h q[6];\n", - "h q[1];\n", - "rx(0.5*pi) q[3];\n", - "h q[7];\n", - "cx q[3], q[7];\n", - "rz(0.25*pi) q[7];\n", - "cx q[3], q[7];\n", - "h q[7];\n", - "rx(-0.5*pi) q[3];\n", - "h q[1];\n", - "cx q[1], q[4];\n", - "rz(1.25*pi) q[4];\n", - "cx q[1], q[4];\n", - "h q[1];\n", - "rx(0.5*pi) q[0];\n", - "rx(0.5*pi) q[6];\n", - "cx q[0], q[6];\n", - "rz(0.75*pi) q[6];\n", - "cx q[0], q[6];\n", - "rx(-0.5*pi) q[6];\n", - "rx(-0.5*pi) q[0];\n", - "rx(0.5*pi) q[0];\n", - "h q[1];\n", - "cx q[0], q[1];\n", - "rz(0.75*pi) q[1];\n", - "cx q[0], q[1];\n", - "h q[1];\n", - "rx(-0.5*pi) q[0];\n", - "cx q[5], q[6];\n", - "rz(1.75*pi) q[6];\n", - "cx q[5], q[6];\n", - "h q[2];\n", - "cx q[0], q[2];\n", - "rz(1.75*pi) q[2];\n", - "cx q[0], q[2];\n", - "h q[2];\n", - "rx(0.5*pi) q[2];\n", - "rx(0.5*pi) q[5];\n", - "cx q[2], q[5];\n", - "rz(1.75*pi) q[5];\n", - "cx q[2], q[5];\n", - "rx(-0.5*pi) q[5];\n", - "rx(-0.5*pi) q[2];\n", - "h q[4];\n", - "cx q[4], q[7];\n", - "rz(1.25*pi) q[7];\n", - "cx q[4], q[7];\n", - "h q[4];\n", - "h q[1];\n", - "rx(0.5*pi) q[7];\n", - "cx q[1], q[7];\n", - "rz(1.75*pi) q[7];\n", - "cx q[1], q[7];\n", - "rx(-0.5*pi) q[7];\n", - "h q[1];\n", - "\"\"\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zx.draw(c)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = c.to_graph()\n", - "g.apply_state(\"00000000\")\n", - "g.apply_effect(\"00001111\")\n", - "zx.draw(g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "h = g + g.adjoint()\n", - "zx.draw(h)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "h.to_matrix()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "m = g.to_matrix()\n", - "len(m)\n", - "[(i,m[i]) for i in range(len(m)) if not np.isclose(m[i], 0)]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = zx.Graph()\n", - "\n", - "ins = [g.add_vertex(B, 0, 1), g.add_vertex(B, 1, 1), g.add_vertex(B, 3, 1)]\n", - "outs = [g.add_vertex(B, 0, 5), g.add_vertex(B, 1, 5), g.add_vertex(B, 3, 5)]\n", - "zs = [g.add_vertex(Z, 0, 4), g.add_vertex(Z, 1, 4), g.add_vertex(Z, 2, 4, Fraction(-1,4)), g.add_vertex(Z, 3, 4, Fraction(-1,2))]\n", - "gs0 = [g.add_vertex(Z, 0.5, 3), g.add_vertex(Z, 1.5, 3), g.add_vertex(Z, 2.5, 3)]\n", - "gs1 = [g.add_vertex(Z, 0.5, 2, Fraction(-1,4)), g.add_vertex(Z, 1.5, 2, Fraction(-1,4)), g.add_vertex(Z, 2.5, 2, Fraction(1,4))]\n", - "\n", - "g.add_edge((ins[0],zs[0]))\n", - "g.add_edge((zs[0],outs[0]))\n", - "g.add_edge((ins[1],zs[1]))\n", - "g.add_edge((zs[1],outs[1]))\n", - "g.add_edge((ins[2],zs[3]))\n", - "g.add_edge((zs[3],outs[2]))\n", - "\n", - "for i in range(3): g.add_edge((gs0[i],gs1[i]),HE)\n", - "g.add_edge((gs0[0],zs[0]), HE)\n", - "g.add_edge((gs0[0],zs[2]), HE)\n", - "g.add_edge((gs0[1],zs[1]), HE)\n", - "g.add_edge((gs0[1],zs[2]), HE)\n", - "g.add_edge((gs0[2],zs[0]), HE)\n", - "g.add_edge((gs0[2],zs[1]), HE)\n", - "g.add_edge((gs0[2],zs[2]), HE)\n", - "g.add_edge((zs[2], zs[3]), HE)\n", - "\n", - "g.set_inputs(ins)\n", - "g.set_outputs(outs)\n", - "zx.draw(g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "m = g.to_matrix()\n", - "print((1 + 1j) * math.sqrt(2)**3 * m)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "(1/math.sqrt(2))**5" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Generate a random circuit on 3 qubits, depth 20\n", - "#final version graph\n", - "g = zx.Graph()\n", - "#fourier thing\n", - "a0 = g.add_vertex(1, 0, 2,Fraction(1,4))\n", - "c0 = g.add_vertex(1, 2, 2,Fraction(1,4))\n", - "e0 = g.add_vertex(1, 4, 2,Fraction(1,4))\n", - "\n", - " \n", - "\n", - "a1 = g.add_vertex(1, 0, 5)\n", - "c1 = g.add_vertex(1, 2, 5)\n", - "e1 = g.add_vertex(1, 4, 5)\n", - "\n", - " \n", - "\n", - "b0 = g.add_vertex(1, 1, 0,Fraction(-1,4))\n", - "b1 = g.add_vertex(2, 1, 1)\n", - "b2 = g.add_vertex(2, 1, 3)\n", - "b3 = g.add_vertex(1, 1, 4,Fraction(-1,4))\n", - "\n", - " \n", - "\n", - "d0 = g.add_vertex(1, 3, 0,Fraction(1,4))\n", - "d1 = g.add_vertex(2, 3, 1)\n", - "d2 = g.add_vertex(2, 3, 3)\n", - "d3 = g.add_vertex(1, 3, 4,Fraction(-1,4))\n", - "\n", - " \n", - "\n", - "g.add_edges([(a0,a1),(c0,c1),(e0,e1),(b0,b1),(b2,b3),(d0,d1),(d2,d3)])\n", - "g.add_edges([(b1,a0),(b1,e0),(b2,a0),(b2,c0),(d1,a0),(d1,c0),(d1,e0),(d2,e0),(d2,c0)])\n", - "\n", - " \n", - "\n", - "e2 = g.add_vertex(2, 4, 6,Fraction(1,2))\n", - "e3 = g.add_vertex(1, 4, 7)\n", - "e4 = g.add_vertex(2, 4, 8,Fraction(1,1))\n", - "e5 = g.add_vertex(1, 4, 9,Fraction(1,1))\n", - "e6 = g.add_vertex(2, 4, 10)\n", - "e7 = g.add_vertex(2, 4, 11)\n", - "e8 = g.add_vertex(1, 4, 12,Fraction(7,4))\n", - "e9 = g.add_vertex(2, 4, 13)\n", - "e10 = g.add_vertex(2, 4, 14)\n", - "e11 = g.add_vertex(1, 4, 15,Fraction(1,1))\n", - "e12 = g.add_vertex(0,4,16)\n", - "g.add_edges([(e1,e2),(e2,e3), (e3,e4),(e4,e5),(e5,e6),(e6,e7),(e7,e8),(e8,e9),(e9,e10),(e10,e11),(e11,e12)])\n", - "c2 =g.add_vertex(2, 2,7 )\n", - "a2 =g.add_vertex(2, 0,7 )\n", - "c3 =g.add_vertex(1, 2,10)\n", - "a3 =g.add_vertex(1, 0,11)\n", - "c4 =g.add_vertex(1, 2,13)\n", - "a4 =g.add_vertex(1, 0,14)\n", - "c5 =g.add_vertex(0, 2,16)\n", - "a5 =g.add_vertex(0, 0,16)\n", - "g.add_edges([(a1,a2),(a2,a3),(a3,a4),(a4,a5)])\n", - "g.add_edges([(c1,c2),(c2,c3),(c3,c4),(c4,c5)])\n", - "g.add_edges([(e3,a2),(e3,c2),(e6,c3),(e7,a3),(e9,c4),(e10,a4)])\n", - "g.set_phase(17,0)\n", - "g.set_phase(23,0)\n", - "\n", - "display(zx.draw(g, labels=True))\n", - "zx.simplify.full_reduce(g)\n", - "display(zx.draw(g, labels=True))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g.auto_detect_io()\n", - "g.to_matrix() * (-1/0.02209709j)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "h = zx.Graph()\n", - "h.add_vertex(ty=Z,qubit=0,row=1,phase=Fraction(1,4))\n", - "h.add_vertex(ty=Z,qubit=1,row=1,phase=Fraction(1,4))\n", - "h.add_vertex(ty=Z,qubit=2,row=1,phase=Fraction(1,4))\n", - "h.add_vertex(ty=B,qubit=0,row=2)\n", - "h.add_vertex(ty=B,qubit=1,row=2)\n", - "h.add_vertex(ty=B,qubit=2,row=2)\n", - "h.add_edge((0,3))\n", - "h.add_edge((1,4))\n", - "h.add_edge((2,5))\n", - "h.auto_detect_io()\n", - "zx.draw(h)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "h.to_matrix()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "c = zx.Circuit(1)\n", - "c.add_gate(zx.gates.ZPhase(0, 1/2))\n", - "c.add_gate(zx.gates.XPhase(0, 1/4))\n", - "c.add_gate(zx.gates.ZPhase(0, -1/2))\n", - "zx.draw(c)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"Loading circuit\")\n", - "t = time.time()\n", - "c = zx.Circuit.load(\"../circuits/Slow/hwb8.qc\")\n", - "t = time.time() - t\n", - "print(\"Done in %s ms\" % (t*1000))\n", - "\n", - "g = c.to_graph()\n", - "print(\"Simplifying\")\n", - "t = time.time()\n", - "zx.simplify.interior_clifford_simp(g, quiet=True)\n", - "t = time.time() - t\n", - "print(\"Done in %s ms\" % (t*1000))\n", - "\n", - "print(\"Extracting\")\n", - "t = time.time()\n", - "c1 = zx.extract_circuit(g, optimize_cnots=0, optimize_czs=False)\n", - "t = time.time() - t\n", - "print(\"Done in %s ms\" % (t*1000))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "c = zx.qasm(\"\"\"\n", - "qreg q[5];\n", - "cx q[3], q[4];\n", - "tdg q[4];\n", - "cx q[0], q[3];\n", - "tdg q[3];\n", - "cx q[0], q[3];\n", - "cx q[1], q[4];\n", - "cx q[0], q[4];\n", - "cx q[1], q[4];\n", - "tdg q[4];\n", - "t q[0];\n", - "\"\"\")\n", - "zx.draw(c)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "c1 = zx.qasm(\"\"\"\n", - "qreg q[5];\n", - "h q[4];\n", - "h q[3];\n", - "h q[0];\n", - "h q[4];\n", - "h q[3];\n", - "h q[0];\n", - "cx q[0], q[1];\n", - "cx q[1], q[2];\n", - "cx q[2], q[0];\n", - "rz(-0.25*pi) q[3];\n", - "h q[3];\n", - "rz(-0.25*pi) q[0];\n", - "h q[0];\n", - "cz q[4], q[0];\n", - "h q[0];\n", - "rz(-0.25*pi) q[4];\n", - "cz q[3], q[0];\n", - "h q[3];\n", - "rz(0.25*pi) q[0];\n", - "\"\"\")\n", - "zx.draw(c1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zx.compare_tensors(c, c1, preserve_scalar=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = c.to_graph()\n", - "zx.full_reduce(g)\n", - "g.normalize()\n", - "zx.draw(g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g1 = c1.to_graph()\n", - "zx.full_reduce(g1)\n", - "g1.normalize()\n", - "zx.draw(g1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "c1 = zx.qasm(\"\"\"\n", - "qreg q[5];\n", - "cx q[4], q[2];\n", - "cx q[2], q[4];\n", - "cx q[4], q[2];\n", - "h q[4];\n", - "h q[4];\n", - "h q[3];\n", - "h q[3];\n", - "h q[2];\n", - "h q[1];\n", - "h q[1];\n", - "h q[0];\n", - "h q[0];\n", - "cz q[2], q[1];\n", - "cz q[1], q[0];\n", - "rz(-0.5*pi) q[1];\n", - "h q[1];\n", - "cz q[1], q[0];\n", - "rz(-0.5*pi) q[0];\n", - "h q[0];\n", - "cz q[4], q[2];\n", - "cz q[3], q[2];\n", - "cz q[2], q[0];\n", - "h q[2];\n", - "cz q[4], q[3];\n", - "cz q[4], q[0];\n", - "cz q[4], q[2];\n", - "rz(-0.75*pi) q[4];\n", - "h q[4];\n", - "cz q[4], q[2];\n", - "cz q[4], q[1];\n", - "cz q[4], q[3];\n", - "rz(0.25*pi) q[4];\n", - "cz q[3], q[0];\n", - "cz q[3], q[2];\n", - "rz(0.75*pi) q[3];\n", - "cz q[2], q[0];\n", - "rz(0.75*pi) q[2];\n", - "rz(-0.5*pi) q[1];\n", - "h q[1];\n", - "rz(1*pi) q[0];\n", - "\"\"\")\n", - "zx.draw(c1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g1 = c1.to_graph()\n", - "zx.spider_simp(g1)\n", - "zx.id_simp(g1)\n", - "zx.draw(g)\n", - "zx.draw(g1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zx.compare_tensors(g, g1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zx.compare_tensors(c, c1, preserve_scalar=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = c.to_graph()\n", - "g1 = c1.to_graph()\n", - "zx.spider_simp(g)\n", - "zx.to_gh(g1)\n", - "zx.spider_simp(g1)\n", - "zx.draw(g)\n", - "zx.draw(g1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "m = zx.Mat2.id(4)\n", - "m.gauss(full_reduce=True, blocksize=3)\n", - "m" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "t = time.time()\n", - "c = zx.Circuit.load('../circuits/feyn_bench/qasm/hwb12.qasm')\n", - "t = time.time() - t\n", - "print(\"Done in %s ms\" % (t * 1000))\n", - "c" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sz = 100000\n", - "\n", - "print(\"Building a Z-spider chain of size: %s\" % sz)\n", - "t = time.time()\n", - "g = zx.Graph()\n", - "g.add_vertex(ty=Z)\n", - "for i in range(1, sz):\n", - " g.add_vertex(ty=Z)\n", - " g.add_edge((i-1, i))\n", - "t = time.time() - t\n", - "print(\"Done in %s ms\" % (t * 1000))\n", - "\n", - "print(\"Fusing all spiders\")\n", - "t = time.time()\n", - "while any(fuse(g, g.edge_s(e), g.edge_t(e)) for e in g.edges()):\n", - " pass\n", - "t = time.time() - t\n", - "print(\"Done in %s s\" % t)\n", - "g" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = zx.Circuit.load(\"../circuits/Fast/QFT8_before\").to_graph()\n", - "qft_stats = zx.simplify.Stats()\n", - "zx.full_reduce(g, stats=qft_stats)\n", - "print(qft_stats)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "random.seed(1337)\n", - "c0 = zx.generate.CNOT_HAD_PHASE_circuit(5, 100)\n", - "print(c0.stats())\n", - "g = c0.to_graph()\n", - "stats = zx.simplify.Stats()\n", - "zx.full_reduce(g, stats=stats)\n", - "g.normalize()\n", - "g = g.copy()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "stats.num_rewrites" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zx.draw(g, labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "1 == Fraction(1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def frontier(g):\n", - " fr = set()\n", - " for i,o in enumerate(g.outputs):\n", - " v = list(g.neighbors(o))[0]\n", - " if v in g.inputs: continue\n", - " fr.add(v)\n", - " return fr\n", - "\n", - "def is_gadget(g, v):\n", - " return g.type(v) == 1 and len(g.neighbors(v)) == 1\n", - " \n", - "def is_gadget_hub(g, v):\n", - " return g.type(v) == 1 and any(is_gadget(g,n) for n in g.neighbors(v))\n", - "\n", - "def ungadget(g, fr):\n", - " for f in fr:\n", - " for n in g.neighbors(f):\n", - " if is_gadget(g, n):\n", - " gen_pivot(g, f, n)\n", - " return True\n", - " return False\n", - "\n", - "def make_gadget(g, fr):\n", - " for f in fr:\n", - " for n in g.neighbors(f):\n", - " if g.type(n) == 1 and g.phase(n) != 0 and g.phase(n) != 1:\n", - " gen_pivot(g, f, n)\n", - " return True\n", - " return False\n", - "\n", - "def make_gadgets(g):\n", - " fr = frontier(g1)\n", - " while make_gadget(g1, fr): pass\n", - " \n", - "def maximal(g):\n", - " mx = set()\n", - " fr = frontier(g)\n", - " for f in fr:\n", - " for n in g.neighbors(f):\n", - " if g.type(n) == 1 and not n in fr: mx.add(n)\n", - " return mx" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g1 = g.copy()\n", - "#make_gadgets(g1)\n", - "out = set(g1.outputs)\n", - "fr = frontier(g1)\n", - "while ungadget(g1, fr): pass\n", - "mx = maximal(g1)\n", - "last = max(mx, key=lambda v: g1.row(v))\n", - "\n", - "frlist = list(fr)\n", - "mxlist = list(mx)\n", - "print(frlist)\n", - "print(mxlist)\n", - "print(last)\n", - "\n", - "# m = bi_adj(g1, mxlist, frlist)\n", - "# print(m)\n", - "# print(m.transpose().nullspace())\n", - "# print(frlist)\n", - "gen_pivot(g1, 19, 20)\n", - "\n", - "fr = frontier(g1)\n", - "#while ungadget(g1, fr): pass\n", - "mx = maximal(g1)\n", - "\n", - "print(fr)\n", - "print(mx)\n", - "m = bi_adj(g1, mx, fr)\n", - "print(m)\n", - "\n", - "zx.draw(g1, labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def normalize_io(g):\n", - " shifti = False\n", - " shifto = False\n", - " iset = set(g.inputs)\n", - " oset = set(g.outputs)\n", - " for b in g.inputs + g.outputs:\n", - " if len(g.neighbors(b)) != 0:\n", - " nb = next(iter(g.neighbors(b)))\n", - " if g.phase(nb) != 0:\n", - " if b in iset: shifti = True\n", - " else: shifto = True\n", - " \n", - " e = g.edge(b,nb)\n", - " et = g.edge_type(e)\n", - " g.remove_edge(e)\n", - " v = g.add_vertex(1, row=g.row(b), qubit=g.qubit(b))\n", - " if et == 1:\n", - " g.add_edge((b,v), 2)\n", - " else:\n", - " g.add_edge((b,v), 1)\n", - " g.add_edge((v,nb),2)\n", - " \n", - " if shifti or shifto:\n", - " for v in g.vertices():\n", - " sh = 0\n", - " if shifti and not v in iset: sh += 1\n", - " if shifto and v in oset: sh += 1\n", - " g.set_row(v, g.row(v) + sh)\n", - " return (shifti, shifto)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = zx.Graph()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "round(g.scalar.to_number().real)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g.scalar" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pyzx.routing.parity_maps import CNOT_tracker\n", - "from pyzx.routing.steiner import steiner_gauss\n", - "from pyzx.routing.architecture import Architecture\n", - "\n", - "# Architecture is given as a numpy adjacency matrix. N.b. nodes must\n", - "# be ordered such that (0,1,...,n-1) is a Hamiltonian path.\n", - "\n", - "# Six qubits in a 3x2 grid.\n", - "arch = Architecture(\"test\", coupling_matrix=np.array(\n", - "[[0,1,0,0,0,1],\n", - " [1,0,1,0,1,0],\n", - " [0,1,0,1,0,0],\n", - " [0,0,1,0,1,0],\n", - " [0,1,0,1,0,1],\n", - " [1,0,0,0,1,0]]\n", - "))\n", - "\n", - "\n", - "# MUST BE FULL RANK\n", - "m = zx.Mat2([[1,0,1,1,0,0],\n", - " [0,1,1,0,1,1],\n", - " [1,1,0,0,1,0],\n", - " [1,0,1,0,1,1],\n", - " [1,1,1,1,0,0],\n", - " [1,1,0,0,1,1]])\n", - "\n", - "# CNOT_tracker is a subclass of Circuit that implements row_add by appending CNOT\n", - "# gates.\n", - "c = CNOT_tracker(6)\n", - "m1 = m.copy()\n", - "\n", - "# y is an optional parameter that tracks inverse row operations. If it is passed\n", - "# a CNOT_tracker, this produces the CNOT decomposition of m1.\n", - "#m1.gauss(full_reduce=True,y=c)\n", - "steiner_gauss(m1,arch,full_reduce=True,y=c)\n", - "\n", - "print(\"PARITY MATRIX:\")\n", - "print(m)\n", - "print(\"m rank:\", m.rank())\n", - "print(\"gates:\", c.gates)\n", - "print(\"correct CNOT circuit:\", c.matrix == m)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = c.to_graph()\n", - "g.map_qubits([\n", - " (0,0), (1, 0), (2, 0),\n", - " (2,1), (1, 1), (0, 1)\n", - "])\n", - "\n", - "for v in g.vertices(): g.set_row(v, g.row(v)/2)\n", - "\n", - "zx.draw(g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "c = zx.qasm(\"\"\"\n", - "qreg q[2];\n", - "t q[0];\n", - "h q[1];\n", - "cz q[0],q[1];\n", - "h q[1];\n", - "cz q[0],q[1];\n", - "\n", - "\"\"\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "c = zx.qasm(\"\"\"\n", - "qreg q[4];\n", - "h q[0];\n", - "rz(0.125*pi) q[2];\n", - "s q[0];\n", - "cx q[2],q[3];\n", - "cx q[1],q[2];\n", - "x q[3];\n", - "cx q[0],q[1];\n", - "cx q[2],q[3];\n", - "tdg q[1];\n", - "cx q[0],q[1];\n", - "cx q[1],q[2];\n", - "rz(-0.125*pi) q[2];\n", - "z q[0];\n", - "\"\"\")\n", - "\n", - "zx.draw(c)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = c.to_graph()\n", - "zx.full_reduce(g)\n", - "zx.draw(g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "random.seed(1337)\n", - "c = zx.generate.CNOT_HAD_PHASE_circuit(4, 200, p_had=0.3, p_t=0.4)\n", - "# c.gates.append(zx.gates.HAD(0))\n", - "# c.gates.append(zx.gates.HAD(1))\n", - "# c.gates.append(zx.gates.HAD(2))\n", - "# c.gates.append(zx.gates.HAD(3))\n", - "# c.add_circuit(zx.generate.CNOT_HAD_PHASE_circuit(4, 100, p_had=0))\n", - "zx.draw(c)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def unfuse(g,v):\n", - " v1 = g.add_vertex(1, phase=g.phase(v), row=g.row(v),qubit=-1)\n", - " g.add_edge((v1,v))\n", - " g.set_phase(v,0)\n", - " return v1\n", - "def gpivot(g,v1,v2):\n", - " n1 = [w for w in g.neighbors(v1) if g.vertex_degree(w)==1]\n", - " n2 = [w for w in g.neighbors(v2) if g.vertex_degree(w)==1]\n", - " zx.rules.apply_rule(g, zx.rules.pivot, [(v1,v2,n1,n2)])\n", - " q,r = g.qubit(v1), g.row(v1)\n", - " g.set_position(v1, g.qubit(v2), g.row(v2))\n", - " g.set_position(v2, q, r)\n", - "def frontier(g):\n", - " return set.union(*[set(g.neighbors(o)) for o in g.outputs])\n", - "def maximal(g):\n", - " fr = frontier(g)\n", - " mx = set()\n", - " for v in g.vertices():\n", - " nhd = g.neighbors(v)\n", - " if g.type(v) == 1 and any(n in fr for n in nhd) and all(g.type(n) != 0 for n in nhd):\n", - " mx.add(v)\n", - " return mx\n", - "\n", - "def fix_max(g):\n", - " hit = True\n", - " while hit:\n", - " fr = frontier(g)\n", - " print(fr)\n", - " for f in fr:\n", - " if g.phase(f): g.set_phase(f, 0)\n", - " out = set(g.outputs)\n", - " hit = False\n", - " mx = maximal(g)\n", - " for m in mx:\n", - " if g.phase(m) != 0 and g.phase(m) != 1:\n", - " m1 = unfuse(g, m)\n", - " f1 = None\n", - " for f in g.neighbors(m):\n", - " b = [f1 for f1 in g.neighbors(f) if g.type(f1)==0]\n", - " if len(b) == 1 and b[0] in out:\n", - " f1 = b[0]\n", - " break\n", - " if not f1:\n", - " raise ValueError(\"no suitable neighbor found\")\n", - " #zx.rules.apply_rule(g, zx.rules.pivot, [(m,f,[m1],[f1])])\n", - " gpivot(g,m,f)\n", - " hit = True\n", - " break" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "random.seed(1337)\n", - "c = zx.generate.CNOT_HAD_PHASE_circuit(4, 200, p_had=0, p_t=0.4)\n", - "g = c.to_graph()\n", - "zx.full_reduce(g)\n", - "g.normalize()\n", - "g = g.copy()\n", - "g.set_phase(4, 0)\n", - "g.set_phase(7, 0)\n", - "g.set_phase(13,0)\n", - "gpivot(g,13,8)\n", - "gpivot(g,9,7)\n", - "gpivot(g,12,4)\n", - "zx.spider_simp(g)\n", - "#fix_max(g)\n", - "# g(12) = {12, 25, 26}\n", - "# Odd(g(12)) = {20}\n", - "#gpivot(g,4,25)\n", - "#gpivot(g,7,26)\n", - "zx.draw(g, labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "random.seed(1337)\n", - "c = zx.generate.CNOT_HAD_PHASE_circuit(4, 200, p_had=0.3, p_t=0.4)\n", - "g = c.to_graph()\n", - "zx.full_reduce(g)\n", - "g.normalize()\n", - "g = g.copy()\n", - "g.set_phase(26,0)\n", - "g.set_phase(28,0)\n", - "unfuse(g,27)\n", - "gpivot(g,27,28)\n", - "# g(28) = {28, 31}\n", - "# Odd(g(28)) = {27, 30}\n", - "unfuse(g,29)\n", - "gpivot(g,29,31)\n", - "unfuse(g,25)\n", - "gpivot(g,25,26)\n", - "zx.draw(g,labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "vs = [4,3,6,8,10,5,11,9,7]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "m0 = zx.extract.bi_adj(g, [4,3,6,8,10,5], [8,10,5,11,9,7])\n", - "m0.rank()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "m = zx.linalg.Mat2([[1 if g.connected(i,j) else 0 for i in vs] for j in vs])\n", - "m.rank()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "id9 = zx.linalg.Mat2.id(9)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "input_pr = zx.linalg.Mat2([[1 if i == j and i > 2 else 0 for i in range(9)] for j in range(9)])\n", - "input_pr" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "output_pr = zx.linalg.Mat2([[1 if i == j and i < 6 else 0 for i in range(9)] for j in range(9)])\n", - "output_pr" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pc_op = zx.linalg.Mat2.id(7)\n", - "pc_op.col_add(4,3)\n", - "pc_op.col_add(6,0)\n", - "pc_op.col_add(5,1)\n", - "pc_op.col_add(4,2)\n", - "c = id9 #input_pr * pc_op * output_pr\n", - "gc = m * c\n", - "print(c)\n", - "print()\n", - "print(gc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, + "execution_count": null, "metadata": {}, "outputs": [], "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fr = frontier(g)\n", - "mx = maximal(g)\n", - "m = next(iter(mx))\n", - "f = next(f for f in g.neighbours(m) if f in fr)\n", - "(m,f)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = c.to_graph()\n", - "zx.full_reduce(g)\n", - "g.normalise()\n", - "g = g.copy()\n", - "v1 = unfuse(g,17)\n", - "zx.rules.apply_rule(g,zx.rules.pivot,[(6,17,[0],[v1])])\n", - "v2 = unfuse(g,4)\n", - "zx.rules.apply_rule(g,zx.rules.pivot,[(4,5,[v2],[3])])\n", - "g.normalise()\n", - "zx.draw(g,labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "m = zx.extract.bi_adj(g, maximal(g), frontier(g))\n", - "print(m)\n", - "m.gauss(full_reduce=True)\n", - "print()\n", - "print(m)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "random.seed(1338)\n", - "qs = 4\n", - "c = zx.Circuit(qs)\n", - "\n", - "for i in range(40):\n", - " ctrl = random.randint(0,qs-1)\n", - " targ = random.randint(0,qs-2)\n", - " if targ == ctrl: targ += 1\n", - " c.add_gate(\"CNOT\", ctrl, targ)\n", - " c.add_gate(\"T\", targ)\n", - " c.add_gate(\"CNOT\", ctrl, targ)\n", - "zx.draw(c)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = c.to_graph()\n", - "zx.simplify.full_reduce(g)\n", - "g.normalise()\n", - "zx.draw(g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g1 = g.copy()\n", - "fix_max(g1)\n", - "g1.normalise()\n", - "zx.rules.apply_rule(g1,zx.rules.pivot,[(12,20,[0],[15])])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zx.draw(g1, labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ge = g.copy()\n", - "ce = zx.extract_circuit(ge)\n", - "zx.draw(ce)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g1 = g.copy()\n", - "g1.normalise()\n", - "# squash phases on boundary\n", - "# g1.set_phase(14, 0)\n", - "# g1.set_phase(13, 0)\n", - "\n", - "# pivot away gadgets\n", - "#zx.rules.apply_rule(g1,zx.rules.pivot,[(9,14,[],[17])])\n", - "\n", - "zx.draw(g1,labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = zx.Graph()\n", - "\n", - "greens = 4\n", - "reds = 3\n", - "for i in range(greens):\n", - " g.add_vertex(ty=1, qubit=i, row=1)\n", - "for i in range(reds):\n", - " v = g.add_vertex(ty=2, qubit=i, row=3)\n", - " g.set_phase(v, 1)\n", - "\n", - "random.seed(1337)\n", - "p_edge = 0.5\n", - "for i in range(greens):\n", - " for j in range(greens, greens+reds):\n", - " if random.random() < p_edge:\n", - " g.add_edge((i,j))\n", - "zx.draw(g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zx.full_reduce(g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g.scalar" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = zx.qasm(\"\"\"\n", - "qreg q[3];\n", - "\n", - "ccz q[0],q[1],q[2];\n", - "h q[2];\n", - "t q[2];\n", - "ccz q[0],q[1],q[2];\n", - "h q[2];\n", - "t q[1];\n", - "ccz q[0],q[1],q[2];\n", - "s q[2];\n", - "ccx q[0],q[1],q[2];\n", - "\"\"\").to_graph(zh=True)\n", - "zx.draw(g, labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "h = g.copy()\n", - "zx.simplify.spider_simp(h)\n", - "zx.hsimplify.to_hbox(h)\n", - "m = zx.hrules.match_hpivot(h)\n", - "print(m)\n", - "display(zx.draw(h,labels=True))\n", - "zx.hrules.hpivot(h,m)\n", - "display(zx.draw(h,labels=True))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zx.hsimplify.hpivot_simp(g)\n", - "zx.d3.draw(g, labels=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "qs = 10\n", - "d = 60\n", - "c = zx.Circuit(qs)\n", - "p_t = 0\n", - "p_h = 0.3\n", - "random.seed(1338)\n", - "for i in range(d):\n", - " p = random.random()\n", - " if p < p_h:\n", - " c.add_gate(zx.circuit.HAD(random.randint(0,qs-1)))\n", - " elif p < p_t + p_h:\n", - " c.add_gate(zx.circuit.T(random.randint(0,qs-1)))\n", - " else:\n", - " q = sorted(random.sample(range(qs), 3))\n", - " c.add_gate(zx.circuit.CCZ(q[0],q[1],q[2]))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zx.d3.draw(c.to_graph(zh=True))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = c.to_graph(zh=True)\n", - "zx.hsimplify.hpivot_simp(g)\n", - "g.normalise()\n", - "zx.d3.draw(g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "[len([v for v in g.vertices() if g.type(v) == t]) for t in [1,3]]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "[max([g.vertex_degree(v) for v in g.vertices() if g.type(v) == t]) for t in [1,3]]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "zx.compare_tensors(c.to_graph(zh=True), g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "d = { 'a': 3, 'b': 4, 'c': 3}\n", - "list(filter(lambda v: v > 3, d.values()))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = [1,2,3]\n", - "a.append(4)\n", - "a" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "2**9" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "c1 = zx.Circuit.load('../circuits/Fast/QFT8_before')\n", - "c1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g = c1.to_graph(zh=True)\n", - "zx.hsimplify.hpivot_simp(g)\n", - "g.normalise()\n", - "print(\"Normal form: \", end='')\n", - "print(g.qubit_count() * 2 == len([v for v in g.vertices() if g.type(v) == 1]))\n", - "zx.d3.draw(g)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "[len([v for v in g.vertices() if g.type(v) == t]) for t in [1,3]]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "g.qubit_count() * 2 == len([v for v in g.vertices() if g.type(v) == 1])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "os.path.join('..', 'circuits', 'Fast')\n", - "for f in os.listdir(d):\n", - " if not os.path.isfile(os.path.join(d,f)): continue\n", - " if f.find('before') != -1:" - ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -4321,7 +417,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.12.0" } }, "nbformat": 4, diff --git a/scratchpads/ak2.ipynb b/scratchpads/ak2.ipynb index dcb5c0e3..ff0680a8 100644 --- a/scratchpads/ak2.ipynb +++ b/scratchpads/ak2.ipynb @@ -22,7 +22,7 @@ { "cell_type": "code", "execution_count": 2, - "id": "4f030a0a", + "id": "abb9cd7f", "metadata": {}, "outputs": [ { @@ -49,7 +49,7 @@ { "cell_type": "code", "execution_count": 3, - "id": "b19c5271", + "id": "b29c0dc8", "metadata": {}, "outputs": [], "source": [ @@ -62,21 +62,21 @@ }, { "cell_type": "code", - "execution_count": 13, - "id": "ccf76e80", + "execution_count": 4, + "id": "a8f60750", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "496\n", + "559\n", "True\n" ] } ], "source": [ - "c = zx.generate.qft(32) #zx.Circuit.load('../circuits/QFT_and_Adders/QFT32_before')\n", + "c = zx.Circuit.load('../circuits/QFT_and_Adders/QFTAdd16_before')\n", "g = c.to_graph()\n", "zx.full_reduce(g)\n", "print(g.num_edges() - g.num_vertices() + g.num_outputs())\n", @@ -85,14 +85,14 @@ }, { "cell_type": "code", - "execution_count": 14, - "id": "90d49c61", + "execution_count": 5, + "id": "aeb06e7f", "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", + "
\n", "" ], @@ -442,8 +442,8 @@ }, { "cell_type": "code", - "execution_count": 15, - "id": "cb5cf517", + "execution_count": 7, + "id": "d9a8bce2", "metadata": {}, "outputs": [], "source": [ @@ -473,18 +473,18 @@ }, { "cell_type": "code", - "execution_count": 16, - "id": "783574b6", + "execution_count": 8, + "id": "79b1ddf4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Circuit on 32 qubits with 2512 gates.\n", - " 1488 is the T-count\n", - " 1024 Cliffords among which\n", - " 992 2-qubit gates (992 CNOT, 0 other) and\n", + "Circuit QFTAdd16_before on 32 qubits with 1822 gates.\n", + " 1026 is the T-count\n", + " 796 Cliffords among which\n", + " 716 2-qubit gates (716 CNOT, 0 other) and\n", " 32 Hadamard gates.\n" ] } @@ -495,7 +495,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 9, "id": "44aad5a9", "metadata": {}, "outputs": [ @@ -504,11 +504,11 @@ "output_type": "stream", "text": [ "extracting circuit...\n", - "Circuit on 32 qubits with 2818 gates.\n", - " 558 is the T-count\n", - " 2260 Cliffords among which\n", - " 1174 2-qubit gates (909 CNOT, 265 other) and\n", - " 1086 Hadamard gates.\n" + "Circuit on 32 qubits with 2142 gates.\n", + " 402 is the T-count\n", + " 1740 Cliffords among which\n", + " 964 2-qubit gates (613 CNOT, 351 other) and\n", + " 776 Hadamard gates.\n" ] } ], @@ -524,8 +524,8 @@ }, { "cell_type": "code", - "execution_count": 19, - "id": "976f071e", + "execution_count": 41, + "id": "c5e37ac3", "metadata": { "scrolled": false }, @@ -535,17 +535,17 @@ "output_type": "stream", "text": [ "extracting circuit...\n", - "Circuit on 32 qubits with 928 gates.\n", - " 180 is the T-count\n", - " 748 Cliffords among which\n", - " 418 2-qubit gates (110 CNOT, 308 other) and\n", - " 330 Hadamard gates.\n" + "Circuit on 32 qubits with 2142 gates.\n", + " 402 is the T-count\n", + " 1740 Cliffords among which\n", + " 964 2-qubit gates (613 CNOT, 351 other) and\n", + " 776 Hadamard gates.\n" ] } ], "source": [ "g1 = c.to_graph()\n", - "squash_reduce(g1, err_budget=0.01, quiet=True)\n", + "squash_reduce(g1, err_budget=2**-15)\n", "print(\"extracting circuit...\")\n", "c1 = zx.extract_circuit(g1, up_to_perm=True)\n", "zx.basic_optimization(c1)\n", @@ -555,8 +555,8 @@ }, { "cell_type": "code", - "execution_count": 20, - "id": "29239690", + "execution_count": 10, + "id": "1c955dc6", "metadata": { "scrolled": false }, @@ -566,11 +566,11 @@ "output_type": "stream", "text": [ "extracting circuit...\n", - "Circuit on 32 qubits with 311 gates.\n", - " 93 is the T-count\n", - " 218 Cliffords among which\n", - " 62 2-qubit gates (0 CNOT, 62 other) and\n", - " 156 Hadamard gates.\n" + "Circuit on 32 qubits with 680 gates.\n", + " 122 is the T-count\n", + " 558 Cliffords among which\n", + " 308 2-qubit gates (121 CNOT, 187 other) and\n", + " 244 Hadamard gates.\n" ] } ], diff --git a/scratchpads/phase-squashing.ipynb b/scratchpads/phase-squashing.ipynb new file mode 100644 index 00000000..67bfc57c --- /dev/null +++ b/scratchpads/phase-squashing.ipynb @@ -0,0 +1,616 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "aa06120b", + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os; sys.path.insert(0, '..')\n", + "import random, logging, math\n", + "import pyzx as zx\n", + "from fractions import Fraction\n", + "from pyzx.gadget_extract import *\n", + "Z = zx.VertexType.Z\n", + "X = zx.VertexType.X\n", + "SE = zx.EdgeType.SIMPLE\n", + "HE = zx.EdgeType.HADAMARD\n", + "# zx.settings.drawing_backend = 'matplotlib'" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "abb9cd7f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.00+0.00i = sqrt(2)^2\n", + "2\n" + ] + } + ], + "source": [ + "c = zx.qasm(\"\"\"\n", + "qreg q[4];\n", + "cx q[0], q[1];\n", + "cx q[1], q[2];\n", + "s q[2];\n", + "\"\"\")\n", + "g = c.to_graph()\n", + "print(g.scalar)\n", + "print(g.num_edges() - g.num_vertices() + g.num_outputs())" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b29c0dc8", + "metadata": {}, + "outputs": [], + "source": [ + "def num_spiders(g):\n", + " return len([v for v in g.vertices() if g.type(v) == Z or g.type(v) == X])\n", + "\n", + "def inv(g):\n", + " return (g.scalar.power2 == g.num_edges() - num_spiders(g) - g.num_outputs())" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "a8f60750", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "559\n", + "True\n" + ] + } + ], + "source": [ + "c = zx.Circuit.load('../circuits/QFT_and_Adders/QFTAdd16_before')\n", + "g = c.to_graph()\n", + "zx.full_reduce(g)\n", + "print(g.num_edges() - g.num_vertices() + g.num_outputs())\n", + "print(g.scalar.power2 == g.num_edges() - num_spiders(g) - g.num_outputs())" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "aeb06e7f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "zx.draw(c)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "d9a8bce2", + "metadata": {}, + "outputs": [], + "source": [ + "def squash_reduce(g, err_budget, quiet=True):\n", + " total_err = 0\n", + " for i in range(50, -1, -1):\n", + " zx.full_reduce(g)\n", + " th = Fraction(1, 2**i)\n", + " if not quiet: print(f'threshold Δθ <= 2^-{i} π')\n", + " for v in list(g.vertices()):\n", + " p = g.phase(v)\n", + " if p.denominator > 2:\n", + " new_p = Fraction(round(2*p), 2)\n", + " err = abs(float(p)-round(new_p))\n", + " if err <= th:\n", + " if total_err + err > err_budget:\n", + " zx.full_reduce(g)\n", + " if not quiet: print('exceeded error budget, done.')\n", + " return\n", + " else:\n", + " norm_err = 2*(math.sin(err/2)**2)\n", + " if not quiet: print(f' * {p} ~ {new_p} (ε = {norm_err})')\n", + " g.set_phase(v, new_p)\n", + " total_err += norm_err\n", + " zx.full_reduce(g)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "79b1ddf4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Circuit QFTAdd16_before on 32 qubits with 1822 gates.\n", + " 1026 is the T-count\n", + " 796 Cliffords among which\n", + " 716 2-qubit gates (716 CNOT, 0 other) and\n", + " 32 Hadamard gates.\n" + ] + } + ], + "source": [ + "print(c.to_basic_gates().stats())" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "c5e37ac3", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "extracting circuit...\n", + "Circuit on 32 qubits with 1278 gates.\n", + " 236 is the T-count\n", + " 1042 Cliffords among which\n", + " 574 2-qubit gates (328 CNOT, 246 other) and\n", + " 466 Hadamard gates.\n" + ] + } + ], + "source": [ + "g1 = c.to_graph()\n", + "squash_reduce(g1, err_budget=0.01)\n", + "print(\"extracting circuit...\")\n", + "c1 = zx.extract_circuit(g1, up_to_perm=True)\n", + "zx.basic_optimization(c1)\n", + "#zx.draw(c1)\n", + "print(c1.stats())" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "44aad5a9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "extracting circuit...\n", + "Circuit on 32 qubits with 2142 gates.\n", + " 402 is the T-count\n", + " 1740 Cliffords among which\n", + " 964 2-qubit gates (613 CNOT, 351 other) and\n", + " 776 Hadamard gates.\n" + ] + } + ], + "source": [ + "g1 = c.to_graph()\n", + "squash_reduce(g1, err_budget=0.0)\n", + "print(\"extracting circuit...\")\n", + "c1 = zx.extract_circuit(g1, up_to_perm=True)\n", + "zx.basic_optimization(c1)\n", + "#zx.draw(c1)\n", + "print(c1.stats())" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "1c955dc6", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "extracting circuit...\n", + "Circuit on 32 qubits with 499 gates.\n", + " 113 is the T-count\n", + " 386 Cliffords among which\n", + " 190 2-qubit gates (12 CNOT, 178 other) and\n", + " 196 Hadamard gates.\n" + ] + } + ], + "source": [ + "g1 = c.to_graph()\n", + "squash_reduce(g1, err_budget=0.1)\n", + "print(\"extracting circuit...\")\n", + "c1 = zx.extract_circuit(g1, up_to_perm=True)\n", + "#zx.draw(c1)\n", + "print(c1.stats())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15f333c6", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}