Browse Source

Merge branch 'pete'

This merge updates abpserver to support a full set of operations
including creation / deletion, gates, local complementation, and
measurements through the web interface. So now you can click on qubits
and measure them in X or whatever.

I've tested it but I bet there are bugs -- please raise an issue if you
find one.

Conflicts:
	examples/visualization/grid_2d.py
master
Pete Shadbolt 7 years ago
parent
commit
9b6b0a83d6
16 changed files with 227 additions and 144 deletions
  1. +1
    -1
      abp/fancy.py
  2. +26
    -4
      abp/graphstate.py
  3. +8
    -10
      abp/static/index.html
  4. +3
    -0
      abp/static/main.css
  5. +7
    -82
      abp/static/scripts/anders_briegel.js
  6. +51
    -25
      abp/static/scripts/editor.js
  7. +18
    -0
      abp/static/scripts/graph.js
  8. +1
    -1
      abp/static/scripts/materials.js
  9. +0
    -4
      abp/static/scripts/tests.js
  10. +14
    -6
      abp/static/scripts/websocket.js
  11. +40
    -2
      bin/abpserver
  12. +16
    -7
      examples/visualization/grid_2d.py
  13. +2
    -1
      examples/visualization/lattice_2d.py
  14. +2
    -1
      examples/visualization/lattice_3d.py
  15. +7
    -0
      tests/test_graphstate.py
  16. +31
    -0
      tests/test_json.py

+ 1
- 1
abp/fancy.py View File

@@ -43,7 +43,7 @@ class GraphState(graphstate.GraphState, nx.Graph):
time.sleep(delay) time.sleep(delay)
except websocket._exceptions.WebSocketTimeoutException: except websocket._exceptions.WebSocketTimeoutException:
print "Timed out ... you might be pushing a bit hard" print "Timed out ... you might be pushing a bit hard"
sys.exit(0)
time.sleep(delay)
#self.ws.close() #self.ws.close()
#self.connect_to_server() #self.connect_to_server()




+ 26
- 4
abp/graphstate.py View File

@@ -42,6 +42,21 @@ class GraphState(object):
for n in range(data): for n in range(data):
self._add_node(n, vop=vop) self._add_node(n, vop=vop)


def add_node(self, *args, **kwargs):
""" Add a node """
self._add_node(self, *args, **kwargs)


def _del_node(self, node):
""" Remove a node. TODO: this is a hack right now! """
if not node in self.node:
return
del self.node[node]
for k in self.adj[node]:
del self.adj[k][node]
del self.adj[node]


def _add_node(self, node, **kwargs): def _add_node(self, node, **kwargs):
""" Add a node. By default, nodes are initialized with ``vop=``:math:`I`, i.e. they are in the :math:`|+\\rangle` state. """ Add a node. By default, nodes are initialized with ``vop=``:math:`I`, i.e. they are in the :math:`|+\\rangle` state.


@@ -56,7 +71,10 @@ class GraphState(object):
fred fred


""" """
assert not node in self.node, "Node {} already exists".format(v)
if node in self.node:
print "Warning: node {} already exists".format(node)
return

default = kwargs.get("default", "identity") default = kwargs.get("default", "identity")
self.adj[node] = {} self.adj[node] = {}
self.node[node] = {} self.node[node] = {}
@@ -405,9 +423,6 @@ class GraphState(object):
>>> with open("graph.json") as f: >>> with open("graph.json") as f:
json.dump(graph.to_json(True), f) json.dump(graph.to_json(True), f)


.. todo::
Implement ``from_json()``!

""" """
if stringify: if stringify:
node = {str(key): value for key, value in self.node.items()} node = {str(key): value for key, value in self.node.items()}
@@ -417,6 +432,13 @@ class GraphState(object):
else: else:
return {"node": self.node, "adj": self.adj} return {"node": self.node, "adj": self.adj}


def from_json(self, data):
""" Construct the graph from JSON data
:param data: JSON data to be read.
"""
self.node = data["node"]
self.adj = data["adj"]

def to_state_vector(self): 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! This method becomes very slow for more than about ten qubits!


+ 8
- 10
abp/static/index.html View File

@@ -29,16 +29,14 @@
<div id=node_name></div> <div id=node_name></div>
<ul> <ul>
<li id=node_vop></li> <li id=node_vop></li>
<!--<li><a href="#">Measure in X</a></li>-->
<!--<li><a href="#">Measure in Y</a></li>-->
<!--<li><a href="#">Measure in Z</a></li>-->
<!--<li><a href="#">Act Hadamard</a></li>-->
<!--<li><a href="#">Act Phase</a></li>-->
<!--<li><a href="#" onclick="editor.localComplementation()">Invert neighbourhood</a></li>-->
<!--<li>-->
<!--<a href="#">IA</a>-->
<!--<a href="#">IB</a>-->
<!--</li>-->
<li>Measure in
<a href="#" onclick="editor.measureX()">X</a> /
<a href="#" onclick="editor.measureY()">Y</a> /
<a href="#" onclick="editor.measureZ()">Z</a></li>
<li>Act
<a href="#" onclick="editor.hadamard()">Hadamard</a> /
<a href="#" onclick="editor.phase()">Phase</a></li>
<li><a href="#" onclick="editor.localComplementation()">Invert neighbourhood</a></li>
<li><a href="#" onclick="editor.deleteNode()">Delete</a></li> <li><a href="#" onclick="editor.deleteNode()">Delete</a></li>
</ul> </ul>
</div> </div>


+ 3
- 0
abp/static/main.css View File

@@ -26,6 +26,9 @@ html, body { margin: 0; padding: 0; overflow: hidden; font-size: 10pt; font-fam
font-size: 9pt; font-size: 9pt;
} }


#node_name {
font-size: 12pt;
}


#node_data { #node_data {
background-color: black; background-color: black;


+ 7
- 82
abp/static/scripts/anders_briegel.js View File

@@ -41,80 +41,19 @@ abj.has_edge = function(a, b) {
return Object.prototype.hasOwnProperty.call(abj.adj[a], b); return Object.prototype.hasOwnProperty.call(abj.adj[a], b);
}; };


abj.toggle_edge = function(a, b) {
if (abj.has_edge(a, b)) {
abj.del_edge(a, b);
} else {
abj.add_edge(a, b);
}
};

abj.get_swap = function(node, avoid) {
for (var i in abj.adj[node]) {
if (i != avoid) {
return i;
}
}
return avoid;
};

abj.remove_vop = function(node, avoid) {
var swap_qubit = abj.get_swap(node, avoid);
var decomposition = tables.decompositions[abj.node[node].vop];
for (var i = decomposition.length - 1; i >= 0; --i) {
var v = decomposition[i];
abj.local_complementation(v == "x" ? node : swap_qubit);
}
};

abj.local_complementation = function(node) {
// TODO: inefficient
var done = {};
for (var i in abj.adj) {
for (var j in abj.adj[i]) {
var name = i>j ? [i,j] : [j,i];
if (done[name]===false){
abj.toggle_edge(i, j);
done[name] = true;
}
}
abj.node[i].vop = tables.times_table[abj.node[i].vop][6];
}
abj.node[node].vop = tables.times_table[abj.node[node].vop][14];
};

abj.act_local_rotation = function(node, operation) {
var rotation = tables.clifford[operation];
abj.node[node].vop = tables.times_table[rotation][abj.node[node].vop];
};

abj.act_hadamard = function(node) {
abj.act_local_rotation(node, 10);
};

abj.is_sole_member = function(group, node) { abj.is_sole_member = function(group, node) {
// TODO: this is slow as heck // TODO: this is slow as heck
var keys = Object.keys(group); var keys = Object.keys(group);
return keys.length == 1 && keys[0] == node; return keys.length == 1 && keys[0] == node;
}; };


abj.act_cz = function(a, b) {
if (abj.is_sole_member(abj.adj[a], b)) {
abj.remove_vop(a, b);
}
if (abj.is_sole_member(abj.adj[b], a)) {
abj.remove_vop(b, a);
}
if (abj.is_sole_member(abj.adj[a], b)) {
abj.remove_vop(a, b);
}
var edge = abj.has_edge(a, b);
var new_state = tables.cz_table[edge ? 1 : 0][abj.node[a].vop][abj.node[b].vop];
abj.node[a].vop = new_state[1];
abj.node[b].vop = new_state[2];
if (new_state[0] != edge) {
abj.toggle_edge(a, b);
}
abj.update = function(newState) {
abj.node = newState.node;
abj.adj = newState.adj;
};

abj.order = function(){
return Object.keys(abj.node).length;
}; };


abj.edgelist = function() { abj.edgelist = function() {
@@ -131,18 +70,4 @@ abj.edgelist = function() {
return output; return output;
}; };


abj.log_graph_state = function() {
console.log(JSON.stringify(abj.node));
console.log(JSON.stringify(abj.adj));
};

abj.update = function(newState) {
abj.node = newState.node;
abj.adj = newState.adj;
};

abj.order = function(){
return Object.keys(abj.node).length;
};




+ 51
- 25
abp/static/scripts/editor.js View File

@@ -32,14 +32,10 @@ editor.onFreeMove = function() {
}; };


editor.focus = function(node) { editor.focus = function(node) {
editor.grid.position.copy(abj.node[node].position);
gui.controls.target.copy(abj.node[node].position);
gui.hideNodeMessage(); gui.hideNodeMessage();
editor.selection = node; editor.selection = node;
gui.serverMessage("Selected node " + node + ".");
node_name.innerHTML = "Node " + node;
node_data.className = "visible";
node_vop.innerHTML = "VOP: " + abj.node[node].vop;

//gui.serverMessage("Selected node " + node + ".");
}; };


editor.addQubitAtPoint = function(point) { editor.addQubitAtPoint = function(point) {
@@ -47,19 +43,13 @@ editor.addQubitAtPoint = function(point) {
return; return;
} }
point.round(); point.round();

// Check for clashes
for (var node in abj.node) {
var delta = new THREE.Vector3();
delta.subVectors(abj.node[node].position, point);
if (delta.length()<0.1){ return; }
var new_node = Math.floor(point.x) + "," + Math.floor(point.y) + "," + Math.floor(point.z);
if (Object.prototype.hasOwnProperty.call(abj.node, new_node)) {
gui.serverMessage("Node " + new_node +" already exists.");
return;
} }

// TODO: This SUCKS
var new_node = point.x + "." + point.y + "." + point.z;
abj.add_node(new_node, { position: point, vop:0 });
websocket.edit({action:"create", name:new_node, position: point});
editor.focus(new_node); editor.focus(new_node);
graph.update();
gui.serverMessage("Created node " + new_node +"."); gui.serverMessage("Created node " + new_node +".");
}; };


@@ -67,6 +57,12 @@ editor.onClick = function() {
var found = editor.findNodeOnRay(mouse.ray); var found = editor.findNodeOnRay(mouse.ray);
if (found) { if (found) {
editor.focus(found); editor.focus(found);
var node=found;
editor.grid.position.copy(abj.node[node].position);
gui.controls.target.copy(abj.node[node].position);
node_name.innerHTML = "Node " + node;
node_data.className = "visible";
node_vop.innerHTML = "VOP: " + abj.node[node].vop;
} else { } else {
var intersection = mouse.ray.intersectPlane(editor.plane); var intersection = mouse.ray.intersectPlane(editor.plane);
if (intersection !== null) { if (intersection !== null) {
@@ -80,10 +76,10 @@ editor.onShiftClick = function() {
if (found === undefined){ return; } if (found === undefined){ return; }
if (editor.selection === undefined){ return; } if (editor.selection === undefined){ return; }
if (found === editor.selection){ return; } if (found === editor.selection){ return; }
abj.act_cz(found, editor.selection);
editor.focus(found);
//abj.act_cz(found, editor.selection);
websocket.edit({action:"cz", start:found, end:editor.selection});
gui.serverMessage("Acted CZ between " + found + " & " + editor.selection + "."); gui.serverMessage("Acted CZ between " + found + " & " + editor.selection + ".");
graph.update();
editor.focus(found);
}; };


editor.onCtrlClick = function() { editor.onCtrlClick = function() {
@@ -91,9 +87,8 @@ editor.onCtrlClick = function() {
if (found === undefined){ return; } if (found === undefined){ return; }
if (editor.selection === undefined){ return; } if (editor.selection === undefined){ return; }
editor.focus(found); editor.focus(found);
abj.act_hadamard(found);
websocket.edit({action:"hadamard", node:found});
gui.serverMessage("Acted H on node " + found + "."); gui.serverMessage("Acted H on node " + found + ".");
graph.update();
}; };




@@ -147,16 +142,47 @@ editor.findNodeOnRay = function(ray) {


editor.deleteNode = function() { editor.deleteNode = function() {
if (editor.selection === undefined){ return; } if (editor.selection === undefined){ return; }
abj.del_node(editor.selection);
graph.update();
websocket.edit({action:"delete", node:editor.selection});
gui.serverMessage("Deleted node " + editor.selection + "."); gui.serverMessage("Deleted node " + editor.selection + ".");
editor.selection = undefined; editor.selection = undefined;
node_data.className = "hidden"; node_data.className = "hidden";
}; };


//TODO: loadsa space for DRY here

editor.hadamard = function() {
if (editor.selection === undefined){ return; }
websocket.edit({action:"hadamard", node:editor.selection});
gui.serverMessage("Acted Hadamard on node " + editor.selection + ".");
};

editor.phase = function() {
if (editor.selection === undefined){ return; }
websocket.edit({action:"phase", node:editor.selection});
gui.serverMessage("Acted phase on node " + editor.selection + ".");
};

editor.measureX = function() {
if (editor.selection === undefined){ return; }
websocket.edit({action:"measure", node:editor.selection, basis:"x"});
gui.serverMessage("Measured node " + editor.selection + " in X.");
};

editor.measureY = function() {
if (editor.selection === undefined){ return; }
websocket.edit({action:"measure", node:editor.selection, basis:"y"});
gui.serverMessage("Measured node " + editor.selection + " in Y.");
};

editor.measureZ = function() {
if (editor.selection === undefined){ return; }
websocket.edit({action:"measure", node:editor.selection, basis:"z"});
gui.serverMessage("Measured node " + editor.selection + " in z.");
};

editor.localComplementation = function() { editor.localComplementation = function() {
if (editor.selection === undefined){ return; } if (editor.selection === undefined){ return; }
websocket.edit({action:"localcomplementation", node:editor.selection});
abj.local_complementation(editor.selection); abj.local_complementation(editor.selection);
graph.update();
gui.serverMessage("Inverted neighbourhood of " + editor.selection + "."); gui.serverMessage("Inverted neighbourhood of " + editor.selection + ".");
}; };

+ 18
- 0
abp/static/scripts/graph.js View File

@@ -47,6 +47,23 @@ graph.update = function(newState) {
edges.add(newEdge); edges.add(newEdge);
} }


if (editor.selection) {
console.log(editor.selection);
var node = editor.selection;
if (Object.prototype.hasOwnProperty.call(abj.node, node)) {
editor.grid.position.copy(abj.node[node].position);
gui.controls.target.copy(abj.node[node].position);
node_name.innerHTML = "Node " + node;
node_data.className = "visible";
node_vop.innerHTML = "VOP: " + abj.node[node].vop;
} else {
editor.selection = undefined;
node_data.className = "hidden";
}
} else {
node_data.className = "hidden";
}

var particles = new THREE.Points(geometry, materials.qubit); var particles = new THREE.Points(geometry, materials.qubit);
var object = new THREE.Object3D(); var object = new THREE.Object3D();
object.name = "graphstate"; object.name = "graphstate";
@@ -54,5 +71,6 @@ graph.update = function(newState) {
object.add(edges); object.add(edges);
gui.scene.add(object); gui.scene.add(object);
gui.render(); gui.render();

}; };



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

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


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




+ 0
- 4
abp/static/scripts/tests.js View File

@@ -1,4 +0,0 @@
QUnit.test( "Adding nodes", function( assert ) {
abj.add_node(0);
assert.ok(abj.node[0].vop === 10, JSON.stringify(abj.node));
});

+ 14
- 6
abp/static/scripts/websocket.js View File

@@ -1,16 +1,20 @@
var websocket = {}; var websocket = {};
websocket.update = undefined;


websocket.connect = function(update) { websocket.connect = function(update) {
var ws = new WebSocket("ws://localhost:5000");
ws.onopen = function(evt) {
websocket.ws = new WebSocket("ws://localhost:5000");
if (update){
websocket.update = update;
}
websocket.ws.onopen = function(evt) {
gui.serverMessage("Connected to server."); gui.serverMessage("Connected to server.");
}; };


ws.onerror = function(err) {
websocket.ws.onerror = function(err) {
gui.serverMessage("Could not connect to server."); gui.serverMessage("Could not connect to server.");
}; };


ws.onmessage = function(evt) {
websocket.ws.onmessage = function(evt) {
json = JSON.parse(evt.data); json = JSON.parse(evt.data);
for (var i in json.node) { for (var i in json.node) {
var pos = json.node[i].position; var pos = json.node[i].position;
@@ -19,10 +23,14 @@ websocket.connect = function(update) {
json.node[i].vop = 0; json.node[i].vop = 0;
} }
} }
update(json);
websocket.update(json);
}; };


ws.onclose = function(evt) {
websocket.ws.onclose = function(evt) {
gui.serverMessage("No connection to server. <a href='#' onclick='javascript:websocket.connect()'>Reconnect</a>.", true); gui.serverMessage("No connection to server. <a href='#' onclick='javascript:websocket.connect()'>Reconnect</a>.", true);
}; };
}; };

websocket.edit = function (data) {
websocket.ws.send("edit:"+JSON.stringify(data));
};

+ 40
- 2
bin/abpserver View File

@@ -12,17 +12,55 @@ import os, sys, threading
import webbrowser import webbrowser
import argparse import argparse
import abp import abp
import json
from pkg_resources import resource_filename from pkg_resources import resource_filename
from pprint import pprint
import time


clients = [] clients = []
local_state = abp.GraphState()

def process_edit(edit, client, server):
action = edit["action"]

pprint(edit)

if action == "create":
local_state.add_qubit(edit["name"], position=edit["position"], vop=0)
elif action == "cz":
local_state.act_cz(edit["start"], edit["end"])
elif action == "hadamard":
local_state.act_hadamard(edit["node"])
elif action == "phase":
local_state.act_local_rotation(edit["node"], "phase")
elif action == "delete":
local_state._del_node(edit["node"])
elif action == "localcomplementation":
local_state.local_complementation(edit["node"])
elif action == "measure":
local_state.measure(edit["node"], "p"+edit["basis"])
else:
pass

#server.send_message(client, json.dumps(local_state.to_json()))
server.send_message_to_all(json.dumps(local_state.to_json()))


def new_message(client, server, message): def new_message(client, server, message):
print "Received update from client {}.".format(client["id"])
server.send_message_to_all(message)
if message.startswith("edit:"):
edit = json.loads(message[5:])
process_edit(edit, client, server)
else:
print "Received update from python {}.".format(client["id"])
print message
local_state.from_json(json.loads(message))
print local_state
server.send_message_to_all(message)


def new_client(client, server): def new_client(client, server):
print "Client {} connected.".format(client["id"]) print "Client {} connected.".format(client["id"])
clients.append(client) clients.append(client)
print "Sent state of {} nodes to client {}".format(local_state.order(), client["id"])
server.send_message(client, json.dumps(local_state.to_json()))


def client_left(client, server): def client_left(client, server):
print "Client {} disconnected.".format(client["id"]) print "Client {} disconnected.".format(client["id"])


+ 16
- 7
examples/visualization/grid_2d.py View File

@@ -2,13 +2,22 @@ from abp.fancy import GraphState
from abp.util import xyz from abp.util import xyz
import itertools import itertools


psi = GraphState()
grid = itertools.product(range(10), range(10))
for i, (x, y) in enumerate(grid):
psi.add_qubit(i, position=xyz(x, y, 0), vop=0)
def grid_2d(width, height):
""" Make a 2D grid """
psi = GraphState()
grid = list(itertools.product(range(width), range(height)))


for i in range(50):
psi.act_cz(i, i+1)
for x, y in grid:
psi.add_qubit((x, y), position=xyz(x, y, 0), vop=0)


psi.update()
for x, y in grid:
if x<width-1: psi.act_cz((x, y), (x+1, y))
if y<height-1: psi.act_cz((x, y), (x, y+1))

return psi


if __name__ == '__main__':
psi = grid_2d(5, 5)
psi.update()



+ 2
- 1
examples/visualization/lattice_2d.py View File

@@ -34,10 +34,11 @@ nodes, edges = lattice(square_unit_cell, (10, 10))


psi = GraphState() psi = GraphState()
for node in nodes: for node in nodes:
psi.add_node(str(node), position=xyz(node[0], node[1]))
psi.add_qubit(str(node), position=xyz(node[0], node[1]))
psi.act_hadamard(str(node)) psi.act_hadamard(str(node))
psi.update(0.1) psi.update(0.1)


for edge in edges: for edge in edges:
psi.act_cz(str(edge[0]), str(edge[1])) psi.act_cz(str(edge[0]), str(edge[1]))
psi.update(0.1)



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

@@ -45,9 +45,10 @@ nodes, edges = lattice(threedee_unit_cell, (3, 3, 3))


psi = GraphState() psi = GraphState()
for node in nodes: for node in nodes:
psi.add_node(str(node), position=xyz(*node))
psi.add_qubit(str(node), position=xyz(*node))
psi.act_hadamard(str(node)) psi.act_hadamard(str(node))


for edge in edges: for edge in edges:
psi.act_cz(str(edge[0]), str(edge[1])) psi.act_cz(str(edge[0]), str(edge[1]))
psi.update()



+ 7
- 0
tests/test_graphstate.py View File

@@ -131,3 +131,10 @@ def test_from_nx():


psi = GraphState(nx.Graph(((0, 1),))) psi = GraphState(nx.Graph(((0, 1),)))


def test_del_node():
""" Test deleting nodes """
g = GraphState(10)
g.act_circuit(mock.random_stabilizer_circuit())
g._del_node(0)
assert g.order() == 9


+ 31
- 0
tests/test_json.py View File

@@ -0,0 +1,31 @@
import mock
import abp

def test_json():
""" Test to_json and from_json """
a = mock.named_node_graph()
j = a.to_json()

b = abp.GraphState()
b.from_json(j)
assert a == b


def test_json_again():
""" Test to_json and from_json """
# Make a random graph
a = abp.GraphState(10)
a.act_circuit(mock.random_graph_circuit())

# Dump it to JSON
j = a.to_json()

# Reconstruct from JSON
b = abp.GraphState()
b.from_json(j)

# Check equality
assert a == b




Loading…
Cancel
Save