Browse Source

Various small requests from Mercedes

- `GraphState.copy()`
- `measure_x` etc.
- `measure(detail=True)`
- Better documentation on `remove_vop` and `act_local_rotation`
master
Pete Shadbolt 7 years ago
parent
commit
02b2f1f3c3
6 changed files with 154 additions and 38 deletions
  1. +92
    -34
      abp/graphstate.py
  2. +1
    -1
      abp/static/scripts/materials.js
  3. +11
    -0
      doc/index.rst
  4. +8
    -0
      examples/tidying_vops.py
  5. +2
    -3
      examples/visualization/lattice_3d.py
  6. +40
    -0
      tests/mercedes.py

+ 92
- 34
abp/graphstate.py View File

@@ -27,17 +27,17 @@ class GraphState(object):

def add_node(self, node, **kwargs):
""" Add a node.
:param node: The name of the node, e.g. ``9``, ``start``
:type node: Any hashable type
:param kwargs: Any extra node attributes
Example of using node attributes ::

>>> g.add_node(0, label="fred", position=(1,2,3))
>>> g.node[0]["label"]
fred
"""
assert not node in self.node, "Node {} already exists".format(v)
self.adj[node] = {}
@@ -55,7 +55,7 @@ class GraphState(object):
:param circuit: An iterable containing tuples of the form ``(node, operation)``. If ``operation`` is a name for a local operation (e.g. ``6``, ``hadamard``) then that operation is performed on ``node``. If ``operation`` is ``cz`` then a CZ is performed on the two nodes in ``node``.

Example (makes a Bell pair)::
>>> g.act_circuit([(0, "hadamard"), (1, "hadamard"), ((0, 1), "cz")])

"""
@@ -93,17 +93,22 @@ class GraphState(object):
for n in v)
return tuple(edges)

def remove_vop(self, a, avoid):
""" Reduces VOP[a] to the identity """
others = set(self.adj[a]) - {avoid}
def remove_vop(self, node, avoid):
""" Attempts to remove the vertex operator on a particular qubit.

:param node: The node whose vertex operator should be reduced to the identity.
:param avoid: We will try to leave this node alone during the process (if possible).

"""
others = set(self.adj[node]) - {avoid}
if self.deterministic:
swap_qubit = min(others) if others else avoid
else:
swap_qubit = others.pop() if others else avoid

for v in reversed(clifford.decompositions[self.node[a]["vop"]]):
for v in reversed(clifford.decompositions[self.node[node]["vop"]]):
if v == "x":
self.local_complementation(a, "U ->")
self.local_complementation(node, "U ->")
else:
self.local_complementation(swap_qubit, "V ->")

@@ -122,7 +127,7 @@ class GraphState(object):
""" Act a local rotation on a qubit

:param node: The index of the node to act on
:param operation: The Clifford-group operation to perform.
:param operation: The Clifford-group operation to perform. You can use any of the names in the :ref:`Clifford group alias table <clifford>`.
"""
rotation = clifford.by_name[str(operation)]
self.node[node]["vop"] = clifford.times_table[
@@ -165,15 +170,17 @@ class GraphState(object):
if new_edge != edge:
self._toggle_edge(a, b)

def measure(self, node, basis, force=None):
""" Measure in an arbitrary basis
def measure(self, node, basis, force=None, detail=False):
""" Measure in an arbitrary basis

:param node: The name of the qubit to measure.
:param basis: The basis in which to measure.
:type basis: :math:`\in` ``{"px", "py", "pz"}``
:param force: Measurements in quantum mechanics are probabilistic. If you want to force a particular outcome, use the ``force``.
:type force: boolean
:param detail: Provide detailed information
:type detail: boolean

"""
basis = clifford.by_name[basis]
ha = clifford.conjugation_table[self.node[node]["vop"]]
@@ -186,11 +193,11 @@ class GraphState(object):
result = not result

if basis == clifford.by_name["px"]:
result = self._measure_x(node, result)
result, determinate = self._measure_graph_x(node, result)
elif basis == clifford.by_name["py"]:
result = self._measure_y(node, result)
result, determinate = self._measure_graph_y(node, result)
elif basis == clifford.by_name["pz"]:
result = self._measure_z(node, result)
result, determinate = self._measure_graph_z(node, result)
else:
raise ValueError("You can only measure in {X,Y,Z}")

@@ -198,7 +205,51 @@ class GraphState(object):
if phase == -1:
result = not result

return result
if detail:
return {"result": int(result),
"determinate": (determinate or force!=None),
"conjugated_basis": basis,
"phase": phase,
"node": node,
"force": force}
else:
return int(result)

def measure_x(self, node, force=None, detail=False):
""" Measure in the X basis

:param node: The name of the qubit to measure.
:param force: Measurements in quantum mechanics are probabilistic. If you want to force a particular outcome, use the ``force``.
:type force: boolean
:param detail: Provide detailed information
:type detail: boolean

"""
return self.measure(node, "px", force, detail)

def measure_y(self, node, force=None, detail=False):
""" Measure in the Y basis

:param node: The name of the qubit to measure.
:param force: Measurements in quantum mechanics are probabilistic. If you want to force a particular outcome, use the ``force``.
:type force: boolean
:param detail: Provide detailed information
:type detail: boolean

"""
return self.measure(node, "py", force, detail)

def measure_z(self, node, force=None, detail=False):
""" Measure in the Z basis

:param node: The name of the qubit to measure.
:param force: Measurements in quantum mechanics are probabilistic. If you want to force a particular outcome, use the ``force``.
:type force: boolean
:param detail: Provide detailed information
:type detail: boolean

"""
return self.measure(node, "pz", force, detail)

def _toggle_edges(self, a, b):
""" Toggle edges between vertex sets a and b """
@@ -211,10 +262,10 @@ class GraphState(object):
done.add((j, i))
self._toggle_edge(i, j)

def _measure_x(self, node, result):
""" Measure the graph in the X-basis """
def _measure_graph_x(self, node, result):
""" Measure the bare graph in the X-basis """
if len(self.adj[node]) == 0:
return 0
return 0, True

# Pick a vertex
if self.deterministic:
@@ -247,10 +298,10 @@ class GraphState(object):
for n in a - {friend}:
self._toggle_edge(friend, n)

return result
return result, False

def _measure_y(self, node, result):
""" Measure the graph in the Y-basis """
def _measure_graph_y(self, node, result):
""" Measure the bare graph in the Y-basis """
# Do some rotations
for neighbour in self.adj[node]:
self._update_vop(neighbour, "sqz" if result else "msqz")
@@ -260,13 +311,12 @@ class GraphState(object):
for i, j in it.combinations(vngbh, 2):
self._toggle_edge(i, j)

# TODO: naming: # lcoS.herm_adjoint() if result else lcoS
self._update_vop(node, 5 if result else 6)
# TODO: naming: # lcoS.herm_adjoint() if result else
# lcoS
return result
return result, False

def _measure_z(self, node, result):
""" Measure the graph in the Z-basis """
def _measure_graph_z(self, node, result):
""" Measure the bare graph in the Z-basis """
# Disconnect
for neighbour in tuple(self.adj[node]):
self._del_edge(node, neighbour)
@@ -280,7 +330,7 @@ class GraphState(object):
else:
self._update_vop(node, "hadamard")

return result
return result, False

def order(self):
""" Get the number of qubits """
@@ -302,11 +352,11 @@ class GraphState(object):

def to_json(self, stringify=False):
""" Convert the graph to JSON-like form.
:param stringify: JSON keys must be strings, But sometimes it is useful to have a JSON-like object whose keys are tuples.

If you want to dump a graph do disk, do something like this::
>>> import json
>>> with open("graph.json") as f:
json.dump(graph.to_json(True), f)
@@ -326,15 +376,15 @@ class GraphState(object):
# TODO

def to_state_vector(self):
""" Get the full state vector corresponding to this stabilizer state. Useful for debugging, interface with other simulators.
""" Get the full state vector corresponding to this stabilizer state. Useful for debugging, interface with other simulators.
This method becomes very slow for more than about ten qubits!

The output state is represented as a ``abp.qi.CircuitModel``::
>>> print g.to_state_vector()
|00000>: 0.18+0.00j
|00001>: 0.18+0.00j ...
.. todo::
Doesn't work with non-``int`` node labels

@@ -370,6 +420,14 @@ class GraphState(object):
""" Check equality between GraphStates """
return self.adj == other.adj and self.node == other.node

def copy(self):
""" Make a copy of this graphstate """
g = GraphState()
g.node = self.node.copy()
g.adj = self.adj.copy()
g.deterministic = self.deterministic
return g

if __name__ == '__main__':
g = GraphState()
g.add_nodes(range(10))


+ 1
- 1
abp/static/scripts/materials.js View File

@@ -1,7 +1,7 @@
var materials = {};

var curveProperties = {
splineDensity: 10,
splineDensity: 1,
curvature: 20
};



+ 11
- 0
doc/index.rst View File

@@ -97,6 +97,17 @@ The ``abp.GraphState`` class is the main interface to ``abp``.

.. automethod:: abp.GraphState.to_stabilizer

.. automethod:: abp.GraphState.remove_vop

.. automethod:: abp.GraphState.measure_x

.. automethod:: abp.GraphState.measure_y
.. automethod:: abp.GraphState.measure_z


.. _clifford:

The Clifford group
----------------------



+ 8
- 0
examples/tidying_vops.py View File

@@ -0,0 +1,8 @@
import abp

# TODO

# make a random state

# try to tidy up such that all VOPs are in (X, Y, Z)


+ 2
- 3
examples/visualization/lattice_3d.py View File

@@ -41,7 +41,7 @@ def lattice(unit_cell, size):
nodes = set(itertools.chain(*edges))
return nodes, edges

nodes, edges = lattice(threedee_unit_cell, (1, 1, 1))
nodes, edges = lattice(threedee_unit_cell, (6, 6, 6))

psi = GraphState()
for node in nodes:
@@ -51,6 +51,5 @@ for node in nodes:
for edge in edges:
psi.act_cz(str(edge[0]), str(edge[1]))

nx.rename_no
print psi.to_state_vector()
#print psi.to_state_vector()


+ 40
- 0
tests/mercedes.py View File

@@ -1,5 +1,6 @@
from abp import GraphState
from abp.util import xyz
from mock import simple_graph

def linear_cluster(n):
g = GraphState(range(n))
@@ -18,3 +19,42 @@ def test_mercedes_example_1():
assert set(g.adj[4]) == {1}


def test_single_qubit_measurements():
""" Various simple tests of measurements """

# Test that measuring |0> in Z gives 0
g = GraphState([0])
assert g.measure_z(0) == 0, "Measuring |0> in Z gives 0"

# Test that measuring |1> in Z gives 1
g = GraphState([0])
g.act_local_rotation(0, "px")
assert g.measure_z(0) == 1, "Measuring |1> in Z gives 1"

# Test that measuring |+> in X gives 0
g = GraphState([0])
g.act_local_rotation(0, "hadamard")
assert g.measure_x(0) == 0
assert g.measure_x(0) == 0, "Measuring |+> in X gives 0"
g.act_local_rotation(0, "pz")
assert g.measure_x(0) == 1, "Measuring |-> in X gives 1"

# Test something else
assert g.measure_y(0, force=0) == 0


def test_is_determinate():
""" Test whether asking if an outcome was random or determinate works """
g = GraphState([0])
assert g.measure_z(0, detail=True)["determinate"] == True
assert g.measure_x(0, detail=True)["determinate"] == False


def test_copy():
""" Make a copy of a graph """
a = simple_graph()
b = a.copy()
assert a == b



Loading…
Cancel
Save