Skip to content
Permalink
Browse files

Added loads of tests. They currently all pass.

  • Loading branch information...
jvdwetering committed Jul 23, 2018
1 parent 89b102f commit fb8908ae157dab6be9cdd6ad0d37b0ab4c918297

Large diffs are not rendered by default.

Oops, something went wrong.

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -42,8 +42,7 @@
{
"data": {
"text/plain": [
"[ 1 1 ]\n",
"[ 0 0 ]"
"True"
]
},
"execution_count": 3,
@@ -52,35 +51,57 @@
}
],
"source": [
"m1 * m2"
"result = Mat2([[1,1],[0,0]])\n",
"m1 * m2 == result"
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[ 1 0 0 0 0 ]\n",
"[ 0 1 0 0 1 ]\n",
"[ 0 0 1 0 1 ]\n",
"[ 1 0 1 0 1 ]\n",
"[ 0 0 0 1 1 ]\n",
"[ 0 0 0 0 0 ]"
"[ 0 0 0 0 1 ]"
]
},
"execution_count": 4,
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"m3c = m3.copy()\n",
"m3c.gauss(full_reduce=True)\n",
"m3c.data[2][0] = 1\n",
"m3c.data[4][4] = 1\n",
"m3c"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"AAAHHH\n"
]
}
],
"source": [
"for i in range(m3c.rows()):\n",
" for j in range(min(m3c.cols(),i)):\n",
" if m3c.data[i][j]: print(\"AAAHHH\")"
]
},
{
"cell_type": "code",
"execution_count": 5,

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -1,3 +1,20 @@
# PyZX - Python library for quantum circuit rewriting
# and optimisation using the ZX-calculus
# Copyright (C) 2018 - Aleks Kissinger and John van de Wetering

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

from fractions import Fraction

from .graph import Graph
@@ -8,9 +25,15 @@ class Circuit(object):
This is just a wrapper for a list of gates with methods for interconverting
between different representations of a quantum circuit."""
def __init__(self, qubit_amount):
self.q = qubit_amount
self.qubits = qubit_amount
self.gates = []

def __str__(self):
return "Circuit({!s} qubits, {!s} gates)".format(self.qubits,len(self.gates))

def __repr__(self):
return str(self)

@staticmethod
def from_graph(g):
"""Produces a :class:`Circuit` containing the gates of the given ZX-graph.
@@ -138,7 +161,7 @@ def to_graph(self, backend=None):
g = Graph(backend)
qs = []
r = 0
for i in range(self.q):
for i in range(self.qubits):
v = g.add_vertex(0,i,r)
g.inputs.append(v)
qs.append(v)
@@ -149,7 +172,7 @@ def to_graph(self, backend=None):
gate.to_graph(g,qs,r)
r += 1

for o in range(self.q):
for o in range(self.qubits):
v = g.add_vertex(0,o,r)
g.outputs.append(v)
g.add_edge((qs[o],v))
@@ -158,12 +181,15 @@ def to_graph(self, backend=None):

def to_quipper(self):
"""Produces a Quipper ASCII description of the circuit."""
s = "Inputs: " + ", ".join("{!s}Qbit".format(i) for i in range(self.q)) + "\n"
s = "Inputs: " + ", ".join("{!s}Qbit".format(i) for i in range(self.qubits)) + "\n"
for g in self.gates:
s += g.to_quipper() + "\n"
s += "Outputs: " + ", ".join("{!s}Qbit".format(i) for i in range(self.q))
s += "Outputs: " + ", ".join("{!s}Qbit".format(i) for i in range(self.qubits))
return s

def to_tensor(self):
return self.to_graph().to_tensor()


class Gate(object):
"""Base class for representing quantum gates."""
@@ -183,6 +209,14 @@ def __str__(self):
def __repr__(self):
return str(self)

def __eq__(self, other):
for a in ["target","control","phase","adjoint"]:
if hasattr(self,a):
if not hasattr(other,a): return False
if getattr(self,a) != getattr(other,a): return False
elif hasattr(other,a): return False
return True

def to_quipper(self):
n = self.name if not hasattr(self, "quippername") else self.quippername
s = 'QGate["{}"]{}({!s})'.format(n,("*" if (hasattr(self,"adjoint") and self.adjoint) else ""),self.target)
@@ -15,14 +15,28 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

__all__ = ['cnots','zigzag', 'zigzag2','cliffords', 'cliffordT']
__all__ = ['cnots','zigzag', 'zigzag2','cliffords', 'cliffordT', 'identity']

import random
from fractions import Fraction

from .graph.graph import Graph


def identity(qubits, depth=1,backend=None):
"""Generates an identity circuit on a given amount of qubits.
``depth`` specifies at which row the outputs should be placed."""
g = Graph(backend)
for i in range(qubits):
v = g.add_vertex(0,i,0)
w = g.add_vertex(0,i,depth)
g.inputs.append(v)
g.outputs.append(w)
g.add_edge((v,w))

return g


def cnots(qubits, depth, backend=None):
"""Generates a circuit consisting of randomly placed CNOT gates.
@@ -17,6 +17,8 @@

import abc

from pyzx.tensor import tensorfy

class DocstringMeta(abc.ABCMeta):
"""Metaclass that allows docstring 'inheritance'"""

@@ -167,6 +169,9 @@ def compose(self, other):
d = self.depth()
self.replace_subgraph(d-1,d,other)

def to_tensor(self):
return tensorfy(self)

def vindex(self):
"""The index given to the next vertex added to the graph. It should always
be equal to max(g.vertices()) + 1."""
@@ -32,6 +32,10 @@ def __init__(self, data):
def __mul__(self, m):
return Mat2([[sum(self.data[i][k] * m.data[k][j] for k in range(len(m.data))) % 2
for j in range(len(m.data[0]))] for i in range(len(self.data))])
def __eq__(self, other):
if not isinstance(other, Mat2): return False
if self.rows() != other.rows() or self.cols() != other.cols(): return False
return all(self.data[i][j] == other.data[i][j] for i in range(len(self.data)) for j in range(len(self.data[i])))
def __str__(self):
return "\n".join("[ " +
" ".join(str(value) for value in row) +
@@ -25,7 +25,7 @@
except ImportError:
pass

__all__ = ['bialg_simp','spider_simp', 'phase_free_simp', 'pivot_simp',
__all__ = ['bialg_simp','spider_simp', 'id_simp', 'phase_free_simp', 'pivot_simp',
'lcomp_simp', 'clifford_simp', 't_count', 'to_gh', 'to_rg']

from .rules import *
@@ -93,7 +93,7 @@ def clifford_simp(g, quiet=False):
id_simp(g, quiet=quiet)
spider_simp(g, quiet=quiet)

def to_gh(g):
def to_gh(g,quiet=True):
"""Turns every red node into a green node by changing regular edges into hadamard edges"""
ty = g.types()
for v in g.vertices():
@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

__all__ = ['tensorfy', 'compare_tensors']
__all__ = ['tensorfy', 'compare_tensors', 'compose_tensors', 'adjoint']

try:
import numpy as np
@@ -40,12 +40,6 @@ def X_to_tensor(arity, phase):
m[i] -= np.exp(1j*phase)
return np.power(np.sqrt(0.5),arity)*m.reshape([2]*arity)

#S = Z_to_tensor(2,0.5*np.pi)
#Xphase = X_to_tensor(2,0.5*np.pi)

#had = np.sqrt(2)*np.exp(-1j*0.25*np.pi) * (S @ Xphase @ S)
#print(had)


def pop_and_shift(verts, indices):
res = []
@@ -60,8 +54,6 @@ def pop_and_shift(verts, indices):
indices[w] = l2
return res


#TODO: Currently id@id and SWAP produce the same tensor.
def tensorfy(g):
"""Takes in a Graph and outputs a multidimensional numpy array
representing the linear map the ZX-diagram implements.
@@ -130,6 +122,14 @@ def tensorfy(g):


def compare_tensors(t1,t2):
"""Returns true if ``t1`` and ``t2`` are tensors equal up to a nonzero number.
Example: To check whether two ZX-graphs are semantically the same you would do::
t1 = tensorfy(g1)
t2 = tensorfy(g2)
compare_tensors(t1,t2)
"""
epsilon = 10**-14
if np.allclose(t1,t2): return True
for i,a in enumerate(t1.flat):
@@ -138,4 +138,47 @@ def compare_tensors(t1,t2):
break
else:
raise ValueError("Tensor is too close to zero")
return np.allclose(t1/a,t2/t2.flat[i])
return np.allclose(t1/a,t2/t2.flat[i])


def compose_tensors(t1,t2):
"""Returns a tensor that is the result of composing the tensors together as if they
were representing circuits::
t1 = tensorfy(circ1)
t2 = tensorfy(circ2)
circ1.compose(circ2)
t3 = tensorfy(circ1)
t4 = compose_tensors(t1,t2)
compare_tensors(t3,t4) # This is True
"""

if len(t1.shape) != len(t2.shape):
raise TypeError("Tensors represent circuits of different amount of qubits, "
"{!s} vs {!s}".format(len(t1.shape)//2,len(t2.shape)//2))
q = len(t1.shape)//2
contr1 = [2*i+1 for i in range(q)]
contr2 = [2*i for i in range(q)]
t = np.tensordot(t1,t2,axes=(contr1,contr2))
transp = []
for i in range(q):
transp.append(i)
transp.append(q+i)
return np.transpose(t,transp)


def adjoint(t):
"""Returns the adjoint of the tensor as if it were representing a circuit::
t = tensorfy(circ)
tadj = tensorfy(circ.adjoint())
compare_tensors(adjoint(t),tadj) # This is True
"""
q = len(t.shape)//2
transp = []
for i in range(q):
transp.append(2*i+1)
transp.append(2*i)
return np.transpose(t.conjugate(),transp)
@@ -0,0 +1,2 @@
python -m unittest discover -s "tests" -t "."
pause
@@ -0,0 +1,2 @@
#!/bin/bash
python -m unittest discover -s "tests" -t "."
@@ -0,0 +1,29 @@
# PyZX - Python library for quantum circuit rewriting
# and optimisation using the ZX-calculus
# Copyright (C) 2018 - Aleks Kissinger and John van de Wetering

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.


if __name__ == '__main__':
import unittest
import sys
sys.path.append('..')
sys.path.append('.')
loader = unittest.TestLoader()
start_dir = '.'
suite = loader.discover(start_dir)

runner = unittest.TextTestRunner()
runner.run(suite)
Oops, something went wrong.

0 comments on commit fb8908a

Please sign in to comment.
You can’t perform that action at this time.