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.pymaster
@@ -43,7 +43,7 @@ class GraphState(graphstate.GraphState, nx.Graph): | |||
time.sleep(delay) | |||
except websocket._exceptions.WebSocketTimeoutException: | |||
print "Timed out ... you might be pushing a bit hard" | |||
sys.exit(0) | |||
time.sleep(delay) | |||
#self.ws.close() | |||
#self.connect_to_server() | |||
@@ -42,6 +42,21 @@ class GraphState(object): | |||
for n in range(data): | |||
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): | |||
""" 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 | |||
""" | |||
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") | |||
self.adj[node] = {} | |||
self.node[node] = {} | |||
@@ -405,9 +423,6 @@ class GraphState(object): | |||
>>> with open("graph.json") as f: | |||
json.dump(graph.to_json(True), f) | |||
.. todo:: | |||
Implement ``from_json()``! | |||
""" | |||
if stringify: | |||
node = {str(key): value for key, value in self.node.items()} | |||
@@ -417,6 +432,13 @@ class GraphState(object): | |||
else: | |||
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): | |||
""" 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! | |||
@@ -29,16 +29,14 @@ | |||
<div id=node_name></div> | |||
<ul> | |||
<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> | |||
</ul> | |||
</div> | |||
@@ -26,6 +26,9 @@ html, body { margin: 0; padding: 0; overflow: hidden; font-size: 10pt; font-fam | |||
font-size: 9pt; | |||
} | |||
#node_name { | |||
font-size: 12pt; | |||
} | |||
#node_data { | |||
background-color: black; | |||
@@ -41,80 +41,19 @@ abj.has_edge = function(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) { | |||
// TODO: this is slow as heck | |||
var keys = Object.keys(group); | |||
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() { | |||
@@ -131,18 +70,4 @@ abj.edgelist = function() { | |||
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; | |||
}; | |||
@@ -32,14 +32,10 @@ editor.onFreeMove = function() { | |||
}; | |||
editor.focus = function(node) { | |||
editor.grid.position.copy(abj.node[node].position); | |||
gui.controls.target.copy(abj.node[node].position); | |||
gui.hideNodeMessage(); | |||
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) { | |||
@@ -47,19 +43,13 @@ editor.addQubitAtPoint = function(point) { | |||
return; | |||
} | |||
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); | |||
graph.update(); | |||
gui.serverMessage("Created node " + new_node +"."); | |||
}; | |||
@@ -67,6 +57,12 @@ editor.onClick = function() { | |||
var found = editor.findNodeOnRay(mouse.ray); | |||
if (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 { | |||
var intersection = mouse.ray.intersectPlane(editor.plane); | |||
if (intersection !== null) { | |||
@@ -80,10 +76,10 @@ editor.onShiftClick = function() { | |||
if (found === undefined){ return; } | |||
if (editor.selection === undefined){ 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 + "."); | |||
graph.update(); | |||
editor.focus(found); | |||
}; | |||
editor.onCtrlClick = function() { | |||
@@ -91,9 +87,8 @@ editor.onCtrlClick = function() { | |||
if (found === undefined){ return; } | |||
if (editor.selection === undefined){ return; } | |||
editor.focus(found); | |||
abj.act_hadamard(found); | |||
websocket.edit({action:"hadamard", node:found}); | |||
gui.serverMessage("Acted H on node " + found + "."); | |||
graph.update(); | |||
}; | |||
@@ -147,16 +142,47 @@ editor.findNodeOnRay = function(ray) { | |||
editor.deleteNode = function() { | |||
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 + "."); | |||
editor.selection = undefined; | |||
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() { | |||
if (editor.selection === undefined){ return; } | |||
websocket.edit({action:"localcomplementation", node:editor.selection}); | |||
abj.local_complementation(editor.selection); | |||
graph.update(); | |||
gui.serverMessage("Inverted neighbourhood of " + editor.selection + "."); | |||
}; |
@@ -47,6 +47,23 @@ graph.update = function(newState) { | |||
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 object = new THREE.Object3D(); | |||
object.name = "graphstate"; | |||
@@ -54,5 +71,6 @@ graph.update = function(newState) { | |||
object.add(edges); | |||
gui.scene.add(object); | |||
gui.render(); | |||
}; | |||
@@ -1,7 +1,7 @@ | |||
var materials = {}; | |||
var curveProperties = { | |||
splineDensity: 1, | |||
splineDensity: 10, | |||
curvature: 20 | |||
}; | |||
@@ -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)); | |||
}); |
@@ -1,16 +1,20 @@ | |||
var websocket = {}; | |||
websocket.update = undefined; | |||
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."); | |||
}; | |||
ws.onerror = function(err) { | |||
websocket.ws.onerror = function(err) { | |||
gui.serverMessage("Could not connect to server."); | |||
}; | |||
ws.onmessage = function(evt) { | |||
websocket.ws.onmessage = function(evt) { | |||
json = JSON.parse(evt.data); | |||
for (var i in json.node) { | |||
var pos = json.node[i].position; | |||
@@ -19,10 +23,14 @@ websocket.connect = function(update) { | |||
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); | |||
}; | |||
}; | |||
websocket.edit = function (data) { | |||
websocket.ws.send("edit:"+JSON.stringify(data)); | |||
}; |
@@ -12,17 +12,55 @@ import os, sys, threading | |||
import webbrowser | |||
import argparse | |||
import abp | |||
import json | |||
from pkg_resources import resource_filename | |||
from pprint import pprint | |||
import time | |||
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): | |||
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): | |||
print "Client {} connected.".format(client["id"]) | |||
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): | |||
print "Client {} disconnected.".format(client["id"]) | |||
@@ -2,13 +2,22 @@ from abp.fancy import GraphState | |||
from abp.util import xyz | |||
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() | |||
@@ -34,10 +34,11 @@ nodes, edges = lattice(square_unit_cell, (10, 10)) | |||
psi = GraphState() | |||
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.update(0.1) | |||
for edge in edges: | |||
psi.act_cz(str(edge[0]), str(edge[1])) | |||
psi.update(0.1) | |||
@@ -45,9 +45,10 @@ nodes, edges = lattice(threedee_unit_cell, (3, 3, 3)) | |||
psi = GraphState() | |||
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)) | |||
for edge in edges: | |||
psi.act_cz(str(edge[0]), str(edge[1])) | |||
psi.update() | |||
@@ -131,3 +131,10 @@ def test_from_nx(): | |||
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 | |||
@@ -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 | |||