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)
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()



+ 26
- 4
abp/graphstate.py View File

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


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

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


+ 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;
}

#node_name {
font-size: 12pt;
}

#node_data {
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);
};

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;
};



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

@@ -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 + ".");
};

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

@@ -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
- 1
abp/static/scripts/materials.js View File

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

var curveProperties = {
splineDensity: 1,
splineDensity: 10,
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 = {};
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));
};

+ 40
- 2
bin/abpserver View File

@@ -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"])


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

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


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

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


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

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


+ 7
- 0
tests/test_graphstate.py View File

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


+ 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