Skip to content

Commit

Permalink
Merge branch 'master' into inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
akissinger committed Jul 11, 2021
2 parents dc33729 + ce77f01 commit 478f9bf
Show file tree
Hide file tree
Showing 17 changed files with 10,625 additions and 2,396 deletions.
2 changes: 1 addition & 1 deletion demos/AllFeatures.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -9086,7 +9086,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
"version": "3.8.5"
}
},
"nbformat": 4,
Expand Down
44 changes: 24 additions & 20 deletions demos/LocalSearch.ipynb

Large diffs are not rendered by default.

3,523 changes: 1,783 additions & 1,740 deletions demos/gettingstarted.ipynb

Large diffs are not rendered by default.

94 changes: 71 additions & 23 deletions pyzx/circuit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import numpy as np

from .gates import Gate, gate_types, ZPhase, XPhase, CZ, CX, CNOT, HAD
from .gates import Gate, gate_types, ZPhase, XPhase, CZ, CX, CNOT, HAD, SWAP, CCZ, Tofolli

from ..graph.base import BaseGraph

Expand All @@ -37,7 +37,7 @@ class Circuit(object):
between different representations of a quantum circuit.
The methods in this class that convert a specification of a circuit into an instance of this class,
generally do not check whether the specification is well-defined. If a bad input is given,
generally do not check whether the specification is well-defined. If a bad input is given,
the behaviour is undefined."""
def __init__(self, qubit_amount: int, name: str = '') -> None:
self.qubits: int = qubit_amount
Expand Down Expand Up @@ -69,12 +69,12 @@ def adjoint(self) -> 'Circuit':
def verify_equality(self, other: 'Circuit', up_to_swaps: bool = False) -> bool:
"""Composes the other circuit with the adjoint of this circuit, and tries to reduce
it to the identity using :func:`simplify.full_reduce``. If successful returns True,
if not returns None.
if not returns None.
Note:
A successful reduction to the identity is strong evidence that the two
circuits are equal, if this function is not able to reduce the graph to the identity
this does not prove anything.
this does not prove anything.
Args:
other: the circuit to compare equality to.
Expand All @@ -95,12 +95,12 @@ def verify_equality(self, other: 'Circuit', up_to_swaps: bool = False) -> bool:
return False

def add_gate(self, gate: Union[Gate,str], *args, **kwargs) -> None:
"""Adds a gate to the circuit. ``gate`` can either be
"""Adds a gate to the circuit. ``gate`` can either be
an instance of a :class:`Gate`, or it can be the name of a gate,
in which case additional arguments should be given.
Example::
circuit.add_gate("CNOT", 1, 4) # adds a CNOT gate with control 1 and target 4
circuit.add_gate("ZPhase", 2, phase=Fraction(3,4)) # Adds a ZPhase gate on qubit 2 with phase 3/4
"""
Expand Down Expand Up @@ -132,15 +132,15 @@ def add_circuit(self, circ: 'Circuit', mask: Optional[List[int]]=None) -> None:
"""Adds the gate of another circuit to this one. If ``mask`` is not given,
then they must have the same amount of qubits and they are mapped one-to-one.
If mask is given then it must be a list specifying to which qubits the qubits
in the given circuit correspond.
in the given circuit correspond.
Example::
c1 = Circuit(qubit_amount=4)
c2 = Circuit(qubit_amount=2)
c2.add_gate("CNOT",0,1)
c1.add_circuit(c2, mask=[0,3]) # Now c1 has a CNOT from the first to the last qubit
If the circuits have the same amount of qubits then it can also be called as an operator::
c1 = Circuit(2)
Expand Down Expand Up @@ -343,7 +343,7 @@ def from_quipper_file(fname: str) -> 'Circuit':
@staticmethod
def from_qasm(s: str) -> 'Circuit':
"""Produces a :class:`Circuit` based on a QASM input string.
It ignores all the non-unitary instructions like measurements in the file.
It ignores all the non-unitary instructions like measurements in the file.
It currently doesn't support custom gates that have parameters."""
from .qasmparser import QASMParser
p = QASMParser()
Expand All @@ -352,7 +352,7 @@ def from_qasm(s: str) -> 'Circuit':
@staticmethod
def from_qasm_file(fname: str) -> 'Circuit':
"""Produces a :class:`Circuit` based on a QASM description of a circuit.
It ignores all the non-unitary instructions like measurements in the file.
It ignores all the non-unitary instructions like measurements in the file.
It currently doesn't support custom gates that have parameters."""
from .qasmparser import QASMParser
p = QASMParser()
Expand Down Expand Up @@ -396,15 +396,32 @@ def tcount(self) -> int:
"""Returns the amount of T-gates necessary to implement this circuit."""
return sum(g.tcount() for g in self.gates)
#return sum(1 for g in self.gates if isinstance(g, (ZPhase, XPhase, ParityPhase)) and g.phase.denominator >= 4)

def twoqubitcount(self) -> int:
"""Returns the amount of 2-qubit gates necessary to implement this circuit."""
c = self.to_basic_gates()
return sum(1 for g in c.gates if g.name in ('CNOT','CZ'))

def stats(self) -> str:
"""Returns statistics on the amount of gates in the circuit, separated into different classes
def stats(self, depth: bool = False) -> str:
"""Returns statistics on the amount of gates in the circuit, separated into different classes
(such as amount of T-gates, two-qubit gates, Hadamard gates)."""
d = self.stats_dict(depth)
s = """Circuit {} on {} qubits with {} gates.
{} is the T-count
{} Cliffords among which
{} 2-qubit gates ({} CNOT, {} other) and
{} Hadamard gates.""".format(d["name"], d["qubits"], d["gates"],
d["tcount"], d["clifford"], d["twoqubit"], d["cnot"], d["twoqubit"] - d["cnot"], d["had"])
if d["other"] > 0:
s += "\nThere are {} gates of a different type".format(d["other"])
if depth:
s += "\nThe circuit depth is {}".format(d["depth"])
s += "\nThe circuit depth if no CZs are possible is {}".format(d["depth_cz"])
return s

def stats_dict(self, depth: bool = False) -> dict:
"""Returns a dictionary containing statistics on the amount of gates in the circuit,
separated into different classes (such as amount of T-gates, two-qubit gates, Hadamard gates)."""
total = 0
tcount = 0
twoqubit = 0
Expand All @@ -420,21 +437,52 @@ def stats(self) -> str:
elif isinstance(g, HAD):
hadamard += 1
clifford += 1
elif isinstance(g, (CZ,CX, CNOT)):
elif isinstance(g, (CZ, CX, CNOT)):
twoqubit += 1
clifford += 1
if isinstance(g, CNOT): cnot += 1
else:
other += 1
s = """Circuit {} on {} qubits with {} gates.
{} is the T-count
{} Cliffords among which
{} 2-qubit gates ({} CNOT, {} other) and
{} Hadamard gates.""".format(self.name, self.qubits, total,
tcount, clifford, twoqubit, cnot, twoqubit - cnot, hadamard)
if other > 0:
s += "\nThere are {} gates of a different type".format(other)
return s
d = dict()
d["qubits"] = self.qubits
d["gates"] = total
d["tcount"] = tcount
d["clifford"] = clifford
d["twoqubit"] = twoqubit
d["cnot"] = cnot
d["had"] = hadamard
d["other"] = other
d["depth"] = 0
d["depth_cz"] = 0
if depth:
c = Circuit(self.qubits)
c.gates = [basic_gate for g in self.gates for basic_gate in g.to_basic_gates()]
d["depth"] = c.depth()
basic_no_cz = []
for g in c.gates:
if isinstance(g, CZ):
basic_no_cz.extend([HAD(g.target), CNOT(g.control, g.target), HAD(g.target)])
else:
basic_no_cz.append(g)
c.gates = basic_no_cz
d["depth_cz"] = c.depth()
return d

def depth(self) -> int:
min_depth = [0] * self.qubits
for g in self.gates:
if isinstance(g, (ZPhase, XPhase, HAD)):
min_depth[g.target] += 1
elif isinstance(g, (CZ, CNOT, SWAP)):
gate_depth = max(min_depth[g.target], min_depth[g.control]) + 1
min_depth[g.target] = gate_depth
min_depth[g.control] = gate_depth
elif isinstance(g, (CCZ, Tofolli)):
gate_depth = max(min_depth[g.target], min_depth[g.ctrl1], min_depth[g.ctrl2]) + 1
min_depth[g.target] = gate_depth
min_depth[g.ctrl1] = gate_depth
min_depth[g.ctrl2] = gate_depth
return max(min_depth)


def determine_file_type(circuitfile: str) -> str:
Expand Down
Loading

0 comments on commit 478f9bf

Please sign in to comment.