abp
¶
This is the documentation for abp
. It’s a work in progress.
abp
is a Python port of Anders and Briegel’ s method for fast simulation of Clifford circuits.
That means that you can make quantum states of thousands of qubits, perform any sequence of Clifford operations, and measure in any of \(\{\sigma_x, \sigma_y, \sigma_z\}\).
Installing¶
You can install from pip
:
$ pip install user abp==0.4.27
Alternatively, clone from the github repo and run setup.py
:
$ git clone https://github.com/peteshadbolt/abp
$ cd abp
$ python setup.py install user
If you want to modify and test abp
without having to reinstall, switch into develop
mode:
$ python setup.py develop user
Quickstart¶
Let’s make a new GraphState
object with a register of three qubits:
>>> from abp import GraphState
>>> g = GraphState(3)
All the qubits are initialized by default in the \(+\rangle\) state:
>>> print g.to_state_vector()
000❭: √1/8 + i √0
100❭: √1/8 + i √0
010❭: √1/8 + i √0
110❭: √1/8 + i √0
001❭: √1/8 + i √0
101❭: √1/8 + i √0
011❭: √1/8 + i √0
111❭: √1/8 + i √0
We can also check the stabilizer tableau:
>>> print g.to_stabilizer()
0 1 2

X
X
X
Or look directly at the vertex operators and neighbour lists:
>>> print g
0: IA 
1: IA 
2: IA 
This representation might be unfamiliar. Each row shows the index of the qubit, then the vertex operator, then a list of neighbouring qubits. To understand vertex operators, read the original paper by Anders and Briegel.
Let’s act a Hadamard gate on the zeroth qubit – this will evolve qubit 0
to the \(H+\rangle = 1\rangle\) state:
>>> g.act_hadamard(0)
>>> print g.to_state_vector()
000❭: √1/4 + i √0
010❭: √1/4 + i √0
001❭: √1/4 + i √0
011❭: √1/4 + i √0
>>> print g
0: YC 
1: IA 
2: IA 
And now run some CZ gates:
>>> g.act_cz(0,1)
>>> g.act_cz(1,2)
>>> print g
0: YC 
1: IA (2,)
2: IA (1,)
>>> print g.to_state_vector()
000❭: √1/4 + i √0
010❭: √1/4 + i √0
001❭: √1/4 + i √0
011❭: √1/4 + i √0
Tidy up a bit:
>>> g.del_node(0)
>>> g.act_hadamard(0)
>>> print g.to_state_vector()
00❭: √1/2 + i √0
11❭: √1/2 + i √0
Cool, we made a Bell state. Incidentally, those those state vectors and stabilizers are genuine Python objects, not just stringy representations of the state:
>>> g = abp.GraphState(2)
>>> g.act_cz(0, 1)
>>> g.act_hadamard(0)
>>> psi = g.to_state_vector()
>>> print psi
00❭: √1/2 + i √0
11❭: √1/2 + i √0
psi
is a state vector – i.e. it is an exponentially large vector of complex numbers. We can still run gates on it:
>>> psi.act_cnot(0, 1)
>>> psi.act_hadamard(0)
>>> print psi
00❭: √1 + i √0
But these operations will be very slow. Let’s have a look at the stabilizer tableau:
>>> tab = g.to_stabilizer()
>>> print tab
0 1

Z Z
X X
>>> print tab.tableau
{0: {0: 3, 1: 3}, 1: {0: 1, 1: 1}}
>>> print tab[0, 0]
3
Quantum mechanics is nondeterminstic. However, it’s often useful to get determinstic behaviour for testing purposes. You can force abp
to behave determinstically by setting:
>>> abp.DETERMINSTIC = True
Visualization¶
abp
comes with a tool to visualize graph states in a WebGL compatible web browser (Chrome, Firefox, Safari etc). It uses a clientserver architecture.
First, run abpserver v
in a terminal:
$ abpserver v
Listening on port 5000 for clients..
This ought to pop open a browser window at http://localhost:5001/
. You can run abpserver help
for help. Now use an instance of abp.VizClient
to show the state in the browser:
>>> from abp import GraphState, VizClient
>>> g = GraphState(10)
>>> g.act_circuit([(i, "hadamard") for i in range(10)])
>>> g.act_circuit([((i, i+1), "cz") for i in range(9)])
>>> v = VizClient()
>>> v.update(g)
And you should see a 3D visualization of the state. You can call update()
in a loop to see an animation.
By default, the graph is automatically laid out in 3D using the FruchtermanReingold forcedirected algorithm (i.e. springs). If you want to specify geometry, give each node a position attribute:
>>> g.add_qubit(0, position={"x": 0, "y":0, "z":0}, vop="identity")
>>> g.add_qubit(0, position={"x": 1, "y":0, "z":0}, vop="identity")
There’s a utility function in abp.util
to construct those dictionaries:
>>> from abp.util import xyz
>>> g.add_qubit(0, position=xyz(0, 0, 0), vop="identity")
>>> g.add_qubit(1, position=xyz(0, 0, 1), vop="identity")
Note that if any nodes are missing a position
attribute, abp
will revert to automatic layout for all qubits.
GraphState API¶
The abp.GraphState
class is the main interface to abp
.

class
abp.
GraphState
(data=(), vop='identity')¶ This is the main class used to model stabilizer states. Internally it uses the same dictionaryofdictionaries data structure as
networkx
.
__init__
(data=(), vop='identity')¶ Construct a
GraphState
Parameters:  data – An iterable of nodes used to construct the graph, or an integer – the number of nodes, or a
nx.Graph
.  vop – The default VOP for new qubits. Setting
vop="identity"
initializes qubits in \(+\rangle\). Settingvop="hadamard"
initializes qubits in \(0\rangle\).
 data – An iterable of nodes used to construct the graph, or an integer – the number of nodes, or a

act_circuit
(circuit)¶ Run many gates in one call.
Parameters: circuit – An iterable containing tuples of the form (node, operation)
. Ifoperation
is a name for a local operation (e.g.6
,hadamard
) then that operation is performed onnode
. Ifoperation
iscz
then a CZ is performed on the two nodes innode
.Example (makes a Bell pair):
>>> g.act_circuit([(0, "hadamard"), (1, "hadamard"), ((0, 1), "cz")])

act_cz
(a, b)¶ Act a controlledphase gate on two qubits
Parameters:  a – The first qubit
 b – The second qubit

act_czs
(*pairs)¶ Shorthand to act many CZs

act_hadamard
(qubit)¶ Shorthand for
self.act_local_rotation(qubit, "hadamard")

act_local_rotation
(node, operation)¶ Act a local rotation on a qubit
Parameters:  node – The index of the node to act on
 operation – The Cliffordgroup operation to perform. You can use any of the names in the Clifford group alias table.

add_node
(*args, **kwargs)¶ Add a node

add_qubit
(name, **kwargs)¶ Add a qubit to the state.
Parameters:  name (Any hashable type) – The name of the node, e.g.
9
,start
.  kwargs – Any extra node attributes
By default, qubits are initialized in the \(0\rangle\) state. Provide the optional
vop
argument to set the initial state.Example of using node attributes
>>> g._add_node(0, label="fred", position=(1,2,3)) >>> g.node[0]["label"] fred
 name (Any hashable type) – The name of the node, e.g.

copy
()¶ Make a copy of this graphstate

del_qubit
(node)¶ Remove a qubit. TODO: this is a hack right now.

edgelist
()¶ Describe a graph as an edgelist # TODO: inefficient

from_json
(data)¶ Construct the graph from JSON data :param data: JSON data to be read.

has_edge
(v1, v2)¶ Test existence of an edge between two vertices

local_complementation
(v)¶ As defined in LISTING 1 of Anders & Briegel

measure
(node, basis, force=None, detail=False, friend=None)¶ Measure in an arbitrary basis
Parameters:  node – The name of the qubit to measure.
 basis (\(\in\)
{"px", "py", "pz"}
) – The basis in which to measure.  friend (Any neighbour of
node
.) – Specify a node to toggle about when performing an \(X\) measurement.  force (boolean) – Forces the measurement outcome.
 detail (boolean) – Get detailed information.
Measurements in quantum mechanics are probabilistic. If you want to force a particular outcome \(\in\{0, 1\}\), use
force
.You can get more information by setting
detail=True
, in which casemeasure()
returns a dictionary with the following keys:outcome
: the measurement outcome.determinate
: indicates whether the outcome was determinate or random. For example, measuring \(0\rangle\) in \(\sigma_x\) always gives a deterministic outcome.determinate
is overridden byforce
– forced outcomes are always determinate.conjugated_basis
: The index of the measurement operator, rotated by the vertex operator of the measured node, i.e. \(U_\text{vop} \sigma_m U_\text{vop}^\dagger\).phase
: The phase of the cojugated basis, \(\pm 1\).node
: The name of the measured node.force
: The value offorce
.

measure_sequence
(measurements, forces=None, detail=False)¶ Measures a sequence of Paulis
Parameters:  measurements – The sequence of measurements to be made, in the form [(node, basis), ...]
 force (list) – Measurements in quantum mechanics are probabilistic. If you want to force a particular outcome, use the
force
. List outcome force values in same order as measurements  detail (boolean) – Provide detailed information

measure_x
(node, force=None, detail=False, friend=None)¶ Measure in the X basis

measure_y
(node, force=None, detail=False)¶ Measure in the Y basis

measure_z
(node, force=None, detail=False)¶ Measure in the Z basis

order
()¶ Get the number of qubits

remove_vop
(node, avoid)¶ Attempts to remove the vertex operator on a particular qubit.
Parameters:  node – The node whose vertex operator should be reduced to the identity.
 avoid – We will try to leave this node alone during the process (if possible).

to_json
(stringify=False)¶ Convert the graph to JSONlike form.
Parameters: stringify – JSON keys must be strings, But sometimes it is useful to have a JSONlike object whose keys are tuples. If you want to dump a graph to disk, do something like this:
>>> import json >>> with open("graph.json") as f: json.dump(graph.to_json(True), f)

to_stabilizer
()¶ Get the stabilizer representation of the state:
>>> print g.to_stabilizer() 0 1 2 3 100 200  X Z Z X Z X Z Z Z X  Z Z X Z Z X

to_state_vector
()¶  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 ...

The Clifford group¶
This module handles operations on the Clifford group. It makes extensive use of the lookup tables in abp.tables
.
The code to generate those tables is included in this distribution as abp/build_tables.py
This package emumerates and labels the singlequbit Clifford group, and provides methods for matrix multiplication and conjugation.
It also includes the lookup table for the CZ gate.
There are 24 members of the singlequbit Clifford group. You can refer to some of them by multiple names. The complete set of aliases for singlequbit Cliffords is as follows:
Index Aliases 0 IA, identity, identity_h
1 XA, px, px_h
2 YA, py, py_h
3 ZA, pz, pz_h
4 IB
5 XB, sqz, msqz_h, phase_h
6 YB, msqz, sqz_h, phase
7 ZB
8 IC
9 XC, msqy, sqy_h
10 YC, hadamard, hadamard_h
11 ZC, sqy, msqy_h
12 ID
13 XD
14 YD, sqx, msqx_h
15 ZD, msqx, sqx_h
16 IE
17 XE
18 YE
19 ZE
20 IF
21 XF
22 YF
23 ZF
The clifford
module provides a few useful functions:

abp.clifford.
use_old_cz
() Use the CZ lookup table from A&B’s code, rather than our own. Useful for testing.
Testing¶
abp
has a bunch of tests. It tests against all sorts of things, including the circuit model, Anders & Briegels’ original code, Scott Aaronson’s chp
, and common sense. You can run all the tests using nose
:
$ nosetests
...
53 tests run in 39.5 seconds (53 tests passed)
Currently I use some reference implementations of chp
and graphsim
which you won’t have installed, so some tests will be skipped. That’s expected.