Index
Motivation and Background
The goal of Intel Labs' River Trail project, also known as Parallel JavaScript, is to enable data parallelism in web applications. In a world where the web browser is the user's window into computing, browser applications must leverage all available computing resources to provide the best possible user experience. Today web applications do not take full advantage of parallel client hardware due to the lack of appropriate programming models. River Trail puts the parallel compute power of the client's hardware into the hands of the web developer while staying within the safe and secure boundaries of the familiar JavaScript programming paradigm. River Trail gently extends JavaScript with simple deterministic data-parallel constructs that are translated at runtime into a low-level hardware abstraction layer. By leveraging multiple CPU cores and vector instructions, River Trail programs can achieve significant speedup over sequential JavaScript.
An open-source prototype implementation of River Trail for Firefox is available on GitHub (see the quick start instructions). This prototype implements a version of the River Trail API that compiles to OpenCL and can be executed on CPUs and GPUs.
This tutorial is a gentle introduction to the River Trail language extensions and API.
The ParallelArray type
The central component of River Trail is
the ParallelArray
type. ParallelArray
objects are
essentially ordered collections of scalar values.
Multi-Dimensional and Uniform
ParallelArray
objects can represent
multi-dimensional collections of scalars.
All ParallelArray
objects have
a shape that succinctly describes the
dimensionality and size of the object.
The shape of a ParallelArray
is specified as
an array of numbers in which the value of the ith
element is the number of elements in the ith
dimension of the ParallelArray
. The number
of dimensions of a ParallelArray
is the
number of elements in its shape array.
For instance, a 4x5 matrix of numbers can be represented
as a ParallelArray
object whose shape
is [4, 5]
. Similarly, a 2D image in which
each pixel has an RGBA value can be represented as
a ParallelArray
object with shape [h,
w, 4]
. (The 4
comes from the fact
that an RGBA value is 4 bytes, one for each of the red,
green, and blue channels and one for the alpha channel.)
An empty ParallelArray
has
shape [0]
, and a
one-dimensional ParallelArray
with three
elements has shape [3]
.
Multi-dimensional ParallelArrays are also required to
be uniform (also
called rectangular). That is, the length of all
inner arrays in a particular dimension must be the
same. For example,
[[0, 1], [2], [3, 4]]
would be non-uniform
and is not allowed.
Immutable
ParallelArray
s are immutable once they are
created. ParallelArray
s are manipulated by
invoking methods on them, which produce and return new
ParallelArray
objects.
Constructors
ParallelArray
objects can be created in a variety of ways.
To create an empty ParallelArray
, call the
constructor with no arguments:
// Create an empty ParallelArray var pa0 = new ParallelArray();
We can pass a regular JavaScript array to
the ParallelArray
constructor, as well.
Here, we are creating a ParallelArray
out of
a nested JS array. The inner arrays will also become
ParallelArray
s.
// Create a ParallelArray out of a nested JS array. // Note that the inner arrays are also ParallelArrays. var pa1 = new ParallelArray([ [0,1], [2,3], [4,5] ]); // pa1 has shape [3,2]
We can also create a new ParallelArray
object from an existing one. The below example creates the
new ParallelArray
pa2
from an existing
one, pa1
. Since ParallelArray
s are
immutable, it does not matter to us at this point whether this creates
a copy of pa1
or whether it simply returns a new
reference to pa1
. We are guaranteed that
both pa1
and pa2
will have the same
structure and the same data in them for their lifetime.
// Create a ParallelArray from another ParallelArray var pa2 = new ParallelArray(pa1); // pa2 has the same contents and shape as pa1
We can also pass multiple arrays to
the ParallelArray
constructor. Here, the
arguments [0, 1]
and [2, 3]
each
have the shape [2]
. The
resulting ParallelArray
object will therefore
have the shape [2,2]
: it contains two
elements, each of which contain two elements.
// Create a ParallelArray from several other // ParallelArrays var pa3 = new ParallelArray([0,1], [2,3]); // pa3 has shape [2,2]
Yet another way to create ParallelArray
objects is by using a comprehension
constructor. We specify the length of
the ParallelArray
we want to construct, and
an elemental function. This elemental function
is invoked once for each element in the result array, with
that element's index as its argument.
In the below example, the length is 3
and the
elemental function is function(i) { return [i, i+1];
}
. For every index i
, the elemental
function returns a two-element array consisting
of i
and i+1
. After the
elemental function has been invoked for
every i
, we have the resulting
new ParallelArray
[[0, 1], [1, 2], [2,
3]]
(where the inner arrays are
all ParallelArray
s as well). Note that the
order in which the calls to the elemental function occur
is irrelevant. We will learn more about writing elemental
functions in the next section.
// Create a ParallelArray of length 3 and // shape [3,2], using the "comprehension" constructor var pa4 = new ParallelArray(3, function(i){return [i, i+1];});
The comprehension constructor can also create
multi-dimensional arrays. We need to supply a shape
vector instead of a length as the first argument to
the constructor, and an elemental function that takes a
vector index
as an argument. In this case,
the shape we specify is [3,2]
, and the
elemental function will be invoked with a two-element
vector argument iv
:
// Create a two-dimensional ParallelArray with shape // [3,2] using the comprehension constructor var pa5 = new ParallelArray([3, 2], function(iv) { return iv[0] * iv[1]; });
Finally, we can also create a
new ParallelArray
object directly from an
HTML5 <canvas>
object. The canvas
object is used for drawing 2D shapes, pictures and video
on a web page. We will see how this is useful later on,
when we build a video web app.
// This creates a ParallelArray with shape // [h, w, 4], // corresponding to the height and width of the // canvas and the RGBA value for each pixel. var pa6 = new ParallelArray(document.createElement("canvas"));
Methods
ParallelArray
objects created with the above
constructors come with several methods to manipulate
them. These methods typically produce a
new ParallelArray
object (except
the reduce
method, which produces a scalar
value).
Map
The first method we will discuss is map
,
probably the most prominent and well-known data-parallel
construct. The map
method expects a function
as its first argument that, given a single value, produces
a new value as its result. We call such
functions elemental functions, since they are
used to produce the elements of
a ParallelArray
object. The map
method computes a new ParallelArray
object
from an existing ParallelArray
object by
applying the provided elemental function to each element
of the source array and storing the result in the
corresponding position in the result array. Let us look
at a simple example:
var source = new ParallelArray([1,2,3,4,5]); var plusOne = source.map(function inc(v) { return v+1; });
Here, we define a new ParallelArray
object source
that contains the numbers 1 to
5. We then call the map
method of our source
array with the function inc
that returns its
argument, incremented by one. Thus, plusOne
contains the values 2 to 6. Also note
that plusOne
has the same shape as the
original array source
. The map
method is shape-preserving.
As you may have noticed, the map
method does
not provide an index to the elemental function it
calls. Not using an index has the advantage that no
indexing errors can be made. However, this added
simplicity comes at the cost of expressiveness:
with map
, one can not inspect neighboring
values in the array, as is commonly required for
convolution-style computations.
Combine
The combine
method addresses this
issue. Like map
, combine
can be
used to compute a new ParallelArray
object by
inspecting an existing ParallelArray
object's
elements. Unlike map
, the elemental function
of combine
is provided with access to the
current index in the source array, along with a reference
to the source array itself. Let us revisit the increment
example from the previous section. When
using combine
, the program can be expressed
as follows:
var source = new ParallelArray([1,2,3,4,5]); var plusOne = source.combine(function inc(i) { return this.get(i)+1; });
As before, we first produce our source array holding the
values 1 to 5. We then apply the combine
method using a slightly modified version of
the inc
function. It now expects an
index i
as
argument. Furthermore, source
is bound to the
variable this
within the elemental function's
body. We can access the value at index i
of
the source ParallelArray
by calling
the get
method with index i
as
argument.
As this example shows, using combine
requires
more code to implement inc
. However, we have
gained expressiveness. As an example, consider the
following code, which reverses the order of elements in an
array:
var source = new ParallelArray([1,2,3,4,5]); var reverse = source.combine(function rev(i) { return this.get(this.length-i[0]-1); });
In the elemental function rev
, we exploit the
access to the index that combine
provides to
compute the reversed index in the source array. Note that
computations are driven by the index position in
the result array, not the source array. We
therefore use the
expression this.length-i[0]-1
to compute the
source index of the reversed element for
position i
in the result array. This code
makes use of the length
property of the
ParallelArray object that gives the number of elements in
the array.
It is important to note here that the index i
is not a scalar value but actually a vector of indices. In
the above example, we therefore have to
use i[0]
in the computation of the source
index.
So far, all our examples have used one-dimensional
arrays, and the use of an index vector
in combine
has been of no
help. However, ParallelArray
objects in River
Trail can have multiple dimensions. The map
method always operates on the outermost dimension only,
that is, on the dimension that corresponds to the first
element of the shape vector. With combine
,
the programmer can choose how deep to traverse, using an
optional first argument to the combine
method. As an example, let us generalise the above
array-reversal operation into a transpose operation on
matrices:
var source = new ParallelArray([4,4], function (iv) { return iv[0]*iv[1]; }); var transpose = source.combine(2, function rev(iv) { return this.get([this.getShape()[0]-iv[0]-1, this.getShape()[1]-iv[1]-1]); });
We use a depth of 2 and, consequently, the index
vector iv
passed to the elemental function
contains two indices, corresponding to the two outermost
dimensions of the source array. This example also uses
the getShape
method, which is the
multi-dimensional counterpart to length
: it
returns a vector that gives the length for each dimension
of a ParallelArray
object. With this.getShape()[0]-iv[0]-1
, we
compute the index at the transposed position within the
source array for the first dimension. Note here
that get
also accepts an index vector as
argument.
Reduce
So far we have concentrated on parallel patterns that
produce a new array out of an existing
array. The reduce
method implements another
important parallel pattern: reduction operations. As the
name suggests, a reduction operation reduces the elements
in an array to a single result. A good example to start
with is computing the sum of all elements of an array:
var source = new ParallelArray([1,2,3,4,5]); var sum = source.reduce(function plus(a,b) { return a+b; });
As the example shows, the reduce
method
expects as its first argument an elemental function that,
given two values as arguments, produces a new value as its
result. In our example, we use plus
, which
adds two values, as the elemental function. A reduction
over plus
then defines the sum
operation.
Note here that the reduction may be computed in any order. In particular, this means that the elemental function has to be commutative and associative to ensure deterministic results. The River Trail runtime will not check this property, but results might be different between calls even on the same platform.
Scan
The reduce
operation reduces an array into a
single value. For some use cases, it can be interesting to
also store the intermediate results of a reduction. An
example is the prefix-sum operation that, given a
vector of numbers, computes another vector of numbers that
each position contains the sum of all elements of the
source vector up to that position. To implement this
parallel pattern, ParallelArray
s support a
scan
method.
var source = new ParallelArray([1,2,3,4,5]); var psum = source.scan(function plus(a,b) { return a+b; });
Here, we again use the plus
elemental
function from the previous reduction example. However,
when used with scan
, it now produces the
prefix-sum array of the source
array, which
is the ParallelArray
[1, 3, 6, 10,
15]
. If we were only interested in the last
element, 15
, we could have just
called reduce
; by using scan
, we
also get all the intermediate results.
The same rules of parallel execution that apply
to reduce
also apply to scan
:
the result of a scan
operation can be
computed in parallel, and therefore, we only guarantee a
deterministic result if the elemental function is
commutative and associative.
Scatter
We have seen that combine
can be used to
produce new arrays out of existing arrays. However,
with combine
we must define for each index
position in the result array how it is to be computed from
the source array. Sometimes, this mapping is difficult to
specify or costly to compute. Instead, it is preferable to
specify for a certain source index where it
should be stored in the result array. This pattern is
supported by the scatter
method in River
Trail. Here is an example:
var source = new ParallelArray([1,2,3,4,5]); var reorder = source.scatter([4,0,3,1,2]);
We first compute our source array source
. In
a second step, we apply the scatter
method
with a single argument: the scatter
vector [4,0,3,1,2]
. Thereby, we specify
that the first element of source
is to become
the fifth element of the result (indexing starts with 0),
the second value in source
becomes the first
in the result, and so on. Overall, the above example
produces the array [2, 4, 5, 3, 1]
.
What happens if we assign two source values to the same
position of the result? As scatter
is
potentially computed in parallel, the result would be
non-deterministic. Therefore, by default, the River Trail
runtime will throw an exception when result positions
conflict. However, in practice, conflicts often are
meaningful and can be deterministically resolved. For
these scenarios, scatter
accepts an optional
second argument: a conflict function
that,
given two conflicting values, produces a single
resolution.
On closer inspection, a conflict function is not enough to produce a fully specified result. If the scatter vector contains the same target index more than once, inevitably it will not fill all indices of the target array. To remedy this, we also allow the programmer to specify an optional default value that will be used for all index positions that are not defined otherwise.
var source = new ParallelArray([1,2,3,4,5]); var reorder = source.scatter([4,0,3,4,2], 3, function max(a, b) { return a>b?a:b; });
In the above example, the first and fourth element of the
source array are both written to the fifth element of the
result array, and no value is written to the second index
position. However, we provide the default
of 3
as the second argument
to scatter
. Lastly, we use a maximum as the
conflict resolution function. Thus, the fifth position in
the result is the maximum of 1
and 4
, which is 4
. Overall, we
get [2, 3, 5, 3, 4]
.
scatter
has a final optional argument: the
result length. By default, the length of the result will
be the same as the source array's. Using a scatter index
outside of the result's length will lead to a range
error. To spread elements out or reduce the total number
of elements, the new length has to be explicitly provided.
Putting it all together, we can implement
a histogram by means of scatter
. A
histogram computes the frequency or number of occurrences
of a value within a sample. Let's assume we have some
source data that contains values from zero to five. We
also need a second vector that contains weights. We will
just use a vector of ones here. Here is the setup:
var source = new ParallelArray([1,2,2,4,2,4,5]); var ones = source.map(function one(v) { return 1; });
We can then implement a histogram by scattering the ones
we have just produced to the correct buckets, that is, we
interpret the source data as scatter indices. Conflicts
are resolved by adding values, ultimately counting how
many times a one has been written to a certain
position. The natural default value
is 0
. Lastly, we have to provide a new
length, since we reduce multiple source values into a
single frequency value. Here is what it looks like:
var hist = ones.scatter(source, 0, function plus(a,b) { return a+b;}, 6);
Filter
With scatter
, we can now create
new ParallelArray
objects by reordering the
elements of an existing ParallelArray
object. However, the result still contains all elements of
the source array, modulo conflicts. We are still missing a
means to simply drop elements from an array. This is where
the filter
method comes in. It expects an
elemental function as its sole argument. The elemental
function has the same signature as
combine
: it gets the current index as an
argument, and the source object is bound
to this
within its body. However, unlike
with combine
, the elemental function passed
to filter
is expected to return a truth value
that indicates whether the source array's element at the
given index position should be included in the result. Let
us look at an example:
var source = new ParallelArray([1,2,3,4,5]); var even = source.filter(function even(iv) { return (this.get(iv) % 2) == 0; });
As before, we first produce a source array containing the
values one to five. Next, we apply the filter
method using even
as the elemental function,
which returns true
for all even
elements. Thus, we remove all odd elements from the source
array, leading to the result [2, 4]
.
Parallel Video Filters with River Trail
In this hands-on tutorial, we will use River Trail to parallelize the computationally intensive parts of a HTML5 video application. If you haven't already, read the above API method descriptions and come back. It is also a good idea to have the method descriptions to refer to while reading through this tutorial.
Setup
Download and Install
If you're using Windows or Linux, you will need to download and install the Intel OpenCL SDK if you haven't done so already (click the "Free Download" button on that page and fill out the form on the following page). Mac users can skip this step, since OpenCL is already packaged as part of OS X.
Also, if you have not done so already, download and install the River Trail Firefox extension. For this tutorial, you do not need to build the extension from source; you only need to install the pre-built extension.
Also, clone the River Trail repository, or download and unzip a snapshot of it.
Finally, we will use the 10-minute
film Big Buck
Bunny for the tutorial. Download
an .ogv
file of the movie, and save it in
the tutorial/src/media/
directory of the
River Trail repository that you just cloned or
unzipped.
Serving the Tutorial Files
Because of Firefox's security policies, for this tutorial
you will need to run a web server that serves files from
the River Trail directory. For instance, if you have
Python installed, running the shell command python
-m SimpleHTTPServer
in the directory where you
cloned or unzipped the River Trail repository is one way
to quickly serve files:
~/repos/RiverTrail$ python -m SimpleHTTPServer Serving HTTP on <IP_ADDRESS> port 8000 ...
Note that you need to serve the files from the
top-level directory where you cloned or unzipped
the repo, not the tutorial/src/
directory!
You should then be able to navigate to,
e.g., http://localhost:8000/tutorial/src/
in your browser and see the tutorial skeleton.
Verify
To verify that the River Trail extension is installed and talking to OpenCL, go to the interactive shell. You should see a message saying:
River Trail Extension loaded. Enabling parallel
mode...
If you see this, then the extension has been installed correctly. However, if you only see something like:
River Trail not found. Enabling sequential
mode...
then the extension is disabled or not installed, or not interfacing with OpenCL properly. In the Firefox Add-ons Manager, make sure that the extension is installed and enabled, and try reloading the page. If you just installed OpenCL, try disabling and re-enabling the extension.
Once the extension is installed and working correctly, you can try running the sample applications before moving on to the rest of this tutorial.
The Skeleton
In the River Trail repo, the tutorial/src/
directory contains a skeleton for the video application
that you can start with. If you've followed the above
directions for serving the tutorial files, you should
already be able to load up,
e.g., http://localhost:8000/tutorial/src/
in Firefox and see the default screen for the application
skeleton: The large box in the
center is
a canvas
that is used for rendering the video output. The video
input is either an HTML5 video stream embedded in
a video
tag, or live video captured by a webcam.
On the right of the screen, you will see the various filters that can applied to this input video stream: sepia toning, lightening, desaturation, and so on. Click on the box in the center screen to start playback of the Big Buck Bunny video and try out these filters. To switch to webcam video, click the "Webcam" toggle in the top left corner.
The sequential JavaScript versions of the filters on the right are already implemented. In this tutorial, we will implement the parallel versions using River Trail. Before we dive into implementation, let's look at the basics of manipulating video using the Canvas API.
Manipulating Pixels on Canvas
Open up tutorial/src/main-skeleton.js
in your
favorite code editor. This file implements all the
functionality in this web application except the filters
themselves. When you load the page,
the doLoad
function is called after the
body of the page has been loaded. This function sets up
the drawing contexts, initializes the list of filters (or
kernels), and assigns a click event handler for the output
canvas.
The computeFrame
function is the workhorse
that reads an input video frame, applies all the selected
filters on it, and produces an output frame that is
written to the output canvas context. The code below
shows how a single frame from an HTML video element is
drawn to a 2D context associated with a canvas
element.
output_context.drawImage(video, 0, 0, output_canvas.width, output_canvas.height);
After this video frame is drawn to canvas, we need to
capture the pixels so that we can apply our filters. This
is done by calling
getImageData
on the context containing the image we want to
capture.
frame = output_context.getImageData(0, 0, input_canvas.width, input_canvas.height); len = frame.data.length; w = frame.width; h = frame.height;
Now we have
an ImageData
object called frame
. The data
attribute of frame
contains the pixel
information, and the width
and height
attributes contain the dimensions
of the image we have captured.
The data
attribute contains RGBA values for
each pixel in a row-major format. That is, for a frame
with h rows of pixels and w columns, it
contains a one-dimensional array of length w
* h * 4, as shown below:
So, for example, to get the color values of a pixel in the 100th row and 50th column in the image, we could write the following code:
var red = frame.data[100*w*4 + 50*4 + 0]; var green = frame.data[100*w*4 + 50*4 + 1]; var blue = frame.data[100*w*4 + 50*4 + 2]; var alpha = frame.data[100*w*4 + 50*4 + 3];
To set, for example, the red value of this pixel, simply
write the new value at the offset shown above in
the frame.data
buffer.
Sepia Toning
Sepia toning is a process performed on black-and-white print photographs to give them a warmer color. This filter simulates the sepia toning process on digital photographs or video.
Let us first look at the sequential implementation of this
filter in the function
called sepia_sequential
in tutorial/src/filters-skeleton.js
.
function sepia_sequential(frame, len, w, h, ctx) { var pix = frame.data; var avg = 0; var r = 0, g = 0, b = 0; for(var i = 0 ; i < len; i = i+4) { r = (pix[i] * 0.393 + pix[i+1] * 0.769 + pix[i+2] * 0.189); g = (pix[i] * 0.349 + pix[i+1] * 0.686 + pix[i+2] * 0.168); b = (pix[i] * 0.272 + pix[i+1] * 0.534 + pix[i+2] * 0.131); if(r > 255) r = 255; if(g > 255) g = 255; if(b > 255) b = 255; if(r < 0) r = 0; if(g < 0) g = 0; if(b < 0) b = 0; pix[i] = r; pix[i+1] = g; pix[i+2] = b; } ctx.putImageData(frame, 0, 0); }
Recall that the frame.data
buffer contains
color values as a linear sequence of RBGA
values. The for
loop
in sepia_sequential
iterates over this
buffer, and for each pixel it reads the red, green and
blue values (which are
in pix[i]
, pix[i+1]
,
and pix[i+2]
, respectively). It computes a
weighted average of these colors to produce the new red,
green, and blue values for that pixel. It then clamps the
new red, green and blue values to the range [0, 255] and
writes them back into the frame.data
buffer. When the loop is finished, we have replaced the
RGB values for all the pixels with their sepia-toned
values and we can now write the image back into the output
context ctx
with
the putImageData
method. The result should look like this (image on the
left is the original frame, image on the right is the
output):
Can we make this parallel?
If you look closely at the sepia_sequential
function above, you'll notice that each pixel can be
processed independently of all other pixels, as its new
RGB values depend only on its current RGB
values. Furthermore, each iteration of
the for
loop does not produce or consume side
effects. This makes it easy to parallelize this operation
with River Trail.
Recall that the ParallelArray
type has a
constructor that takes a canvas object as an argument and
returns a freshly minted ParallelArray
object
containing the pixel data from that canvas.
var pa = new ParallelArray(canvas);
This creates a
three-dimensional ParallelArray
pa
with shape [h, w, 4]
that looks like the
following:
So, for the pixel on the canvas at coordinates (x,
y)
, pa.get(x, y, 0)
will contain the
red value, pa.get(x, y, 1)
will contain the
green value, and pa.get(x, y, 2)
will contain
the blue value. In computeFrame
, we create a
new ParallelArray
from this canvas:
else if (execution_mode === "parallel") { frame = output_context.getImageData(0, 0, input_canvas.width, input_canvas.height); stage_output = stage_input = new ParallelArray(input_canvas); w = input_canvas.width; h = input_canvas.height; }
Here, stage_input
and stage_output
are
ParallelArray
objects that contain the input
and output pixel data for each filtering "stage". Now
let's look at the code that causes the filters to be
applied:
if(execution_mode === "parallel") { switch(filterName) { ... case "sepia": /* Add your code here... */ break; ... } // Make this filter's output the input to the next filter. stage_input = stage_output; }
To implement a particular filter, we add code to produce a
new ParallelArray
object containing the
transformed pixel data and assign it to
stage_output
. For example, for the sepia
filter, we would write:
case "sepia": stage_output = /* new ParallelArray containing transformed pixel data */; break;
Now, all we have to do above is produce a
new ParallelArray
object on the right-hand
side of the statement above. We can produce this
new ParallelArray
one of two ways: by using
the powerful ParallelArray
constructor, or by
using the combine
method. Let us look at the
constructor approach first.
Recall that the comprehension constructor has the following form:
var pa = new ParallelArray(shape_vector, elemental_function, arg1, arg2, ...);
where elemental_function
is a JavaScript
function that produces the value of an element at a
particular index in pa
.
Recall that the input to our filter stage_input
is a
ParallelArray
with shape [h, w,
4]
. You can think of it as a two-dimensional
ParallelArray
with shape [h, w]
in which each element (which corresponds to a single
pixel) is itself a ParallelArray
of
shape [4]
. The
output ParallelArray
we will produce will
have this same shape: we will produce a new
ParallelArray
of shape [h, w]
in
which each element has a shape of
[4]
, thereby making
the ParallelArray
have a final shape
of [h, w, 4]
. We can do so as follows:
case "sepia": stage_output = new ParallelArray([h, w], kernelName, stage_input); break;
The first argument, [h, w]
, specifies the
shape of the new ParallelArray
we want to
create. kernelName
is the elemental function
for the sepia filter (which we will talk about in a
moment), and stage_input
is an argument to
this elemental function. Hence this line of code creates a
new ParallelArray
object of shape [h,
w]
in which each element is produced by executing
the function kernelName
. This
new ParallelArray
is then assigned
to stage_output
.
Finally, we have to write the elemental function that
produces the color values for each pixel. We can think of
it as a function that, when supplied indices, produces
the ParallelArray
elements at those
indices.
Open the
file tutorial/src/filters-skeleton.js
in your
editor and find the stub for
the sepia_parallel
function:
function sepia_parallel(indices, frame) { /* Add your code here... */ }
The first argument indices
is a vector of
indices from the iteration space [h,
w]
. indices[0]
is the index along the
1st dimension (from 0
to h-1
)
and indices[1]
is the index along the 2nd
dimension (from 0
to w-1
).
The frame
argument is
the ParallelArray
object that was passed as
an argument to the constructor above.
Now let's fill in the body of the elemental function:
function sepia_parallel(indices, frame) { var i = indices[0]; var j = indices[1]; var old_r = frame[i][j][0]; var old_g = frame[i][j][1]; var old_b = frame[i][j][2]; var a = frame[i][j][3]; var r = old_r*0.393 + old_g*0.769 + old_b*0.189; var g = old_r*0.349 + old_g*0.686 + old_b*0.168; var b = old_r*0.272 + old_g*0.534 + old_b*0.131; return [r, g, b, a]; }
In this code, we grab the indices i
and j
and read the RGBA values from the
input frame
. Then, just like in the
sequential version of the code, we mix these colors and
return a four-element array consisting of the new color
values for the pixel at position (i, j)
.
And that's it. After filling in
the sepia_parallel
implementation, select the
"River Trail" toggle on the top right of the app screen
and play the video (perhaps after restarting your web
server). You should see the same sepia toning effect you
saw with the sequential implementation.
The River Trail compiler takes your elemental function and parallelizes its application over the iteration space. Note that you did not have to create or manage threads, write any non-JavaScript code, or deal with race conditions and deadlocks.
We could have also implemented the sepia filter by
calling the combine
method on
the stage_input
ParallelArray
. As
an optional exercise, try implementing a version of the sepia
filter using combine
.
One last thing to note: although the sepia filter is being
applied to every pixel in parallel, instead of in a
sequential loop as before, there may not be any obvious
parallel speedup, because the cost of allocating
new ParallelArray
s overtakes the advantage of
parallel speedup for this particular filter. In general,
parallel execution is worthwhile only when the amount of
work done is enough to offset the overhead of doing so.
Next, we'll look at a more sophisticated video filter for
which parallel execution is more worth the cost.
Stereoscopic 3D
Let's consider a slightly more complicated filter: one that transforms the input video stream into 3D in real time. Stereoscopic 3D is a method of creating the illusion of depth by simulating the different images that a normal pair of eyes (see binocular disparity). In essence, when looking at a three-dimensional object, our eyes each see a slightly different 2D image due to the distance between them on our head. Our brain uses this difference to extract depth information from these 2D images. To implement stereoscopic 3D, we will use this same methodology: we present two 2D images, each one slightly different from the other to the viewer's eyes. The difference between these images—let's call them left-eye and right-eye images—are two-fold. Firstly, the right-eye image is offset slightly to the left in the horizontal direction. Secondly, the red channel is masked off in the right-eye image and the blue and green channels are masked off in the left-eye image. The result looks something like the following (image on the left is the original, image on the right is the 3D version):
The function A3D_sequential
in tutorial/src/filters-skeleton.js
implements a sequential version of the stereoscopic 3D
filter. Let's look at this sequential implementation:
function A3D_sequential(frame, len, w, h, dist, ctx) { var pix = frame.data; var new_pix = new Array(len); var r1, g1, b1; var r2, g2, b2; var rb, gb, bb; for(var i = 0 ; i < len; i = i+4) { var j = i-(dist*4); if(Math.floor(j/(w*4)) !== Math.floor(i/(w*4))) j = i; r1 = pix[i]; g1 = pix[i+1]; b1 = pix[i+2]; r2 = pix[j]; g2 = pix[j+1]; b2 = pix[j+2]; var left = dubois_blend_left(r1, g1, b1); var right = dubois_blend_right(r2, g2, b2); rb = left[0] + right[0] + 0.5; gb = left[1] + right[1] + 0.5; bb = left[2] + right[2] + 0.5; new_pix[i] = rb; new_pix[i+1] = gb; new_pix[i+2] = bb; new_pix[i+3] = pix[i+3]; } for(var i = 0 ; i < len; i = i+1) { pix[i] = new_pix[i]; } ctx.putImageData(frame, 0, 0); }
Don't worry about the details of the implementation just
yet; just note that the structure is somewhat similar to
that of the sepia filter. One important distinction is
that while the sepia filter updated the pixel data
in-place, we cannot do that here; processing each pixel
involves reading a neighboring pixel. If we updated
in-place, we could end up reading the updated value for
this neighboring pixel. In other words, there is a
write-after-read loop-carried dependence here. So, instead
of updating in place, we allocate a new
buffer new_pix
for holding the updated values
of the pixels.
Let's start implementing the parallel version. What we
want to implement is an operation that reads the pixel
data in the input ParallelArray
object and
produces new pixel data into another. So, just as with the
sepia filter, we can use the ParallelArray
constructor with an elemental function.
In tutorial/src/main-skeleton.js
, modify
the computeFrame
function to call
the ParallelArray
constructor as follows:
if(execution_mode === "parallel") { switch(filterName) { ... case "A3D": stage_output = new ParallelArray([h, w], kernelName, stage_input, w, h); break; ... } ... }
Then, in tutorial/src/filters-skeleton.js
,
find the stub for the A3D_parallel
elemental
function and begin modifying it as follows:
function A3D_parallel(indices, frame, w, h, dist) { var i = indices[0]; var j = indices[1]; }
Each pair (i, j)
corresponds to a pixel in
the output frame. Recall that each pixel in the output
frame is generated by blending two images, the left-eye
and right-eye images, the latter being a copy of the
former except shifted along the negative x-axis (that is,
to the left).
Let's call the pixel frame[i][j]
the left-eye
pixel. To get the right-eye pixel we will simply read a
neighbor of the left eye pixel that is some distance
away. This distance is given to us as the
argument dist
(which is updated every time
the 3D slider on the UI is moved):
function A3D_parallel(indices, frame, w, h, dist) { var i = indices[0]; var j = indices[1]; var k = j - dist; if(k < 0) k = j; }
Now frame[i][k]
is the right-eye pixel. We
need to guard against the fact that if dist
large, we cannot get the right-eye pixel, as it would be
outside the frame we have. There are several approaches
for dealing with this situation; for simplicity, we simply
make the right-eye pixel the same as the left-eye pixel.
The line if(k < 0) k = j
accomplishes this.
Now, let's mask off the appropriate colors in each of the
left and right eye pixels. We use
the dubois_blend_left
and dubois_blend_right
functions for
this. You don't have to understand the details of these
functions for this tutorial; just that they take in an RGB
tuple and produce a new RGB tuple that is appropriately
masked for the right and left eyes. For details on these
functions, read about
the Dubois
method.
function A3D_parallel(indices, frame, w, h, dist) { var i = indices[0]; var j = indices[1]; var k = j - dist; if(k < 0) k = j; var r_l = frame[i][j][0]; var g_l = frame[i][j][1]; var b_l = frame[i][j][2]; var r_r = frame[i][k][0]; var g_r = frame[i][k][1]; var b_r = frame[i][k][2]; var left = dubois_blend_left(r_l, g_l, b_l); var right = dubois_blend_right(r_r, g_r, b_r); }
Now we have the separately masked and blended left- and right-eye pixels. We now blend these two pixels together to produce the final color values:
function A3D_parallel(indices, frame, w, h, dist) { var i = indices[0]; var j = indices[1]; var k = j - dist; if(k < 0) k = j; var r_l = frame[i][j][0]; var g_l = frame[i][j][1]; var b_l = frame[i][j][2]; var r_r = frame[i][k][0]; var g_r = frame[i][k][1]; var b_r = frame[i][k][2]; var left = dubois_blend_left(r_l, g_l, b_l); var right = dubois_blend_right(r_r, g_r, b_r); var rb = left[0] + right[0] + 0.5; var gb = left[1] + right[1] + 0.5; var bb = left[2] + right[2] + 0.5; return [rb, gb, bb, 255]; }
And that's it. With the "River Trail" execution mode enabled, play the video and select the 3D filter. With red/cyan 3D glasses, you should be able to notice the depth effect. Without the glasses, this is how it looks (original video frame on the left, with the filter applied on the right):
Edge Detection and Sharpening
Finally, let's move on to something a little more complicated: edge detection and sharpening. Edge detection is a common tool used in digital image processing and computer vision that seeks to highlight points in an image where the image brightness changes sharply. Select the "Edge Detect" effect and play the video to see the result of the effect.
There are many diverse approaches to edge detection, but we are interested in the 2D discrete convolution-based approach:
At a high level, discrete convolution on a single pixel in an image involves taking this pixel (shown in dark blue above) and computing the weighted sum of its neighbors that lie within some specific window to produce the output pixel (shown in dark red above). The weights and the window are described by the convolution kernel. This process is repeated for all the pixels to produce the final output of the convolution.
Consider a 5x5 matrix convolved with a 3x3 kernel as shown below. For simplicity, we are only interested in the input element highlighted in blue.
The weighted sum for this element is:
(1*2) + (1*3) + (2*0) +
(2*0) + (2*1) + (1*3) +
(1*3) + (5*0) + (0*3)
= 13.
The value of the element in the output matrix is therefore 13.
The edge_detect_sequential
function
in tutorial/src/filters-skeleton.js
gives a
sequential implementation of edge detection. Don't worry
about understanding it in detail yet.
Let us try and implement this filter using River
Trail. In tutorial/src/filter-skeleton.js
,
find the stub for the edge_detect_parallel
function and modify it as folllows:
function edge_detect_parallel(index, frame, w, h) { var m = index[0]; var n = index[1]; var ekernel = [[1,1,1,1,1], [1,2,2,2,1], [1,2,-32,2,1], [1,2,2,2,1], [1,1,1,1,1]]; var kernel_width = (ekernel.length-1)/2; // will be '2' for this kernel var neighbor_sum = [0, 0, 0, 255]; }
The first two lines of the body are the same as the
beginning of the parallel sepia implementation. (m,
n)
is now the position of a pixel in the
input ParallelArray
frame
. The
variable ekernel
is the 5x5 kernel we will be
using for convolution (you can copy this over from the
sequential version). And we also need a 4-element
array neighbor_sum
to hold the weighted
sum.
At this point we have an input frame (frame
)
and a specific pixel (m, n)
which we will
call the input pixel. Now we need to define a
"window" of neighboring pixels such that this window is
centered at this input pixel. We can define such a window
by using a nested loop, as follows:
function edge_detect_parallel(index, frame, w, h) { var m = index[0]; var n = index[1]; var ekernel = [[1,1,1,1,1], [1,2,2,2,1], [1,2,-32,2,1], [1,2,2,2,1], [1,1,1,1,1]]; var kernel_width = (ekernel.length-1)/2; // will be '2' for this kernel var neighbor_sum = [0, 0, 0, 255]; for(var i = -1*kernel_width; i <= kernel_width; i++) { for(var j = -1*kernel_width; j <= kernel_width; j++) { var x = m+i; var y = n+j; } } }
Now we have an iteration space (x, y)
that
goes from [m-2, n-2]
to [m+2,
n+2]
, which is precisely the set of neighboring
pixels we want to add up. That
is, frame[x][y]
is a pixel within the
neighbor window. So let's add them up with the weights
from ekernel
:
function edge_detect_parallel(index, frame, w, h) { var m = index[0]; var n = index[1]; var ekernel = [[1,1,1,1,1], [1,2,2,2,1], [1,2,-32,2,1], [1,2,2,2,1], [1,1,1,1,1]]; var kernel_width = (ekernel.length-1)/2; // will be '2' for this kernel var neighbor_sum = [0, 0, 0, 255]; var weight; for(var i = -1*kernel_width; i <= kernel_width; i++) { for(var j = -1*kernel_width; j <= kernel_width; j++) { var x = m+i; var y = n+j; weight = ekernel[i+kernel_width][j+kernel_width]; neighbor_sum[0] += frame[x][y][0] * weight; neighbor_sum[1] += frame[x][y][1] * weight; neighbor_sum[2] += frame[x][y][2] * weight; } } }
There is a detail we have ignored so far: what do we do
with pixels on the borders of the image for which the
neighbor window goes out of the image? There are several
approaches to handle this situation. We could pad the
original ParallelArray
on all four sides so
that the neighbor window is guaranteed to never go out of
bounds. Another approach is to wrap around the image. For
simplicity, we will simply clamp the neighbor window to
the borders of the image.
var x = m+i; var y = n+j; if(x < 0) x = 0; if(x > h-1) x = h-1; if(y < 0) y = 0; if(y > w-1) y = w-1; weight = ekernel[i+kernel_width][j+kernel_width];
After the loops are done, we have our weighted sum for
each color in neighbor_sum
, which we return.
The completed edge_detect_parallel
function
looks like this:
function edge_detect_parallel(index, frame, w, h) { var m = index[0]; var n = index[1]; var ekernel = [[1,1,1,1,1], [1,2,2,2,1], [1,2,-32,2,1], [1,2,2,2,1], [1,1,1,1,1]]; var kernel_width = (ekernel.length-1)/2; // will be '2' for this kernel var neighbor_sum = [0, 0, 0, 255]; var weight; for(var i = -1*kernel_width; i <= kernel_width; i++) { for(var j = -1*kernel_width; j <= kernel_width; j++) { var x = m+i; var y = n+j; if(x < 0) x = 0; if(x > h-1) x = h-1; if(y < 0) y = 0; if(y > w-1) y = w-1; weight = ekernel[i+kernel_width][j+kernel_width]; neighbor_sum[0] += frame[x][y][0] * weight; neighbor_sum[1] += frame[x][y][1] * weight; neighbor_sum[2] += frame[x][y][2] * weight; } } }
Summary
The River Trail programming model allows programmers to utilize hardware parallelism on clients at the SIMD unit level as well as the multi-core level. With its high-level API, programmers do not have to explicitly manage threads or orchestrate shared data synchronization or scheduling. Moreover, since the API is JavaScript, programmers do not have to learn a new language or semantics to use it.
To learn more about River Trail, visit the project on GitHub.


Toggle navigation
Search for Users
About Us
Compare Pictures
Leaderboards
Profile
Menu
All Users
FAQ
AGB's
Shootouts
Create shootout
×
You have completed the shootout.
Check out the shootout ranking:
×
Help
Please enter the name of your new shootout
and choose a category for your shootout.
If you choose 'Usershootout' as your category, all of your pictures will be added to the shootout.
If you choose a different category, the pictures belonging to that category from all users will be added to the shootout.
Create your shootout!
Name
Select a category:
User Shootout
Animals
Art
Fashion
Food
Nature
Portrait
Compare Pictures
Click on the one you like better
Help
To participate in a shootout select one listed in the sidebar on the left.
Then click on the picture you like better out of the two pictures displayed in the center of the page.
lick on the button "Create Shootout" to create your own new shootout.


-
$allowed_assoc_types
-
-
$allowed_assoc_types
-
-
$allowed_assoc_types
-
-
$allowed_assoc_types
-
-
$allowed_assoc_types
-
-
$allowed_assoc_types
-
-
add
-
-
add
-
Add an attribute to this attribute exchange request.
-
addAlias
-
-
addExtension
-
-
addExtension
-
Add an extension to this checkid request.
-
addExtensionArg
-
Add an extension argument to this OpenID authentication request.
-
addPolicyURI
-
Add an acceptable authentication policy URI to this request
-
addPolicyURI
-
Add a authentication policy to this response
-
addValue
-
Add a single value for the given attribute type to the message. If there are already values specified for this type, this value will be sent in addition to the values already specified.
-
allRequestedFields
-
A list of all of the simple registration fields that were requested, whether they were required or optional.
-
answer
-
-
answer
-
-
answer
-
-
answer
-
-
answer
-
-
answerUnsupported
-
-
associationKey
-
Memcache key is prefixed with 'openid_association_' string.
-
associationServerKey
-
Memcache key is prefixed with 'openid_association_' string.
-
attributes
-
Return the attributes of a specified node.
-
attributes
-
-
attributes
-
-
Association.php
-
-
AX.php
-
-
Auth_OpenID_AlreadySigned
-
An error that indicates that a response was already signed.
-
Auth_OpenID_AssociateRequest
-
A request to associate with the server.
-
Auth_OpenID_AssociateRequest
-
-
Auth_OpenID_Association
-
This class represents an association between a server and a consumer. In general, users of this library will never see instances of this object. The only exception is if you implement a custom
Auth_OpenID_OpenIDStore.
-
Auth_OpenID_AuthRequest
-
Initialize an authentication request with the specified token, association, and endpoint.
-
Auth_OpenID_AuthRequest
-
This class represents an authentication request from a consumer to an OpenID server.
-
Auth_OpenID_AX
-
AX utility class.
-
Auth_OpenID_AX_AttrInfo
-
Represents a single attribute in an attribute exchange request. This should be added to an AXRequest object in order to request the attribute.
-
Auth_OpenID_AX_AttrInfo
-
Construct an attribute information object. Do not call this directly; call make(...) instead.
-
Auth_OpenID_AX_checkAlias
-
Check an alias for invalid characters; raise AXError if any are found. Return None if the alias is valid.
-
Auth_OpenID_AX_Error
-
Results from data that does not meet the attribute exchange 1.0 specification
-
Auth_OpenID_AX_Error
-
-
Auth_OpenID_AX_FetchRequest
-
-
Auth_OpenID_AX_FetchRequest
-
An attribute exchange 'fetch_request' message. This message is sent by a relying party when it wishes to obtain attributes about the subject of an OpenID authentication request.
-
Auth_OpenID_AX_FetchResponse
-
-
Auth_OpenID_AX_FetchResponse
-
A fetch_response attribute exchange message.
-
Auth_OpenID_AX_KeyValueMessage
-
-
Auth_OpenID_AX_KeyValueMessage
-
An abstract class that implements a message that has attribute keys and values. It contains the common code between fetch_response and store_request.
-
Auth_OpenID_AX_Message
-
Abstract class containing common code for attribute exchange messages.
-
Auth_OpenID_AX_MINIMUM_SUPPORTED_ALIAS_LENGTH
-
-
Auth_OpenID_AX_NS_URI
-
-
Auth_OpenID_AX_StoreRequest
-
A store request attribute exchange message representation.
-
Auth_OpenID_AX_StoreResponse
-
-
Auth_OpenID_AX_StoreResponse
-
An indication that the store request was processed along with this OpenID transaction. Use make(), NOT the constructor, to create response objects.
-
Auth_OpenID_AX_toTypeURIs
-
Given a namespace mapping and a string containing a comma-separated list of namespace aliases, return a list of type URIs that correspond to those aliases.
-
Auth_OpenID_AX_UNLIMITED_VALUES
-
-
Auth_OpenID_BARE_NS
-
-
Auth_OpenID_CANCEL
-
Status to indicate cancellation of OpenID authentication.
-
Auth_OpenID_CancelResponse
-
A response with a status of Auth_OpenID_CANCEL. Indicates that the user cancelled the OpenID authentication request. This has two relevant attributes:
-
Auth_OpenID_CancelResponse
-
-
Auth_OpenID_CheckAuthRequest
-
-
Auth_OpenID_CheckAuthRequest
-
A request to verify the validity of a previous response.
-
Auth_OpenID_checkFieldName
-
Check to see that the given value is a valid simple registration data field name. Return true if so, false if not.
-
Auth_OpenID_CheckIDRequest
-
-
Auth_OpenID_CheckIDRequest
-
A request to confirm the identity of a user.
-
Auth_OpenID_checkSessionType
-
-
Auth_OpenID_checkTimestamp
-
-
Auth_OpenID_Consumer
-
Initialize a Consumer instance.
-
Auth_OpenID_Consumer
-
An OpenID consumer implementation that performs discovery and does session management. See the Consumer.php file documentation for more information.
-
Auth_OpenID_ConsumerResponse
-
The base class for responses from the Auth_OpenID_Consumer.
-
Auth_OpenID_DatabaseConnection
-
An empty base class intended to emulate PEAR connection
-
Auth_OpenID_Decoder
-
-
Auth_OpenID_Decoder
-
Decode an incoming query into an Auth_OpenID_Request.
-
Auth_OpenID_DiffieHellmanSHA1ConsumerSession
-
A class implementing HMAC/DH-SHA1 consumer sessions.
-
Auth_OpenID_DiffieHellmanSHA1ConsumerSession
-
-
Auth_OpenID_DiffieHellmanSHA1ServerSession
-
-
Auth_OpenID_DiffieHellmanSHA1ServerSession
-
A class implementing DH-SHA1 server sessions.
-
Auth_OpenID_DiffieHellmanSHA256ConsumerSession
-
A class implementing HMAC/DH-SHA256 consumer sessions.
-
Auth_OpenID_DiffieHellmanSHA256ServerSession
-
A class implementing DH-SHA256 server sessions.
-
Auth_OpenID_digits
-
-
Auth_OpenID_DO_ABOUT
-
Status code returned when there were no OpenID arguments passed. This code indicates that the caller should return a 200 OK response and display an HTML page that says that this is an OpenID server endpoint.
-
Auth_OpenID_DO_AUTH
-
Status code returned when the caller needs to authenticate the
-
Auth_OpenID_DumbStore
-
This is a store for use in the worst case, when you have no way of saving state on the consumer site. Using this store makes the consumer vulnerable to replay attacks, as it's unable to use nonces. Avoid using this store if it is at all possible.
-
Auth_OpenID_DumbStore
-
Creates a new
Auth_OpenID_DumbStore instance. For the security of the tokens generated by the library, this class attempts to at least have a secure implementation of getAuthKey.
-
Auth_OpenID_Encoder
-
-
Auth_OpenID_EncodingError
-
An error that indicates an encoding problem occurred.
-
Auth_OpenID_EncodingError
-
-
Auth_OpenID_Extension
-
A base class for accessing extension request and response data for the OpenID 2 protocol.
-
Auth_OpenID_FAILURE
-
This is the status code completeAuth returns when the value it received indicated an invalid login.
-
Auth_OpenID_FailureResponse
-
A response with a status of Auth_OpenID_FAILURE. Indicates that the OpenID protocol has failed. This could be locally or remotely triggered. This has three relevant attributes:
-
Auth_OpenID_FailureResponse
-
-
Auth_OpenID_FileStore
-
This is a filesystem-based store for OpenID associations and
-
Auth_OpenID_FileStore
-
Initializes a new
Auth_OpenID_FileStore. This initializes the nonce and association directories, which are subdirectories of the directory passed in.
-
Auth_OpenID_GenericConsumer
-
-
Auth_OpenID_GenericConsumer
-
This class is the interface to the OpenID consumer logic.
-
Auth_OpenID_getAllAssociationTypes
-
-
Auth_OpenID_getAvailableSessionTypes
-
Returns available session types.

-
Auth_OpenID_LOCAL_ERROR
-
Status code returned by the server when the only option is to show an error page, since we do not have enough information to redirect back to the consumer. The associated value is an error message that should be displayed on an HTML error page.
-
Auth_OpenID_MalformedReturnURL
-
-
Auth_OpenID_MalformedReturnURL
-
An error indicating that the return_to URL is malformed.
-
Auth_OpenID_MalformedTrustRoot
-
-
Auth_OpenID_MalformedTrustRoot
-
This error is returned when the trust_root value is malformed.
-
Auth_OpenID_Mapping
-
An Auth_OpenID_Mapping maintains a mapping from arbitrary keys to arbitrary values. (This is unlike an ordinary PHP array, whose keys may be only simple scalars.)
-
Auth_OpenID_Mapping
-
Initialize a mapping. If $classic_array is specified, its keys and values are used to populate the mapping.
-
Auth_OpenID_MemcachedStore
-
-
Auth_OpenID_MemcachedStore
-
This is a memcached-based store for OpenID associations and nonces.
-
Auth_OpenID_Message
-
In the implementation of this object, null represents the global namespace as well as a namespace with no key.
-
Auth_OpenID_Message
-
-
Auth_OpenID_mkNonce
-
-
Auth_OpenID_MySQLStore
-
An SQL store that uses MySQL as its backend.
-
Auth_OpenID_NamespaceMap
-
Maintains a bijective map between namespace uris and aliases.
-
Auth_OpenID_NamespaceMap
-
-
Auth_OpenID_Nonce_CHRS
-
This is the characters that the nonces are made from.
-
Auth_OpenID_Nonce_REGEX
-
-
Auth_OpenID_Nonce_TIME_FMT
-
-
Auth_OpenID_NoReturnToError
-
-
Auth_OpenID_NoReturnToError
-
Error returned by the server code when a return_to is absent from a request.
-
Auth_OpenID_NO_DEFAULT
-
-
Auth_OpenID_NULL_NAMESPACE
-
-
Auth_OpenID_OPENID1_NS
-
-
Auth_OpenID_OPENID1_URL_LIMIT
-
-
Auth_OpenID_OPENID2_NS
-
-
Auth_OpenID_OpenIDStore
-
This is the interface for the store objects the OpenID library
-
Auth_OpenID_OPENID_NS
-
-
Auth_OpenID_PAPE_NS_URI
-
-
Auth_OpenID_PAPE_Request
-
A Provider Authentication Policy request, sent from a relying party to a provider
-
Auth_OpenID_PAPE_Request
-
-
Auth_OpenID_PAPE_Response
-
-
Auth_OpenID_PAPE_Response
-
A Provider Authentication Policy response, sent from a provider to
-
Auth_OpenID_PARSE_ERROR
-
This is the status code beginAuth returns when the page fetched from the entered OpenID URL doesn't contain the necessary link tags to function as an identity page.
-
Auth_OpenID_PlainTextConsumerSession
-
A class implementing plaintext consumer sessions.
-
Auth_OpenID_PlainTextServerSession
-
A class implementing plaintext server sessions.
-
Auth_OpenID_PostgreSQLStore
-
An SQL store that uses PostgreSQL as its backend.
-
Auth_OpenID_punct
-
-
Auth_OpenID_REDIRECT
-
Status code returned when there is a redirect back to the consumer. The value is the URL to redirect back to. The caller should return a 302 Found redirect with a Location: header containing the URL.
-
Auth_OpenID_registerNamespaceAlias
-
Registers a (namespace URI, alias) mapping in a global namespace
-
Auth_OpenID_REMOTE_ERROR
-
Status code returned when there is an error to return in key-value form to the consumer. The caller should return a 400 Bad Request response with content-type text/plain and the value as the body.
-
Auth_OpenID_REMOTE_OK
-
Status code returned when there is a key-value form OK response to the consumer. The value associated with this code is the response. The caller should return a 200 OK response with content-type text/plain and the value as the body.
-
Auth_OpenID_removeNamespaceAlias
-
Removes a (namespace_uri, alias) registration from the global namespace alias map. Returns true if the removal succeeded; false if not (if the mapping did not exist).
-
Auth_OpenID_Request
-
The base class for all server request classes.
-
Auth_OpenID_Server
-
I handle requests for an OpenID server.
-
Auth_OpenID_Server
-
-
Auth_OpenID_ServerError
-
An error class which gets instantiated and returned whenever an OpenID protocol error occurs. Be prepared to use this in place of an ordinary server response.
-
Auth_OpenID_ServerErrorContainer
-
Exception that is raised when the server returns a 400 response code to a direct request.
-
Auth_OpenID_ServerErrorContainer
-
-
Auth_OpenID_ServerRequest
-
Object that holds the state of a request to the OpenID server
-
Auth_OpenID_ServerRequest
-
-
Auth_OpenID_ServerResponse
-
-
Auth_OpenID_ServerResponse
-
This class encapsulates the response to an OpenID server request.
-
Auth_OpenID_ServiceEndpointLoader
-
A concrete loader implementation for Auth_OpenID_ServiceEndpoints.
-
Auth_OpenID_SessionNegotiator
-
-
Auth_OpenID_SessionNegotiator
-
A session negotiator controls the allowed and preferred association types and association session types. Both the
Auth_OpenID_Consumer and
Auth_OpenID_Server use negotiators when creating associations.
-
Auth_OpenID_SetupNeededResponse
-
A response with a status of Auth_OpenID_SETUP_NEEDED. Indicates that the request was in immediate mode, and the server is unable to authenticate the user without further interaction.
-
Auth_OpenID_SetupNeededResponse
-
-
Auth_OpenID_SETUP_NEEDED
-
This is the status code completeAuth returns when the
Auth_OpenID_Consumer instance is in immediate mode, and the identity server sends back a URL to send the user to to complete his or her login.
-
Auth_OpenID_Signatory
-
Create a new signatory using a given store.
-
Auth_OpenID_Signatory
-
Responsible for the signature of query data and the verification of OpenID signature values.
-
Auth_OpenID_SigningEncoder
-
-
Auth_OpenID_SigningEncoder
-
An encoder which also takes care of signing fields when required.
-
Auth_OpenID_splitNonce
-
-
Auth_OpenID_SQLiteStore
-
An SQL store that uses SQLite as its backend.
-
Auth_OpenID_SQLStore
-
This creates a new SQLStore instance. It requires an established database connection be given to it, and it allows overriding the default table names.
-
Auth_OpenID_SQLStore
-
This is the parent class for the SQL stores, which contains the logic common to all of the SQL stores.
-
Auth_OpenID_SRegBase
-
A base class for classes dealing with Simple Registration protocol messages.
-
Auth_OpenID_SRegRequest
-
An object to hold the state of a simple registration request.
-
Auth_OpenID_SRegResponse
-
-
Auth_OpenID_SRegResponse
-
Represents the data returned in a simple registration response inside of an OpenID C{id_res} response. This object will be created by the OpenID server, added to the C{id_res} response object, and then extracted from the C{id_res} message by the Consumer.
-
Auth_OpenID_SREG_NS_URI
-
-
Auth_OpenID_SREG_NS_URI_1_0
-
-
Auth_OpenID_SREG_NS_URI_1_1
-
-
Auth_OpenID_SREG_URI
-
-
Auth_OpenID_SUCCESS
-
This is the status code returned when the complete method returns successfully.
-
Auth_OpenID_SuccessResponse
-
A response with a status of Auth_OpenID_SUCCESS. Indicates that
-
Auth_OpenID_supportsSReg
-
Does the given endpoint advertise support for simple registration?
-
Auth_OpenID_THE_OTHER_OPENID1_NS
-
-
Auth_OpenID_TypeURIMismatch
-
A specific, internal failure used to detect type URI mismatch.
-
Auth_OpenID_UntrustedReturnURL
-
An error that indicates that the given return_to is not under the given trust_root.
-
Auth_OpenID_UntrustedReturnURL
-
-
Auth_OpenID_VERSION
-
The library version string
-
Auth_OpenID_WebResponse
-
-
Auth_OpenID_WebResponse
-
A web-capable response object which you can use to generate a user-agent response.
-
Auth_Yadis_CONTENT_TYPE
-
XRDS (yadis) content type
-
Auth_Yadis_Discovery
-
Initialize a discovery object.
-
Auth_Yadis_Discovery
-
State management for discovery.
-
Auth_Yadis_DiscoveryResult
-
-
Auth_Yadis_DiscoveryResult
-
Contains the result of performing Yadis discovery on a URI.
-
Auth_Yadis_dom
-
-
Auth_Yadis_dom
-
This concrete implementation of Auth_Yadis_XMLParser implements
-
Auth_Yadis_domxml
-
-
Auth_Yadis_domxml
-
This concrete implementation of Auth_Yadis_XMLParser implements
-
Auth_Yadis_getNSMap
-
-
Auth_Yadis_getServiceEndpoints
-
Perform the Yadis protocol on the input URL and return an iterable of resulting endpoint objects.
-
Auth_Yadis_getSupportedExtensions
-
-
Auth_Yadis_getXMLParser
-
Returns an instance of a Auth_Yadis_XMLParser subclass based on the availability of PHP extensions for XML parsing. If Auth_Yadis_setDefaultParser has been called, the parser used in that call will be returned instead.
-
Auth_Yadis_getXRDExpiration
-
-
Auth_Yadis_HEADER_NAME
-
Yadis header
-
Auth_Yadis_Manager
-
The Yadis service manager which stores state in a session and iterates over <Service> elements in a Yadis XRDS document and lets a caller attempt to use each one. This is used by the Yadis library internally.
-
Auth_Yadis_ManagerLoader
-
A concrete loader implementation for Auth_Yadis_Managers.
-
Auth_Yadis_PHPSession
-
The base session class used by the Auth_Yadis_Manager. This class wraps the default PHP session machinery and should be subclassed if your application doesn't use PHP sessioning.
-
Auth_Yadis_Service
-
This class represents a <Service> element in an XRDS document.
-
Auth_Yadis_Service
-
Creates an empty service object.
-
Auth_Yadis_SessionLoader
-
A session helper class designed to translate between arrays and
-
Auth_Yadis_setDefaultParser
-
Set a default parser to override the extension-driven selection of available parser classes. This is helpful in a test environment or one in which multiple parsers can be used but one is more desirable.
-
Auth_Yadis_XMLNS_XRDS
-
XRDS XML namespace
-
Auth_Yadis_XMLNS_XRD_2_0
-
XRD XML namespace
-
Auth_Yadis_XMLParser
-
The base class for wrappers for available PHP XML-parsing
-
Auth_Yadis_XRDS
-
This class performs parsing of XRDS documents.
-
Auth_Yadis_XRDS
-
Instantiate a Auth_Yadis_XRDS object. Requires an XPath instance which has been used to parse a valid XRDS document.
-
Auth_Yadis_Yadis
-
This is the core of the PHP Yadis library. This is the only class a user needs to use to perform Yadis discovery. This class performs the discovery AND stores the result of the discovery.
-
autoCommit
-
Sets auto-commit mode on this database connection.
-
get
-
Gets a specified value from the mapping, associated with the specified key. If the key does not exist in the mapping, $default is returned instead.
-
get
-
-
get
-
Get the list of values for this attribute in the fetch_response.
-
get
-
Get a key's value from the session.
-
getAlias
-
-
getAliasedArg
-
-
getAll
-
Run an SQL query with the specified parameters, if any.
-
getAllowedType
-
Get a pair of assocation type and session type that are supported.
-
getArg
-
-
getArgs
-
-
getAssociation
-
Given an association handle, get the association from the store, or return a ServerError or null if something goes wrong.
-
getAssociation
-
Retrieve an association. If no handle is specified, return the association with the most recent issue time.
-
getAssociation
-
-
getAssociation
-
This implementation always returns null.
-
getAssociation
-
Read association from memcached. If no handle given
-
getAssociation
-
This method returns an Association object from storage that matches the server URL and, if specified, handle. It returns null if no such association is found or if the matching association is expired.
-
getAssociationFilename
-
Create a unique filename for a given server url and
-
getAuthKey
-
This method returns the auth key generated by the constructor.
-
getCancelURL
-
-
getDH
-
-
getDisplayIdentifier
-
Return the display identifier for this response.
-
getElements
-
Used to get XML elements from this object's <Service> element.
-
getExpiresIn
-
This returns the number of seconds this association is still valid for, or 0 if the association is no longer valid.
-
getExtensionArgs
-
Serialize this object into arguments in the attribute exchange namespace
-
getExtensionArgs
-
Get the serialized form of this attribute fetch request.
-
getExtensionArgs
-
-
getExtensionArgs
-
-
getExtensionArgs
-
-
getExtensionArgs
-
-
getExtensionArgs
-
-
getExtensionArgs
-
Get the string arguments that should be added to an OpenID message for this extension.
-
getExtensionArgs
-
Get a dictionary of unqualified simple registration arguments representing this request.
-
getHTTPFetcher
-
Returns an HTTP fetcher object. If the CURL extension is present, an instance of Auth_Yadis_ParanoidHTTPFetcher is returned. If not, an instance of Auth_Yadis_PlainHTTPFetcher is returned.
-
getKey
-
-
getMessage
-
-
getNamespaceURI
-
-
getNextService
-
Return the next authentication service for the pair of user_input and session. This function handles fallback.
-
getOne
-
Run an SQL query and return the first column of the first row of the result set, if any.
-
getOpenIDNamespace
-
-
getPriority
-
Returns the "priority" attribute value of this <Service> element, if the attribute is present. Returns null if not.
-
getRequest
-
-
getRequest
-
-
getRequiredAttrs
-
Get the type URIs for all attributes that have been marked as required.
-
getReturnTo
-
Get the openid.return_to argument from this response.
-
getReturnTo
-
-
getRow
-
Run an SQL query and return the first row of the result set, if any.
-
getSessionClasses
-
-
getSigned
-
-
getSignedNS
-
-
getSingle
-
Get a single value for an attribute. If no value was sent for this attribute, use the supplied default. If there is more than one value for this attribute, this method will fail.
-
getTypes
-
Return the URIs in the "Type" elements, if any, of this Service element.
-
getURIs
-
Return the URIs in the "URI" elements, if any, of this Service element. The URIs are returned sorted in priority order.


Task List
Task
Required By
Category
personal
spritual
comunity
Social
Name
Due
Category
Return Library books
2013-10-14
personal
Perform Project demo to stakeholders
2013-10-14
work
Meet friends for dinner
2013-10-14
personal
You have 3 tasks


innovative new robotic AI that triples as a harddrive, router, and cat
| Model | Capacity | Weight | Speed | Battery | Special Feature |
|---|---|---|---|---|---|
| Original | 128 GB | 8 lbs. | 20 Mbps | 12 hours | Classic design |
| Skippy | 32 GB | 6 lbs. | 45 Mbps | 24 hours | High Jump |
| Chipper | 128 GB | 10 lbs. | 45 Mbps | 48 hours | 9 Bonus Lives |
| Buddy | 256 GB | 12 lbs. | 30 Mbps | 48 hours | Free Fur |
| Toonses | 128 GB | 7 lbs. | 30 Mbps | 48 hours | Shed Resistant |
| Buckets | 128 GB | 8 lbs. | 30 Mbps | 12 hours | Waterproof |
| Jerry | 128 GB | 10 lbs. | 45 Mbps | 48 hours | Kinetic Belly Rub Charging |
| Butter | 32 GB | 4 lbs. | 45 Mbps | 12 hours | Travel Sized |
| Wanda | 128 GB | 10 lbs. | 40 Mbps | 12 hours | Invisibility Cloak |
| Laura Dern | 128 GB | 10 lbs. | 45 Mbps | 24 hours | Regal AF |


Media types are used in AeroGear Controller to determine how a request should be consumed, and what a route should produce.
A route can specify the type of data it consumes by using the consumes
method. For example, if a route specifies a media type of application/json
it will cause the body of the HTTP request to be
transformed from JSON into a Java Object representation.
A consumer is added to the system by adding a class that implements the Consumer interface. This will be picked up by the CDI container and registered for that specific content type.
A route can specify the type of data it produces by using the produces
method. For example, if a route specifies a media type of
JSON
the Java Object returned from the endpoint will be transformed into a JSON representation which will be sent back
as the body of the HTTP response.
Adding a producer differs from adding a consumer. The reason for this is that there is a need to be able to respond
to the same media type in different ways.
For example, a client may request to receive content in a certain format using the
Accept header, like Accept: text/html
.
But the route could choose to respond with either a dynamically generated JSP, or a static HTML page. So there needs to be a way
for a route to specify the exact type of response handling it desires.
Example of a custom media type responder that returns data in a custom format to the caller:
public class CustomResponder extends AbstractRestResponder {
public static final MediaType CUSTOM_MEDIA_TYPE = new MediaType("application/custom", CustomResponder.class);
public CustomResponder() {
super(CUSTOM_MEDIA_TYPE);
}
public void writeResponse(Object entity, RouteContext routeContext) throws Exception {
HttpServletResponse response = routeContext.getResponse();
response.getWriter().write("CustomResponder returned: " + entity);
}
}
In the writeResponse
method you have access to the entity which is what the endpoint method returned, and the RouteContext
which gives access to the HTTPServletResponse.
If you also implement Responder directly if you have the need to forward to a view instead of responding of if you simply prefer to implement an interface.


The Shiba Inu is the smallest of the six original and distinct spitz breeds of dog from Japan. A small, agile dog that copes very well with mountainous terrain, the Shiba Inu was originally bred for hunting.
My Biography.
Here you can control the various settings associated with your app. There's no need to hit a save button, as the values are updated automatically.


Respected Alumni,
Your contribution to
your alma mater upkeeps its glory and thereby brings glory to all of us who are currently studying here. We would like to hereby heartily
thank you on behalf of our
entire constitution and also request you to continue your
invaluable support. It is highly appreciated. Being an
alumnus of our institute, we are hopeful that you will also support and assist us in our attempt to further elevate our institute's fame and glory by means of our
Robowar initiative by funding our project. We would be immensely grateful if you could
fund this initiative of ours and we assure you that we will
stand up to the
expectations that are held from us.


Interface that will attach to a batch server. The batch server that it connects to is typically created through a call to the GEOPM DBus interface io.github.geopm.PlatformStartBatch.
More...
#include <BatchClient.hpp>
|
| | BatchClient ()=default |
| |
| virtual | ~BatchClient ()=default |
| |
| virtual std::vector< double > | read_batch (void)=0 |
| | Ask batch server to read all signal values and return result. More...
|
| |
| virtual void | write_batch (std::vector< double > settings)=0 |
| | Ask batch server to write all of the control values. More...
|
| |
| virtual void | stop_batch (void)=0 |
| | Send message to batch server asking it to quit. More...
|
| |
Interface that will attach to a batch server. The batch server that it connects to is typically created through a call to the GEOPM DBus interface io.github.geopm.PlatformStartBatch.
◆ BatchClient()
| geopm::BatchClient::BatchClient |
( |
| ) |
|
|
default |
◆ ~BatchClient()
| virtual geopm::BatchClient::~BatchClient |
( |
| ) |
|
|
virtualdefault |
◆ make_unique()
| std::unique_ptr< BatchClient > geopm::BatchClient::make_unique |
( |
const std::string & |
server_key, |
|
|
double |
timeout, |
|
|
int |
num_signal, |
|
|
int |
num_control |
|
) |
| |
|
static |
Factory method to create a pointer to a BatchClient object.
The BatchClient interface is used to communcate with an existing GEOPM batch server. The batch server is typically created with the PlatformStartBatch GEOPM DBus interface.
- Parameters
-
| server_key | [in] The server key that was returned when the batch server was created. |
| timeout | [in] Maximum wait time to attach to batch server in units of seconds. |
| num_signal | [in] Number of signal requests made when starting the batch server. |
| num_signal | [in] Number of control requests made when starting the batch server. |
- Returns
- New unique pointer to an object that supports the BatchClient interface.
◆ read_batch()
| virtual std::vector<double> geopm::BatchClient::read_batch |
( |
void |
| ) |
|
|
pure virtual |
Ask batch server to read all signal values and return result.
Command is issued to batch server to read all pushed signal values. All of the values read by the batch server are returned.
- Returns
- A vector containing all values read by batch server
Implemented in geopm::BatchClientImp.
◆ stop_batch()
| virtual void geopm::BatchClient::stop_batch |
( |
void |
| ) |
|
|
pure virtual |
◆ write_batch()
| virtual void geopm::BatchClient::write_batch |
( |
std::vector< double > |
settings | ) |
|
|
pure virtual |
Ask batch server to write all of the control values.
Sends the vector of settings to the batch server. This function blocks until the batch server has written all values.
- Parameters
-
| settings | [in] Control settings to be written: one for each control requests made when batch server was created. |
Implemented in geopm::BatchClientImp.
The documentation for this class was generated from the following files:


BME280 Sensor
System
Status
WiFi Connect
MQTT Broker
WiFi Connect
Connect to an existing Wifi network
Use AP (Wireless access point)
SSID (Name)
Password
AP Name
Password
Save
Error!
Submit the form failed.
×
Saved!
WiFi settings saved.
Reboot?
×


Delete one or more Deployed Solutions
Credentials in organization
Username:
Password:
Portal:
Connect
Select deployed Solutions to delete:
Select all
Delete Solution(s) one-by-one
Delete Solution(s) en masse
Cancel Deletion
Confirm Deletion
Continue


Id: 270
Name: Prince Nebulon
Status: Dead
Species: Alien
Type: Zigerion
Gender: Male
OriginName: unknown
OriginUrl:
LocationName: Zigerion's Base
LocationUrl: https://rickandmortyapi.com/api/location/46
Image: https://rickandmortyapi.com/api/character/avatar/270.jpeg
Episode: ['https://rickandmortyapi.com/api/episode/4']
Url: https://rickandmortyapi.com/api/character/270
Created: 2017-12-31T14:05:24.406Z


Colors & backgrounds
Text color
The template defines 9 text colors: White, Silver, Grey, Anthracite, Black, Red, Orange, Green and Blue
Background colors
The template is also bundled with a set of 9 background colors, each one available as a simple tone, a fine gradient and a glossy gradient. Every color class includes a background color, a border color and a hover effect for links. Of course, these colors may be used on any element!
Textured backgrounds
Several textured background styles are available, for use on a block or event the whole document:


We are 5 year in the market, we are ruby on rails developers, we are experienced, focused, client-oriented, we are ruby on rails contributors, love agile.


Visualization of reported incidents of crime that occurred in the City of Chicago from
- to
-
No. of Data Records:
- crimes
Data source:
Chicago data portal
Data analysis: play with the data by selecting data segments in barcharts or ‘Select Area’ directly in the map.


White to play 22
pips White=153 Black=139
(CorrectAnswer= 0/0, ErrorScore= 0)
1. 23/21 13/11(2) 10/8 eq: +0.373 59.80% (G:13.72% B:0.39%) 40.20% (G: 7.85% B:0.29%) 2. 13/11 13/9 10/8 eq: +0.346 (-0.027) 59.03% (G:15.37% B:0.52%) 40.97% (G: 9.97% B:0.42%) 3. 23/21 13/9 10/8 eq: +0.230 (-0.143) 56.88% (G:12.98% B:0.40%) 43.12% (G: 9.81% B:0.36%) 4. 23/21 13/11 13/9 eq: +0.168 (-0.205) 55.09% (G:12.97% B:0.36%) 44.91% (G: 9.50% B:0.39%) 5. 13/9(2) eq: +0.134 (-0.240) 53.47% (G:13.89% B:0.41%) 46.53% (G:10.50% B:0.43%)


Contact us
Blog
Shop
Page
About us
Home
AFFORDABLE PARIS TOURS
city tour/tour tickets/tour guides
New Collection
add to cart
BLUE TSHIRT
$26
add to cart
WOMAN SHIRT
$31
add to cart
YELLOW TSHIRT
$21
add to cart
COOL LINGERY
$56


1 MB 5 minutes 37 seconds over a dial-up connection 38 seconds over a broadband connection
Click Install to start this setup now.


What is a Process Category?
In the project-statusdb context, lims processes are categorised into groups that define, or are used to define a certain type of status-db key in a project database. The categories are specified here.
Adding a work flow.
If a work flow does not fit with the categories one might have to change the category definitions or ad new categories. This needs to be done in corperation with the developer of project_summary_uppload_LIMS.py. The cathegories are defined in process_categories.py within the objectsDB package.
SEQSTART
These processes are used to set the sequencing_start_date
| ID |
process Name |
| 26 |
Denature, Dilute and Load Sample (MiSeq) 4.0 |
| 23 |
Cluster Generation (Illumina SBS) 4.0 |
LIBVALFINISHEDLIB
| ID |
process Name |
| 24 |
Customer Gel QC |
| 20 |
CaliperGX QC (DNA) |
| 17 |
Bioanalyzer QC (Library Validation) 4.0 |
| 62 |
qPCR QC (Library Validation) 4.0 |
| 64 |
Quant-iT QC (Library Validation) 4.0 |
| 67 |
Qubit QC (Library Validation) 4.0 |
PREPREPSTART
Process/processes that can be defined as a start of the library preparation protocol. If the work flow involves two library preparation protocols, such as for exome captue, only the steps of the first protocol should be given here.
| ID |
process Name |
| 304 |
Ligate 3’ adapters (TruSeq small RNA) 1.0 |
INITALQCFINISHEDLIB
All processes included in the initial qc protocol for finished libraries, except the aggregation step.
| ID |
process Name |
| 24 |
Customer Gel QC |
| 20 |
CaliperGX QC (DNA) |
| 17 |
Bioanalyzer QC (Library Validation) 4.0 |
| 62 |
qPCR QC (Library Validation) 4.0 |
| 64 |
Quant-iT QC (Library Validation) 4.0 |
| 67 |
Qubit QC (Library Validation) 4.0 |
AGRINITQC
Aggregation step of the initial qc protocol
| ID |
process Name |
| 9 |
Aggregate QC (RNA) 4.0 |
| 7 |
Aggregate QC (DNA) 4.0 |
POOLING
To identify the reagent labels (indexes) of each sample. If multiple pooling steps, the first pooling step after indexing should be specified
| ID |
process Name |
| 308 |
Library Pooling (TruSeq Small RNA) 1.0 |
| 58 |
Pooling For Multiplexed Sequencing (SS XT) 4.0 |
| 255 |
Library Pooling (Finished Libraries) 4.0 |
| 44 |
Library Pooling (TruSeq Amplicon) 4.0 |
| 45 |
Library Pooling (TruSeq Exome) 4.0 |
| 42 |
Library Pooling (Illumina SBS) 4.0 |
| 43 |
Library Pooling (MiSeq) 4.0 |
| 404 |
Pre-Pooling (Illumina SBS) 4.0 |
| 508 |
Applications Pre-Pooling |
| 506 |
Pre-Pooling (MiSeq) 4.0 |
CALIPER
| ID |
process Name |
| 116 |
CaliperGX QC (RNA) |
| 20 |
CaliperGX QC (DNA) |
WORKSET
To identify the work sets on which the samples has been run. The process used to define a workset for the protocol.
| ID |
process Name |
| 204 |
Setup Workset/Plate |
PREPEND
Process that can be defined as a end of the library preparation. If more than one library preparation protocol is included in the work flow, only the prep end step of the second protocol should be given here. Used to set the prep finished date.
| ID |
process Name |
| 157 |
Applications Finish Prep |
| 311 |
Sample Placement (Size Selection) |
| 456 |
Purification (ThruPlex) |
| 406 |
End repair, size selection, A-tailing and adapter ligation (TruSeq PCR-free DNA) 4.0 |
| 109 |
CA Purification |
| 111 |
Amplify Captured Libraries to Add Index Tags (SS XT) 4.0 |
DILSTART
These processes are used to set the dilution_and_pooling_start_date
| ID |
process Name |
| 39 |
Library Normalization (Illumina SBS) 4.0 |
| 40 |
Library Normalization (MiSeq) 4.0 |
INITALQC
All processes included in the initial qc protocol, except the aggrigation step.
| ID |
process Name |
| 63 |
Quant-iT QC (DNA) 4.0 |
| 65 |
Quant-iT QC (RNA) 4.0 |
| 66 |
Qubit QC (DNA) 4.0 |
| 68 |
Qubit QC (RNA) 4.0 |
| 24 |
Customer Gel QC |
| 20 |
CaliperGX QC (DNA) |
| 16 |
Bioanalyzer QC (DNA) 4.0 |
| 18 |
Bioanalyzer QC (RNA) 4.0 |
| 116 |
CaliperGX QC (RNA) |
| 504 |
Volume Measurement QC |
SUMMARY
| ID |
process Name |
| 356 |
Project Summary 1.3 |
LIBVAL
All processes included in the library validation protocol, except the aggregation step. If the work flow involves two library preparation protocols, such as for exome capture, only the steps of the second protocol should be given here.
| ID |
process Name |
| 20 |
CaliperGX QC (DNA) |
| 17 |
Bioanalyzer QC (Library Validation) 4.0 |
| 62 |
qPCR QC (Library Validation) 4.0 |
| 64 |
Quant-iT QC (Library Validation) 4.0 |
| 67 |
Qubit QC (Library Validation) 4.0 |
SEQUENCING
Sequencing
| ID |
process Name |
| 46 |
MiSeq Run (MiSeq) 4.0 |
| 38 |
Illumina Sequencing (Illumina SBS) 4.0 |
DEMULTIPLEX
| ID |
process Name |
| 13 |
Bcl Conversion & Demultiplexing (Illumina SBS) 4.0 |
PREPSTART
Process/processes that can be defined as a start of the library preparation protocol. The first one of these that are run in lims is used to set the prep start date. If the work flow involves two library preparation protocols, such as for exome capture, the prep start step of the second protocol should be given here.
| ID |
process Name |
| 407 |
Fragment DNA (ThruPlex) |
| 10 |
Aliquot Libraries for Hybridization (SS XT) |
| 117 |
Applications Generic Process |
| 33 |
Fragment DNA (TruSeq DNA) 4.0 |
| 47 |
mRNA Purification, Fragmentation & cDNA synthesis (TruSeq RNA) 4.0 |
| 308 |
Library Pooling (TruSeq Small RNA) 1.0 |
| 405 |
RiboZero depletion |
AGRLIBVAL
The aggregation step of the library validation protocol
| ID |
process Name |
| 8 |
Aggregate QC (Library Validation) 4.0 |


I am currently a Research Scientist at Waymo Research since 2020. I am also a lecturer of ESE 5420, Statistics for Data Science, at UPenn MCIT Online.
In 2020, I earned my Ph.D. degree in Computer and Information Science from University of Pennsylvania, advised by Prof. Jianbo Shi and Prof. Stella Yu at UC Berkeley / ICSI. During my study, I was fortunate to have the opportunities to intern at Facebook AI Research and Google Research.
Before coming to the U.S., I received the B.S. and M.S. degrees in EE, advised by Prof. Liang-Gee Chen, from National Taiwan University and worked with Dr. Tyng-Luh Liu as a research assistant at Academia Sinica.
My research interests are broadly in computer vision, deep learning, and machine learning. Particularly, I am interested in understanding general image/video structures and sensor fusion.
[ Paper ]
[ Paper ]
[ Paper ]
[ Paper ]


Single-serve git-over-i2p repositories using SAM.
This is still pretty much a toy. I made it as a goof after I got tired this Saturday. I do intend to refine it as I get more chances to do so but for now it's just for fun.
This simple application combines an I2P Tunnel created by sam-forwarder and a git repository created by gitkit in order to simply host a Git repository over the I2P network. It creates two tunnels that serve the same repository, one of them is read-only and served with plain HTTP-over-I2P, and one of them is read-write and served with SSH.
Soon, it will optionally add a post-recieve hook which will check out the latest changes. That will essentially make the read-only branch also function as a project web site for the code in the repository. Sort of like github pages.
Eventually, it will have extensive options for optimizing your git configuration.


Setting up a controller connected to the PC is entirely not too difficult. The PS4 controller has Bluetooth capabilities that make it easy to connect to the computer as long as Bluetooth is enabled.
Connecting the controller to the Epic Games platform, however is much more difficult of a process. It requires extra programming to be mapped correctly to all the controls.
The Epic Games platform does not natively accept controllers, simply the keyboard. However with the DS4Windows program you are able to set up a controller with the platform.
The only issue that I've run into with connecting these two is that Epic Games recognizes the PS4 controller as an XBOX controller. While this isn't a huge issue some of the buttons had to be remapped in order match the PS4 controls and be used properly. This was easy within the DS4 interface. Once all the buttons are mapped properly the controller worked flawlessly.


grammar::me::cpu::gasm - ME assembler
This package provides a simple in-memory assembler. Its origin is that of a support package for use by packages converting PEG and other grammars into a corresponding matcher based on the ME virtual machine, like page::compiler::peg::mecpu. Despite that it is actually mostly agnostic regarding the instructions, users can choose any instruction set they like.
The program under construction is held in a graph structure (See package struct::graph) during assembly and subsequent manipulation, with instructions represented by nodes, and the flow of execution between instructions explicitly encoded in the arcs between them.
In this model jumps are not encoded explicitly, they are implicit in the arcs. The generation of explicit jumps is left to any code converting the graph structure into a more conventional representation. The same goes for branches. They are implicitly encoded by all instructions which have two outgoing arcs, whereas all other instructions have only one outgoing arc. Their conditonality is handled by tagging their outgoing arcs with information about the conditions under which they are taken.
While the graph the assembler operates on is supplied from the outside, i.e. external, it does manage some internal state, namely:
The handle of the graph node most assembler operations will work on, the anchor.
A mapping from arbitrary strings to instructions. I.e. it is possible to label an instruction during assembly, and later recall that instruction by its label.
The condition code to use when creating arcs between instructions, which is one of always, ok, and fail.
The current operation mode, one of halt, okfail, and !okfail.
The name of a node in a tree. This, and the operation mode above are the parts most heavily influenced by the needs of a grammar compiler, as they assume some basic program structures (selected through the operation mode), and intertwine the graph with a tree, like the AST for the grammar to be compiled.
As the graph the assembler is operating on, and the tree it is intertwined with, are supplied to the assembler from the outside it is necessary to specify the API expected from them, and to describe the structures expected and/or generated by the assembler in either.
Any graph object command used by the assembler has to provide the API as specified in the documentation for the package struct::graph.
Any tree object command used by the assembler has to provide the API as specified in the documentation for the package struct::tree.
Any instruction (node) generated by the assembler in a graph will have at least two, and at most three attributes:
The value of this attribute is the name of the instruction. The only names currently defined by the assembler are the three pseudo-instructions
This instruction does nothing. Useful for fixed framework nodes, unchanging jump destinations, and the like. No arguments.
A .NOP to allow the insertion of arbitrary comments into the instruction stream, i.e. a comment node. One argument, the text of the comment.
A .NOP serving as explicitly coded conditional branch. No arguments.
However we reserve the space of all instructions whose names begin with a "." (dot) for future use by the assembler.
The value of this attribute is a list of strings, the arguments of the instruction. The contents are dependent on the actual instruction and the assembler doesn't know or care about them. This means for example that it has no builtin knowledge about what instruction need which arguments and thus doesn't perform any type of checking.
This attribute is optional. When it is present its value is the name of a node in the tree intertwined with the graph.
Any arc between two instructions will have one attribute:
The value of this attribute determines under which condition execution will take this arc. It is one of always, ok, and fail. The first condition is used for all arcs which are the single outgoing arc of an instruction. The other two are used for the two outgoing arcs of an instruction which implicitly encode a branch.
A tree node given to the assembler for cross-referencing will be written to and given the following attributes, some fixed, some dependent on the operation mode. All values will be references to nodes in the instruction graph. Some of the instruction will expect some or specific sets of these attributes.
Always written.
Written for all modes but okfail.
Written for mode okfail.
Written for mode okfail.
This command starts the assembly of an instruction sequence, and (re)initializes the state of the assembler. After completion of the instruction sequence use ::grammar::me::cpu::gasm::done to finalize the assembler.
It will operate on the graph g in the specified mode (Default is okfail). As part of the initialization it will always create a standard .NOP instruction and label it "entry". The creation of the remaining standard instructions is mode-dependent:
An "icf_halt" instruction labeled "exit/return".
An "icf_ntreturn" instruction labeled "exit/return".
Two .NOP instructions labeled "exit/ok" and "exit/fail" respectively.
The note, if specified (default is not), is given to the "entry" .NOP instruction.
The node reference n is simply stored for use by ::grammar::me::cpu::gasm::done. It has to refer to a node in the tree t argument of that command.
After the initialization is done the "entry" instruction will be the anchor, and the condition code will be set to always.
The command returns the empy string as its result.
This command finalizes the creation of an instruction sequence and then clears the state of the assembler. NOTE that this does not delete any of the created instructions. They can be made available to future begin/done cycles. Further assembly will be possible only after reinitialization of the system via ::grammar::me::cpu::gasm::begin.
Before the state is cleared selected references to selected instructions will be written to attributes of the node n in the tree t. Which instructions are saved is mode-dependent. Both mode and the destination node n were specified during invokation of ::grammar::me::cpu::gasm::begin.
Independent of the mode a reference to the instruction labeled "entry" will be saved to the attribute gas::entry of n. The reference to the node n will further be saved into the attribute "expr" of the "entry" instruction. Beyond that
A reference to the instruction labeled "exit/return" will be saved to the attribute gas::exit of n.
See halt.
Reference to the two instructions labeled "exit/ok" and "exit/fail" will be saved to the attributes gas::exit::ok and gas::exit::fail of n respectively.
The command returns the empy string as its result.
This command returns the current state of the assembler. Its format is not documented and considered to be internal to the package.
This command takes a serialized assembler state s as returned by ::grammar::me::cpu::gasm::state and makes it the current state of the assembler.
Note that this may overwrite label definitions, however all non-conflicting label definitions in the state before are not touched and merged with s.
The command returns the empty string as its result.
This command operates on the tree t. It copies the contents of the attributes gas::entry, gas::exit::ok and gas::exit::fail from the node src to the node dst. It returns the empty string as its result.
This command links an instruction sequence created by an earlier begin/done pair into the current instruction sequence.
To this end it
reads the instruction references from the attributes gas::entry, gas::exit::ok, and gas::exit::fail from the node n of the tree t and makes them available to assembler und the labels label/entry, label/exit::ok, and label/exit::fail respectively.
Creates an arc from the anchor to the node labeled label/entry, and tags it with the current condition code.
Makes the node labeled label/exit/ok the new anchor.
The command returns the empty string as its result.
This is the basic command to add instructions to the graph. It creates a new instruction of type cmd with the given arguments arg... If the anchor was defined it will also create an arc from the anchor to the new instruction using the current condition code. After the call the new instruction will be the anchor and the current condition code will be set to always.
The command returns the empty string as its result.
This is a convenience command to create a .BRA pseudo-instruction. It uses ::grammar::me::cpu::gasm::Cmd to actually create the instruction and inherits its behaviour.
This is a convenience command to create a .NOP pseudo-instruction. It uses ::grammar::me::cpu::gasm::Cmd to actually create the instruction and inherits its behaviour. The text will be saved as the first and only argument of the new instruction.
This is a convenience command to create a .C pseudo-instruction, i.e. a comment. It uses ::grammar::me::cpu::gasm::Cmd to actually create the instruction and inherits its behaviour. The text will be saved as the first and only argument of the new instruction.
This command creates an arc from the anchor to the instruction labeled with label, and tags with the the current condition code.
The command returns the empty string as its result.
This command creates an arc from the anchor to one of the exit instructions, based on the operation mode (see ::grammar::me::cpu::gasm::begin), and tags it with current condition code.
For mode okfail it links to the instruction labeled either "exit/ok" or "exit/fail", depending on the current condition code, and tagging it with the current condition code For the other two modes it links to the instruction labeled "exit/return", tagging it condition code always, independent the current condition code.
The command returns the empty string as its result.
This command returns a reference to the instruction labeled with label.
This command labels the anchor with name. Note that an instruction can have more than one label.
The command returns the empty string as its result.
This command clears the anchor, leaving it undefined, and further resets the current condition code to always.
The command returns the empty string as its result.
This command sets the current condition code to ok.
The command returns the empty string as its result.
This command sets the current condition code to fail.
The command returns the empty string as its result.
This command sets the anchor to the instruction labeled with name, and further resets the current condition code to always.
The command returns the empty string as its result.
This command marks the anchor as the last instruction in a loop body, by creating the attribute LOOP.
The command returns the empty string as its result.
This document, and the package it describes, will undoubtedly contain bugs and other problems. Please report such in the category grammar_me of the Tcllib Trackers. Please also report any ideas for enhancements you may have for either package and/or documentation.
When proposing code changes, please provide unified diffs, i.e the output of diff -u.
Note further that attachments are strongly preferred over inlined patches. Attachments can be made by going to the Edit form of the ticket immediately after its creation, and then using the left-most button in the secondary navigation bar.
Grammars and finite automata
Copyright © 2005 Andreas Kupries <andreas_kupries@users.sourceforge.net>


--- layout: minimal title: "About me" permalink: /about/index.html description: "Code, GIS, AI, and some other stuff Sean Barnett does." ---
Sean Barnett presently works as a data engineer, but has a plethora of interests, including, but certainly not limited to: GIS, AI, and cognitive science, among others. He has extensive software development and research experience and is always ready for the opportunity to put his skills to work.


There is a strange bug: if this is loaded from file:// , whether using 'canvas' or 'dom', though OL does seem to create all the right DOM elements , the tiles seem to not get loaded.


| Package | Description |
|---|---|
| ml.alternet.misc |
Misc classes.
|
| ml.alternet.parser |
Allow to design grammar, parse input and generate a custom data structure.
|
| ml.alternet.parser.ast |
Helper classes to build an abstract syntax tree
thanks to token mappers and rule mappers.
|
| ml.alternet.parser.handlers |
Various handlers.
|
| ml.alternet.parser.util |
Data structures utils.
|
| ml.alternet.scan |
Scan Unicode character streams.
|
| Class and Description |
|---|
| Presentable
A Presentable object can provide a pretty string representation of
itself.
|
| Class and Description |
|---|
| Initializable
An object can be initialized.
|
| Presentable
A Presentable object can provide a pretty string representation of
itself.
|
| Rewindable
Allow to set marks and to rewind to the marked position.
|
| Trackable
A trackable object can supply informations about its position (within an
input stream).
|
| Unwrappable
An unwrappable object is an object that wraps another object for
compatibility purpose, and that can be safely unwrapped.
|
| Class and Description |
|---|
| Rewindable
Allow to set marks and to rewind to the marked position.
|
| Class and Description |
|---|
| Rewindable
Allow to set marks and to rewind to the marked position.
|
| Class and Description |
|---|
| Initializable
An object can be initialized.
|
| Rewindable
Allow to set marks and to rewind to the marked position.
|
| Class and Description |
|---|
| Presentable
A Presentable object can provide a pretty string representation of
itself.
|
| Rewindable
Allow to set marks and to rewind to the marked position.
|
| Trackable
A trackable object can supply informations about its position (within an
input stream).
|
Copyright © 2020 Alternet. All rights reserved.