Giter VIP home page Giter VIP logo

pyzx's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyzx's Issues

Split extract.py into submodules

The extract module contains multiple independent extraction functions (streaming_extract, modified_extract, clifford_extract, simple_extract, circuit_extract), including some that are commented-out.
We could split them into extract.* submodules, so it's easier to modify or add new extraction strategies.

I can make the PR if this is OK.

Importing circuit_extract fails

This comes up when one attempts to execute the test suite:

>>> from pyzx.extract import circuit_extract
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'circuit_extract' from 'pyzx.extract'

since the definition of circuit_extract method is commented out.

Removing support for running PyZX in Quantomatic

Currently, pyzx can be run as a library inside of Quantomatic to perform rewrites inside the Quantomatic GUI. I will remove this functionality for several reasons:

  • As far as I know it isn't being used by anyone.
  • Quantomatic uses Jython to interact with Python. Jython does not support Python 3. Hence, we cannot use Python 3 features in PyZX if we wish to preserve this compatibility.
  • I think PyZX already uses some Python 3 features, and hence this functionality is already broken as far as I am aware.

verify_equality checks equality up to a permutation

It appears that the Circuit.verify_equality method checks equality up to a permutation rather than strict equality. I tested it with several different circuits with the final permutation changed. The method would still return true. I think this is a useful feature because I don't have to revert the final permutation in order to test whether a circuit optimization strategy works. However, it should be mentioned in the API documentation.

Index out of range while extracting big(-ish) circuit

To reproduce:

c = zx.Circuit.load('../circuits/Slow/Adder16_before').to_basic_gates()
g = c.to_graph()
zx.simplify.full_reduce(g)
c1 = zx.extract.extract_circuit(g)

Raises exception:

IndexError                                Traceback (most recent call last)
<ipython-input-94-0ba920683a86> in <module>
      2 g = c.to_graph()
      3 zx.simplify.full_reduce(g)
----> 4 c1 = zx.extract.extract_circuit(g)

~/git/pyzx/pyzx/extract.py in extract_circuit(g, optimize_czs, optimize_cnots, quiet)
    425             m2 = m.copy()
    426             for cnot in cnots:
--> 427                 m2.row_add(cnot.target,cnot.control)
    428             extractable = set()
    429             for i, row in enumerate(m2.data):

~/git/pyzx/pyzx/linalg.py in row_add(self, r0, r1)
     67     def row_add(self, r0: int, r1: int) -> None:
     68         """Add r0 to r1"""
---> 69         row1 = self.data[r0]
     70         row2 = self.data[r1]
     71         for i, v in enumerate(row1):

IndexError: list index out of range

This also happens for Adder64, but none of the others in circuits/Slow. This might also happen with Adder32, bit it's throwing another exception (non-extractable) first.

Issue with the different representations of an arity 2 H box leading to radically different matrices

The following seems to occur when studying the following diagrams
image

The first is correct, the second is nonsense.

The Json for the first is here

spinjson = '{"wire_vertices": {"b0": {"annotation": {"boundary": true, "coord": [0.45399999999999974, -1.010875], "input": true, "output": false}}, "b1": {"annotation": {"boundary": true, "coord": [0.8039999999999998, -9.410874999999999], "input": true, "output": false}}, "b2": {"annotation": {"boundary": true, "coord": [21.9, -0.6000000000000001], "input": false, "output": true}}, "b3": {"annotation": {"boundary": true, "coord": [22.1, -10.9], "input": false, "output": true}}}, "node_vertices": {"v0": {"annotation": {"coord": [5.415683593750001, -1.0614531250000003]}, "data": {"type": "Z"}}, "v1": {"annotation": {"coord": [15.778683593750003, -1.0313281250000004]}, "data": {"type": "Z"}}, "v2": {"annotation": {"coord": [4.542058593750001, -9.526578125000002]}, "data": {"type": "Z"}}, "v3": {"annotation": {"coord": [16.200433593750002, -9.978453125000001]}, "data": {"type": "Z"}}, "v4": {"annotation": {"coord": [6.741183593750001, -5.068078125000001]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v5": {"annotation": {"coord": [13.218058593750001, -5.0379531250000005]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v8": {"annotation": {"coord": [9.96455859375, -2.085703125]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v9": {"annotation": {"coord": [10.265808593750002, -7.598578125000001]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v10": {"annotation": {"coord": [10.386308593750002, -5.339203125000001]}, "data": {"type": "Z"}}, "v20": {"annotation": {"coord": [2.7948085937500005, -8.532453125000002]}, "data": {"type": "Z"}}, "v6": {"annotation": {"coord": [4.270933593750001, -3.531703125000001]}, "data": {"type": "X", "value": "\\pi"}}, "v7": {"annotation": {"coord": [1.5898085937500004, -3.8932031250000003]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi/6"}}, "v11": {"annotation": {"coord": [1.7404335937500002, -5.670578125000001]}, "data": {"type": "hadamard", "is_edge": "false", "value": "11\\pi/6"}}, "v12": {"annotation": {"coord": [2.7948085937500005, -4.586078125000001]}, "data": {"type": "Z", "value": "\\pi/2"}}, "v13": {"annotation": {"coord": [0.5354335937500001, -4.736703125000001]}, "data": {"type": "X", "value": "\\pi"}}, "v14": {"annotation": {"coord": [3.0960585937500005, -6.604453125000001]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v15": {"annotation": {"coord": [3.8491835937500003, -8.411953125]}, "data": {"type": "Z"}}, "v16": {"annotation": {"coord": [3.969683593750001, -6.303203125000001]}, "data": {"type": "X", "value": "\\pi"}}, "v17": {"annotation": {"coord": [10.597183593750001, -1.0463906250000004]}, "data": {"type": "hadamard", "is_edge": "true"}}, "v18": {"annotation": {"coord": [18.839341796875, -0.8156640625000002]}, "data": {"type": "hadamard", "is_edge": "true"}}, "v19": {"annotation": {"coord": [10.371246093750003, -9.752515625000001]}, "data": {"type": "hadamard", "is_edge": "true"}}, "v21": {"annotation": {"coord": [2.6730292968750007, -9.4687265625]}, "data": {"type": "hadamard", "is_edge": "true"}}}, "undir_edges": {"e0": {"src": "v0", "tgt": "b0"}, "e1": {"src": "v0", "tgt": "v4"}, "e2": {"src": "v0", "tgt": "v8"}, "e3": {"src": "v0", "tgt": "v17"}, "e4": {"src": "v1", "tgt": "v17"}, "e5": {"src": "v1", "tgt": "v5"}, "e6": {"src": "v1", "tgt": "v8"}, "e7": {"src": "v1", "tgt": "v18"}, "e8": {"src": "b2", "tgt": "v18"}, "e9": {"src": "v2", "tgt": "v4"}, "e10": {"src": "v2", "tgt": "v9"}, "e11": {"src": "v2", "tgt": "v19"}, "e12": {"src": "v3", "tgt": "v19"}, "e13": {"src": "v2", "tgt": "v21"}, "e14": {"src": "b1", "tgt": "v21"}, "e15": {"src": "v3", "tgt": "b3"}, "e16": {"src": "v3", "tgt": "v5"}, "e17": {"src": "v3", "tgt": "v9"}, "e18": {"src": "v4", "tgt": "v10"}, "e19": {"src": "v5", "tgt": "v10"}, "e20": {"src": "v8", "tgt": "v10"}, "e21": {"src": "v9", "tgt": "v10"}, "e22": {"src": "v10", "tgt": "v6"}, "e23": {"src": "v6", "tgt": "v12"}, "e24": {"src": "v7", "tgt": "v13"}, "e25": {"src": "v7", "tgt": "v12"}, "e26": {"src": "v11", "tgt": "v13"}, "e27": {"src": "v11", "tgt": "v12"}, "e28": {"src": "v12", "tgt": "v16"}, "e29": {"src": "v14", "tgt": "v20"}, "e30": {"src": "v14", "tgt": "v15"}, "e31": {"src": "v14", "tgt": "v16"}}}':

teleport_reduce sometimes does not preserve semantics

Consider the code run on the following example circuit:

s = """OPENQASM 2.0;
include "qelib1.inc";
qreg q[3];
x q[2];
ccx q[0], q[2], q[1];
x q[2];
ccx q[2], q[1], q[0];
cx q[2], q[0];
x q[2];
ccx q[0], q[2], q[1];
ccx q[0], q[2], q[1];"""

c = zx.qasm(s)
g = c.to_graph()
c2 = zx.Circuit.from_graph(zx.simplify.teleport_reduce(g))
c.verify_equality(c2) # This should return True

This fails

Identifying Phase gadgets in PyZX graphs

Hi,

I was wondering if there any simple way to search for phase gadgets in PyZX quantum circuit/graph?
From my understanding the search and merge of phase gadgets acting on the same set of qubits is important part of circuit optimization with PyZX.
In particular the search and optimization of phase gadgets are very important for quantum chemistry circuits (e.g. Unitary Coupled Cluster ansatz as was discussed in Sec. 6 of https://arxiv.org/abs/1906.01734).

I found somewhat related functionality in pyx.rules in function match_phase_gadgets(...).
So I wrote a function to search for phase gadgets in PyZX graph g just following the code in match_phase_gadgets(...).

def get_gadgets(g):
    gadgets = {}
    for v in g.vertices():
        if g.vertex_degree(v) == 1 and v not in g.inputs and v not in g.outputs:
            n = list(g.neighbors(v))[0]
            gadgets[n] = v
    return gadgets

Next I consider a toy example of a 2 qubit circuit with a single gate Rzz gate: Rzz(theta) = e^{i theta/2 Z_1 Z_2}.
The Rzz gate is a two-qubit phase gadget by definition.
Then I construct an equivalent circuit expressed in terms of Cnot and Rz gates for Rzz(pi/4), convert the circuit to PyZX graph. When I search for phase gadgets none were found.

rzz_qasm_str = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
cx q[0], q[1];
rz(-1.1780972450961724) q[1];
rz(1.9634954084936207) q[1];
cx q[0], q[1];
"""
g = zx.sqasm(rzz_qasm_str, simplify=True)
get_gadgets(g)

image

Interestingly, when applying this function to a circuit from demo example (AllFeatures.ipynb), it finds phase gadgets.
image

The phase gadget is identified only if there is a vertex that has only one neighboring vertex with the value of the phase stored.

Could you please help me with this issue?
Is there any way to convert PyZX graph, such that the structure pf phase gadgets would be obvious and it will be easy to identify them?

Thanks!

[Circuit optimisation]: Run pyzx from the command-line without giving destination file.

Hi,
I tried to run the command python -m pyzx opt input_circuit.qasm in order to optimise a circuit but I am getting an error: TypeError: expected str, bytes or os.PathLike object, not Namespace.

I could post the traceback but in a few words the error is caused by splitext method in circ2circ.py file, line 64:
base = os.path.splitext(options)[0]

In python 3.6, splitext method uses os.fspath() method that takes a string, bytes or os.PathLike object and not a Namespace object (as the error indicates).

options is a Namespace object though.

When I changed the "base" variable's declaration to base = "output", in order to write the optimised circuit in output.qasm file, everything worked fine.

Another solution is to add the destination file when running the command:
python -m pyzx opt -d output_circuit.qasm input_circuit.qasm

Thank you.

zx.qasm does not recognize gates imported from qelib1.inc

qelib1.inc defines the controlled Hadamard gate ch, but zx.qasm does not recognize ch even when qelib1.inc is imported

import pyzx
g = zx.qasm('''
OPENQASM 2.0;
include "qelib1.inc";

qreg a[2];
ch a[0], a[1];
''')

TypeError Traceback (most recent call last)
in
6 qreg a[2];
7 ch a[0], a[1];
----> 8 ''')

/opt/conda/lib/python3.7/site-packages/pyzx/circuit/qasmparser.py in qasm(s)
188 """Parses a string representing a program in QASM, and outputs a Circuit."""
189 p = QASMParser()
--> 190 return p.parse(s, strict=False)
191

/opt/conda/lib/python3.7/site-packages/pyzx/circuit/qasmparser.py in parse(self, s, strict)
64 gates: List[Gate] = []
65 for c in commands:
---> 66 self.gates.extend(self.parse_command(c, self.registers))
67
68 circ = Circuit(self.qubit_count)

/opt/conda/lib/python3.7/site-packages/pyzx/circuit/qasmparser.py in parse_command(self, c, registers)
182 gates.append(g)
183 continue
--> 184 raise TypeError("Unknown gate name: {}".format(c))
185 return gates
186

TypeError: Unknown gate name: ch a[0], a[1]

qubit count and qubit number can be wrong after simplification

Seems that the simplifier sometimes messes up the qubit numbering. In particular sometimes the qubit index can be negative (which I think is a bug but maybe you want to allow it) and the qubit count is off. For example, the following obviously has three qubits but qubit count reports only 1.

(Also I think that both these methods should return ints not floats but opinions may differ)

file_name = "qgraphs/small.qgraph"
with open(file_name, 'r') as qgraph :
    js = qgraph.read()
    g3 = zx.json_to_graph(js)
    zx.simplify.clifford_simp(g3)
    display(zx.draw(g3,labels=True,h_edge_draw='box'))
    count = g3.qubit_count()
    index = g3.qubit(28)
    print("qubit count : " + str(count))
    print("qubit index of vertex 28 : " + str(index))

screen shot 2018-09-06 at 15 12 43

example.zip

Notebooks in the docs with nbsphinx

Hello PyZX people!

Just dropping by to let you know I've just added a back-and-forth interface between DisCoPy and PyZX.

Also, I've started using nbsphinx for including jupyter notebooks into readthedocs. It works so well that you can even interact with PyZX diagrams (drag and drop nodes) directly inside the docs: https://discopy.readthedocs.io/en/main/notebooks/new-features-0.3.3.html

That would surely be a great feature for PyZX's documentation. Let me know if you want some help in setting it up.

Cheers,
Alexis

CNOT Benchmark demo broken: `TypeError: Graph doesn't seem circuit like: multiple parents`

Running the CNOT Benchmark example under demos fails with

Circuit              qubits G-count 2-count G-NRSCM 2-NRSCM  G-Tpar 2-Tpar G-PyZX 2-PyZX Time-Simp Time-Opt
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-16-fa5aee888fdf> in <module>
      1 print("Circuit".ljust(20), "qubits", "G-count", "2-count", "G-NRSCM", "2-NRSCM", " G-Tpar", "2-Tpar", "G-PyZX", "2-PyZX", "Time-Simp", "Time-Opt")
      2 for c in fast_circuits:
----> 3     print(c.get_output())

<ipython-input-14-eb2f95ae4779> in get_output(self)
     54     def get_output(self):
     55         if not self.has_run:
---> 56             self.run()
     57         s = self.name.ljust(20) + str(self.qubits).rjust(7)
     58         s += " | " + str(self.gatecount).rjust(5) + str(self.cnotcount).rjust(6) + " | "

<ipython-input-14-eb2f95ae4779> in run(self)
     43         self.time_simpl = time.time() - t
     44         t = time.time()
---> 45         c = zx.Circuit.from_graph(g).split_phase_gates()
     46         c = zx.optimize.basic_optimization(c).to_basic_gates().split_phase_gates()
     47         self.circuit_opt = c

~/dev/proj/pyzx/pyzx/circuit.py in from_graph(g, split_phases)
     82                 neigh = [w for w in g.neighbours(v) if rs[w]<r]
     83                 if len(neigh) != 1:
---> 84                     raise TypeError("Graph doesn't seem circuit like: multiple parents")
     85                 n = neigh[0]
     86                 if qs[n] != q:

TypeError: Graph doesn't seem circuit like: multiple parents

Error importing pyzx on Windows when installation is on a different drive

Stack trace:

c:\miniconda\lib\site-packages\pyzx\__init__.py:19: in <module>
    from .graph.graph import Graph
c:\miniconda\lib\site-packages\pyzx\graph\__init__.py:27: in <module>
    from .graph import Graph
c:\miniconda\lib\site-packages\pyzx\graph\graph.py:19: in <module>
    from .base import BaseGraph
c:\miniconda\lib\site-packages\pyzx\graph\base.py:28: in <module>
    from ..utils import EdgeType, VertexType, toggle_edge, vertex_is_zx, toggle_vertex
c:\miniconda\lib\site-packages\pyzx\utils.py:95: in <module>
    relpath = os.path.relpath(settings.javascript_location, os.getcwd())
c:\miniconda\lib\ntpath.py:703: in relpath
    raise ValueError("path is on mount %r, start on mount %r" % (

ValueError: path is on mount 'c:', start on mount 'D:'

The pyzx package is installed on the C drive, but the program is being run from the D drive, so there is no relative path.

Would it be possible to use an absolute path instead?

Syntax error compiling `cnot_mapper.py`

  *** Error compiling '/tmp/pip-unpacked-wheel-kgj3yp34/pyzx/scripts/cnot_mapper.py'...
    File "/tmp/pip-unpacked-wheel-kgj3yp34/pyzx/scripts/cnot_mapper.py", line 31
      np = pass
              ^
  SyntaxError: invalid syntax

Probably should be np = None as elsewhere?

proposed Graph API changes

Two proposed changes to the API. (We can keep the old methods as aliases, but deprecate them if you like.)

  1. I've recently overloaded * to compose diagrams in fomula-order. I.e. g * h means g AFTER h. I suggest keeping an operator for composing in diagram order, but changing it from from + to >>.
  2. change auto_detect_inputs to auto_detect_io. I find current name confusing, because it does both.

Cannot parse quipper circuit with uncontrolled gates

I found my self needing to convert circuits that were generated within the quipper framework, to the qasm format.
The problem is that in those circuits, gates with no controls do not end with the "nocontrol" keyword, thus raising an exception.

Is this a real issue or are those circuits wrongly written?

ctrl-click and ctrl-drag on macOS

Hello! First of all, very nice package!
One issue I found on Mac OS is ctrl+click on a Mac is mapped to a right mouse click.
In the editor mode this results in boxes appearing when defining vertices and unconnected edges, that don't attach to vertices, see attached snapshots. Any idea of what is the problem here? I tried several browsers but still same problem :/ Thanks!

Screen Shot 2020-09-06 at 12 42 34 PM

Screen Shot 2020-09-06 at 12 43 03 PM

Screen Shot 2020-09-06 at 12 43 13 PM

CNOT_HAD_PHASE_circuit() argument differences

I wanted to give the "Getting Started" example in the documentation a try but I ran into the following problem:

TypeError: CNOT_HAD_PHASE_circuit() got an unexpected keyword argument 'depth'

Invoking CNOT_HAD_PHASE_circuit() without any arguments (just to tease what the arguments should be) gives the following:

TypeError: CNOT_HAD_PHASE_circuit() missing 4 required positional arguments: 'qubits', 'gates', 'p_had', and 'p_t'

Which is rather odd considering the PyZX source gives the following, expected definition:

def CNOT_HAD_PHASE_circuit(
        qubits: int, 
        depth: int, 
        p_had: float = 0.2, 
        p_t: float = 0.2, 
        clifford:bool=False
        ) -> Circuit:

The gates keyword argument seems to come from nowhere and I'm not sure if this is part of a larger problem or something with my installation.

I'm currently running Python 3.7.3 with PyZX installed through the recommended pip install pyzx in a Docker environment. I've also done the same installation locally on my Mac OS (just to make sure my Docker image wasn't the culprit.

type annotations for numpy objects

NumPy 1.20 added type annotations, which broke mypy. I've added dummy type: ignores in the relevant places, but at some point appropriate types for numpy have to be added.

qc_name, qasm_name

Hi, Why do some gates not have qc_name like "ZPhase" and "XPhase" and some like SWAP, CX are given "undefined" as names in those fields? I think swap is swap in qasm.

What is ZX-calculus

You could explain in the readme what "ZX calculus" actually is. Google search returns mainly papers, which is not the easiest way to explain things.
Just two or three sentences would be already an improvement :)

Un-extractable circuit in fast benchmarks

To reproduce:

c = zx.Circuit.load('../circuits/Fast/qcla_mod_7_before').to_basic_gates()
g = c.to_graph()
zx.simplify.full_reduce(g)
c1 = zx.extract.extract_circuit(g)

Gives Exception: No extractable vertex found. Something went wrong

All the other examples in circuits/Fast seem to work fine.

Submitting to JOSS?

I was wondering if you had considered submitting pyzx to the Journal of Open Source Software?

Pyzx strikes me as a mature software project and it would be good to recognize it at such, not just for your own benefit but also to encourage good open source development practices in the ACT community.

You already have an accompanying paper for the tool, so there would not be much to write up.

missing submodules

Several of the example notebooks require pyzx.examples which don't actually ship with the public version of the repo.

AttributeError: module 'pyzx' has no attribute 'examples'

question: circuit depth

Is there a way to get the depth of a circuit? I could use len(self.gates) from Circuit, but is there some gate parallelism being performed somewhere in the code?

Deprecate Python 3.6

The newest version of NumPy doesn't support 3.6. Tket also doesn't support 3.6. So it looks like it is starting to be time to deprecate 3.6 support. Are there any arguments against this?

Error when running T-count benchmark

Hi,
I'm trying to run benchmarking module in Jupyter notebook /demos/T-count Benchmark.ipynb.
After execution of corresponding line in Jupyter notebook
for c in fast_circuits: print(c.get_output())
I'm getting following error:
NameError: name 'ZPhase' is not defined.

If I comment out the line causing the error
c_opt = zx.Circuit.from_graph(g).split_phase_gates().to_basic_gates()
it works on a few circuits and then again throws a new error:
TypeError: Unknown gate XPhase(0,phase=1)

Please see screenshots below. Would appreciate any feedback.
Thanks!

Screen Shot 2020-01-18 at 3 08 00 PM

image

Screen Shot 2020-01-18 at 3 12 35 PM

image

image

Rows and columns flipped when loading JSON

Loading the attached qgraph file gives the rows and columns backwards, so the circuit reports its depth as 2.0.

file_name = "small.qgraph"
with open(file_name, 'r') as qgraph :
    js = qgraph.read()
    g3 = zx.json_to_graph(js)
    print(g3.depth())

Also, I think the depth should be an integer not a float.

small.qgraph.zip

Circuit extraction fails when it shouldn't

Title says it all. The attached zip file contains the "small.qgraph" referenced in the code. It's obviously a circuit. After running clifford_simp it's still obviously a circuit, but circuit_extract barfs.

file_name = "qgraphs/small.qgraph"
with open(file_name, 'r') as qgraph :
    js = qgraph.read()
    g3 = zx.json_to_graph(js)
    display(zx.draw(g3,labels=True,h_edge_draw='box'))    
    zx.simplify.clifford_simp(g3)
    display(zx.draw(g3,labels=True,h_edge_draw='box'))
    zx.circuit_extract(g3)

screen shot 2018-09-06 at 14 36 33

example.zip

Compression of Trotter decomposition circuits

Hi,

I'm trying to employ PyZX FullReduce pass to optimize quantum circuits corresponding to Trotterized evolution of a quantum system. The target circuit corresponds to a Trotterization of evolution operator of 1D transversal field Ising model with nearest neighbor interactions. The circuit was build using QiskitAqua (PauliTrotterEvolution).

I'm observing a somewhat strange behavior of PyZX FullReduce pass. For small number of qubits (N=4) PyZX was able to reduce CX count substantially (~20%). However, when increasing number of qubits in the target circuit (# of spins in TFIM, N=16), PyZX increases final CX count. It inserts dense blocks of CZ gates and it looks like that the CZ overhead grows with the number of qubits.

Is this behavior expected? Is there any way to (maybe by using a specific combination of PyZX passes) to overcome this problem?

I'm attaching screenshots of fragments of the circuit before/after PyZX FullReduce.
I'm also attaching QASM files before/after optimization.

Thanks!

Before:
Screen Shot 2020-12-25 at 11 45 51 AM

After:
Screen Shot 2020-12-25 at 11 42 58 AM

CX count before 1200
CZ count after 3666
CX count after 180

trotter_16q.zip

Simplification does not preserve 'circuit-like-ness'

This may be related to #10; at least the symptom sounds the same.

Running the sequence of commands in the "Getting Started" document leads (usually) to an error after running Circuit.from_graph after clifford_simp, with the message "Graph doesn't seem circuit like: multiple parents".

A simple example that demonstrates the issue is:

def bad_graph():
    g = zx.Graph()
    g.add_vertices(8)
    g.set_type(2,1)
    g.set_type(3,1)
    g.set_type(4,2)
    g.set_type(5,1)
    g.set_position(0,0,0)
    g.set_position(1,1,0)
    g.set_position(2,0,1)
    g.set_position(3,1,1)
    g.set_position(4,0,2)
    g.set_position(5,1,2)
    g.set_position(6,0,3)
    g.set_position(7,1,3)
    g.add_edges([(0,2),(2,4),(4,6),(1,3),(3,5),(5,7),(4,5)])
    g.inputs.extend([0,1])
    g.outputs.extend([6,7])
    return g

g = bad_graph()
zx.simplify.clifford_simp(g)
Circuit.from_graph(g)

In fact, just doing spider_simp (the first step in clifford_simp) is enough to cause the error.

The problem is that the two green spiders on qubit 1 get merged to a position to the left of the (previously) red spider to which they remain connected.

I am not sure what the actual bug is or even if this is supposed to work -- but in any case the example code is not working.

Circuit simplification on a predefined circuit topology

Hi PyZX developers!

I have a following question: how to run a circuit optimization (e.g. zx.simplify.full_reduce(g)) for a circuit on a predefined hardware topology?

In order to perform the mapping to hardware topology I tried to use a function map_cnot_circuit(..) (located In pyzx.scripts.cnot_mapper) together with an architecture created using pyzx.routing.architecture.create_architecture.

I got a very strange result (the output circuit is either empty or only contains a few CNOTs) and a bunch of warning.

Could you guys please help me?

image

request: applicable rules for given node index

I found in rules.py a collection of methods for applying simplifications. How difficult would it be to have a method like applicable_rule(node_id) that returns a list of possible rule applications?

Further issues with automatic rewriting

There seems to be continued problems with the application of rewrite rules, though it is less clear exactly what rules are causing problems as I can't replicate the issue in the editor - they emerge from hsimplify. Specifically I find if I apply zx.hsimplify.hpivot_simp(circle, quiet=True) to the graph derived from the json below I arrive at a very different matrix, which shouldn't be possible. I am also of the opinion that something is off with the scalars but I can't quite tell if this is related to the errors in the transformations.

image

circle_json = '{"wire_vertices": {"b0": {"annotation": {"boundary": true, "coord": [10.37171875, -1.8646875], "input": false, "output": true}}, "b1": {"annotation": {"boundary": true, "coord": [11.04171875, -4.8796875], "input": false, "output": true}}, "b2": {"annotation": {"boundary": true, "coord": [-0.23828125, -1.2296874999999998], "input": true, "output": false}}, "b3": {"annotation": {"boundary": true, "coord": [-0.23828125, -4.7796875], "input": true, "output": false}}}, "node_vertices": {"v0": {"annotation": {"coord": [1.45171875, -0.12468749999999984]}, "data": {"type": "X"}}, "v1": {"annotation": {"coord": [1.51171875, -1.7046875]}, "data": {"type": "Z"}}, "v2": {"annotation": {"coord": [1.53671875, -4.6546875]}, "data": {"type": "Z"}}, "v3": {"annotation": {"coord": [1.6867187499999998, -3.0046875]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v4": {"annotation": {"coord": [3.0267187499999997, -0.024687500000000195]}, "data": {"type": "X"}}, "v5": {"annotation": {"coord": [2.50171875, -0.7246874999999999]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v6": {"annotation": {"coord": [2.42671875, -4.8496875]}, "data": {"type": "Z"}}, "v7": {"annotation": {"coord": [3.80171875, -4.7496875]}, "data": {"type": "Z"}}, "v8": {"annotation": {"coord": [2.17671875, -1.9996874999999998]}, "data": {"type": "Z"}}, "v9": {"annotation": {"coord": [3.22671875, -2.0246874999999998]}, "data": {"type": "Z"}}, "v10": {"annotation": {"coord": [2.7017187500000004, -2.049687499999999]}, "data": {"type": "X"}}, "v11": {"annotation": {"coord": [3.00171875, -4.8246875]}, "data": {"type": "X"}}, "v12": {"annotation": {"coord": [3.85171875, -0.09968749999999993]}, "data": {"type": "Z"}}, "v13": {"annotation": {"coord": [5.50171875, -0.024687500000000195]}, "data": {"type": "Z"}}, "v14": {"annotation": {"coord": [5.461718750000001, -1.9496875]}, "data": {"type": "Z"}}, "v15": {"annotation": {"coord": [4.83671875, -4.8746875]}, "data": {"type": "Z"}}, "v16": {"annotation": {"coord": [6.421718749999999, -1.9346875]}, "data": {"type": "Z"}}, "v17": {"annotation": {"coord": [6.356718749999999, -5.0996875]}, "data": {"type": "Z"}}, "v18": {"annotation": {"coord": [4.51171875, -2.049687499999999]}, "data": {"type": "X", "value": "\\pi"}}, "v19": {"annotation": {"coord": [5.956718750000001, -2.0096875]}, "data": {"type": "X", "value": "\\pi"}}, "v20": {"annotation": {"coord": [4.34671875, -5.0046875]}, "data": {"type": "X", "value": "\\pi"}}, "v21": {"annotation": {"coord": [5.611718749999999, -4.9496875]}, "data": {"type": "X", "value": "\\pi"}}, "v22": {"annotation": {"coord": [5.73171875, -3.6996874999999996]}, "data": {"type": "Z"}}, "v23": {"annotation": {"coord": [6.956718750000001, -3.4346875]}, "data": {"type": "Z"}}, "v24": {"annotation": {"coord": [5.26171875, -3.2996875]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v25": {"annotation": {"coord": [6.28171875, -3.3346875000000002]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v26": {"annotation": {"coord": [0.88671875, -1.3546874999999998]}, "data": {"type": "X", "value": "\\pi"}}, "v27": {"annotation": {"coord": [0.81171875, -4.7546875]}, "data": {"type": "X", "value": "\\pi"}}, "v28": {"annotation": {"coord": [2.87671875, -1.0496875]}, "data": {"type": "hadamard", "is_edge": "false", "value": "3\\pi/2"}}, "v29": {"annotation": {"coord": [3.4017187499999997, -0.9646875000000001]}, "data": {"type": "hadamard", "is_edge": "false", "value": "3\\pi/2"}}, "v30": {"annotation": {"coord": [3.87671875, -0.9996874999999998]}, "data": {"type": "hadamard", "is_edge": "false", "value": "3\\pi/2"}}, "v31": {"annotation": {"coord": [2.5767187500000004, -3.9496874999999996]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi/2"}}, "v32": {"annotation": {"coord": [3.1517187499999997, -3.6746875]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi/2"}}, "v33": {"annotation": {"coord": [3.87671875, -3.7496875000000003]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi/2"}}, "v34": {"annotation": {"coord": [3.05171875, -1.5496875]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v35": {"annotation": {"coord": [3.12671875, -4.2496875]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v36": {"annotation": {"coord": [2.03671875, -4.7046875]}, "data": {"type": "X", "value": "\\pi"}}, "v37": {"annotation": {"coord": [2.08671875, -1.6546875]}, "data": {"type": "X", "value": "\\pi"}}, "v38": {"annotation": {"coord": [6.989375, -1.754375]}, "data": {"type": "X", "value": "\\pi"}}, "v39": {"annotation": {"coord": [8.269375, -1.5743749999999999]}, "data": {"type": "X", "value": "\\pi"}}, "v40": {"annotation": {"coord": [7.789375, -5.374375]}, "data": {"type": "X", "value": "\\pi"}}, "v41": {"annotation": {"coord": [8.969375, -5.354375]}, "data": {"type": "X", "value": "\\pi"}}, "v42": {"annotation": {"coord": [9.689375, -5.194375]}, "data": {"type": "X"}}, "v43": {"annotation": {"coord": [7.089375, -5.234375]}, "data": {"type": "Z"}}, "v44": {"annotation": {"coord": [8.309375, -5.394375]}, "data": {"type": "Z"}}, "v45": {"annotation": {"coord": [8.969375, -1.854375]}, "data": {"type": "Z"}}, "v46": {"annotation": {"coord": [7.649374999999999, -1.5543749999999998]}, "data": {"type": "Z"}}, "v47": {"annotation": {"coord": [9.029375, 0.18562499999999993]}, "data": {"type": "Z"}}, "v48": {"annotation": {"coord": [8.449375, 0.34562500000000007]}, "data": {"type": "X"}}, "v49": {"annotation": {"coord": [7.349375, 0.6456249999999999]}, "data": {"type": "X"}}, "v50": {"annotation": {"coord": [6.669375, 0.6256250000000001]}, "data": {"type": "X"}}, "v51": {"annotation": {"coord": [5.909375, 0.8656250000000001]}, "data": {"type": "X"}}, "v52": {"annotation": {"coord": [4.549375, 0.22562499999999996]}, "data": {"type": "Z"}}, "v53": {"annotation": {"coord": [7.809374999999999, -3.6743750000000004]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v54": {"annotation": {"coord": [8.569375, -3.5943750000000003]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v55": {"annotation": {"coord": [7.149374999999999, -0.6543749999999999]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v56": {"annotation": {"coord": [7.909375000000001, -0.5543749999999998]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}}, "undir_edges": {"e0": {"src": "v0", "tgt": "v4"}, "e1": {"src": "v1", "tgt": "v3"}, "e2": {"src": "v1", "tgt": "v26"}, "e3": {"src": "v1", "tgt": "v37"}, "e4": {"src": "v2", "tgt": "v3"}, "e5": {"src": "v2", "tgt": "v27"}, "e6": {"src": "v2", "tgt": "v36"}, "e7": {"src": "v3", "tgt": "v5"}, "e8": {"src": "v4", "tgt": "v5"}, "e9": {"src": "v4", "tgt": "v12"}, "e10": {"src": "v6", "tgt": "v11"}, "e11": {"src": "v6", "tgt": "v31"}, "e12": {"src": "v6", "tgt": "v36"}, "e13": {"src": "v7", "tgt": "v11"}, "e14": {"src": "v7", "tgt": "v20"}, "e15": {"src": "v7", "tgt": "v33"}, "e16": {"src": "v8", "tgt": "v10"}, "e17": {"src": "v8", "tgt": "v28"}, "e18": {"src": "v8", "tgt": "v37"}, "e19": {"src": "v9", "tgt": "v10"}, "e20": {"src": "v9", "tgt": "v30"}, "e21": {"src": "v9", "tgt": "v18"}, "e22": {"src": "v10", "tgt": "v34"}, "e23": {"src": "v11", "tgt": "v35"}, "e24": {"src": "v12", "tgt": "v28"}, "e25": {"src": "v12", "tgt": "v29"}, "e26": {"src": "v12", "tgt": "v30"}, "e27": {"src": "v12", "tgt": "v52"}, "e28": {"src": "v13", "tgt": "v24"}, "e29": {"src": "v13", "tgt": "v25"}, "e30": {"src": "v13", "tgt": "v31"}, "e31": {"src": "v13", "tgt": "v32"}, "e32": {"src": "v13", "tgt": "v33"}, "e33": {"src": "v13", "tgt": "v52"}, "e34": {"src": "v14", "tgt": "v24"}, "e35": {"src": "v14", "tgt": "v18"}, "e36": {"src": "v14", "tgt": "v19"}, "e37": {"src": "v15", "tgt": "v24"}, "e38": {"src": "v15", "tgt": "v20"}, "e39": {"src": "v15", "tgt": "v21"}, "e40": {"src": "v16", "tgt": "v25"}, "e41": {"src": "v16", "tgt": "v19"}, "e42": {"src": "v16", "tgt": "v38"}, "e43": {"src": "v17", "tgt": "v25"}, "e44": {"src": "v17", "tgt": "v21"}, "e45": {"src": "v17", "tgt": "v43"}, "e46": {"src": "v22", "tgt": "v24"}, "e47": {"src": "v23", "tgt": "v25"}, "e48": {"src": "v26", "tgt": "b2"}, "e49": {"src": "v27", "tgt": "b3"}, "e50": {"src": "v29", "tgt": "v34"}, "e51": {"src": "v32", "tgt": "v35"}, "e52": {"src": "v38", "tgt": "v46"}, "e53": {"src": "v39", "tgt": "v46"}, "e54": {"src": "v39", "tgt": "v45"}, "e55": {"src": "v40", "tgt": "v44"}, "e56": {"src": "v40", "tgt": "v43"}, "e57": {"src": "v41", "tgt": "v42"}, "e58": {"src": "v41", "tgt": "v44"}, "e59": {"src": "v42", "tgt": "b1"}, "e60": {"src": "v42", "tgt": "v47"}, "e61": {"src": "v43", "tgt": "v53"}, "e62": {"src": "v44", "tgt": "v54"}, "e63": {"src": "v45", "tgt": "b0"}, "e64": {"src": "v45", "tgt": "v54"}, "e65": {"src": "v46", "tgt": "v53"}, "e66": {"src": "v47", "tgt": "v48"}, "e67": {"src": "v48", "tgt": "v49"}, "e68": {"src": "v48", "tgt": "v56"}, "e69": {"src": "v49", "tgt": "v50"}, "e70": {"src": "v49", "tgt": "v55"}, "e71": {"src": "v50", "tgt": "v52"}, "e72": {"src": "v50", "tgt": "v51"}, "e73": {"src": "v53", "tgt": "v55"}, "e74": {"src": "v54", "tgt": "v56"}}}'

Question about Quipper parser

Hi, I'm wondering what the factor of 2 is for on line 64 of pyzx/circuit/quipperparser.py. Is this due to a difference between quipper & PyZX's representation of phase gates?

For example, I expected the first rotation in QFT8 to be translated as -PI/8, but the produced translation is -PI/4. Am I interpreting the gates wrong? I just want to make sure I'm translating files correctly. Thanks!

Require Python >= 3.6

So far there is no official minimum Python version that PyZX requires. I suggest making it Python 3.6 for the following reasons:

I think PyZX is already incompatible with Python 2 so it would be good to make the dropping of that support explicit.

Python 3.4 is already retired, so the earliest release that would make sense is 3.5. However, 3.6 offers more support in terms of typing hints. I'm considering adding these kinds of type hints over the coming period and it would then be useful to have the features of 3.6.

Are there any other considerations I'm missing regarding which version of Python to support?

Mixed British and American spelling

Hi, the mixed spelling of functions makes it hard to remember, for instance, normalise(), but optimize(). In the document also, optimization has spelled in both ways, so search does not find all matches.

`AllFeatures.ipynb` text mismatch

If I run pip install pyzx followed by running the notebook, I receive various errors, presumably because the pip version is out of sync with the source, as per #42.

When I install from source and run the notebook, there are a few sections where the text doesn't accurately describe what's going on.

  1. Text under cell 20 says: "This method combines all T-like phases with a phaseless spider to turn it into a phase gadget." However, it looks like there are still 5 spiders that haven't been gadgetized.
  2. Text under cell 21 says: "That has reduced the T-count from 22 down to 18!" except when I run the cell, the T-count is 8.
  3. Text under cell 22 says: "We do one final round of clifford_simp to make the graph a bit smaller." except it doesn't actually make it any smaller.
  4. In "Extracting and Optimizing Circuits" it says: "For extracting circuits out of ZX-graphs there is only a single method in PyZX that you have to call: zx.extract.streaming_extract", but we never actually call that method.

ValueError: Parallel edges between H-boxes are not supported

I get the following error:

ValueError: Parallel edges between H-boxes are not supported

when trying to ZH simplify (' zx.hsimplify.hpivot_simp(spin, quiet=False) ') the following diagram encoded in JSON below:

spinjson = '{"wire_vertices": {"b0": {"annotation": {"boundary": true, "coord": [1.9, -0.7999999999999998], "input": true, "output": false}}, "b1": {"annotation": {"boundary": true, "coord": [2.25, -9.2], "input": true, "output": false}}, "b2": {"annotation": {"boundary": true, "coord": [21.9, -0.6000000000000001], "input": false, "output": true}}, "b3": {"annotation": {"boundary": true, "coord": [22.1, -10.9], "input": false, "output": true}}}, "node_vertices": {"v0": {"annotation": {"coord": [6.861683593750001, -0.8505781250000002]}, "data": {"type": "Z"}}, "v1": {"annotation": {"coord": [17.22468359375, -0.8204531250000002]}, "data": {"type": "Z"}}, "v2": {"annotation": {"coord": [5.988058593750001, -9.315703125]}, "data": {"type": "Z"}}, "v3": {"annotation": {"coord": [17.646433593750004, -9.767578125000002]}, "data": {"type": "Z"}}, "v4": {"annotation": {"coord": [8.187183593750001, -4.857203125000001]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v5": {"annotation": {"coord": [14.664058593750003, -4.827078125000001]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v6": {"annotation": {"coord": [18.941808593750004, -0.8505781250000002]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v7": {"annotation": {"coord": [4.029933593750001, -9.315703125]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v8": {"annotation": {"coord": [11.410558593750002, -1.8748281250000005]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v9": {"annotation": {"coord": [11.711808593750002, -7.387703125000002]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v10": {"annotation": {"coord": [11.832308593750001, -5.128328125000001]}, "data": {"type": "Z"}}, "v11": {"annotation": {"coord": [11.832308593750001, -9.225328125]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v12": {"annotation": {"coord": [11.320183593750002, -1.0614531250000003]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v13": {"annotation": {"coord": [6.379683593750001, -4.947578125000001]}, "data": {"type": "X", "value": "\\pi"}}, "v14": {"annotation": {"coord": [3.0358085937500006, -3.6823281250000006]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi/6"}}, "v15": {"annotation": {"coord": [3.1864335937500003, -5.459703125000001]}, "data": {"type": "hadamard", "is_edge": "false", "value": "11\\pi/6"}}, "v16": {"annotation": {"coord": [4.240808593750001, -4.3752031250000005]}, "data": {"type": "Z", "value": "\\pi/2"}}, "v17": {"annotation": {"coord": [1.9814335937500003, -4.525828125000001]}, "data": {"type": "X", "value": "\\pi"}}, "v18": {"annotation": {"coord": [4.542058593750001, -6.393578125000001]}, "data": {"type": "hadamard", "is_edge": "false", "value": "\\pi"}}, "v19": {"annotation": {"coord": [5.295183593750001, -8.201078125]}, "data": {"type": "Z"}}, "v20": {"annotation": {"coord": [4.240808593750001, -8.321578125000002]}, "data": {"type": "Z"}}, "v21": {"annotation": {"coord": [5.415683593750001, -6.092328125000002]}, "data": {"type": "X"}}}, "undir_edges": {"e0": {"src": "b0", "tgt": "v0"}, "e1": {"src": "b1", "tgt": "v7"}, "e2": {"src": "b2", "tgt": "v6"}, "e3": {"src": "b3", "tgt": "v3"}, "e4": {"src": "v0", "tgt": "v4"}, "e5": {"src": "v0", "tgt": "v8"}, "e6": {"src": "v0", "tgt": "v12"}, "e7": {"src": "v1", "tgt": "v6"}, "e8": {"src": "v1", "tgt": "v5"}, "e9": {"src": "v1", "tgt": "v8"}, "e10": {"src": "v1", "tgt": "v12"}, "e11": {"src": "v2", "tgt": "v7"}, "e12": {"src": "v2", "tgt": "v4"}, "e13": {"src": "v2", "tgt": "v9"}, "e14": {"src": "v2", "tgt": "v11"}, "e15": {"src": "v3", "tgt": "v5"}, "e16": {"src": "v3", "tgt": "v9"}, "e17": {"src": "v3", "tgt": "v11"}, "e18": {"src": "v10", "tgt": "v5"}, "e19": {"src": "v10", "tgt": "v4"}, "e20": {"src": "v10", "tgt": "v8"}, "e21": {"src": "v10", "tgt": "v9"}, "e22": {"src": "v10", "tgt": "v13"}, "e23": {"src": "v14", "tgt": "v17"}, "e24": {"src": "v14", "tgt": "v16"}, "e25": {"src": "v15", "tgt": "v17"}, "e26": {"src": "v15", "tgt": "v16"}, "e27": {"src": "v16", "tgt": "v13"}, "e28": {"src": "v16", "tgt": "v21"}, "e29": {"src": "v18", "tgt": "v20"}, "e30": {"src": "v18", "tgt": "v19"}, "e31": {"src": "v18", "tgt": "v21"}}}'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.