Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated firing pattern plot #522

Merged
merged 14 commits into from
Sep 1, 2015
1 change: 1 addition & 0 deletions nengo_gui/components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .netgraph import NetGraph
from .ace_editor import AceEditor
from .htmlview import HTMLView
from .spike_grid import SpikeGrid

# Old versions of the .cfg files used Templates which had slightly different
# names than the Components currently use. This code allows us to
Expand Down
1 change: 1 addition & 0 deletions nengo_gui/components/netgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ def get_extra_info(self, obj):
info['dimensions'] = int(obj.size_out)
elif isinstance(obj, nengo.Ensemble):
info['dimensions'] = int(obj.size_out)
info['n_neurons'] = int(obj.n_neurons)
info['sp_targets'] = (
nengo_gui.components.pointer.Pointer.applicable_targets(obj))
return info
Expand Down
73 changes: 73 additions & 0 deletions nengo_gui/components/spike_grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import nengo
import numpy as np
import struct

from nengo_gui.components.component import Component

class SpikeGrid(Component):
def __init__(self, obj, n_neurons=None):
super(SpikeGrid, self).__init__()
self.obj = obj
self.data = []
self.max_neurons = self.obj.neurons.size_out
if n_neurons is None:
n_neurons = self.max_neurons
self.n_neurons = n_neurons
self.pixels_x = np.ceil(np.sqrt(self.n_neurons))
self.pixels_y = np.ceil(float(self.n_neurons) / self.pixels_x)
self.n_pixels = self.pixels_x * self.pixels_y
self.struct = struct.Struct('<f%dB' % (self.n_pixels))
self.max_value = 1.0

def attach(self, page, config, uid):
super(SpikeGrid, self).attach(page, config, uid)
self.label = page.get_label(self.obj)

def add_nengo_objects(self, page):
with page.model:
self.node = nengo.Node(self.gather_data,
size_in=self.obj.neurons.size_out)
self.conn = nengo.Connection(self.obj.neurons,
self.node, synapse=0.01)

def remove_nengo_objects(self, page):
page.model.connections.remove(self.conn)
page.model.nodes.remove(self.node)

def gather_data(self, t, x):
self.max_value = max(self.max_value, np.max(x))

# TODO: pass only spiking neurons, using subclass of Nengo.Image?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What Nengo.Image are you talking about? Dammit. Just figured it out by reading more. Maybe note that you're talking about Nengo.js?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that comment's a bit outdated anyway, as we don't want to just send spike data when we're doing the synapse filter in Nengo. I removed it.

# Considerations include how to filter if we're only passing spike
# times, i.e. need to write a new DataStore.
if len(x) > self.n_neurons:
x = x[:self.n_neurons]
y = np.zeros(self.n_pixels, dtype=np.uint8)
if self.max_value > 0:
y[:x.size] = x * 255 / self.max_value
data = self.struct.pack(t, *y)
self.data.append(data)

def update_client(self, client):
length = len(self.data)
if length > 0:
item = bytes().join(self.data[:length])
del self.data[:length]
try:
client.write(item, binary=True)
except:
# if there is a communication problem, just drop the frame
# (this usually happens when there is too much data to send)
pass

def javascript(self):
info = dict(uid=id(self), label=self.label,
pixels_x=self.pixels_x, pixels_y=self.pixels_y)
json = self.javascript_config(info)
return 'new Nengo.Image(main, sim, %s);' % json

def code_python_args(self, uids):
args = [uids[self.obj]]
if self.n_neurons != self.max_neurons:
args.append('n_neurons=%d' % self.n_neurons)
return args
123 changes: 123 additions & 0 deletions nengo_gui/static/components/image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* Shows an image or pixel grid over time
* @constructor
*
* @param {dict} args - A set of constructor arguments (see Nengo.Component)
* @param {int} args.n_lines - number of decoded values
* @param {float} args.miny - minimum value on y-axis
* @param {float} args.maxy - maximum value on y-axis
* @param {Nengo.SimControl} args.sim - the simulation controller
*/

Nengo.Image = function(parent, sim, args) {
var self = this;

Nengo.Component.call(self, parent, args);
self.sim = sim;
self.display_time = args.display_time;
self.pixels_x = args.pixels_x;
self.pixels_y = args.pixels_y;
self.n_pixels = self.pixels_x * self.pixels_y;

/** for storing the accumulated data */
self.data_store = new Nengo.DataStore(self.n_pixels, self.sim, 0);

/** draw the plot as an SVG */
self.svg = d3.select(self.div).append('svg')
.attr('width', '100%')
.attr('height', '100%')
.attr('style', [
'padding-top:', '2em',
].join(""));

/** call schedule_update whenever the time is adjusted in the SimControl */
self.sim.div.addEventListener('adjust_time',
function(e) {self.schedule_update();}, false);

/** create the image */
self.image = self.svg.append("image")
.attr("x", 0)
.attr("y", 0)
.attr("width", "100%")
.attr("height", "100%")
.attr("style", [
"image-rendering: -webkit-optimize-contrast;",
"image-rendering: -moz-crisp-edges;",
"image-rendering: pixelated;"
].join(""));

self.canvas = document.createElement('CANVAS');
self.canvas.width = self.pixels_x;
self.canvas.height = self.pixels_y;

self.on_resize(this.get_screen_width(), this.get_screen_height());

};
Nengo.Image.prototype = Object.create(Nengo.Component.prototype);
Nengo.Image.prototype.constructor = Nengo.Image;

/**
* Receive new line data from the server
*/
Nengo.Image.prototype.on_message = function(event) {
var data = new Uint8Array(event.data);
var msg_size = this.n_pixels + 4;

for (var i = 0; i < data.length; i += msg_size) {
var time_data = new Float32Array(event.data.slice(i, i + 4));
data = Array.prototype.slice.call(data, i + 3, i + msg_size);
data[0] = time_data[0];
this.data_store.push(data);
}
this.schedule_update();
}

/**
* Redraw the lines and axis due to changed data
*/
Nengo.Image.prototype.update = function() {
var self = this;

/** let the data store clear out old values */
self.data_store.update();

var data = self.data_store.get_last_data();
var ctx = self.canvas.getContext("2d");
var imgData = ctx.getImageData(0, 0, self.pixels_x, self.pixels_y);
for (var i = 0; i < self.n_pixels; i++) {
imgData.data[4*i + 0] = data[i];
imgData.data[4*i + 1] = data[i];
imgData.data[4*i + 2] = data[i];
imgData.data[4*i + 3] = 255;
}
ctx.putImageData(imgData, 0, 0);
var dataURL = self.canvas.toDataURL("image/png");

self.image.attr("xlink:href", dataURL);
};

/**
* Adjust the graph layout due to changed size
*/
Nengo.Image.prototype.on_resize = function(width, height) {
var self = this;
if (width < self.minWidth) {
width = self.minWidth;
}
if (height < self.minHeight) {
height = self.minHeight;
};

self.svg
.attr("width", width)
.attr("height", height);

self.update();

self.label.style.width = width;

self.width = width;
self.height = height;
self.div.style.width = width;
self.div.style.height = height;
};
21 changes: 10 additions & 11 deletions nengo_gui/static/components/netgraph_item.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,30 +336,31 @@ Nengo.NetGraphItem.prototype.generate_menu = function () {
}
}
if (this.type == 'ens') {
items.push(['Value', function() {self.create_graph('Value');}])
items.push(['Value', function() {self.create_graph('Value');}]);
if (this.dimensions > 1) {
items.push(['XY-value', function() {self.create_graph('XYValue');}])
items.push(['XY-value', function() {self.create_graph('XYValue');}]);
}
items.push(['Spikes', function() {self.create_graph('Raster');}])
items.push(['Voltages', function() {self.create_graph('Voltage');}])
items.push(['Spikes', function() {self.create_graph('Raster');}]);
items.push(['Voltages', function() {self.create_graph('Voltage');}]);
items.push(['Firing pattern', function() {self.create_graph('SpikeGrid');}]);
}
if (this.type == 'node') {
items.push(['Slider', function() {self.create_graph('Slider');}])
items.push(['Slider', function() {self.create_graph('Slider');}]);
if (this.dimensions > 0) {
items.push(['Value', function() {self.create_graph('Value');}])
items.push(['Value', function() {self.create_graph('Value');}]);
}
if (this.dimensions > 1) {
items.push(['XY-value', function() {self.create_graph('XYValue');}])
items.push(['XY-value', function() {self.create_graph('XYValue');}]);
}
if (this.html_node) {
items.push(['HTML', function() {self.create_graph('HTMLView');}])
}
}
if (this.sp_targets.length > 0) {
items.push(['Semantic pointer',
function() {self.create_graph('Pointer', self.sp_targets[0]);}])
function() {self.create_graph('Pointer', self.sp_targets[0]);}]);
}
items.push(['Details ...', function() {self.create_modal();}])
items.push(['Details ...', function() {self.create_modal();}]);
return items;
};

Expand Down Expand Up @@ -644,7 +645,6 @@ Nengo.NetGraphItem.prototype.redraw_child_connections = function() {
};



Nengo.NetGraphItem.prototype.redraw_connections = function() {
/** update any connections into and out of this */
for (var i in this.conn_in) {
Expand Down Expand Up @@ -779,7 +779,6 @@ Nengo.NetGraphItem.prototype.get_height = function() {
}



/** force a redraw of the item */
Nengo.NetGraphItem.prototype.redraw = function() {
this.set_position(this.pos[0], this.pos[1]);
Expand Down
1 change: 1 addition & 0 deletions nengo_gui/templates/page.html
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ <h4 class="modal-title"></h4>
<script src="static/components/netgraph.js"></script>
<script src="static/components/netgraph_item.js"></script>
<script src="static/components/netgraph_conn.js"></script>
<script src="static/components/image.js"></script>
<script src="static/sim_control.js"></script>
<script src="static/top_toolbar.js"></script>
<script src="static/modal.js"></script>
Expand Down