Skip to content
2 changes: 1 addition & 1 deletion doc/BufSines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@

The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove bubbly pitchy artefactss, but is more CPU intensive and might reject quick pitch material.

:control trackingMethod:
:control trackMethod:

The algorithm used to track the sinusoidal continuity between spectral frames. 0 is the default, "Greedy", and 1 is a more expensive [^"Hungarian"]( Neri, J., and Depalle, P., "Fast Partial Tracking of Audio with Real-Time Capability through Linear Programming". Proceedings of DAFx-2018. ) one.

Expand Down
8 changes: 8 additions & 0 deletions doc/DataSet.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@

Merge sourceDataSet in the current DataSet. It will update the value of points with the same identifier if overwrite is set to 1. ​To add columns instead, see the 'transformJoin' method of FluidDataSetQuery.

:message kNearest:

:arg buffer: A |buffer| containing a data point to match against. The number of frames in the buffer must match the dimensionality of the DataSet.

:arg k: The number of nearest neighbours to return. The identifiers will be sorted, beginning with the nearest.

Returns the identifiers of the ``k`` points nearest to the one passed. Note that this is a brute force distance measure, and comparatively inefficient for repeated queries against large datasets. For such cases, :fluid-obj:`KDTree` will be more efficient.

:message print:

Post an abbreviated content of the DataSet in the window by default, but you can supply a custom action instead.
Expand Down
8 changes: 7 additions & 1 deletion doc/KDTree.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
:discussion:
:fluid-obj:`KDTree` facilitates efficient nearest neighbour searches of multi-dimensional data stored in a :fluid-obj:`DataSet`.

k-d trees are most useful for *repeated* querying of a dataset, because there is a cost associated with building them. If you just need to do a single lookup then using the kNearest message of :fluid-obj:`DataSet` will probably be quicker

Whilst k-d trees can offer very good performance relative to naïve search algorithms, they suffer from something called “the curse of dimensionality” (like many algorithms for multi-dimensional data). In practice, this means that as the number of dimensions of your data goes up, the relative performance gains of a k-d tree go down.

:control numNeighbours:
Expand Down Expand Up @@ -34,13 +36,17 @@

:arg buffer: A |buffer| containing a data point to match against. The number of frames in the buffer must match the dimensionality of the :fluid-obj:`DataSet` the tree was fitted to.

:arg k: The number of nearest neighbours to return. The identifiers will be sorted, beginning with the nearest.
:arg k: (optional) The number of nearest neighbours to return. The identifiers will be sorted, beginning with the nearest.

:arg action: A function that will run when the query returns, whose argument is an array of distances.

Returns the identifiers of the ``k`` points nearest to the one passed.

:message kNearestDist:

:arg buffer: A |buffer| containing a data point to match against. The number of frames in the buffer must match the dimensionality of the :fluid-obj:`DataSet` the tree was fitted to.

:arg k: (optional) The number of nearest neighbours to return. The identifiers will be sorted, beginning with the nearest.

:arg action: A function that will run when the query returns, whose argument is an array of distances.

Expand Down
2 changes: 1 addition & 1 deletion doc/Sines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove bubbly pitchy artefacts, but is more CPU intensive and might reject quick pitch material.

:control trackingMethod:
:control trackMethod:

The algorithm used to track the sinusoidal continuity between spectral frames. 0 is the default, "Greedy", and 1 is a more expensive [^"Hungarian"]( Neri, J., and Depalle, P., "Fast Partial Tracking of Audio with Real-Time Capability through Linear Programming". Proceedings of DAFx-2018. ) one.

Expand Down
25 changes: 25 additions & 0 deletions example-code/sc/DataSet.scd
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,29 @@ fork{
}
)

::
strong::Nearest Neighbour Search in a DataSet::

Note: A FluidDataSet can be queried with an input point to return the nearest match to that point. Note: This feature is can be computationally expensive on a large dataset, as it needs to compute the distance of the queried point to each point in the dataset. If you need to perform multiple nearest neighbour queries on a fluid.dataset~ it is recommended to use FluidKDTree. This facility is most useful with smaller, ephemeral datasets such as those returned by FluidDataSetQuery.

code::

// create a small DataSet...
f = FluidDataSet(s)
// and fill it with a grid of data
f.load(Dictionary.newFrom(["cols", 2, "data", Dictionary.newFrom(9.collect{|i|["item-%".format(i), [i.div(3), i.mod(3)] / 2]}.flatten(1))]))

// the data looks like this
// (item-0 -> [ 0.0, 0.0 ]) (item-1 -> [ 0.0, 0.5 ]) (item-2 -> [ 0.0, 1.0 ])
// (item-3 -> [ 0.5, 0.0 ]) (item-4 -> [ 0.5, 0.5 ]) (item-5 -> [ 0.5, 1.0 ])
// (item-6 -> [ 1.0, 0.0 ]) (item-7 -> [ 1.0, 0.5 ]) (item-8 -> [ 1.0, 1.0 ])

// create a query buffer...
b = Buffer.alloc(s,2)

// and fill it with a point
b.sendCollection([1,0]);

// and request 9 nearest neighbours
f.kNearest(b,9,{|x|x.postln;})
::
41 changes: 28 additions & 13 deletions example-code/sc/KDTree.scd
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ds.dump({
defer{
view.highlight_(id);
};

{
var start = Index.kr(slicePoints,index);
var end = Index.kr(slicePoints,index+1);
Expand All @@ -40,15 +40,8 @@ ds.dump({
strong::radius and num neighbours::
code::

// set some initial values
(
~numNeighbours = 3;
~tree.radius_(0.04);
)


// then make the plot, once it's up and you're clicking around,
// change the numbers and re-run the code above to see the differences
// Make a plot; once it's up and you're clicking around, change the numbers of
// queried neighbours and the permitted radius to see the different behaviours
(
var ds = FluidDataSet(s).load(
Dictionary.newFrom([
Expand All @@ -61,18 +54,40 @@ var ds = FluidDataSet(s).load(
)
])
);
~tree = FluidKDTree(s).fit(ds);

~tree = FluidKDTree(s);
~tree.numNeighbours = 3;
~tree.radius_(0.04);
~tree.fit(ds);

ds.dump({
arg dict;
var nn, nnd;
var xybuf = Buffer.alloc(s,2);
defer{
FluidPlotter(dict:dict,mouseMoveAction:{
w = Window(\KDTree,Rect(0,0,705,500)).front;
StaticText(w,Rect(500,5,200,20)).string_("numNeighbours:");
TextField(w,Rect(500,25,200,20)).string_(~tree.numNeighbours.asString).action_{|x|~tree.numNeighbours = x.value.asInteger};
StaticText(w,Rect(500,45,200,20)).string_("radius:");
TextField(w,Rect(500,65,200,20)).string_(~tree.radius.asString).action_{|x|~tree.radius = x.value.asFloat};
StaticText(w,Rect(500,85,200,20)).string_("neighbours:");
nn = TextView(w, Rect(500,105,200,40)).string_("").editable_(false);
StaticText(w,Rect(500,145,200,20)).string_("distances:");
nnd = TextView(w, Rect(500,165,200,200)).string_("").editable_(false);
FluidPlotter(w, Rect(5,5,490,490), dict:dict,mouseMoveAction:{
arg view, x, y;
xybuf.setn(0,[x,y]);
~tree.kNearest(xybuf,~numNeighbours,{
~tree.kNearest(xybuf,action:{
arg id;
defer{
view.highlight_(id);
nn.string = id.asString;
};
});
~tree.kNearestDist(xybuf,action:{
arg id;
defer{
nnd.string = id.asString;
};
});
});
Expand Down