浏览代码

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 8 年前
父节点
当前提交
9b6b0a83d6
共有 16 个文件被更改,包括 227 次插入144 次删除
  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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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




正在加载...
取消
保存