diff --git a/demos/AllFeatures.ipynb b/demos/AllFeatures.ipynb
index af029675..e8862f16 100644
--- a/demos/AllFeatures.ipynb
+++ b/demos/AllFeatures.ipynb
@@ -8,7 +8,7 @@
"\n",
"## Contents:\n",
"* [Loading and saving circuits](#circuits)\n",
- "* [Interacting with Quantomatic](#quantomatic)\n",
+ "* [Importing, exporting and editing diagrams](#diagram-io)\n",
"* [Optimizing ZX-diagrams](#optimization-zx)\n",
"* [Extracting and optimizing circuits](#optimization-circuits)\n",
"* [Phase Teleportation](#phase-teleportation)"
@@ -172,11 +172,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "# Interacting with Quantomatic\n",
- "PyZX allows easy integration with quantomatic.\n",
- "\n",
- "First of all, Quantomatic graph files can be imported into PyZX:"
+ "\n",
+ "# Importing, exporting and editing diagrams\n",
+ "PyZX also has its own json format for exporting graphs and interacts with [ZXLive](https://github.com/zxcalc/zxlive) to make manually modifying diagrams easy. First, let's see that we can indeed load diagrams:"
]
},
{
@@ -195,24 +193,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "PyZX saves the names of the vertices:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(g.vdata(12,'name'))\n",
- "print(g.vdata(1,'name'))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Because this graph was originally exported from PyZX, it has automatically remembered what its inputs and outputs are:"
+ "Because this graph was originally exported from PyZX starting as a circuit, it has automatically remembered what its inputs and outputs are:"
]
},
{
@@ -228,7 +209,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "For a graph that originated from Quantomatic we need to tell it what its inputs and outputs are.\n",
+ "For a graph that was built manually, we might need to tell it what its inputs and outputs are.\n",
"\n",
"This can be done either manually:\n",
"\n",
@@ -243,17 +224,17 @@
"metadata": {},
"outputs": [],
"source": [
- "g.set_inputs(())\n",
+ "g.set_inputs(()) # Reset the inputs and outputs, so we can let PyZX auto-detect them\n",
"g.set_outputs(())\n",
"g.auto_detect_io()\n",
"print(g.inputs(), g.outputs())"
]
},
{
- "cell_type": "markdown",
+ "cell_type": "raw",
"metadata": {},
"source": [
- "We can also call Quantomatic from PyZX. To do this we first need to tell PyZX where the Quantomatic executable can be found:"
+ "we can export a diagram to a JSON format that can be loaded back into PyZX (or ZXLive):"
]
},
{
@@ -262,14 +243,14 @@
"metadata": {},
"outputs": [],
"source": [
- "zx.quantomatic.quantomatic_location = os.path.join('path', 'to', 'Quantomatic.jar')"
+ "print(g.to_json())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Now, we can load a PyZX graph into Quantomatic using the following line:"
+ "We can call ZXLive from within a Jupyter notebook, in order to modify diagrams on the fly:"
]
},
{
@@ -278,18 +259,39 @@
"metadata": {},
"outputs": [],
"source": [
- "result = zx.quantomatic.edit_graph(g)"
+ "%gui qt6\n",
+ "\n",
+ "# First make sure zxlive is installed by `pip install zxlive`\n",
+ "from zxlive import app\n",
+ "\n",
+ "g = zx.Graph()\n",
+ "g.add_vertex(zx.VertexType.Z, 0, 0)\n",
+ "g.add_vertex(zx.VertexType.X, 0, 1)\n",
+ "g.add_edge((0, 1))\n",
+ "zx.draw(g)\n",
+ "\n",
+ "zxl = app.get_embedded_app()\n",
+ "zxl.edit_graph(g, 'g1')\n",
+ "zxl.edit_graph(g, 'g2')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "This starts Quantomatic with the graph ``g`` loaded. When you are done editing the graph, you simply save the file in Quantomatic, and close it. The result is then loaded and returned.\n",
- "\n",
- "NOTE1: The Notebook will be blocked until the Quantomatic executable is closed.\n",
- "\n",
- "NOTE2: Currently this only works with a recent build of Quantomatic that is as of yet only available via the repository, so make sure you are working with an up-to-date branch of Quantomatic."
+ "After making some edits within ZXLive, we can get the diagram back into this window so we can continue to do further work with them:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "zx.draw(zxl.get_copy_of_graph('g1'))\n",
+ "zx.draw(zxl.get_copy_of_graph('g2'))\n",
+ "#Note that ZXLive only works with MultiGraph's, and hence zxl.get_copy_of_graph() always returns an instance of MultiGraph, \n",
+ "#and not of the default graph backend."
]
},
{
@@ -689,7 +691,7 @@
],
"metadata": {
"kernelspec": {
- "display_name": "Python 3",
+ "display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
@@ -703,7 +705,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.8.10"
+ "version": "3.11.2"
}
},
"nbformat": 4,
diff --git a/doc/api.rst b/doc/api.rst
index 2139c27e..362286d6 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -150,7 +150,7 @@ Below is listed the content of ``drawing.py``.
:undoc-members:
-Tikz and Quantomatic functionality
+Tikz functionality
----------------------------------
.. _tikz:
@@ -165,11 +165,3 @@ Below is listed the content of ``tikz.py``.
.. _quanto:
-Below is listed the content of ``quantomatic.py``.
-
-.. module:: quantomatic
-
-.. automodule:: pyzx.quantomatic
- :members:
- :undoc-members:
-
diff --git a/pyzx/__init__.py b/pyzx/__init__.py
index 53e142bb..197940fd 100644
--- a/pyzx/__init__.py
+++ b/pyzx/__init__.py
@@ -31,7 +31,6 @@
from .local_search.genetic import GeneticOptimizer
from .circuit.qasmparser import qasm
from .circuit.sqasm import sqasm
-from . import quantomatic
from . import generate
from . import todd
from . import linalg
diff --git a/pyzx/graph/jsonparser.py b/pyzx/graph/jsonparser.py
index 8286fb69..5015a8a8 100644
--- a/pyzx/graph/jsonparser.py
+++ b/pyzx/graph/jsonparser.py
@@ -431,22 +431,3 @@ 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
-
diff --git a/pyzx/io.py b/pyzx/io.py
deleted file mode 100644
index ffb4b344..00000000
--- a/pyzx/io.py
+++ /dev/null
@@ -1,262 +0,0 @@
-# PyZX - Python library for quantum circuit rewriting
-# and optimization using the ZX-calculus
-# Copyright (C) 2018 - Aleks Kissinger and John van de Wetering
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import json
-from fractions import Fraction
-from typing import List, Dict, Any
-
-from .utils import FractionLike
-from .graph import Graph, EdgeType, VertexType
-from .graph.base import BaseGraph, VT, ET
-from .symbolic import Poly
-
-__all__ = ['json_to_graph', 'graph_to_json', 'to_graphml']
-
-def _quanto_value_to_phase(s: str) -> Fraction:
- if not s: return Fraction(0)
- if r'\pi' in s:
- try:
- r = s.replace(r'\pi','').strip()
- if r.startswith('-'): r = "-1"+r[1:]
- if r.startswith('/'): r = "1"+r
- return Fraction(str(r)) if r else Fraction(1)
- except ValueError:
- raise ValueError("Invalid phase '{}'".format(s))
- return Fraction(s)
-
-def _phase_to_quanto_value(p: FractionLike) -> str:
- if not p: return ""
- if isinstance(p, Poly):
- raise ValueError("Symbolic phases not supported")
- p = Fraction(p)
- if p.numerator == -1: v = "-"
- elif p.numerator == 1: v = ""
- else: v = str(p.numerator)
- d = "/"+str(p.denominator) if p.denominator!=1 else ""
- return r"{}\pi{}".format(v,d)
-
-
-def json_to_graph(js: str, force_deprecated_behavior=False) -> BaseGraph:
- """Converts the json representation of a .qgraph Quantomatic graph into
- a pyzx graph."""
- print("json_to_graph(js) is deprecated. Please use zx.Graph.from_json(js) instead.")
- if not force_deprecated_behavior:
- return Graph.from_json(js) # type: ignore
- j = json.loads(js)
- g = Graph()
-
- names: Dict[str, Any] = {} # TODO: Any = VT
- hadamards: Dict[str, List[Any]] = {}
-
- inputs = []
- outputs = []
- for name,attr in j.get('node_vertices',{}).items():
- if ('data' in attr and 'type' in attr['data'] and attr['data']['type'] == "hadamard"
- and 'is_edge' in attr['data'] and attr['data']['is_edge'] == 'true'):
- hadamards[name] = []
- continue
- c = attr['annotation']['coord']
- q, r = -c[1], c[0]
- if q == int(q): q = int(q)
- if r == int(r): r = int(r)
- v = g.add_vertex(qubit=q, row=r)
- g.set_vdata(v,'name',name)
- names[name] = v
- if 'data' in attr:
- d = attr['data']
- if not 'type' in d or d['type'] == 'Z': g.set_type(v,VertexType.Z)
- elif d['type'] == 'X': g.set_type(v,VertexType.X)
- elif d['type'] == 'hadamard': g.set_type(v,VertexType.H_BOX)
- else: raise TypeError("unsupported type '{}'".format(d['type']))
- if 'value' in d:
- g.set_phase(v,_quanto_value_to_phase(d['value']))
- else:
- g.set_phase(v,Fraction(0,1))
- else:
- g.set_type(v,VertexType.Z)
- g.set_phase(v,Fraction(0,1))
-
- #g.set_vdata(v, 'x', c[0])
- #g.set_vdata(v, 'y', c[1])
- for name,attr in j.get('wire_vertices',{}).items():
- ann = attr['annotation']
- c = ann['coord']
- q, r = -c[1], c[0]
- if q == int(q): q = int(q)
- if r == int(r): r = int(r)
- v = g.add_vertex(VertexType.BOUNDARY,q,r)
- g.set_vdata(v,'name',name)
- names[name] = v
- if "input" in ann and ann["input"]: inputs.append(v)
- if "output" in ann and ann["output"]: outputs.append(v)
- #g.set_vdata(v, 'x', c[0])
- #g.set_vdata(v, 'y', c[1])
-
- g.set_inputs(tuple(inputs))
- g.set_outputs(tuple(outputs))
-
- edges: Dict[Any, List[int]] = {} # TODO: Any = ET
- for edge in j.get('undir_edges',{}).values():
- n1, n2 = edge['src'], edge['tgt']
- if n1 in hadamards and n2 in hadamards: #Both
- v = g.add_vertex(VertexType.Z)
- name = "v"+str(len(names))
- g.set_vdata(v, 'name',name)
- names[name] = v
- hadamards[n1].append(v)
- hadamards[n2].append(v)
- continue
- if n1 in hadamards:
- hadamards[n1].append(names[n2])
- continue
- if n2 in hadamards:
- hadamards[n2].append(names[n1])
- continue
-
- amount = edges.get(g.edge(names[n1],names[n2]),[0,0])
- amount[0] += 1
- edges[g.edge(names[n1],names[n2])] = amount
-
- for l in hadamards.values():
- if len(l) != 2: raise TypeError("Can't parse graphs with irregular Hadamard nodes")
- e = g.edge(*tuple(l))
- amount = edges.get(e,[0,0])
- amount[1] += 1
- edges[e] = amount
- g.add_edge_table(edges)
-
- return g
-
-def graph_to_json(g: BaseGraph[VT,ET], force_deprecated_behavior=False) -> str:
- """Converts a PyZX graph into JSON output compatible with Quantomatic."""
- print("graph_to_json(g) is deprecated. Please use g.to_json() instead (for a given graph g).")
- if not force_deprecated_behavior:
- return g.to_json()
- node_vs: Dict[str, Dict[str, Any]] = {}
- wire_vs: Dict[str, Dict[str, Any]] = {}
- edges: Dict[str, Dict[str, str]] = {}
- names: Dict[VT, str] = {}
- freenamesv = ["v"+str(i) for i in range(g.num_vertices()+g.num_edges())]
- freenamesb = ["b"+str(i) for i in range(g.num_vertices())]
-
- inputs = g.inputs()
- outputs = g.outputs()
-
- for v in g.vertices():
- t = g.type(v)
- coord = [g.row(v),-g.qubit(v)]
- name = g.vdata(v, 'name')
- if not name:
- if t == VertexType.BOUNDARY: name = freenamesb.pop(0)
- else: name = freenamesv.pop(0)
- else:
- try:
- freenamesb.remove(name) if t==VertexType.BOUNDARY else freenamesv.remove(name)
- except:
- pass
- #print("couldn't remove name '{}'".format(name))
-
- names[v] = name
- if t == VertexType.BOUNDARY:
- wire_vs[name] = {"annotation":{"boundary":True,"coord":coord,
- "input":(v in inputs), "output":(v in outputs)}}
- else:
- node_vs[name] = {"annotation": {"coord":coord},"data":{}}
- if t==VertexType.Z:
- node_vs[name]["data"]["type"] = "Z"
- elif t==VertexType.X:
- node_vs[name]["data"]["type"] = "X"
- elif t==VertexType.H_BOX:
- node_vs[name]["data"]["type"] = "hadamard"
- node_vs[name]["data"]["is_edge"] = "false"
- else: raise Exception("Unkown vertex type "+ str(t))
- phase = _phase_to_quanto_value(g.phase(v))
- if phase: node_vs[name]["data"]["value"] = phase
- if not node_vs[name]["data"]: del node_vs[name]["data"]
-
- i = 0
- for e in g.edges():
- src,tgt = g.edge_st(e)
- et = g.edge_type(e)
- if et == EdgeType.SIMPLE:
- edges["e"+ str(i)] = {"src": names[src],"tgt": names[tgt]}
- i += 1
- elif et==EdgeType.HADAMARD:
- x1,y1 = g.row(src), -g.qubit(src)
- x2,y2 = g.row(tgt), -g.qubit(tgt)
- hadname = freenamesv.pop(0)
- node_vs[hadname] = {"annotation": {"coord":[(x1+x2)/2.0,(y1+y2)/2.0]},
- "data": {"type": "hadamard","is_edge": "true"}}
- edges["e"+str(i)] = {"src": names[src],"tgt": hadname}
- i += 1
- edges["e"+str(i)] = {"src": names[tgt],"tgt": hadname}
- i += 1
- else:
- raise TypeError("Edge of type 0")
-
-
- return json.dumps({"wire_vertices": wire_vs,
- "node_vertices": node_vs,
- "undir_edges": edges})
-
-def to_graphml(g: BaseGraph[VT,ET], force_deprecated_behavior=False) -> str:
- gml = """
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
-"""
- print("to_graphml(g) is deprecated. Please use g.to_graphml() instead (where g is a Graph instance).")
- if not force_deprecated_behavior:
- return g.to_graphml()
-
- for v in g.vertices():
- gml += (
- (8*" " +
- """{!s}{!s}"""+
- """{!s}{!s}\n"""
- ).format(
- v, g.type(v), g.phase(v), g.row(v) * 100, g.qubit(v) * 100
- ))
-
- for e in g.edges():
- s,t = g.edge_st(e)
- gml += (
- (8*" " +
- """"""+
- """{!s}\n"""
- ).format(
- s, t, s, t, g.edge_type(e)
- ))
- gml += """
-
-
-"""
-
- return gml
diff --git a/pyzx/quantomatic.py b/pyzx/quantomatic.py
deleted file mode 100644
index 6e6968bf..00000000
--- a/pyzx/quantomatic.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# PyZX - Python library for quantum circuit rewriting
-# and optimization using the ZX-calculus
-# Copyright (C) 2018 - Aleks Kissinger and John van de Wetering
-
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Implements methods for interacting with Quantomatic::
-
- import pyzx as zx
- zx.settings.quantomatic_location = "path/to/quantomatic/jar/file.jar"
- g = zx.generate.cliffordT(3,10,0.2)
- g2 = zx.quantomatic.edit_graph(g) # Opens Quantomatic with the graph g opened. Execution is blocked until Quantomatic is closed again.
- # If you have saved the qgraph file in quantomatic, then g2 should now contain your changes.
-
-"""
-
-import tempfile
-import os
-import subprocess
-
-from .utils import settings
-from .io import json_to_graph, graph_to_json
-from .graph.base import BaseGraph
-
-def edit_graph(g: BaseGraph) -> BaseGraph:
- """Opens Quantomatic with the graph ``g`` loaded. When you are done editing the graph,
- you save it in Quantomatic and close the executable. The resulting graph is returned by this function.
- Note that this function blocks until the Quantomatic executable is closed. For this function to work
- you must first set ``zx.settings.quantomatic_location`` to point towards the Quantomatic .jar file."""
- if not settings.quantomatic_location or not os.path.exists(settings.quantomatic_location):
- raise Exception("Please point towards the Quantomatic jar file with pyzx.settings.quantomatic_location")
-
- with tempfile.TemporaryDirectory() as tmpdirname:
- projectname = os.path.join(tmpdirname, "main.qgraph")
- with open(projectname,'w') as f:
- f.write(pyzx_qproject)
- js = graph_to_json(g)
- fname = os.path.join(tmpdirname, "pyzxgraph.qgraph")
- with open(fname,'w') as f:
- f.write(js)
- print("Opening Quantomatic...")
- subprocess.check_call(["java", "-jar",settings.quantomatic_location, projectname, fname])
- print("Done")
- with open(fname, 'r') as f:
- js = f.read()
- g = json_to_graph(js)
- return g
-
-
-pyzx_qproject = """
-{"name":"PyZX",
-"theory":{"name":"Red/green theory","core_name":"red_green",
-"vertex_types":{
- "X":{"value":{"type":"angle_expr","latex_constants":true,"validate_with_core":false},
- "style":{"label":{"position":"inside","fg_color":[1.0,1.0,1.0]},"stroke_color":[0.0,0.0,0.0],"fill_color":[1.0,0.0,0.0],"shape":"circle","stroke_width":1},"default_data":{"type":"X","value":""}},
- "Z":{"value":{"type":"angle_expr","latex_constants":true,"validate_with_core":false},
- "style":{"label":{"position":"inside","fg_color":[0.0,0.0,0.0]},"stroke_color":[0.0,0.0,0.0],"fill_color":[0.0,0.800000011920929,0.0],"shape":"circle","stroke_width":1},"default_data":{"type":"Z","value":""}},
- "hadamard":{"value":{"type":"string","latex_constants":false,"validate_with_core":false},
- "style":{"label":{"position":"inside","fg_color":[0.0,0.20000000298023224,0.0]},"stroke_color":[0.0,0.0,0.0],"fill_color":[1.0,1.0,0.0],"shape":"rectangle","stroke_width":1},"default_data":{"type":"hadamard","value":""}},
- "var":{"value":{"type":"string","latex_constants":false,"validate_with_core":false},
- "style":{"label":{"position":"inside","fg_color":[0.0,0.0,0.0]},"stroke_color":[0.0,0.0,0.0],"fill_color":[0.6000000238418579,1.0,0.800000011920929],"shape":"rectangle","stroke_width":1},"default_data":{"type":"var","value":""}}
- },
-"default_vertex_type":"Z",
-"default_edge_type":"string",
-"edge_types":{
- "string":{"value":{"type":"string","latex_constants":false,"validate_with_core":false},"style":{"stroke_color":[0.0,0.0,0.0],"stroke_width":1,"label":{"position":"center","fg_color":[0.0,0.0,1.0],"bg_color":[0.800000011920929,0.800000011920929,1.0,0.699999988079071]}},"default_data":{"type":"string","value":""}}}
- }
-}"""
\ No newline at end of file