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.
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 safely 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 23D points
s.boot;
(
fork{
    ~ds = FluidDataSet.new(s,\kdtree_help_rand2d);
	~dsL = FluidDataSet.new(s,\kdtree_help_indices);// for use later in KR query
    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,lookupDataSet:~dsL);

//Fit it to the DataSet
~tree.fit(~ds);

// Should be 100 points, 2 columns
~tree.size;
~tree.cols;


//Return labels of k nearest points to a new point
(
~p = [ 1.0.linrand,1.0.linrand ];
~tree.numNeighbours = 5;
~tmpbuf = Buffer.loadCollection(s, ~p, 1, {
	~tree.kNearest(~tmpbuf,{ |a|a.postln;~nearest = a;})
});
)

// Labels of nearest points
~nearest.postln;

// Values
(
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 });
::

subsection:: Server Side Queries

code::

//set the buffers and busses needed
(
~inputPoint = Buffer.alloc(s,2);
~predictPoint = Buffer.alloc(s,5);
~pitchingBus = Bus.control;
~catchingBus = Bus.control;
)

//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
(
fork{
    d = Dictionary.with(
        *[\cols -> 1,\data -> Dictionary.newFrom(
            100.collect{|i| [i, [ i ]]}.flatten)]);
    s.sync;
    ~dsL.load(d, {~dsL.print});
}
)

(

~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);

)



::

