You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
5.3 KiB
Plaintext
163 lines
5.3 KiB
Plaintext
TITLE:: FluidKDTree
|
|
summary:: KD Tree on the server
|
|
categories:: FluidManipulation
|
|
related:: Classes/FluidDataSet
|
|
|
|
DESCRIPTION::
|
|
A server-side K-Dimensional tree for efficient neighbourhood searches of multi-dimensional data.
|
|
|
|
See https://scikit-learn.org/stable/modules/neighbors.html#nearest-neighbor-algorithms for more on KD Trees
|
|
|
|
CLASSMETHODS::
|
|
|
|
METHOD:: new
|
|
Make a new KDTree model for the given server.
|
|
ARGUMENT:: server
|
|
The server on which to make the model.
|
|
ARGUMENT:: numNeighbours
|
|
The number of neighbours to return. A 0 will return all points in order of distance. When a radius is defined, numNeighbours is the maximum of items returned.
|
|
ARGUMENT:: radius
|
|
The threshold of acceptable distance for a point to be returned. A 0 will bypass this function, returning numNeighbours points.
|
|
ARGUMENT:: lookupDataSet
|
|
An optional link::Classes/FluidDataSet:: from which data points will be returned for realtime queries. This does not need to be the same DataSet that the tree was fitted against, but does need to have matching labels. Using this mechanism, we have a way to, e.g. associate labels with segments of playback buffers, without needing pass strings around the server. warning::This parameter can not be changed after the instance of FluidKDTree has been created::
|
|
|
|
INSTANCEMETHODS::
|
|
|
|
METHOD:: fit
|
|
Build the tree by scanning the points of a LINK::Classes/FluidDataSet::
|
|
ARGUMENT:: dataSet
|
|
The LINK::Classes/FluidDataSet:: of interest. This can either be a data set object itself, or the name of one.
|
|
ARGUMENT:: action
|
|
A function to run when indexing is complete.
|
|
|
|
METHOD:: kNearest
|
|
Returns the IDs of the CODE::k:: points nearest to the one passed.
|
|
ARGUMENT:: buffer
|
|
A LINK::Classes/Buffer:: containing a data point to match against. The number of frames in the buffer must match the dimensionality of the LINK::Classes/FluidDataSet:: the tree was fitted to.
|
|
ARGUMENT:: action
|
|
A function that will run when the query returns, whose argument is an array of point IDs from the tree's LINK::Classes/FluidDataSet::
|
|
|
|
METHOD:: kNearestDist
|
|
Get the distances of the K nearest neighbours to a point.
|
|
ARGUMENT:: buffer
|
|
A LINK::Classes/Buffer:: containing a data point to match against. The number of frames in the buffer must match the dimensionality of the LINK::Classes/FluidDataSet:: the tree was fitted to.
|
|
ARGUMENT:: action
|
|
A function that will run when the query returns, whose argument is an array of distances.
|
|
|
|
|
|
EXAMPLES::
|
|
|
|
code::
|
|
|
|
|
|
// Make a DataSet of random 2D points
|
|
s.reboot;
|
|
(
|
|
fork{
|
|
~ds = FluidDataSet.new(s,\kdtree_help_rand2d);
|
|
d = Dictionary.with(
|
|
*[\cols -> 2,\data -> Dictionary.newFrom(
|
|
100.collect{|i| [i, [ 1.0.linrand,1.0.linrand]]}.flatten)]);
|
|
s.sync;
|
|
~ds.load(d, {~ds.print});
|
|
}
|
|
)
|
|
|
|
// Make a new tree, and fit it to the DataSet
|
|
~tree = FluidKDTree(s,numNeighbours:5);
|
|
|
|
//Fit it to the DataSet
|
|
~tree.fit(~ds);
|
|
|
|
// Should be 100 points, 2 columns
|
|
~tree.size;
|
|
~tree.cols;
|
|
|
|
//Return the labels of k nearest points to a new point
|
|
(
|
|
~p = [ 1.0.linrand,1.0.linrand ];
|
|
~tmpbuf = Buffer.loadCollection(s, ~p, 1, {
|
|
~tree.kNearest(~tmpbuf,{ |a|a.postln;~nearest = a;})
|
|
});
|
|
)
|
|
|
|
// Retrieve values from the DataSet by iterating through the returned labels
|
|
(
|
|
fork{
|
|
~nearest.do{|n|
|
|
~ds.getPoint(n, ~tmpbuf, {~tmpbuf.getn(0, 2, {|x|x.postln})});
|
|
s.sync;
|
|
}
|
|
}
|
|
)
|
|
// Distances of the nearest points
|
|
~tree.kNearestDist(~tmpbuf, { |a| a.postln });
|
|
|
|
// Explore changing the number of neighbourgs
|
|
~tree.numNeighbours = 11; // note that this value needs to be sent to the server
|
|
~tree.kNearest(~tmpbuf,{ |a|a.postln;~nearest = a;});
|
|
~tree.numNeighbours = 0; // 0 will return all items in order of distance
|
|
|
|
// Limit the search to an acceptable distance in a radius
|
|
// Define a point, and observe typical distance values
|
|
~p = [ 0.2,0.2];
|
|
(
|
|
~tmpbuf = Buffer.loadCollection(s, ~p, 1, {
|
|
~tree.kNearestDist(~tmpbuf,{ |a|a.postln;~nearest = a;});
|
|
});
|
|
)
|
|
|
|
// enter a valid radius.
|
|
~tree.radius = 0.05;
|
|
// FluidKDTree will return only values that are within that radius, up to numNeighbours values
|
|
(
|
|
~tmpbuf = Buffer.loadCollection(s, ~p, 1, {
|
|
~tree.kNearest(~tmpbuf,{ |a|a.postln;~nearest = a;});
|
|
});
|
|
)
|
|
::
|
|
|
|
subsection:: Server Side Queries
|
|
|
|
code::
|
|
//populate the lookupDataSet
|
|
//here we populate with numbers that are in effect the indicies, but it could be anything numerical that will be returned on the server-side and would be usable on that side
|
|
(
|
|
~dsL = FluidDataSet.new(s,\kdtree_help_indices);
|
|
fork{
|
|
d = Dictionary.with(
|
|
*[\cols -> 1,\data -> Dictionary.newFrom(
|
|
100.collect{|i| [i, [ i ]]}.flatten)]);
|
|
s.sync;
|
|
~dsL.load(d, {~dsL.print});
|
|
}
|
|
)
|
|
|
|
// instantiate a tree with a lookup dataset. Note that this 'association' has to be done at instantiation.
|
|
~tree = FluidKDTree(s,numNeighbours:5, lookupDataSet:~dsL);
|
|
~tree.fit(~ds)
|
|
|
|
//set the buffers and busses needed
|
|
(
|
|
~inputPoint = Buffer.alloc(s,2);
|
|
~predictPoint = Buffer.alloc(s,5);
|
|
~pitchingBus = Bus.control;
|
|
~catchingBus = Bus.control;
|
|
)
|
|
|
|
(
|
|
~tree.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint);
|
|
{
|
|
var trig = Impulse.kr(4); //can go as fast as ControlRate.ir/2
|
|
var point = 2.collect{TRand.kr(0,1,trig)};
|
|
point.collect{|p,i| BufWr.kr([p],~inputPoint,i)};
|
|
Poll.kr(trig,point);
|
|
Out.kr(~pitchingBus.index,[trig]);
|
|
Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,Array.iota(5)));
|
|
Silent.ar;
|
|
}.play(~tree.synth,addAction:\addBefore);
|
|
|
|
)
|
|
::
|
|
|