From 1dfea5c46b9f5885a62884da214b601293c900e4 Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 11 Aug 2023 16:31:55 +0100 Subject: [PATCH 001/153] dtw/fastdtw client files --- include/algorithms/public/DTW.hpp | 0 include/algorithms/public/FastDTW.hpp | 0 include/clients/nrt/DTWClient.hpp | 0 include/clients/nrt/FastDTWClient.hpp | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 include/algorithms/public/DTW.hpp create mode 100644 include/algorithms/public/FastDTW.hpp create mode 100644 include/clients/nrt/DTWClient.hpp create mode 100644 include/clients/nrt/FastDTWClient.hpp diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp new file mode 100644 index 000000000..e69de29bb diff --git a/include/algorithms/public/FastDTW.hpp b/include/algorithms/public/FastDTW.hpp new file mode 100644 index 000000000..e69de29bb diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp new file mode 100644 index 000000000..e69de29bb diff --git a/include/clients/nrt/FastDTWClient.hpp b/include/clients/nrt/FastDTWClient.hpp new file mode 100644 index 000000000..e69de29bb From fe0d3cf9126817c6a35f26f75d9e97f789f3ba11 Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 15 Aug 2023 09:56:20 +0100 Subject: [PATCH 002/153] dtw initial structure --- include/algorithms/public/DTW.hpp | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index e69de29bb..c1b12c10f 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -0,0 +1,37 @@ +/* +Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) +Copyright University of Huddersfield. +Licensed under the BSD-3 License. +See license.md file in the project root for full license information. +This project has received funding from the European Research Council (ERC) +under the European Union’s Horizon 2020 research and innovation programme +(grant agreement No 725899). +*/ + +#pragma once + +#include "../util/FluidEigenMappings.hpp" +#include "../../data/FluidDataSet.hpp" +#include "../../data/FluidIndex.hpp" +#include "../../data/FluidTensor.hpp" +#include "../../data/TensorTypes.hpp" +#include "../../data/FluidMemory.hpp" +#include +#include + +namespace fluid { +namespace algorithm { + +class DTW +{ +public: + explicit DTW() = default; + ~DTW() = default; + + void init() + { + + } +} +} +} \ No newline at end of file From 6903f19b0cb8ed015cff76949f2808619e5ac8ae Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 22 Aug 2023 11:02:40 +0100 Subject: [PATCH 003/153] plagiarise datasetclient for now --- include/clients/nrt/DataSeriesClient.hpp | 317 +++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 include/clients/nrt/DataSeriesClient.hpp diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp new file mode 100644 index 000000000..6a2df0070 --- /dev/null +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -0,0 +1,317 @@ +/* +Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) +Copyright University of Huddersfield. +Licensed under the BSD-3 License. +See license.md file in the project root for full license information. +This project has received funding from the European Research Council (ERC) +under the European Union’s Horizon 2020 research and innovation programme +(grant agreement No 725899). +*/ + +#pragma once +#include "DataClient.hpp" +#include "LabelSetClient.hpp" +#include "NRTClient.hpp" +#include "../common/SharedClientUtils.hpp" +#include "../../algorithms/public/DataSetIdSequence.hpp" +#include "../../data/FluidDataSet.hpp" +#include +#include + +namespace fluid { +namespace client { +namespace dataset { + +enum { kName }; + +constexpr auto DataSetParams = + defineParameters(StringParam>("name", "Name of the DataSet")); + +class DataSetClient : public FluidBaseClient, + OfflineIn, + OfflineOut, + public DataClient> +{ +public: + using string = std::string; + using BufferPtr = std::shared_ptr; + using InputBufferPtr = std::shared_ptr; + using DataSet = FluidDataSet; + using LabelSet = FluidDataSet; + + template + Result process(FluidContext&) + { + return {}; + } + + using ParamDescType = decltype(DataSetParams); + + using ParamSetViewType = ParameterSetView; + std::reference_wrapper mParams; + + void setParams(ParamSetViewType& p) { mParams = p; } + + template + auto& get() const + { + return mParams.get().template get(); + } + + static constexpr auto& getParameterDescriptors() { return DataSetParams; } + + DataSetClient(ParamSetViewType& p, FluidContext&) : mParams(p) {} + + MessageResult addPoint(string id, InputBufferPtr data) + { + DataSet& dataset = mAlgorithm; + if (!data) return Error(NoBuffer); + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numFrames() == 0) return Error(EmptyBuffer); + if (dataset.size() == 0) + { + if (dataset.dims() != buf.numFrames()) dataset = DataSet(buf.numFrames()); + } + else if (buf.numFrames() != dataset.dims()) + return Error(WrongPointSize); + RealVector point(dataset.dims()); + point <<= buf.samps(0, dataset.dims(), 0); + return dataset.add(id, point) ? OK() : Error(DuplicateIdentifier); + } + + MessageResult getPoint(string id, BufferPtr data) const + { + if (!data) return Error(NoBuffer); + BufferAdaptor::Access buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + Result resizeResult = buf.resize(mAlgorithm.dims(), 1, buf.sampleRate()); + if (!resizeResult.ok()) + return {resizeResult.status(), resizeResult.message()}; + RealVector point(mAlgorithm.dims()); + point <<= buf.samps(0, mAlgorithm.dims(), 0); + bool result = mAlgorithm.get(id, point); + if (result) + { + buf.samps(0, mAlgorithm.dims(), 0) <<= point; + return OK(); + } + else + { + return Error(PointNotFound); + } + } + + MessageResult updatePoint(string id, InputBufferPtr data) + { + if (!data) return Error(NoBuffer); + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); + RealVector point(mAlgorithm.dims()); + point <<= buf.samps(0, mAlgorithm.dims(), 0); + return mAlgorithm.update(id, point) ? OK() : Error(PointNotFound); + } + + MessageResult setPoint(string id, InputBufferPtr data) + { + if (!data) return Error(NoBuffer); + + { // restrict buffer lock to this scope in case addPoint is called + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); + RealVector point(mAlgorithm.dims()); + point <<= buf.samps(0, mAlgorithm.dims(), 0); + bool result = mAlgorithm.update(id, point); + if (result) return OK(); + } + return addPoint(id, data); + } + + MessageResult deletePoint(string id) + { + return mAlgorithm.remove(id) ? OK() : Error(PointNotFound); + } + + MessageResult merge(SharedClientRef datasetClient, + bool overwrite) + { + auto datasetClientPtr = datasetClient.get().lock(); + if (!datasetClientPtr) return Error(NoDataSet); + auto srcDataSet = datasetClientPtr->getDataSet(); + if (srcDataSet.size() == 0) return Error(EmptyDataSet); + if (srcDataSet.pointSize() != mAlgorithm.pointSize()) + return Error(WrongPointSize); + auto ids = srcDataSet.getIds(); + RealVector point(srcDataSet.pointSize()); + for (index i = 0; i < srcDataSet.size(); i++) + { + srcDataSet.get(ids(i), point); + bool added = mAlgorithm.add(ids(i), point); + if (!added && overwrite) mAlgorithm.update(ids(i), point); + } + return OK(); + } + + MessageResult + fromBuffer(InputBufferPtr data, bool transpose, + SharedClientRef labels) + { + if (!data) return Error(NoBuffer); + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + auto bufView = transpose ? buf.allFrames() : buf.allFrames().transpose(); + if (auto labelsPtr = labels.get().lock()) + { + auto& labelSet = labelsPtr->getLabelSet(); + if (labelSet.size() != bufView.rows()) + { return Error("Label set size needs to match the buffer size"); } + mAlgorithm = DataSet(labelSet.getData().col(0), + FluidTensorView(bufView)); + } + else + { + algorithm::DataSetIdSequence seq("", 0, 0); + FluidTensor newIds(bufView.rows()); + seq.generate(newIds); + mAlgorithm = DataSet(newIds, FluidTensorView(bufView)); + } + return OK(); + } + + MessageResult toBuffer(BufferPtr data, bool transpose, + LabelSetClientRef labels) + { + if (!data) return Error(NoBuffer); + BufferAdaptor::Access buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + index nFrames = transpose ? mAlgorithm.dims() : mAlgorithm.size(); + index nChannels = transpose ? mAlgorithm.size() : mAlgorithm.dims(); + Result resizeResult = buf.resize(nFrames, nChannels, buf.sampleRate()); + if (!resizeResult.ok()) return Error(resizeResult.message()); + buf.allFrames() <<= + transpose ? mAlgorithm.getData() + : FluidTensorView(mAlgorithm.getData()) + .transpose(); + auto labelsPtr = labels.get().lock(); + if (labelsPtr) labelsPtr->setLabelSet(getIdsLabelSet()); + return OK(); + } + + MessageResult getIds(LabelSetClientRef dest) + { + auto destPtr = dest.get().lock(); + if (!destPtr) return Error(NoDataSet); + destPtr->setLabelSet(getIdsLabelSet()); + return OK(); + } + + MessageResult> kNearest(InputBufferPtr data, + index nNeighbours) const + { + // check for nNeighbours > 0 and < size of DS + if (nNeighbours > mAlgorithm.size()) + return Error>(SmallDataSet); + if (nNeighbours <= 0) return Error>(SmallK); + + InBufferCheck bufCheck(mAlgorithm.dims()); + + if (!bufCheck.checkInputs(data.get())) + return Error>(bufCheck.error()); + + FluidTensor point( + BufferAdaptor::ReadAccess(data.get()).samps(0, mAlgorithm.dims(), 0)); + + std::vector indices(asUnsigned(mAlgorithm.size())); + std::iota(indices.begin(), indices.end(), 0); + std::vector distances(asUnsigned(mAlgorithm.size())); + + auto ds = mAlgorithm.getData(); + + std::transform( + indices.begin(), indices.end(), distances.begin(), + [&point, &ds, this](index i) { return distance(point, ds.row(i)); }); + + std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { + return distances[asUnsigned(a)] < distances[asUnsigned(b)]; + }); + + FluidTensor labels(nNeighbours); + + std::transform( + indices.begin(), indices.begin() + nNeighbours, labels.begin(), + [this](index i) { + std::string const& id = mAlgorithm.getIds()[i]; + return rt::string{id, 0, id.size(), FluidDefaultAllocator()}; + }); + + return labels; + } + + MessageResult clear() + { + mAlgorithm = DataSet(0); + return OK(); + } + + MessageResult print() + { + return "DataSet " + std::string(get()) + ": " + mAlgorithm.print(); + } + + const DataSet getDataSet() const { return mAlgorithm; } + void setDataSet(DataSet ds) { mAlgorithm = ds; } + + static auto getMessageDescriptors() + { + return defineMessages( + makeMessage("addPoint", &DataSetClient::addPoint), + makeMessage("getPoint", &DataSetClient::getPoint), + makeMessage("setPoint", &DataSetClient::setPoint), + makeMessage("updatePoint", &DataSetClient::updatePoint), + makeMessage("deletePoint", &DataSetClient::deletePoint), + makeMessage("merge", &DataSetClient::merge), + makeMessage("dump", &DataSetClient::dump), + makeMessage("load", &DataSetClient::load), + makeMessage("print", &DataSetClient::print), + makeMessage("size", &DataSetClient::size), + makeMessage("cols", &DataSetClient::dims), + makeMessage("clear", &DataSetClient::clear), + makeMessage("write", &DataSetClient::write), + makeMessage("read", &DataSetClient::read), + makeMessage("fromBuffer", &DataSetClient::fromBuffer), + makeMessage("toBuffer", &DataSetClient::toBuffer), + makeMessage("getIds", &DataSetClient::getIds), + makeMessage("kNearest", &DataSetClient::kNearest)); + } + +private: + LabelSet getIdsLabelSet() + { + algorithm::DataSetIdSequence seq("", 0, 0); + FluidTensor newIds(mAlgorithm.size()); + FluidTensor labels(mAlgorithm.size(), 1); + labels.col(0) <<= mAlgorithm.getIds(); + seq.generate(newIds); + return LabelSet(newIds, labels); + }; + + double distance(FluidTensorView point1, FluidTensorView point2) const + { + return std::transform_reduce(point1.begin(), point1.end(), point2.begin(), 0.0, std::plus{}, [](double v1, double v2){ + return (v1-v2) * (v1-v2); + }); + }; +}; + +} // namespace dataset + +using DataSetClientRef = SharedClientRef; +using InputDataSetClientRef = SharedClientRef; + +using NRTThreadedDataSetClient = + NRTThreadingAdaptor; + +} // namespace client +} // namespace fluid From 46ecaf30c3dc9dda7a8f4c03f903c54b423d1903 Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 22 Aug 2023 11:24:59 +0100 Subject: [PATCH 004/153] rename object to dataseries --- include/clients/nrt/DataSeriesClient.hpp | 64 ++++++++++++------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 6a2df0070..f7626ca80 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -20,14 +20,16 @@ under the European Union’s Horizon 2020 research and innovation programme namespace fluid { namespace client { -namespace dataset { +namespace dataseries { enum { kName }; -constexpr auto DataSetParams = - defineParameters(StringParam>("name", "Name of the DataSet")); +constexpr auto DataSeriesParams = defineParameters( + StringParam>("name", "Name of the DataSeries"), + LongParam("frameLen", "length of one frame", 0) +); -class DataSetClient : public FluidBaseClient, +class DataSeriesClient : public FluidBaseClient, OfflineIn, OfflineOut, public DataClient> @@ -45,7 +47,7 @@ class DataSetClient : public FluidBaseClient, return {}; } - using ParamDescType = decltype(DataSetParams); + using ParamDescType = decltype(DataSeriesParams); using ParamSetViewType = ParameterSetView; std::reference_wrapper mParams; @@ -58,9 +60,9 @@ class DataSetClient : public FluidBaseClient, return mParams.get().template get(); } - static constexpr auto& getParameterDescriptors() { return DataSetParams; } + static constexpr auto& getParameterDescriptors() { return DataSeriesParams; } - DataSetClient(ParamSetViewType& p, FluidContext&) : mParams(p) {} + DataSeriesClient(ParamSetViewType& p, FluidContext&) : mParams(p) {} MessageResult addPoint(string id, InputBufferPtr data) { @@ -134,10 +136,10 @@ class DataSetClient : public FluidBaseClient, return mAlgorithm.remove(id) ? OK() : Error(PointNotFound); } - MessageResult merge(SharedClientRef datasetClient, + MessageResult merge(SharedClientRef dataseriesClient, bool overwrite) { - auto datasetClientPtr = datasetClient.get().lock(); + auto datasetClientPtr = dataseriesClient.get().lock(); if (!datasetClientPtr) return Error(NoDataSet); auto srcDataSet = datasetClientPtr->getDataSet(); if (srcDataSet.size() == 0) return Error(EmptyDataSet); @@ -266,24 +268,24 @@ class DataSetClient : public FluidBaseClient, static auto getMessageDescriptors() { return defineMessages( - makeMessage("addPoint", &DataSetClient::addPoint), - makeMessage("getPoint", &DataSetClient::getPoint), - makeMessage("setPoint", &DataSetClient::setPoint), - makeMessage("updatePoint", &DataSetClient::updatePoint), - makeMessage("deletePoint", &DataSetClient::deletePoint), - makeMessage("merge", &DataSetClient::merge), - makeMessage("dump", &DataSetClient::dump), - makeMessage("load", &DataSetClient::load), - makeMessage("print", &DataSetClient::print), - makeMessage("size", &DataSetClient::size), - makeMessage("cols", &DataSetClient::dims), - makeMessage("clear", &DataSetClient::clear), - makeMessage("write", &DataSetClient::write), - makeMessage("read", &DataSetClient::read), - makeMessage("fromBuffer", &DataSetClient::fromBuffer), - makeMessage("toBuffer", &DataSetClient::toBuffer), - makeMessage("getIds", &DataSetClient::getIds), - makeMessage("kNearest", &DataSetClient::kNearest)); + makeMessage("addPoint", &DataSeriesClient::addPoint), + makeMessage("getPoint", &DataSeriesClient::getPoint), + makeMessage("setPoint", &DataSeriesClient::setPoint), + makeMessage("updatePoint", &DataSeriesClient::updatePoint), + makeMessage("deletePoint", &DataSeriesClient::deletePoint), + makeMessage("merge", &DataSeriesClient::merge), + makeMessage("dump", &DataSeriesClient::dump), + makeMessage("load", &DataSeriesClient::load), + makeMessage("print", &DataSeriesClient::print), + makeMessage("size", &DataSeriesClient::size), + makeMessage("cols", &DataSeriesClient::dims), + makeMessage("clear", &DataSeriesClient::clear), + makeMessage("write", &DataSeriesClient::write), + makeMessage("read", &DataSeriesClient::read), + makeMessage("fromBuffer", &DataSeriesClient::fromBuffer), + makeMessage("toBuffer", &DataSeriesClient::toBuffer), + makeMessage("getIds", &DataSeriesClient::getIds), + makeMessage("kNearest", &DataSeriesClient::kNearest)); } private: @@ -307,11 +309,11 @@ class DataSetClient : public FluidBaseClient, } // namespace dataset -using DataSetClientRef = SharedClientRef; -using InputDataSetClientRef = SharedClientRef; +using DataSeriesClientRef = SharedClientRef; +using InputDataSeriesClientRef = SharedClientRef; -using NRTThreadedDataSetClient = - NRTThreadingAdaptor; +using NRTThreadedDataSeriesClient = + NRTThreadingAdaptor; } // namespace client } // namespace fluid From 9993a1e979cfbade0051f2c53aaf35d21812b824 Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 22 Aug 2023 11:46:00 +0100 Subject: [PATCH 005/153] functional clone of dataset --- include/clients/nrt/DataSeriesClient.hpp | 38 ++-- include/data/FluidDataSeries.hpp | 212 +++++++++++++++++++++++ include/data/FluidJSON.hpp | 33 ++++ 3 files changed, 264 insertions(+), 19 deletions(-) create mode 100644 include/data/FluidDataSeries.hpp diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index f7626ca80..6e23ccd69 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -14,7 +14,7 @@ under the European Union’s Horizon 2020 research and innovation programme #include "NRTClient.hpp" #include "../common/SharedClientUtils.hpp" #include "../../algorithms/public/DataSetIdSequence.hpp" -#include "../../data/FluidDataSet.hpp" +#include "../../data/FluidDataSeries.hpp" #include #include @@ -32,13 +32,13 @@ constexpr auto DataSeriesParams = defineParameters( class DataSeriesClient : public FluidBaseClient, OfflineIn, OfflineOut, - public DataClient> + public DataClient> { public: using string = std::string; using BufferPtr = std::shared_ptr; using InputBufferPtr = std::shared_ptr; - using DataSet = FluidDataSet; + using DataSeries = FluidDataSeries; using LabelSet = FluidDataSet; template @@ -66,14 +66,14 @@ class DataSeriesClient : public FluidBaseClient, MessageResult addPoint(string id, InputBufferPtr data) { - DataSet& dataset = mAlgorithm; + DataSeries& dataset = mAlgorithm; if (!data) return Error(NoBuffer); BufferAdaptor::ReadAccess buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() == 0) return Error(EmptyBuffer); if (dataset.size() == 0) { - if (dataset.dims() != buf.numFrames()) dataset = DataSet(buf.numFrames()); + if (dataset.dims() != buf.numFrames()) dataset = DataSeries(buf.numFrames()); } else if (buf.numFrames() != dataset.dims()) return Error(WrongPointSize); @@ -139,17 +139,17 @@ class DataSeriesClient : public FluidBaseClient, MessageResult merge(SharedClientRef dataseriesClient, bool overwrite) { - auto datasetClientPtr = dataseriesClient.get().lock(); - if (!datasetClientPtr) return Error(NoDataSet); - auto srcDataSet = datasetClientPtr->getDataSet(); - if (srcDataSet.size() == 0) return Error(EmptyDataSet); - if (srcDataSet.pointSize() != mAlgorithm.pointSize()) + auto dataseriesClientPtr = dataseriesClient.get().lock(); + if (!dataseriesClientPtr) return Error(NoDataSet); + auto srcDataSeries = dataseriesClientPtr->getDataSeries(); + if (srcDataSeries.size() == 0) return Error(EmptyDataSet); + if (srcDataSeries.pointSize() != mAlgorithm.pointSize()) return Error(WrongPointSize); - auto ids = srcDataSet.getIds(); - RealVector point(srcDataSet.pointSize()); - for (index i = 0; i < srcDataSet.size(); i++) + auto ids = srcDataSeries.getIds(); + RealVector point(srcDataSeries.pointSize()); + for (index i = 0; i < srcDataSeries.size(); i++) { - srcDataSet.get(ids(i), point); + srcDataSeries.get(ids(i), point); bool added = mAlgorithm.add(ids(i), point); if (!added && overwrite) mAlgorithm.update(ids(i), point); } @@ -169,7 +169,7 @@ class DataSeriesClient : public FluidBaseClient, auto& labelSet = labelsPtr->getLabelSet(); if (labelSet.size() != bufView.rows()) { return Error("Label set size needs to match the buffer size"); } - mAlgorithm = DataSet(labelSet.getData().col(0), + mAlgorithm = DataSeries(labelSet.getData().col(0), FluidTensorView(bufView)); } else @@ -177,7 +177,7 @@ class DataSeriesClient : public FluidBaseClient, algorithm::DataSetIdSequence seq("", 0, 0); FluidTensor newIds(bufView.rows()); seq.generate(newIds); - mAlgorithm = DataSet(newIds, FluidTensorView(bufView)); + mAlgorithm = DataSeries(newIds, FluidTensorView(bufView)); } return OK(); } @@ -253,7 +253,7 @@ class DataSeriesClient : public FluidBaseClient, MessageResult clear() { - mAlgorithm = DataSet(0); + mAlgorithm = DataSeries(0); return OK(); } @@ -262,8 +262,8 @@ class DataSeriesClient : public FluidBaseClient, return "DataSet " + std::string(get()) + ": " + mAlgorithm.print(); } - const DataSet getDataSet() const { return mAlgorithm; } - void setDataSet(DataSet ds) { mAlgorithm = ds; } + const DataSeries getDataSeries() const { return mAlgorithm; } + void setDataSeries(DataSeries ds) { mAlgorithm = ds; } static auto getMessageDescriptors() { diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp new file mode 100644 index 000000000..e09b3574a --- /dev/null +++ b/include/data/FluidDataSeries.hpp @@ -0,0 +1,212 @@ +#pragma once + +#include "data/FluidIndex.hpp" +#include "data/FluidTensor.hpp" +#include "data/TensorTypes.hpp" +#include +#include +#include +#include + +namespace fluid { + +template +class FluidDataSeries +{ + +public: + explicit FluidDataSeries() = default; + ~FluidDataSeries() = default; + + // Construct from list of dimensions for each data point, + // e.g. FluidDataSet(2, 3) is a dataset of 2x3 tensors + template ()>> + FluidDataSeries(Dims... dims) : mData(0, dims...), mDim(dims...) + { + static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); + } + + // Construct from existing tensors of ids and data points + FluidDataSeries(FluidTensorView ids, + FluidTensorView points) + : mIds(ids), mData(points) + { + initFromData(); + } + + // Construct from existing tensors of ids and data points + // (from convertible type for data, typically float -> double) + template + FluidDataSeries(FluidTensorView ids, + FluidTensorView points, + std::enable_if_t::value>* = nullptr) + : mIds(ids), mData(points) + { + initFromData(); + } + + // Resize data point layout (if empty) + template ()>> + bool resize(Dims... dims) + { + static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); + if (size() == 0) + { + mData = FluidTensor(0, dims...); + mDim = FluidTensorSlice(dims...); + return true; + } + else + { + return false; + } + } + + bool add(idType const& id, FluidTensorView point) + { + assert(sameExtents(mDim, point.descriptor())); + index pos = mData.rows(); + auto result = mIndex.insert({id, pos}); + if (!result.second) return false; + mData.resizeDim(0, 1); + mData.row(mData.rows() - 1) <<= point; + mIds.resizeDim(0, 1); + mIds(mIds.rows() - 1) = id; + return true; + } + + bool get(idType const& id, FluidTensorView point) const + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + point <<= mData.row(pos->second); + return true; + } + + FluidTensorView get(idType const& id) const + { + auto pos = mIndex.find(id); + return pos != mIndex.end() + ? mData.row(pos->second) + : FluidTensorView{nullptr, 0, 0}; + } + + index getIndex(idType const& id) const + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) + return -1; + else + return pos->second; + } + + bool update(idType const& id, FluidTensorView point) + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) + return false; + else + mData.row(pos->second) <<= point; + return true; + } + + bool remove(idType const& id) + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) { return false; } + else + { + auto current = pos->second; + mData.deleteRow(current); + mIds.deleteRow(current); + mIndex.erase(id); + for (auto& point : mIndex) + if (point.second > current) point.second--; + } + return true; + } + + FluidTensorView getData() { return mData; } + FluidTensorView getIds() { return mIds; } + FluidTensorView getData() const { return mData; } + FluidTensorView getIds() const { return mIds; } + + index pointSize() const { return mDim.size; } + index dims() const { return mDim.size; } + index size() const { return mIds.size(); } + bool initialized() const { return (size() > 0); } + + std::string printRow(FluidTensorView row, + index maxCols) const + { + using namespace std; + ostringstream result; + if (row.size() < maxCols) + { + for (index c = 0; c < row.size(); c++) + { + result << setw(10) << setprecision(5) << row(c); + } + } + else + { + for (index c = 0; c < maxCols / 2; c++) + { + result << setw(10) << setprecision(5) << row(c); + } + result << setw(10) << "..."; + for (index c = maxCols / 2; c > 0; c--) + { + result << setw(10) << setprecision(5) << row(row.size() - c); + } + } + return result.str(); + } + + std::string print(index maxRows = 6, index maxCols = 6) const + { + using namespace std; + if (size() == 0) return "{}"; + ostringstream result; + result << endl << "rows: " << size() << " cols: " << pointSize() << endl; + if (size() < maxRows) + { + for (index r = 0; r < size(); r++) + { + result << mIds(r) << " " << printRow(mData.row(r), maxCols) + << std::endl; + } + } + else + { + for (index r = 0; r < maxRows / 2; r++) + { + result << mIds(r) << " " << printRow(mData.row(r), maxCols) + << std::endl; + } + result << setw(10) << "..." << std::endl; + for (index r = maxRows / 2; r > 0; r--) + { + result << mIds(size() - r) << " " + << printRow(mData.row(size() - r), maxCols) << std::endl; + } + } + return result.str(); + } + +private: + void initFromData() + { + assert(mIds.rows() == mData.rows()); + mDim = mData.cols(); + for (index i = 0; i < mIds.size(); i++) { mIndex.insert({mIds[i], i}); } + } + + std::unordered_map mIndex; + FluidTensor mIds; + FluidTensor mData; + FluidTensorSlice mDim; +}; +} // namespace fluid diff --git a/include/data/FluidJSON.hpp b/include/data/FluidJSON.hpp index 0cdd5ede4..2e7b79be5 100644 --- a/include/data/FluidJSON.hpp +++ b/include/data/FluidJSON.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -133,6 +134,38 @@ void from_json(const nlohmann::json &j, FluidDataSet &ds) { } } +// FluidDataSeries +template +void to_json(nlohmann::json &j, const FluidDataSeries &ds) { + auto ids = ds.getIds(); + auto data = ds.getData(); + j["cols"] = ds.pointSize(); + for (index r = 0; r < ds.size(); r++) { + j["data"][ids[r]] = data.row(r); + } +} + +template +bool check_json(const nlohmann::json &j, + const FluidDataSeries &) { + return fluid::check_json(j, + {"data", "cols"}, + {JSONTypes::OBJECT, JSONTypes::NUMBER} + ); +} + +template +void from_json(const nlohmann::json &j, FluidDataSeries &ds) { + auto rows = j.at("data"); + index pointSize = j.at("cols").get(); + ds.resize(pointSize); + FluidTensor tmp(pointSize); + for (auto r = rows.begin(); r != rows.end(); ++r) { + r.value().get_to(tmp); + ds.add(r.key(), tmp); + } +} + namespace algorithm { // KDTree void to_json(nlohmann::json &j, const KDTree &tree) { From 0aa7d8b623b461eb57eb821787c6247b6e60deea Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 10:35:47 +0100 Subject: [PATCH 006/153] dataseries CTORs --- include/data/FluidDataSeries.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index e09b3574a..824171209 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -22,14 +22,14 @@ class FluidDataSeries // e.g. FluidDataSet(2, 3) is a dataset of 2x3 tensors template ()>> - FluidDataSeries(Dims... dims) : mData(0, dims...), mDim(dims...) + FluidDataSeries(Dims... dims) : mDim(dims...) { static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); } // Construct from existing tensors of ids and data points FluidDataSeries(FluidTensorView ids, - FluidTensorView points) + std::vector> points) : mIds(ids), mData(points) { initFromData(); @@ -39,7 +39,7 @@ class FluidDataSeries // (from convertible type for data, typically float -> double) template FluidDataSeries(FluidTensorView ids, - FluidTensorView points, + std::vector> points, std::enable_if_t::value>* = nullptr) : mIds(ids), mData(points) { @@ -54,7 +54,7 @@ class FluidDataSeries static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); if (size() == 0) { - mData = FluidTensor(0, dims...); + mData = std::vector>(); mDim = FluidTensorSlice(dims...); return true; } From 1bd2e9356c4286f39548f6ff294ffded26b888ae Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 10:36:06 +0100 Subject: [PATCH 007/153] addSeries member --- include/data/FluidDataSeries.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 824171209..e36e7fb37 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -64,16 +64,18 @@ class FluidDataSeries } } - bool add(idType const& id, FluidTensorView point) + bool addSeries(idType const& id, FluidTensorView series) { - assert(sameExtents(mDim, point.descriptor())); - index pos = mData.rows(); - auto result = mIndex.insert({id, pos}); + assert(sameExtents(mDim, series[0].descriptor())); + + auto result = mIndex.insert({id, mData.size()}); if (!result.second) return false; - mData.resizeDim(0, 1); - mData.row(mData.rows() - 1) <<= point; + + mData.push_back(series); + mIds.resizeDim(0, 1); mIds(mIds.rows() - 1) = id; + return true; } From 12e52c4487278baca8d80a4716f057ad239e7aa2 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 10:36:17 +0100 Subject: [PATCH 008/153] getSeries member --- include/data/FluidDataSeries.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index e36e7fb37..1bd5b3d33 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -79,11 +79,15 @@ class FluidDataSeries return true; } - bool get(idType const& id, FluidTensorView point) const + bool getSeries(idType const& id, FluidTensorView series) const { auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - point <<= mData.row(pos->second); + + series <<= mData[pos->second]; + + return true; + } return true; } From f54ce931fdec04722b7737a48f9bf6b1bd095add Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 10:36:35 +0100 Subject: [PATCH 009/153] addFrame/getFrame members --- include/data/FluidDataSeries.hpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 1bd5b3d33..6f73c4754 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -88,6 +88,38 @@ class FluidDataSeries return true; } + + bool addFrame(idType const& id, FluidTensorView frame) + { + assert(sameExtents(mDim, frame.descriptor())); + + auto pos = mIndex.find(id); + if (pos == mIndex.end()) + { + FluidTensor newPoint; + newPoint.resizeDim(0, 1); + newPoint.row(0) <<= frame; + return addSeries(id, newPoint); + } + + FluidTensorView bucket = mData[pos->second]; + bucket.resizeDim(0, 1); + bucket.row(bucket.rows() - 1) <<= frame; + + return true; + } + + bool getFrame(idType const& id, index time, FluidTensorView frame) + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + + FluidTensorView bucket = mData[pos->second]; + + assert(time < bucket.rows()); + + frame <<= bucket.row(time); + return true; } From 725342b7f035094c4daaf4f2bb2464b5ef88acff Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 10:37:15 +0100 Subject: [PATCH 010/153] get member function --- include/data/FluidDataSeries.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 6f73c4754..9782838a1 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -123,12 +123,12 @@ class FluidDataSeries return true; } - FluidTensorView get(idType const& id) const + FluidTensorView get(idType const& id) const { auto pos = mIndex.find(id); return pos != mIndex.end() - ? mData.row(pos->second) - : FluidTensorView{nullptr, 0, 0}; + ? mData[pos->second] + : FluidTensorView{nullptr, 0, 0}; } index getIndex(idType const& id) const From 8ffcfd3ec843c7126be4d2cc522ae3db82d60f53 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 10:37:37 +0100 Subject: [PATCH 011/153] modify member getters --- include/data/FluidDataSeries.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 9782838a1..acc66204d 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -166,9 +166,10 @@ class FluidDataSeries return true; } - FluidTensorView getData() { return mData; } + std::vector> getData() { return mData; } + std::vector> getData() const { return mData; } + FluidTensorView getIds() { return mIds; } - FluidTensorView getData() const { return mData; } FluidTensorView getIds() const { return mIds; } index pointSize() const { return mDim.size; } From 8a9a92d04c8e2e3f0bf70016397f33d613fc8974 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 10:38:02 +0100 Subject: [PATCH 012/153] change container type to vector of tensors --- include/data/FluidDataSeries.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index acc66204d..e53f55c9b 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -245,7 +245,7 @@ class FluidDataSeries std::unordered_map mIndex; FluidTensor mIds; - FluidTensor mData; - FluidTensorSlice mDim; + std::vector> mData; + FluidTensorSlice mDim; // dimensions for one frame }; } // namespace fluid From c8aa44c7b5aadd9d913aa1bb22aaa7d9bde50086 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 10:38:20 +0100 Subject: [PATCH 013/153] modify initFromData for vector change --- include/data/FluidDataSeries.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index e53f55c9b..e9337e27f 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -238,8 +238,8 @@ class FluidDataSeries private: void initFromData() { - assert(mIds.rows() == mData.rows()); - mDim = mData.cols(); + assert(mIds.rows() == mData.size()); + mDim = mData[0].cols(); for (index i = 0; i < mIds.size(); i++) { mIndex.insert({mIds[i], i}); } } From a1b89a87dda25cac2505af8cefe1a5fc2b0d41ec Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 10:48:27 +0100 Subject: [PATCH 014/153] removeSeries/Frame and updateSeries/Frame member functions --- include/data/FluidDataSeries.hpp | 53 ++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index e9337e27f..46426d459 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -115,9 +115,7 @@ class FluidDataSeries if (pos == mIndex.end()) return false; FluidTensorView bucket = mData[pos->second]; - assert(time < bucket.rows()); - frame <<= bucket.row(time); return true; @@ -140,29 +138,66 @@ class FluidDataSeries return pos->second; } - bool update(idType const& id, FluidTensorView point) + bool updateSeries(idType const& id, FluidTensorView series) { auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; else - mData.row(pos->second) <<= point; + mData[pos->second] <<= series; return true; } - bool remove(idType const& id) + bool updateFrame(idType const& id, index time, FluidTensorView frame) { auto pos = mIndex.find(id); - if (pos == mIndex.end()) { return false; } - else + if (pos == mIndex.end()) return false; + + FluidTensorView bucket = mData[pos->second]; + assert(time < bucket.rows()); + bucket.row(time) <<= frame; + + return true; + } + + bool removeSeries(idType const& id) + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + + index current = pos->second; + mData.erase(mData.begin() + current); + mIds.deleteRow(current); + mIndex.erase(id); + + for (auto& point : mIndex) + { + if (point.second > current) point.second--; + } + + return true; + } + + bool removeFrame(idType const& id, index time) + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + + index current = pos->second; + FluidTensorView bucket = mData[current]; + assert(time < bucket.rows()) + bucket.deleteRow(time); + + if(bucket.rows() == 0) { - auto current = pos->second; - mData.deleteRow(current); mIds.deleteRow(current); mIndex.erase(id); for (auto& point : mIndex) + { if (point.second > current) point.second--; + } } + return true; } From af564f9dd62a6a92cbe6cdab0476f93b476a8509 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 11:10:14 +0100 Subject: [PATCH 015/153] remove superfluous dataset members and convert names --- include/clients/nrt/DataSeriesClient.hpp | 214 ++++++++++++----------- 1 file changed, 109 insertions(+), 105 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 6e23ccd69..a0fce58a6 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -22,7 +22,7 @@ namespace fluid { namespace client { namespace dataseries { -enum { kName }; +enum { kName, kFrameLen }; constexpr auto DataSeriesParams = defineParameters( StringParam>("name", "Name of the DataSeries"), @@ -64,7 +64,7 @@ class DataSeriesClient : public FluidBaseClient, DataSeriesClient(ParamSetViewType& p, FluidContext&) : mParams(p) {} - MessageResult addPoint(string id, InputBufferPtr data) + MessageResult addFrame(string id, InputBufferPtr data) { DataSeries& dataset = mAlgorithm; if (!data) return Error(NoBuffer); @@ -79,10 +79,11 @@ class DataSeriesClient : public FluidBaseClient, return Error(WrongPointSize); RealVector point(dataset.dims()); point <<= buf.samps(0, dataset.dims(), 0); - return dataset.add(id, point) ? OK() : Error(DuplicateIdentifier); + dataset.addFrame(id, point); + return OK(); } - MessageResult getPoint(string id, BufferPtr data) const + MessageResult getFrame(string id, index time, BufferPtr data) const { if (!data) return Error(NoBuffer); BufferAdaptor::Access buf(data.get()); @@ -92,7 +93,7 @@ class DataSeriesClient : public FluidBaseClient, return {resizeResult.status(), resizeResult.message()}; RealVector point(mAlgorithm.dims()); point <<= buf.samps(0, mAlgorithm.dims(), 0); - bool result = mAlgorithm.get(id, point); + bool result = mAlgorithm.getFrame(id, time, point); if (result) { buf.samps(0, mAlgorithm.dims(), 0) <<= point; @@ -104,7 +105,7 @@ class DataSeriesClient : public FluidBaseClient, } } - MessageResult updatePoint(string id, InputBufferPtr data) + MessageResult updateFrame(string id, index time, InputBufferPtr data) { if (!data) return Error(NoBuffer); BufferAdaptor::ReadAccess buf(data.get()); @@ -112,10 +113,10 @@ class DataSeriesClient : public FluidBaseClient, if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); RealVector point(mAlgorithm.dims()); point <<= buf.samps(0, mAlgorithm.dims(), 0); - return mAlgorithm.update(id, point) ? OK() : Error(PointNotFound); + return mAlgorithm.updateFrame(id, time, point) ? OK() : Error(PointNotFound); } - MessageResult setPoint(string id, InputBufferPtr data) + MessageResult setFrame(string id, index time, InputBufferPtr data) { if (!data) return Error(NoBuffer); @@ -125,15 +126,15 @@ class DataSeriesClient : public FluidBaseClient, if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); RealVector point(mAlgorithm.dims()); point <<= buf.samps(0, mAlgorithm.dims(), 0); - bool result = mAlgorithm.update(id, point); + bool result = mAlgorithm.updateFrame(id, time, point); if (result) return OK(); } - return addPoint(id, data); + return addFrame(id, data); } - MessageResult deletePoint(string id) + MessageResult deleteFrame(string id, index time) { - return mAlgorithm.remove(id) ? OK() : Error(PointNotFound); + return mAlgorithm.removeFrame(id, time) ? OK() : Error(PointNotFound); } MessageResult merge(SharedClientRef dataseriesClient, @@ -141,65 +142,67 @@ class DataSeriesClient : public FluidBaseClient, { auto dataseriesClientPtr = dataseriesClient.get().lock(); if (!dataseriesClientPtr) return Error(NoDataSet); + auto srcDataSeries = dataseriesClientPtr->getDataSeries(); if (srcDataSeries.size() == 0) return Error(EmptyDataSet); if (srcDataSeries.pointSize() != mAlgorithm.pointSize()) return Error(WrongPointSize); + auto ids = srcDataSeries.getIds(); - RealVector point(srcDataSeries.pointSize()); for (index i = 0; i < srcDataSeries.size(); i++) { - srcDataSeries.get(ids(i), point); - bool added = mAlgorithm.add(ids(i), point); - if (!added && overwrite) mAlgorithm.update(ids(i), point); + auto series = srcDataSeries.getSeries(ids(i)); + bool added = mAlgorithm.addSeries(ids(i), series); + if (!added && overwrite) mAlgorithm.updateSeries(ids(i), series); } - return OK(); - } - MessageResult - fromBuffer(InputBufferPtr data, bool transpose, - SharedClientRef labels) - { - if (!data) return Error(NoBuffer); - BufferAdaptor::ReadAccess buf(data.get()); - if (!buf.exists()) return Error(InvalidBuffer); - auto bufView = transpose ? buf.allFrames() : buf.allFrames().transpose(); - if (auto labelsPtr = labels.get().lock()) - { - auto& labelSet = labelsPtr->getLabelSet(); - if (labelSet.size() != bufView.rows()) - { return Error("Label set size needs to match the buffer size"); } - mAlgorithm = DataSeries(labelSet.getData().col(0), - FluidTensorView(bufView)); - } - else - { - algorithm::DataSetIdSequence seq("", 0, 0); - FluidTensor newIds(bufView.rows()); - seq.generate(newIds); - mAlgorithm = DataSeries(newIds, FluidTensorView(bufView)); - } return OK(); } - MessageResult toBuffer(BufferPtr data, bool transpose, - LabelSetClientRef labels) - { - if (!data) return Error(NoBuffer); - BufferAdaptor::Access buf(data.get()); - if (!buf.exists()) return Error(InvalidBuffer); - index nFrames = transpose ? mAlgorithm.dims() : mAlgorithm.size(); - index nChannels = transpose ? mAlgorithm.size() : mAlgorithm.dims(); - Result resizeResult = buf.resize(nFrames, nChannels, buf.sampleRate()); - if (!resizeResult.ok()) return Error(resizeResult.message()); - buf.allFrames() <<= - transpose ? mAlgorithm.getData() - : FluidTensorView(mAlgorithm.getData()) - .transpose(); - auto labelsPtr = labels.get().lock(); - if (labelsPtr) labelsPtr->setLabelSet(getIdsLabelSet()); - return OK(); - } + // MessageResult + // fromBuffer(InputBufferPtr data, bool transpose, + // SharedClientRef labels) + // { + // if (!data) return Error(NoBuffer); + // BufferAdaptor::ReadAccess buf(data.get()); + // if (!buf.exists()) return Error(InvalidBuffer); + // auto bufView = transpose ? buf.allFrames() : buf.allFrames().transpose(); + // if (auto labelsPtr = labels.get().lock()) + // { + // auto& labelSet = labelsPtr->getLabelSet(); + // if (labelSet.size() != bufView.rows()) + // { return Error("Label set size needs to match the buffer size"); } + // mAlgorithm = DataSeries(labelSet.getData().col(0), + // FluidTensorView(bufView)); + // } + // else + // { + // algorithm::DataSetIdSequence seq("", 0, 0); + // FluidTensor newIds(bufView.rows()); + // seq.generate(newIds); + // mAlgorithm = DataSeries(newIds, FluidTensorView(bufView)); + // } + // return OK(); + // } + + // MessageResult toBuffer(BufferPtr data, bool transpose, + // LabelSetClientRef labels) + // { + // if (!data) return Error(NoBuffer); + // BufferAdaptor::Access buf(data.get()); + // if (!buf.exists()) return Error(InvalidBuffer); + // index nFrames = transpose ? mAlgorithm.dims() : mAlgorithm.size(); + // index nChannels = transpose ? mAlgorithm.size() : mAlgorithm.dims(); + // Result resizeResult = buf.resize(nFrames, nChannels, buf.sampleRate()); + // if (!resizeResult.ok()) return Error(resizeResult.message()); + // buf.allFrames() <<= + // transpose ? mAlgorithm.getData() + // : FluidTensorView(mAlgorithm.getData()) + // .transpose(); + // auto labelsPtr = labels.get().lock(); + // if (labelsPtr) labelsPtr->setLabelSet(getIdsLabelSet()); + // return OK(); + // } MessageResult getIds(LabelSetClientRef dest) { @@ -209,47 +212,47 @@ class DataSeriesClient : public FluidBaseClient, return OK(); } - MessageResult> kNearest(InputBufferPtr data, - index nNeighbours) const - { - // check for nNeighbours > 0 and < size of DS - if (nNeighbours > mAlgorithm.size()) - return Error>(SmallDataSet); - if (nNeighbours <= 0) return Error>(SmallK); + // MessageResult> kNearest(InputBufferPtr data, + // index nNeighbours) const + // { + // // check for nNeighbours > 0 and < size of DS + // if (nNeighbours > mAlgorithm.size()) + // return Error>(SmallDataSet); + // if (nNeighbours <= 0) return Error>(SmallK); - InBufferCheck bufCheck(mAlgorithm.dims()); + // InBufferCheck bufCheck(mAlgorithm.dims()); - if (!bufCheck.checkInputs(data.get())) - return Error>(bufCheck.error()); + // if (!bufCheck.checkInputs(data.get())) + // return Error>(bufCheck.error()); - FluidTensor point( - BufferAdaptor::ReadAccess(data.get()).samps(0, mAlgorithm.dims(), 0)); + // FluidTensor point( + // BufferAdaptor::ReadAccess(data.get()).samps(0, mAlgorithm.dims(), 0)); - std::vector indices(asUnsigned(mAlgorithm.size())); - std::iota(indices.begin(), indices.end(), 0); - std::vector distances(asUnsigned(mAlgorithm.size())); + // std::vector indices(asUnsigned(mAlgorithm.size())); + // std::iota(indices.begin(), indices.end(), 0); + // std::vector distances(asUnsigned(mAlgorithm.size())); - auto ds = mAlgorithm.getData(); + // auto ds = mAlgorithm.getData(); - std::transform( - indices.begin(), indices.end(), distances.begin(), - [&point, &ds, this](index i) { return distance(point, ds.row(i)); }); + // std::transform( + // indices.begin(), indices.end(), distances.begin(), + // [&point, &ds, this](index i) { return distance(point, ds.row(i)); }); - std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { - return distances[asUnsigned(a)] < distances[asUnsigned(b)]; - }); + // std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { + // return distances[asUnsigned(a)] < distances[asUnsigned(b)]; + // }); - FluidTensor labels(nNeighbours); + // FluidTensor labels(nNeighbours); - std::transform( - indices.begin(), indices.begin() + nNeighbours, labels.begin(), - [this](index i) { - std::string const& id = mAlgorithm.getIds()[i]; - return rt::string{id, 0, id.size(), FluidDefaultAllocator()}; - }); + // std::transform( + // indices.begin(), indices.begin() + nNeighbours, labels.begin(), + // [this](index i) { + // std::string const& id = mAlgorithm.getIds()[i]; + // return rt::string{id, 0, id.size(), FluidDefaultAllocator()}; + // }); - return labels; - } + // return labels; + // } MessageResult clear() { @@ -268,11 +271,11 @@ class DataSeriesClient : public FluidBaseClient, static auto getMessageDescriptors() { return defineMessages( - makeMessage("addPoint", &DataSeriesClient::addPoint), - makeMessage("getPoint", &DataSeriesClient::getPoint), - makeMessage("setPoint", &DataSeriesClient::setPoint), - makeMessage("updatePoint", &DataSeriesClient::updatePoint), - makeMessage("deletePoint", &DataSeriesClient::deletePoint), + makeMessage("addPoint", &DataSeriesClient::addFrame), + makeMessage("getPoint", &DataSeriesClient::getFrame), + makeMessage("setPoint", &DataSeriesClient::setFrame), + makeMessage("updatePoint", &DataSeriesClient::updateFrame), + makeMessage("deletePoint", &DataSeriesClient::deleteFrame), makeMessage("merge", &DataSeriesClient::merge), makeMessage("dump", &DataSeriesClient::dump), makeMessage("load", &DataSeriesClient::load), @@ -282,10 +285,11 @@ class DataSeriesClient : public FluidBaseClient, makeMessage("clear", &DataSeriesClient::clear), makeMessage("write", &DataSeriesClient::write), makeMessage("read", &DataSeriesClient::read), - makeMessage("fromBuffer", &DataSeriesClient::fromBuffer), - makeMessage("toBuffer", &DataSeriesClient::toBuffer), - makeMessage("getIds", &DataSeriesClient::getIds), - makeMessage("kNearest", &DataSeriesClient::kNearest)); + // makeMessage("fromBuffer", &DataSeriesClient::fromBuffer), + // makeMessage("toBuffer", &DataSeriesClient::toBuffer), + makeMessage("getIds", &DataSeriesClient::getIds) + // makeMessage("kNearest", &DataSeriesClient::kNearest) + ); } private: @@ -299,12 +303,12 @@ class DataSeriesClient : public FluidBaseClient, return LabelSet(newIds, labels); }; - double distance(FluidTensorView point1, FluidTensorView point2) const - { - return std::transform_reduce(point1.begin(), point1.end(), point2.begin(), 0.0, std::plus{}, [](double v1, double v2){ - return (v1-v2) * (v1-v2); - }); - }; + // double distance(FluidTensorView point1, FluidTensorView point2) const + // { + // return std::transform_reduce(point1.begin(), point1.end(), point2.begin(), 0.0, std::plus{}, [](double v1, double v2){ + // return (v1-v2) * (v1-v2); + // }); + // }; }; } // namespace dataset From db247f6da66f4eac61f986962507e9dba0a36249 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 11:11:21 +0100 Subject: [PATCH 016/153] remove kFrameLen paramter --- include/clients/nrt/DataSeriesClient.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index a0fce58a6..e119ab628 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -22,11 +22,10 @@ namespace fluid { namespace client { namespace dataseries { -enum { kName, kFrameLen }; +enum { kName }; constexpr auto DataSeriesParams = defineParameters( - StringParam>("name", "Name of the DataSeries"), - LongParam("frameLen", "length of one frame", 0) + StringParam>("name", "Name of the DataSeries") ); class DataSeriesClient : public FluidBaseClient, From 2774b291bbbf0c19a92eca7fa5888b9e5865226d Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 11:12:15 +0100 Subject: [PATCH 017/153] add const qualifiers to input views --- include/data/FluidDataSeries.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 46426d459..da9b3d08e 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -64,7 +64,7 @@ class FluidDataSeries } } - bool addSeries(idType const& id, FluidTensorView series) + bool addSeries(idType const& id, FluidTensorView series) { assert(sameExtents(mDim, series[0].descriptor())); @@ -89,7 +89,7 @@ class FluidDataSeries return true; } - bool addFrame(idType const& id, FluidTensorView frame) + bool addFrame(idType const& id, FluidTensorView frame) { assert(sameExtents(mDim, frame.descriptor())); @@ -109,7 +109,7 @@ class FluidDataSeries return true; } - bool getFrame(idType const& id, index time, FluidTensorView frame) + bool getFrame(idType const& id, index time, FluidTensorView frame) const { auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; @@ -121,7 +121,7 @@ class FluidDataSeries return true; } - FluidTensorView get(idType const& id) const + FluidTensorView getSeries(idType const& id) const { auto pos = mIndex.find(id); return pos != mIndex.end() @@ -138,7 +138,7 @@ class FluidDataSeries return pos->second; } - bool updateSeries(idType const& id, FluidTensorView series) + bool updateSeries(idType const& id, FluidTensorView series) { auto pos = mIndex.find(id); if (pos == mIndex.end()) @@ -148,7 +148,7 @@ class FluidDataSeries return true; } - bool updateFrame(idType const& id, index time, FluidTensorView frame) + bool updateFrame(idType const& id, index time, FluidTensorView frame) { auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; From d875bd4be67e0cc5b4133012c4e06c0869f1bd46 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 11:14:45 +0100 Subject: [PATCH 018/153] add `DataSeries` to libmanipulation --- FlucomaClients.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/FlucomaClients.cmake b/FlucomaClients.cmake index 2208b82b1..078362d24 100644 --- a/FlucomaClients.cmake +++ b/FlucomaClients.cmake @@ -142,6 +142,7 @@ add_client(Transients clients/rt/TransientClient.hpp CLASS RTTransientClient ) #lib manipulation client group add_client(DataSet clients/nrt/DataSetClient.hpp CLASS NRTThreadedDataSetClient GROUP MANIPULATION) +add_client(DataSeries clients/nrt/DataSeriesClient.hpp CLASS NRTThreadedDataSeriesClient GROUP MANIPULATION) add_client(DataSetQuery clients/nrt/DataSetQueryClient.hpp CLASS NRTThreadedDataSetQueryClient GROUP MANIPULATION) add_client(LabelSet clients/nrt/LabelSetClient.hpp CLASS NRTThreadedLabelSetClient GROUP MANIPULATION) add_client(KDTree clients/nrt/KDTreeClient.hpp CLASS NRTThreadedKDTreeClient GROUP MANIPULATION) From 9e116faccf9fffbcf7e102e294387273805d5500 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 12:22:17 +0100 Subject: [PATCH 019/153] fix constness point setting issues --- include/data/FluidDataSeries.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index da9b3d08e..13154b32c 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -71,7 +71,7 @@ class FluidDataSeries auto result = mIndex.insert({id, mData.size()}); if (!result.second) return false; - mData.push_back(series); + mData.emplace_back(series); mIds.resizeDim(0, 1); mIds(mIds.rows() - 1) = id; From 66897f9c758df8505dac811199c58328e4ec79a6 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 12:23:36 +0100 Subject: [PATCH 020/153] xvalue views for casting issues --- include/data/FluidDataSeries.hpp | 33 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 13154b32c..e506a76f7 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -102,9 +102,8 @@ class FluidDataSeries return addSeries(id, newPoint); } - FluidTensorView bucket = mData[pos->second]; - bucket.resizeDim(0, 1); - bucket.row(bucket.rows() - 1) <<= frame; + mData[pos->second].resizeDim(0, 1); + mData[pos->second].row(mData[pos->second].rows() - 1) <<= frame; return true; } @@ -114,19 +113,21 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - FluidTensorView bucket = mData[pos->second]; - assert(time < bucket.rows()); - frame <<= bucket.row(time); + assert(time < mData[pos->second].rows()); + frame <<= mData[pos->second].row(time); return true; } - FluidTensorView getSeries(idType const& id) const + FluidTensorView getFrame(idType const& id, index time) const { auto pos = mIndex.find(id); - return pos != mIndex.end() - ? mData[pos->second] - : FluidTensorView{nullptr, 0, 0}; + if(pos != mIndex.end()) + { + assert(time < mData[pos->second].rows()); + return mData[pos->second].row(time); + } + else { return FluidTensorView{nullptr, 0, 0}; } } index getIndex(idType const& id) const @@ -153,9 +154,8 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - FluidTensorView bucket = mData[pos->second]; - assert(time < bucket.rows()); - bucket.row(time) <<= frame; + assert(time < mData[pos->second].rows()); + mData[pos->second].row(time) <<= frame; return true; } @@ -184,11 +184,10 @@ class FluidDataSeries if (pos == mIndex.end()) return false; index current = pos->second; - FluidTensorView bucket = mData[current]; - assert(time < bucket.rows()) - bucket.deleteRow(time); + assert(time < mData[current].rows()); + mData[current].deleteRow(time); - if(bucket.rows() == 0) + if(mData[current].rows() == 0) { mIds.deleteRow(current); mIndex.erase(id); From 534fd365a3bfcc707b4f0e4dd54082f40afc8e69 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 12:23:53 +0100 Subject: [PATCH 021/153] printing shenanigains --- include/data/FluidDataSeries.hpp | 66 ++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index e506a76f7..a140a31e9 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -211,59 +211,95 @@ class FluidDataSeries index size() const { return mIds.size(); } bool initialized() const { return (size() > 0); } - std::string printRow(FluidTensorView row, - index maxCols) const + std::string printFrame(FluidTensorView frame, + index maxCols) const { using namespace std; ostringstream result; - if (row.size() < maxCols) + if (frame.size() < maxCols) { - for (index c = 0; c < row.size(); c++) + for (index c = 0; c < frame.size(); c++) { - result << setw(10) << setprecision(5) << row(c); + result << setw(10) << setprecision(5) << frame(c); } } else { for (index c = 0; c < maxCols / 2; c++) { - result << setw(10) << setprecision(5) << row(c); + result << setw(10) << setprecision(5) << frame(c); } result << setw(10) << "..."; for (index c = maxCols / 2; c > 0; c--) { - result << setw(10) << setprecision(5) << row(row.size() - c); + result << setw(10) << setprecision(5) << frame(frame.size() - c); } } return result.str(); } - std::string print(index maxRows = 6, index maxCols = 6) const + std::string printSeries(FluidTensorView series, + index maxFrames, index maxCols) const + { + using namespace std; + ostringstream result; + + for (index t = 0; t < series.rows(); t++) + { + using namespace std; + ostringstream result; + if (series.rows() < maxFrames) + { + for (index r = 0; r < series.rows(); r++) + { + result << "t = " << r << ": {" << endl << printFrame(series.row(r), maxCols) + << endl << "}" << endl; + } + } + else + { + for (index r = 0; r < maxFrames / 2; r++) + { + result << "t = " << r << " {" << endl << printFrame(series.row(r), maxCols) + << endl << "}" << endl; + } + result << setw(10) << "..." << std::endl; + for (index r = maxFrames / 2; r > 0; r--) + { + result << "t = " << (size() - r) << " {" + << printFrame(series.row(size() - r), maxCols) << " }" << endl; + } + } + return result.str(); + } + } + + std::string print(index maxRows = 6, index maxFrames = 6, index maxCols = 6) const { using namespace std; if (size() == 0) return "{}"; ostringstream result; - result << endl << "rows: " << size() << " cols: " << pointSize() << endl; + result << endl << "points: " << size() << " frame size: " << pointSize() << endl; if (size() < maxRows) { for (index r = 0; r < size(); r++) { - result << mIds(r) << " " << printRow(mData.row(r), maxCols) - << std::endl; + result << mIds(r) << ": {" << endl << printSeries(mData[r], maxFrames, maxCols) + << " }" << endl; } } else { for (index r = 0; r < maxRows / 2; r++) { - result << mIds(r) << " " << printRow(mData.row(r), maxCols) - << std::endl; + result << mIds(r) << ": {" << endl << printSeries(mData[r], maxFrames, maxCols) + << endl << "}" << endl; } result << setw(10) << "..." << std::endl; for (index r = maxRows / 2; r > 0; r--) { - result << mIds(size() - r) << " " - << printRow(mData.row(size() - r), maxCols) << std::endl; + result << mIds(size() - r) << " {" + << printSeries(mData[size() - r], maxFrames, maxCols) << " }" << endl; } } return result.str(); From 9536952eccb9c505888b57183c601a7144d4fc32 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 12:24:09 +0100 Subject: [PATCH 022/153] series const getter --- include/data/FluidDataSeries.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index a140a31e9..5acb53a9a 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -89,6 +89,14 @@ class FluidDataSeries return true; } + FluidTensorView getSeries(idType const& id) const + { + auto pos = mIndex.find(id); + return pos != mIndex.end() + ? mData[pos->second] + : FluidTensorView{nullptr, 0, 0, 0}; + } + bool addFrame(idType const& id, FluidTensorView frame) { assert(sameExtents(mDim, frame.descriptor())); From 4576eb97164b72c464f43196e398c22e63f81b77 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 12:24:43 +0100 Subject: [PATCH 023/153] fix json (de)serialisation --- include/data/FluidJSON.hpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/include/data/FluidJSON.hpp b/include/data/FluidJSON.hpp index 2e7b79be5..128a39b70 100644 --- a/include/data/FluidJSON.hpp +++ b/include/data/FluidJSON.hpp @@ -140,8 +140,11 @@ void to_json(nlohmann::json &j, const FluidDataSeries &ds) { auto ids = ds.getIds(); auto data = ds.getData(); j["cols"] = ds.pointSize(); - for (index r = 0; r < ds.size(); r++) { - j["data"][ids[r]] = data.row(r); + for (index r = 0; r < ds.size(); r++) + { + auto series = data[r]; + for (index s = 0; s < series.size(); s++) + j["data"][ids[r]]["t" + std::to_string(s)] = data[r].row(s); } } @@ -149,20 +152,24 @@ template bool check_json(const nlohmann::json &j, const FluidDataSeries &) { return fluid::check_json(j, - {"data", "cols"}, - {JSONTypes::OBJECT, JSONTypes::NUMBER} + {"cols", "data"}, + {JSONTypes::NUMBER, JSONTypes::OBJECT} ); } template void from_json(const nlohmann::json &j, FluidDataSeries &ds) { - auto rows = j.at("data"); + auto data = j.at("data"); index pointSize = j.at("cols").get(); ds.resize(pointSize); FluidTensor tmp(pointSize); - for (auto r = rows.begin(); r != rows.end(); ++r) { - r.value().get_to(tmp); - ds.add(r.key(), tmp); + for (auto r = data.begin(); r != data.end(); ++r) + { + for (auto s = r.begin(); r != r.end(); ++r) + { + s.value().get_to(tmp); + ds.addFrame(r.key(), tmp); + } } } From 2145f2547e1f0a983abead314a9090c3fd702ab9 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 13:05:29 +0100 Subject: [PATCH 024/153] deft solution for tensor vector to tensorview vector casting --- include/data/FluidDataSeries.hpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 5acb53a9a..5f7aaa772 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -208,8 +208,29 @@ class FluidDataSeries return true; } - std::vector> getData() { return mData; } - std::vector> getData() const { return mData; } + std::vector> getData() + { + std::vector> viewVec(mData.size()); + + // hacky fix to force conversion of vector to views of mData + // doesn't actually copy anything, it uses the FluidTensor ctor of FluidTensorView + // which creates a view/ref, so ends up creating what we want + std::copy(mData.begin(), mData.end(), std::back_inserter(viewVec)); + + return viewVec; + } + + const std::vector> getData() const + { + std::vector> viewVec; + + // hacky fix to force conversion of vector to views of mData + // doesn't actually copy anything, it uses the FluidTensor ctor of FluidTensorView + // which creates a view/ref, so ends up creating what we want + std::copy(mData.cbegin(), mData.cend(), std::back_inserter(viewVec)); + + return viewVec; + } FluidTensorView getIds() { return mIds; } FluidTensorView getIds() const { return mIds; } From 6e93be4bf570251e9a058eda2ae4b104f892c40d Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 13:05:46 +0100 Subject: [PATCH 025/153] acc incrementing the right iterator might help --- include/data/FluidJSON.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/data/FluidJSON.hpp b/include/data/FluidJSON.hpp index 128a39b70..ef9976912 100644 --- a/include/data/FluidJSON.hpp +++ b/include/data/FluidJSON.hpp @@ -165,7 +165,7 @@ void from_json(const nlohmann::json &j, FluidDataSeries &ds) FluidTensor tmp(pointSize); for (auto r = data.begin(); r != data.end(); ++r) { - for (auto s = r.begin(); r != r.end(); ++r) + for (auto s = r->begin(); s != r->end(); ++s) { s.value().get_to(tmp); ds.addFrame(r.key(), tmp); From 14a9d8eafb44ff2dabb74d8bd2890395e552e44e Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 15:09:22 +0100 Subject: [PATCH 026/153] const casting shenanigains --- include/data/FluidDataSeries.hpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 5f7aaa772..0f6b8a1de 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -22,14 +22,16 @@ class FluidDataSeries // e.g. FluidDataSet(2, 3) is a dataset of 2x3 tensors template ()>> - FluidDataSeries(Dims... dims) : mDim(dims...) + FluidDataSeries(Dims... dims) + : mData(0, FluidTensor(0, dims...)), + mDim(dims...) { static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); } // Construct from existing tensors of ids and data points - FluidDataSeries(FluidTensorView ids, - std::vector> points) + FluidDataSeries(FluidTensorView ids, + std::vector> points) : mIds(ids), mData(points) { initFromData(); @@ -39,8 +41,8 @@ class FluidDataSeries // (from convertible type for data, typically float -> double) template FluidDataSeries(FluidTensorView ids, - std::vector> points, - std::enable_if_t::value>* = nullptr) + std::vector> points, + std::enable_if_t::value>* = nullptr) : mIds(ids), mData(points) { initFromData(); @@ -64,7 +66,7 @@ class FluidDataSeries } } - bool addSeries(idType const& id, FluidTensorView series) + bool addSeries(idType const& id, FluidTensorView series) { assert(sameExtents(mDim, series[0].descriptor())); @@ -97,7 +99,7 @@ class FluidDataSeries : FluidTensorView{nullptr, 0, 0, 0}; } - bool addFrame(idType const& id, FluidTensorView frame) + bool addFrame(idType const& id, FluidTensorView frame) { assert(sameExtents(mDim, frame.descriptor())); @@ -147,7 +149,7 @@ class FluidDataSeries return pos->second; } - bool updateSeries(idType const& id, FluidTensorView series) + bool updateSeries(idType const& id, FluidTensorView series) { auto pos = mIndex.find(id); if (pos == mIndex.end()) @@ -157,7 +159,7 @@ class FluidDataSeries return true; } - bool updateFrame(idType const& id, index time, FluidTensorView frame) + bool updateFrame(idType const& id, index time, FluidTensorView frame) { auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; @@ -212,7 +214,7 @@ class FluidDataSeries { std::vector> viewVec(mData.size()); - // hacky fix to force conversion of vector to views of mData + // hacky fix to force conversion of vector of tensors to vector of views of mData // doesn't actually copy anything, it uses the FluidTensor ctor of FluidTensorView // which creates a view/ref, so ends up creating what we want std::copy(mData.begin(), mData.end(), std::back_inserter(viewVec)); From eab21629f4be1a504218c2f4edce7d29a1c31d93 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 15:10:02 +0100 Subject: [PATCH 027/153] change buffer frame return paradigm --- include/clients/nrt/DataSeriesClient.hpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index e119ab628..0020ae663 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -76,10 +76,11 @@ class DataSeriesClient : public FluidBaseClient, } else if (buf.numFrames() != dataset.dims()) return Error(WrongPointSize); - RealVector point(dataset.dims()); - point <<= buf.samps(0, dataset.dims(), 0); - dataset.addFrame(id, point); - return OK(); + + RealVector frame(dataset.dims()); + frame <<= buf.samps(0, dataset.dims(), 0); + dataset.addFrame(id, frame); + return OK(); } MessageResult getFrame(string id, index time, BufferPtr data) const @@ -148,9 +149,11 @@ class DataSeriesClient : public FluidBaseClient, return Error(WrongPointSize); auto ids = srcDataSeries.getIds(); + RealMatrix series; + for (index i = 0; i < srcDataSeries.size(); i++) { - auto series = srcDataSeries.getSeries(ids(i)); + srcDataSeries.getSeries(ids(i), series); bool added = mAlgorithm.addSeries(ids(i), series); if (!added && overwrite) mAlgorithm.updateSeries(ids(i), series); } From 4459cf972bbc37c258a8481a40beec31de901524 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 15:10:12 +0100 Subject: [PATCH 028/153] rename messages --- include/clients/nrt/DataSeriesClient.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 0020ae663..6f3d8957e 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -273,11 +273,11 @@ class DataSeriesClient : public FluidBaseClient, static auto getMessageDescriptors() { return defineMessages( - makeMessage("addPoint", &DataSeriesClient::addFrame), - makeMessage("getPoint", &DataSeriesClient::getFrame), - makeMessage("setPoint", &DataSeriesClient::setFrame), - makeMessage("updatePoint", &DataSeriesClient::updateFrame), - makeMessage("deletePoint", &DataSeriesClient::deleteFrame), + makeMessage("addFrame", &DataSeriesClient::addFrame), + makeMessage("getFrame", &DataSeriesClient::getFrame), + makeMessage("setFrame", &DataSeriesClient::setFrame), + makeMessage("updateFrame", &DataSeriesClient::updateFrame), + makeMessage("deleteFrame", &DataSeriesClient::deleteFrame), makeMessage("merge", &DataSeriesClient::merge), makeMessage("dump", &DataSeriesClient::dump), makeMessage("load", &DataSeriesClient::load), From adb599d9365d08ecfec09043846764d45ab7f398 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 15:10:37 +0100 Subject: [PATCH 029/153] fix assert crash by casting new points properly --- include/data/FluidDataSeries.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 0f6b8a1de..8e553cc1f 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -106,9 +106,7 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) { - FluidTensor newPoint; - newPoint.resizeDim(0, 1); - newPoint.row(0) <<= frame; + FluidTensorView newPoint(frame); return addSeries(id, newPoint); } From 440bfa6f1fc2ce7bd00e7cc6d531f6093bb5c866 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 15:42:25 +0100 Subject: [PATCH 030/153] printing now neat and tidyy --- include/clients/nrt/DataSeriesClient.hpp | 2 +- include/data/FluidDataSeries.hpp | 67 +++++++++++++----------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 6f3d8957e..b07d2ca52 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -264,7 +264,7 @@ class DataSeriesClient : public FluidBaseClient, MessageResult print() { - return "DataSet " + std::string(get()) + ": " + mAlgorithm.print(); + return "DataSeries " + std::string(get()) + ": " + mAlgorithm.print(); } const DataSeries getDataSeries() const { return mAlgorithm; } diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 8e553cc1f..184b7d3d9 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -245,6 +245,7 @@ class FluidDataSeries { using namespace std; ostringstream result; + if (frame.size() < maxCols) { for (index c = 0; c < frame.size(); c++) @@ -258,12 +259,15 @@ class FluidDataSeries { result << setw(10) << setprecision(5) << frame(c); } + result << setw(10) << "..."; + for (index c = maxCols / 2; c > 0; c--) { result << setw(10) << setprecision(5) << frame(frame.size() - c); } } + return result.str(); } @@ -273,64 +277,67 @@ class FluidDataSeries using namespace std; ostringstream result; - for (index t = 0; t < series.rows(); t++) + if (series.rows() < maxFrames) { - using namespace std; - ostringstream result; - if (series.rows() < maxFrames) + for (index t = 0; t < series.rows(); t++) { - for (index r = 0; r < series.rows(); r++) - { - result << "t = " << r << ": {" << endl << printFrame(series.row(r), maxCols) - << endl << "}" << endl; - } + result << setw(10) << "t" << t << ": " << printFrame(series.row(t), maxCols) + << endl; } - else + } + else + { + for (index t = 0; t < maxFrames / 2; t++) { - for (index r = 0; r < maxFrames / 2; r++) - { - result << "t = " << r << " {" << endl << printFrame(series.row(r), maxCols) - << endl << "}" << endl; - } - result << setw(10) << "..." << std::endl; - for (index r = maxFrames / 2; r > 0; r--) - { - result << "t = " << (size() - r) << " {" - << printFrame(series.row(size() - r), maxCols) << " }" << endl; - } + result << setw(10) << "t" << t << ": " << printFrame(series.row(t), maxCols) + << endl; + } + + result << setw(10) << "..." << std::endl; + + for (index t = maxFrames / 2; t > 0; t--) + { + result << setw(10) << "t" << (series.rows() - t) << ": " + << printFrame(series.row(size() - t), maxCols) << endl; } - return result.str(); } + + return result.str(); } std::string print(index maxRows = 6, index maxFrames = 6, index maxCols = 6) const { using namespace std; - if (size() == 0) return "{}"; ostringstream result; - result << endl << "points: " << size() << " frame size: " << pointSize() << endl; + + if (size() == 0) return "{}"; + result << endl << "points: " << size() << endl << "frame size: " << pointSize() << endl; + if (size() < maxRows) { for (index r = 0; r < size(); r++) { - result << mIds(r) << ": {" << endl << printSeries(mData[r], maxFrames, maxCols) - << " }" << endl; + result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) + << endl; } } else { for (index r = 0; r < maxRows / 2; r++) { - result << mIds(r) << ": {" << endl << printSeries(mData[r], maxFrames, maxCols) - << endl << "}" << endl; + result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) + << endl; } + result << setw(10) << "..." << std::endl; + for (index r = maxRows / 2; r > 0; r--) { - result << mIds(size() - r) << " {" - << printSeries(mData[size() - r], maxFrames, maxCols) << " }" << endl; + result << mIds(size() - r) << ":" + << printSeries(mData[size() - r], maxFrames, maxCols) << endl; } } + return result.str(); } From 55039694e7fe633a73e18d0b5079eb8e7fb70a02 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 15:57:19 +0100 Subject: [PATCH 031/153] fixed read/write operations --- include/data/FluidJSON.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/data/FluidJSON.hpp b/include/data/FluidJSON.hpp index ef9976912..7b191ebfa 100644 --- a/include/data/FluidJSON.hpp +++ b/include/data/FluidJSON.hpp @@ -143,7 +143,7 @@ void to_json(nlohmann::json &j, const FluidDataSeries &ds) { for (index r = 0; r < ds.size(); r++) { auto series = data[r]; - for (index s = 0; s < series.size(); s++) + for (index s = 0; s < series.rows(); s++) j["data"][ids[r]]["t" + std::to_string(s)] = data[r].row(s); } } @@ -163,6 +163,7 @@ void from_json(const nlohmann::json &j, FluidDataSeries &ds) index pointSize = j.at("cols").get(); ds.resize(pointSize); FluidTensor tmp(pointSize); + for (auto r = data.begin(); r != data.end(); ++r) { for (auto s = r->begin(); s != r->end(); ++s) From 5a34df79b6ddbdfedea3267a89ebce60279fa750 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 16:08:52 +0100 Subject: [PATCH 032/153] replace asserts with errors --- include/data/FluidDataSeries.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 184b7d3d9..88418271d 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -121,7 +121,8 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - assert(time < mData[pos->second].rows()); + if (time >= mData[pos->second].rows()) return false; + frame <<= mData[pos->second].row(time); return true; @@ -162,7 +163,7 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - assert(time < mData[pos->second].rows()); + if (time >= mData[pos->second].rows()) return false; mData[pos->second].row(time) <<= frame; return true; @@ -192,9 +193,9 @@ class FluidDataSeries if (pos == mIndex.end()) return false; index current = pos->second; - assert(time < mData[current].rows()); + if (time >= mData[current].rows()) return false; + mData[current].deleteRow(time); - if(mData[current].rows() == 0) { mIds.deleteRow(current); From 327ed07282cd4d8dadcea9a7bb77ebcffbce026c Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 16:12:48 +0100 Subject: [PATCH 033/153] consistent whitespace formatting --- include/clients/nrt/DataSeriesClient.hpp | 25 ++++++++++------ include/data/FluidDataSeries.hpp | 38 ++++++------------------ 2 files changed, 25 insertions(+), 38 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index b07d2ca52..bcc8005a7 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -67,52 +67,58 @@ class DataSeriesClient : public FluidBaseClient, { DataSeries& dataset = mAlgorithm; if (!data) return Error(NoBuffer); + BufferAdaptor::ReadAccess buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() == 0) return Error(EmptyBuffer); + if (dataset.size() == 0) { if (dataset.dims() != buf.numFrames()) dataset = DataSeries(buf.numFrames()); } - else if (buf.numFrames() != dataset.dims()) - return Error(WrongPointSize); + else if (buf.numFrames() != dataset.dims()) { return Error(WrongPointSize); } RealVector frame(dataset.dims()); frame <<= buf.samps(0, dataset.dims(), 0); dataset.addFrame(id, frame); + return OK(); } MessageResult getFrame(string id, index time, BufferPtr data) const { if (!data) return Error(NoBuffer); + BufferAdaptor::Access buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); + Result resizeResult = buf.resize(mAlgorithm.dims(), 1, buf.sampleRate()); if (!resizeResult.ok()) return {resizeResult.status(), resizeResult.message()}; + RealVector point(mAlgorithm.dims()); point <<= buf.samps(0, mAlgorithm.dims(), 0); + bool result = mAlgorithm.getFrame(id, time, point); if (result) { buf.samps(0, mAlgorithm.dims(), 0) <<= point; return OK(); } - else - { - return Error(PointNotFound); - } + else { return Error(PointNotFound); } } MessageResult updateFrame(string id, index time, InputBufferPtr data) { if (!data) return Error(NoBuffer); + BufferAdaptor::ReadAccess buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); + RealVector point(mAlgorithm.dims()); point <<= buf.samps(0, mAlgorithm.dims(), 0); + return mAlgorithm.updateFrame(id, time, point) ? OK() : Error(PointNotFound); } @@ -124,11 +130,14 @@ class DataSeriesClient : public FluidBaseClient, BufferAdaptor::ReadAccess buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); + RealVector point(mAlgorithm.dims()); point <<= buf.samps(0, mAlgorithm.dims(), 0); + bool result = mAlgorithm.updateFrame(id, time, point); if (result) return OK(); } + return addFrame(id, data); } @@ -211,6 +220,7 @@ class DataSeriesClient : public FluidBaseClient, auto destPtr = dest.get().lock(); if (!destPtr) return Error(NoDataSet); destPtr->setLabelSet(getIdsLabelSet()); + return OK(); } @@ -287,10 +297,7 @@ class DataSeriesClient : public FluidBaseClient, makeMessage("clear", &DataSeriesClient::clear), makeMessage("write", &DataSeriesClient::write), makeMessage("read", &DataSeriesClient::read), - // makeMessage("fromBuffer", &DataSeriesClient::fromBuffer), - // makeMessage("toBuffer", &DataSeriesClient::toBuffer), makeMessage("getIds", &DataSeriesClient::getIds) - // makeMessage("kNearest", &DataSeriesClient::kNearest) ); } diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 88418271d..b1617454d 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -142,19 +142,16 @@ class FluidDataSeries index getIndex(idType const& id) const { auto pos = mIndex.find(id); - if (pos == mIndex.end()) - return -1; - else - return pos->second; + if (pos == mIndex.end()) return -1; + else return pos->second; } bool updateSeries(idType const& id, FluidTensorView series) { auto pos = mIndex.find(id); - if (pos == mIndex.end()) - return false; - else - mData[pos->second] <<= series; + if (pos == mIndex.end()) return false; + else mData[pos->second] <<= series; + return true; } @@ -250,23 +247,17 @@ class FluidDataSeries if (frame.size() < maxCols) { for (index c = 0; c < frame.size(); c++) - { result << setw(10) << setprecision(5) << frame(c); - } } else { - for (index c = 0; c < maxCols / 2; c++) - { + for (index c = 0; c < maxCols / 2; c++) result << setw(10) << setprecision(5) << frame(c); - } result << setw(10) << "..."; for (index c = maxCols / 2; c > 0; c--) - { result << setw(10) << setprecision(5) << frame(frame.size() - c); - } } return result.str(); @@ -281,26 +272,20 @@ class FluidDataSeries if (series.rows() < maxFrames) { for (index t = 0; t < series.rows(); t++) - { result << setw(10) << "t" << t << ": " << printFrame(series.row(t), maxCols) << endl; - } } else { for (index t = 0; t < maxFrames / 2; t++) - { result << setw(10) << "t" << t << ": " << printFrame(series.row(t), maxCols) - << endl; - } + << endl; result << setw(10) << "..." << std::endl; for (index t = maxFrames / 2; t > 0; t--) - { result << setw(10) << "t" << (series.rows() - t) << ": " << printFrame(series.row(size() - t), maxCols) << endl; - } } return result.str(); @@ -317,26 +302,20 @@ class FluidDataSeries if (size() < maxRows) { for (index r = 0; r < size(); r++) - { result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) << endl; - } } else { for (index r = 0; r < maxRows / 2; r++) - { result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) << endl; - } result << setw(10) << "..." << std::endl; for (index r = maxRows / 2; r > 0; r--) - { result << mIds(size() - r) << ":" << printSeries(mData[size() - r], maxFrames, maxCols) << endl; - } } return result.str(); @@ -347,7 +326,8 @@ class FluidDataSeries { assert(mIds.rows() == mData.size()); mDim = mData[0].cols(); - for (index i = 0; i < mIds.size(); i++) { mIndex.insert({mIds[i], i}); } + for (index i = 0; i < mIds.size(); i++) + mIndex.insert({mIds[i], i}); } std::unordered_map mIndex; From 08ec779baf60a4e33a0592e2f6f4ebb9e5f17f14 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 23 Aug 2023 16:18:23 +0100 Subject: [PATCH 034/153] deleteSeries message --- include/clients/nrt/DataSeriesClient.hpp | 93 ++---------------------- 1 file changed, 6 insertions(+), 87 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index bcc8005a7..93ab02601 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -146,6 +146,11 @@ class DataSeriesClient : public FluidBaseClient, return mAlgorithm.removeFrame(id, time) ? OK() : Error(PointNotFound); } + MessageResult deleteSeries(string id) + { + return mAlgorithm.removeSeries(id) ? OK() : Error(PointNotFound); + } + MessageResult merge(SharedClientRef dataseriesClient, bool overwrite) { @@ -170,51 +175,6 @@ class DataSeriesClient : public FluidBaseClient, return OK(); } - // MessageResult - // fromBuffer(InputBufferPtr data, bool transpose, - // SharedClientRef labels) - // { - // if (!data) return Error(NoBuffer); - // BufferAdaptor::ReadAccess buf(data.get()); - // if (!buf.exists()) return Error(InvalidBuffer); - // auto bufView = transpose ? buf.allFrames() : buf.allFrames().transpose(); - // if (auto labelsPtr = labels.get().lock()) - // { - // auto& labelSet = labelsPtr->getLabelSet(); - // if (labelSet.size() != bufView.rows()) - // { return Error("Label set size needs to match the buffer size"); } - // mAlgorithm = DataSeries(labelSet.getData().col(0), - // FluidTensorView(bufView)); - // } - // else - // { - // algorithm::DataSetIdSequence seq("", 0, 0); - // FluidTensor newIds(bufView.rows()); - // seq.generate(newIds); - // mAlgorithm = DataSeries(newIds, FluidTensorView(bufView)); - // } - // return OK(); - // } - - // MessageResult toBuffer(BufferPtr data, bool transpose, - // LabelSetClientRef labels) - // { - // if (!data) return Error(NoBuffer); - // BufferAdaptor::Access buf(data.get()); - // if (!buf.exists()) return Error(InvalidBuffer); - // index nFrames = transpose ? mAlgorithm.dims() : mAlgorithm.size(); - // index nChannels = transpose ? mAlgorithm.size() : mAlgorithm.dims(); - // Result resizeResult = buf.resize(nFrames, nChannels, buf.sampleRate()); - // if (!resizeResult.ok()) return Error(resizeResult.message()); - // buf.allFrames() <<= - // transpose ? mAlgorithm.getData() - // : FluidTensorView(mAlgorithm.getData()) - // .transpose(); - // auto labelsPtr = labels.get().lock(); - // if (labelsPtr) labelsPtr->setLabelSet(getIdsLabelSet()); - // return OK(); - // } - MessageResult getIds(LabelSetClientRef dest) { auto destPtr = dest.get().lock(); @@ -224,48 +184,6 @@ class DataSeriesClient : public FluidBaseClient, return OK(); } - // MessageResult> kNearest(InputBufferPtr data, - // index nNeighbours) const - // { - // // check for nNeighbours > 0 and < size of DS - // if (nNeighbours > mAlgorithm.size()) - // return Error>(SmallDataSet); - // if (nNeighbours <= 0) return Error>(SmallK); - - // InBufferCheck bufCheck(mAlgorithm.dims()); - - // if (!bufCheck.checkInputs(data.get())) - // return Error>(bufCheck.error()); - - // FluidTensor point( - // BufferAdaptor::ReadAccess(data.get()).samps(0, mAlgorithm.dims(), 0)); - - // std::vector indices(asUnsigned(mAlgorithm.size())); - // std::iota(indices.begin(), indices.end(), 0); - // std::vector distances(asUnsigned(mAlgorithm.size())); - - // auto ds = mAlgorithm.getData(); - - // std::transform( - // indices.begin(), indices.end(), distances.begin(), - // [&point, &ds, this](index i) { return distance(point, ds.row(i)); }); - - // std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { - // return distances[asUnsigned(a)] < distances[asUnsigned(b)]; - // }); - - // FluidTensor labels(nNeighbours); - - // std::transform( - // indices.begin(), indices.begin() + nNeighbours, labels.begin(), - // [this](index i) { - // std::string const& id = mAlgorithm.getIds()[i]; - // return rt::string{id, 0, id.size(), FluidDefaultAllocator()}; - // }); - - // return labels; - // } - MessageResult clear() { mAlgorithm = DataSeries(0); @@ -288,6 +206,7 @@ class DataSeriesClient : public FluidBaseClient, makeMessage("setFrame", &DataSeriesClient::setFrame), makeMessage("updateFrame", &DataSeriesClient::updateFrame), makeMessage("deleteFrame", &DataSeriesClient::deleteFrame), + makeMessage("deleteSeries", &DataSeriesClient::deleteFrame), makeMessage("merge", &DataSeriesClient::merge), makeMessage("dump", &DataSeriesClient::dump), makeMessage("load", &DataSeriesClient::load), From 9e41fb06f635e9b2ba5d342d475354d1f767bb8e Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 10:44:12 +0100 Subject: [PATCH 035/153] view converting methods for buffer `float` shenanigains --- include/data/FluidDataSeries.hpp | 24 +++++++++++++++++------- include/data/FluidJSON.hpp | 5 +++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index b1617454d..836361e08 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -66,10 +66,12 @@ class FluidDataSeries } } - bool addSeries(idType const& id, FluidTensorView series) + template + bool addSeries(idType const& id, FluidTensorView series) { - assert(sameExtents(mDim, series[0].descriptor())); - + static_assert(std::is_convertible::value, "Can't convert between types"); + + // dont crete another view auto result = mIndex.insert({id, mData.size()}); if (!result.second) return false; @@ -99,14 +101,16 @@ class FluidDataSeries : FluidTensorView{nullptr, 0, 0, 0}; } - bool addFrame(idType const& id, FluidTensorView frame) + template + bool addFrame(idType const& id, FluidTensorView frame) { + static_assert(std::is_convertible::value, "Can't convert between types"); assert(sameExtents(mDim, frame.descriptor())); auto pos = mIndex.find(id); if (pos == mIndex.end()) { - FluidTensorView newPoint(frame); + FluidTensorView newPoint(frame); return addSeries(id, newPoint); } @@ -146,8 +150,11 @@ class FluidDataSeries else return pos->second; } - bool updateSeries(idType const& id, FluidTensorView series) + template + bool updateSeries(idType const& id, FluidTensorView series) { + static_assert(std::is_convertible::value, "Can't convert between types"); + auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; else mData[pos->second] <<= series; @@ -155,8 +162,11 @@ class FluidDataSeries return true; } - bool updateFrame(idType const& id, index time, FluidTensorView frame) + template + bool updateFrame(idType const& id, index time, FluidTensorView frame) { + static_assert(std::is_convertible::value, "Can't convert between types"); + auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; diff --git a/include/data/FluidJSON.hpp b/include/data/FluidJSON.hpp index 7b191ebfa..6020ba3c9 100644 --- a/include/data/FluidJSON.hpp +++ b/include/data/FluidJSON.hpp @@ -161,15 +161,16 @@ template void from_json(const nlohmann::json &j, FluidDataSeries &ds) { auto data = j.at("data"); index pointSize = j.at("cols").get(); - ds.resize(pointSize); FluidTensor tmp(pointSize); + + ds.resize(pointSize); for (auto r = data.begin(); r != data.end(); ++r) { for (auto s = r->begin(); s != r->end(); ++s) { s.value().get_to(tmp); - ds.addFrame(r.key(), tmp); + ds.addFrame(r.key(), FluidTensorView{tmp}); } } } From 409d3cdb7e797dd55e9e623b1be37efc97ff2b4a Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 10:44:21 +0100 Subject: [PATCH 036/153] fix printing issues --- include/data/FluidDataSeries.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 836361e08..29aa37db2 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -321,10 +321,10 @@ class FluidDataSeries result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) << endl; - result << setw(10) << "..." << std::endl; + result << setw(10) << "⋮" << endl; for (index r = maxRows / 2; r > 0; r--) - result << mIds(size() - r) << ":" + result << mIds(size() - r) << ":" << endl << printSeries(mData[size() - r], maxFrames, maxCols) << endl; } From 99015b83d59907f3319c025aa56a62627eeffabd Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 10:45:01 +0100 Subject: [PATCH 037/153] remove superflous reference creation --- include/clients/nrt/DataSeriesClient.hpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 93ab02601..f15f516df 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -65,22 +65,19 @@ class DataSeriesClient : public FluidBaseClient, MessageResult addFrame(string id, InputBufferPtr data) { - DataSeries& dataset = mAlgorithm; if (!data) return Error(NoBuffer); BufferAdaptor::ReadAccess buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() == 0) return Error(EmptyBuffer); - if (dataset.size() == 0) + if (mAlgorithm.size() == 0) { - if (dataset.dims() != buf.numFrames()) dataset = DataSeries(buf.numFrames()); + if (mAlgorithm.dims() != buf.numFrames()) mAlgorithm = DataSeries(buf.numFrames()); } - else if (buf.numFrames() != dataset.dims()) { return Error(WrongPointSize); } + else if (buf.numFrames() != mAlgorithm.dims()) { return Error(WrongPointSize); } - RealVector frame(dataset.dims()); - frame <<= buf.samps(0, dataset.dims(), 0); - dataset.addFrame(id, frame); + mAlgorithm.addFrame(id, buf.samps(0, mAlgorithm.dims(), 0)); return OK(); } From 06692a065b8ae5c04af6e2c59d7396e1c1f345c7 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 10:45:17 +0100 Subject: [PATCH 038/153] convert to template view function calls --- include/clients/nrt/DataSeriesClient.hpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index f15f516df..52c114382 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -113,10 +113,7 @@ class DataSeriesClient : public FluidBaseClient, if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); - RealVector point(mAlgorithm.dims()); - point <<= buf.samps(0, mAlgorithm.dims(), 0); - - return mAlgorithm.updateFrame(id, time, point) ? OK() : Error(PointNotFound); + return mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)) ? OK() : Error(PointNotFound); } MessageResult setFrame(string id, index time, InputBufferPtr data) @@ -128,10 +125,7 @@ class DataSeriesClient : public FluidBaseClient, if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); - RealVector point(mAlgorithm.dims()); - point <<= buf.samps(0, mAlgorithm.dims(), 0); - - bool result = mAlgorithm.updateFrame(id, time, point); + bool result = mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)); if (result) return OK(); } @@ -160,11 +154,10 @@ class DataSeriesClient : public FluidBaseClient, return Error(WrongPointSize); auto ids = srcDataSeries.getIds(); - RealMatrix series; for (index i = 0; i < srcDataSeries.size(); i++) { - srcDataSeries.getSeries(ids(i), series); + InputRealMatrixView series = srcDataSeries.getSeries(ids(i)); bool added = mAlgorithm.addSeries(ids(i), series); if (!added && overwrite) mAlgorithm.updateSeries(ids(i), series); } From a6a23ef58d3b79fd0d0b10ef353d67073f8ef1cc Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 10:45:28 +0100 Subject: [PATCH 039/153] addSeries message --- include/clients/nrt/DataSeriesClient.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 52c114382..8b0aea117 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -82,6 +82,24 @@ class DataSeriesClient : public FluidBaseClient, return OK(); } + MessageResult addSeries(string id, InputBufferPtr data) + { + if (!data) return Error(NoBuffer); + + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numFrames() == 0) return Error(EmptyBuffer); + + if (mAlgorithm.size() == 0) + { + if (mAlgorithm.dims() != buf.numChans()) mAlgorithm = DataSeries(buf.numChans()); + } + else if (buf.numChans() != mAlgorithm.dims()) { return Error(WrongPointSize); } + + return mAlgorithm.addSeries(id, buf.allFrames().transpose()) + ? OK() : Error(DuplicateIdentifier); + } + MessageResult getFrame(string id, index time, BufferPtr data) const { if (!data) return Error(NoBuffer); @@ -192,6 +210,7 @@ class DataSeriesClient : public FluidBaseClient, { return defineMessages( makeMessage("addFrame", &DataSeriesClient::addFrame), + makeMessage("addSeries", &DataSeriesClient::addSeries), makeMessage("getFrame", &DataSeriesClient::getFrame), makeMessage("setFrame", &DataSeriesClient::setFrame), makeMessage("updateFrame", &DataSeriesClient::updateFrame), From 77251f5580daaef50f3518c8b84bcd249684a939 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 10:51:15 +0100 Subject: [PATCH 040/153] register new series-level messages --- include/clients/nrt/DataSeriesClient.hpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 8b0aea117..d0bb876d1 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -85,7 +85,7 @@ class DataSeriesClient : public FluidBaseClient, MessageResult addSeries(string id, InputBufferPtr data) { if (!data) return Error(NoBuffer); - + BufferAdaptor::ReadAccess buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() == 0) return Error(EmptyBuffer); @@ -123,6 +123,11 @@ class DataSeriesClient : public FluidBaseClient, else { return Error(PointNotFound); } } + MessageResult getSeries(string id, BufferPtr data) const + { + return OK(); + } + MessageResult updateFrame(string id, index time, InputBufferPtr data) { if (!data) return Error(NoBuffer); @@ -134,6 +139,11 @@ class DataSeriesClient : public FluidBaseClient, return mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)) ? OK() : Error(PointNotFound); } + MessageResult updateSeries(string id, InputBufferPtr data) + { + return OK(); + } + MessageResult setFrame(string id, index time, InputBufferPtr data) { if (!data) return Error(NoBuffer); @@ -150,6 +160,11 @@ class DataSeriesClient : public FluidBaseClient, return addFrame(id, data); } + MessageResult setSeries(string id, InputBufferPtr data) + { + return OK(); + } + MessageResult deleteFrame(string id, index time) { return mAlgorithm.removeFrame(id, time) ? OK() : Error(PointNotFound); @@ -212,8 +227,11 @@ class DataSeriesClient : public FluidBaseClient, makeMessage("addFrame", &DataSeriesClient::addFrame), makeMessage("addSeries", &DataSeriesClient::addSeries), makeMessage("getFrame", &DataSeriesClient::getFrame), + makeMessage("getSeries", &DataSeriesClient::getSeries), makeMessage("setFrame", &DataSeriesClient::setFrame), + makeMessage("setSeries", &DataSeriesClient::setSeries), makeMessage("updateFrame", &DataSeriesClient::updateFrame), + makeMessage("updateSeries", &DataSeriesClient::updateSeries), makeMessage("deleteFrame", &DataSeriesClient::deleteFrame), makeMessage("deleteSeries", &DataSeriesClient::deleteFrame), makeMessage("merge", &DataSeriesClient::merge), From 2845d375ce503451c02460c0a1a93c6d44ef29ee Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 10:54:36 +0100 Subject: [PATCH 041/153] regroup member functions --- include/data/FluidDataSeries.hpp | 114 +++++++++++++++---------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 29aa37db2..32e606a59 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -66,41 +66,6 @@ class FluidDataSeries } } - template - bool addSeries(idType const& id, FluidTensorView series) - { - static_assert(std::is_convertible::value, "Can't convert between types"); - - // dont crete another view - auto result = mIndex.insert({id, mData.size()}); - if (!result.second) return false; - - mData.emplace_back(series); - - mIds.resizeDim(0, 1); - mIds(mIds.rows() - 1) = id; - - return true; - } - - bool getSeries(idType const& id, FluidTensorView series) const - { - auto pos = mIndex.find(id); - if (pos == mIndex.end()) return false; - - series <<= mData[pos->second]; - - return true; - } - - FluidTensorView getSeries(idType const& id) const - { - auto pos = mIndex.find(id); - return pos != mIndex.end() - ? mData[pos->second] - : FluidTensorView{nullptr, 0, 0, 0}; - } - template bool addFrame(idType const& id, FluidTensorView frame) { @@ -120,6 +85,23 @@ class FluidDataSeries return true; } + template + bool addSeries(idType const& id, FluidTensorView series) + { + static_assert(std::is_convertible::value, "Can't convert between types"); + + // dont crete another view + auto result = mIndex.insert({id, mData.size()}); + if (!result.second) return false; + + mData.emplace_back(series); + + mIds.resizeDim(0, 1); + mIds(mIds.rows() - 1) = id; + + return true; + } + bool getFrame(idType const& id, index time, FluidTensorView frame) const { auto pos = mIndex.find(id); @@ -143,23 +125,22 @@ class FluidDataSeries else { return FluidTensorView{nullptr, 0, 0}; } } - index getIndex(idType const& id) const + bool getSeries(idType const& id, FluidTensorView series) const { auto pos = mIndex.find(id); - if (pos == mIndex.end()) return -1; - else return pos->second; + if (pos == mIndex.end()) return false; + + series <<= mData[pos->second]; + + return true; } - template - bool updateSeries(idType const& id, FluidTensorView series) + FluidTensorView getSeries(idType const& id) const { - static_assert(std::is_convertible::value, "Can't convert between types"); - auto pos = mIndex.find(id); - if (pos == mIndex.end()) return false; - else mData[pos->second] <<= series; - - return true; + return pos != mIndex.end() + ? mData[pos->second] + : FluidTensorView{nullptr, 0, 0, 0}; } template @@ -176,20 +157,14 @@ class FluidDataSeries return true; } - bool removeSeries(idType const& id) + template + bool updateSeries(idType const& id, FluidTensorView series) { + static_assert(std::is_convertible::value, "Can't convert between types"); + auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - - index current = pos->second; - mData.erase(mData.begin() + current); - mIds.deleteRow(current); - mIndex.erase(id); - - for (auto& point : mIndex) - { - if (point.second > current) point.second--; - } + else mData[pos->second] <<= series; return true; } @@ -216,6 +191,31 @@ class FluidDataSeries return true; } + bool removeSeries(idType const& id) + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + + index current = pos->second; + mData.erase(mData.begin() + current); + mIds.deleteRow(current); + mIndex.erase(id); + + for (auto& point : mIndex) + { + if (point.second > current) point.second--; + } + + return true; + } + + index getIndex(idType const& id) const + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return -1; + else return pos->second; + } + std::vector> getData() { std::vector> viewVec(mData.size()); From 784fff61adc086109e4d0f931075f1b4f624d37e Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 11:47:56 +0100 Subject: [PATCH 042/153] `getSeries` message --- include/clients/nrt/DataSeriesClient.hpp | 20 +++++++++++++++++++- include/data/FluidDataSeries.hpp | 7 +++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index d0bb876d1..399236f9d 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -125,7 +125,25 @@ class DataSeriesClient : public FluidBaseClient, MessageResult getSeries(string id, BufferPtr data) const { - return OK(); + if (!data) return Error(NoBuffer); + + BufferAdaptor::Access buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + + Result resizeResult = buf.resize(mAlgorithm.getNumFrames(id), mAlgorithm.dims(), buf.sampleRate()); + if (!resizeResult.ok()) + return {resizeResult.status(), resizeResult.message()}; + + RealMatrix point(mAlgorithm.getNumFrames(id), mAlgorithm.dims()); + point <<= buf.allFrames().transpose(); + + bool result = mAlgorithm.getSeries(id, point); + if (result) + { + buf.allFrames() <<= point.transpose(); + return OK(); + } + else { return Error(PointNotFound); } } MessageResult updateFrame(string id, index time, InputBufferPtr data) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 32e606a59..f1e22f9af 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -216,6 +216,13 @@ class FluidDataSeries else return pos->second; } + index getNumFrames(idType const& id) const + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return -1; + else return mData[pos->second].rows(); + } + std::vector> getData() { std::vector> viewVec(mData.size()); From 2c46d518b6265ac551175e4bb1650adba6a036e3 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 11:48:50 +0100 Subject: [PATCH 043/153] `setSeries` message --- include/clients/nrt/DataSeriesClient.hpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 399236f9d..169e31389 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -180,7 +180,18 @@ class DataSeriesClient : public FluidBaseClient, MessageResult setSeries(string id, InputBufferPtr data) { - return OK(); + if (!data) return Error(NoBuffer); + + { // restrict buffer lock to this scope in case addPoint is called + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); + + bool result = mAlgorithm.updateSeries(id, buf.allFrames().transpose()); + if (result) return OK(); + } + + return addSeries(id, data); } MessageResult deleteFrame(string id, index time) From 6128d916b0ade3cb955ba1d20451edf2262bb6ee Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 11:49:05 +0100 Subject: [PATCH 044/153] `updateSeries` message --- include/clients/nrt/DataSeriesClient.hpp | 8 +++++++- include/data/FluidDataSeries.hpp | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 169e31389..66d3f66df 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -159,7 +159,13 @@ class DataSeriesClient : public FluidBaseClient, MessageResult updateSeries(string id, InputBufferPtr data) { - return OK(); + if (!data) return Error(NoBuffer); + + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); + + return mAlgorithm.updateSeries(id, buf.allFrames().transpose()) ? OK() : Error(PointNotFound); } MessageResult setFrame(string id, index time, InputBufferPtr data) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index f1e22f9af..81e797829 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -164,7 +164,11 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - else mData[pos->second] <<= series; + else + { + mData[pos->second].resizeDim(0, series.rows() - mData[pos->second].rows()); + mData[pos->second] <<= series; + } return true; } From 54d48c65271057a61cdb6cf5d8f9b9ceef7e4750 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 12:47:52 +0100 Subject: [PATCH 045/153] `getDataSet` message --- include/clients/nrt/DataSeriesClient.hpp | 43 ++++++++++++++++++------ include/data/FluidDataSeries.hpp | 2 ++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 66d3f66df..65a2da63f 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -11,6 +11,7 @@ under the European Union’s Horizon 2020 research and innovation programme #pragma once #include "DataClient.hpp" #include "LabelSetClient.hpp" +#include "DataSetClient.hpp" #include "NRTClient.hpp" #include "../common/SharedClientUtils.hpp" #include "../../algorithms/public/DataSetIdSequence.hpp" @@ -38,6 +39,7 @@ class DataSeriesClient : public FluidBaseClient, using BufferPtr = std::shared_ptr; using InputBufferPtr = std::shared_ptr; using DataSeries = FluidDataSeries; + using DataSet = FluidDataSet; using LabelSet = FluidDataSet; template @@ -233,6 +235,17 @@ class DataSeriesClient : public FluidBaseClient, return OK(); } + MessageResult getDataSet(DataSetClientRef dest, index time) const + { + auto destPtr = dest.get().lock(); + if (!destPtr) return Error(NoDataSet); + destPtr->setDataSet(getSliceDataSet(time)); + + if(destPtr->size() == 0) return Error(EmptyDataSet); + + return OK(); + } + MessageResult getIds(LabelSetClientRef dest) { auto destPtr = dest.get().lock(); @@ -262,11 +275,11 @@ class DataSeriesClient : public FluidBaseClient, makeMessage("addFrame", &DataSeriesClient::addFrame), makeMessage("addSeries", &DataSeriesClient::addSeries), makeMessage("getFrame", &DataSeriesClient::getFrame), - makeMessage("getSeries", &DataSeriesClient::getSeries), + makeMessage("getSeries", &DataSeriesClient::getSeries), makeMessage("setFrame", &DataSeriesClient::setFrame), - makeMessage("setSeries", &DataSeriesClient::setSeries), + makeMessage("setSeries", &DataSeriesClient::setSeries), makeMessage("updateFrame", &DataSeriesClient::updateFrame), - makeMessage("updateSeries", &DataSeriesClient::updateSeries), + makeMessage("updateSeries", &DataSeriesClient::updateSeries), makeMessage("deleteFrame", &DataSeriesClient::deleteFrame), makeMessage("deleteSeries", &DataSeriesClient::deleteFrame), makeMessage("merge", &DataSeriesClient::merge), @@ -278,7 +291,8 @@ class DataSeriesClient : public FluidBaseClient, makeMessage("clear", &DataSeriesClient::clear), makeMessage("write", &DataSeriesClient::write), makeMessage("read", &DataSeriesClient::read), - makeMessage("getIds", &DataSeriesClient::getIds) + makeMessage("getIds", &DataSeriesClient::getIds), + makeMessage("getDataSet", &DataSeriesClient::getDataSet) ); } @@ -292,13 +306,20 @@ class DataSeriesClient : public FluidBaseClient, seq.generate(newIds); return LabelSet(newIds, labels); }; - - // double distance(FluidTensorView point1, FluidTensorView point2) const - // { - // return std::transform_reduce(point1.begin(), point1.end(), point2.begin(), 0.0, std::plus{}, [](double v1, double v2){ - // return (v1-v2) * (v1-v2); - // }); - // }; + + DataSet getSliceDataSet(index time) const + { + DataSet ds(mAlgorithm.dims()); + decltype(mAlgorithm)::FrameType frame(mAlgorithm.dims()); + + for(auto id : mAlgorithm.getIds()) + { + bool ret = mAlgorithm.getFrame(id, time, frame); + if(ret) ds.add(id, frame); + } + + return ds; + } }; } // namespace dataset diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 81e797829..a9cf93e38 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -15,6 +15,8 @@ class FluidDataSeries { public: + using FrameType = FluidTensor; + explicit FluidDataSeries() = default; ~FluidDataSeries() = default; From 3ab06c233d09303810ada7452138f2d93d874fa0 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 15:01:55 +0100 Subject: [PATCH 046/153] actually pointing to the right member might be helpful --- include/clients/nrt/DataSeriesClient.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 65a2da63f..a2d5a30cd 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -281,7 +281,7 @@ class DataSeriesClient : public FluidBaseClient, makeMessage("updateFrame", &DataSeriesClient::updateFrame), makeMessage("updateSeries", &DataSeriesClient::updateSeries), makeMessage("deleteFrame", &DataSeriesClient::deleteFrame), - makeMessage("deleteSeries", &DataSeriesClient::deleteFrame), + makeMessage("deleteSeries", &DataSeriesClient::deleteSeries), makeMessage("merge", &DataSeriesClient::merge), makeMessage("dump", &DataSeriesClient::dump), makeMessage("load", &DataSeriesClient::load), From 7d15aaf53d227ff85a039cc5e17fe66b777fca19 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 15:21:07 +0100 Subject: [PATCH 047/153] fix `deleteframe` case with single frame in series --- include/data/FluidDataSeries.hpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index a9cf93e38..dea7b2ed8 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -183,16 +183,8 @@ class FluidDataSeries index current = pos->second; if (time >= mData[current].rows()) return false; - mData[current].deleteRow(time); - if(mData[current].rows() == 0) - { - mIds.deleteRow(current); - mIndex.erase(id); - for (auto& point : mIndex) - { - if (point.second > current) point.second--; - } - } + if(mData[current].rows() == 1) return removeSeries(id); + else mData[current].deleteRow(time); return true; } From f9214a9bbe0217ea31571a29fb27cf0a6a595db8 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 16:06:31 +0100 Subject: [PATCH 048/153] Squash merge `data-series` into `dtime-warp` --- include/clients/nrt/DataSeriesClient.hpp | 334 +++++++++++++++++++++ include/data/FluidDataSeries.hpp | 353 +++++++++++++++++++++++ include/data/FluidJSON.hpp | 42 +++ 3 files changed, 729 insertions(+) create mode 100644 include/clients/nrt/DataSeriesClient.hpp create mode 100644 include/data/FluidDataSeries.hpp diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp new file mode 100644 index 000000000..a2d5a30cd --- /dev/null +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -0,0 +1,334 @@ +/* +Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) +Copyright University of Huddersfield. +Licensed under the BSD-3 License. +See license.md file in the project root for full license information. +This project has received funding from the European Research Council (ERC) +under the European Union’s Horizon 2020 research and innovation programme +(grant agreement No 725899). +*/ + +#pragma once +#include "DataClient.hpp" +#include "LabelSetClient.hpp" +#include "DataSetClient.hpp" +#include "NRTClient.hpp" +#include "../common/SharedClientUtils.hpp" +#include "../../algorithms/public/DataSetIdSequence.hpp" +#include "../../data/FluidDataSeries.hpp" +#include +#include + +namespace fluid { +namespace client { +namespace dataseries { + +enum { kName }; + +constexpr auto DataSeriesParams = defineParameters( + StringParam>("name", "Name of the DataSeries") +); + +class DataSeriesClient : public FluidBaseClient, + OfflineIn, + OfflineOut, + public DataClient> +{ +public: + using string = std::string; + using BufferPtr = std::shared_ptr; + using InputBufferPtr = std::shared_ptr; + using DataSeries = FluidDataSeries; + using DataSet = FluidDataSet; + using LabelSet = FluidDataSet; + + template + Result process(FluidContext&) + { + return {}; + } + + using ParamDescType = decltype(DataSeriesParams); + + using ParamSetViewType = ParameterSetView; + std::reference_wrapper mParams; + + void setParams(ParamSetViewType& p) { mParams = p; } + + template + auto& get() const + { + return mParams.get().template get(); + } + + static constexpr auto& getParameterDescriptors() { return DataSeriesParams; } + + DataSeriesClient(ParamSetViewType& p, FluidContext&) : mParams(p) {} + + MessageResult addFrame(string id, InputBufferPtr data) + { + if (!data) return Error(NoBuffer); + + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numFrames() == 0) return Error(EmptyBuffer); + + if (mAlgorithm.size() == 0) + { + if (mAlgorithm.dims() != buf.numFrames()) mAlgorithm = DataSeries(buf.numFrames()); + } + else if (buf.numFrames() != mAlgorithm.dims()) { return Error(WrongPointSize); } + + mAlgorithm.addFrame(id, buf.samps(0, mAlgorithm.dims(), 0)); + + return OK(); + } + + MessageResult addSeries(string id, InputBufferPtr data) + { + if (!data) return Error(NoBuffer); + + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numFrames() == 0) return Error(EmptyBuffer); + + if (mAlgorithm.size() == 0) + { + if (mAlgorithm.dims() != buf.numChans()) mAlgorithm = DataSeries(buf.numChans()); + } + else if (buf.numChans() != mAlgorithm.dims()) { return Error(WrongPointSize); } + + return mAlgorithm.addSeries(id, buf.allFrames().transpose()) + ? OK() : Error(DuplicateIdentifier); + } + + MessageResult getFrame(string id, index time, BufferPtr data) const + { + if (!data) return Error(NoBuffer); + + BufferAdaptor::Access buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + + Result resizeResult = buf.resize(mAlgorithm.dims(), 1, buf.sampleRate()); + if (!resizeResult.ok()) + return {resizeResult.status(), resizeResult.message()}; + + RealVector point(mAlgorithm.dims()); + point <<= buf.samps(0, mAlgorithm.dims(), 0); + + bool result = mAlgorithm.getFrame(id, time, point); + if (result) + { + buf.samps(0, mAlgorithm.dims(), 0) <<= point; + return OK(); + } + else { return Error(PointNotFound); } + } + + MessageResult getSeries(string id, BufferPtr data) const + { + if (!data) return Error(NoBuffer); + + BufferAdaptor::Access buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + + Result resizeResult = buf.resize(mAlgorithm.getNumFrames(id), mAlgorithm.dims(), buf.sampleRate()); + if (!resizeResult.ok()) + return {resizeResult.status(), resizeResult.message()}; + + RealMatrix point(mAlgorithm.getNumFrames(id), mAlgorithm.dims()); + point <<= buf.allFrames().transpose(); + + bool result = mAlgorithm.getSeries(id, point); + if (result) + { + buf.allFrames() <<= point.transpose(); + return OK(); + } + else { return Error(PointNotFound); } + } + + MessageResult updateFrame(string id, index time, InputBufferPtr data) + { + if (!data) return Error(NoBuffer); + + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); + + return mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)) ? OK() : Error(PointNotFound); + } + + MessageResult updateSeries(string id, InputBufferPtr data) + { + if (!data) return Error(NoBuffer); + + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); + + return mAlgorithm.updateSeries(id, buf.allFrames().transpose()) ? OK() : Error(PointNotFound); + } + + MessageResult setFrame(string id, index time, InputBufferPtr data) + { + if (!data) return Error(NoBuffer); + + { // restrict buffer lock to this scope in case addPoint is called + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); + + bool result = mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)); + if (result) return OK(); + } + + return addFrame(id, data); + } + + MessageResult setSeries(string id, InputBufferPtr data) + { + if (!data) return Error(NoBuffer); + + { // restrict buffer lock to this scope in case addPoint is called + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error(InvalidBuffer); + if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); + + bool result = mAlgorithm.updateSeries(id, buf.allFrames().transpose()); + if (result) return OK(); + } + + return addSeries(id, data); + } + + MessageResult deleteFrame(string id, index time) + { + return mAlgorithm.removeFrame(id, time) ? OK() : Error(PointNotFound); + } + + MessageResult deleteSeries(string id) + { + return mAlgorithm.removeSeries(id) ? OK() : Error(PointNotFound); + } + + MessageResult merge(SharedClientRef dataseriesClient, + bool overwrite) + { + auto dataseriesClientPtr = dataseriesClient.get().lock(); + if (!dataseriesClientPtr) return Error(NoDataSet); + + auto srcDataSeries = dataseriesClientPtr->getDataSeries(); + if (srcDataSeries.size() == 0) return Error(EmptyDataSet); + if (srcDataSeries.pointSize() != mAlgorithm.pointSize()) + return Error(WrongPointSize); + + auto ids = srcDataSeries.getIds(); + + for (index i = 0; i < srcDataSeries.size(); i++) + { + InputRealMatrixView series = srcDataSeries.getSeries(ids(i)); + bool added = mAlgorithm.addSeries(ids(i), series); + if (!added && overwrite) mAlgorithm.updateSeries(ids(i), series); + } + + return OK(); + } + + MessageResult getDataSet(DataSetClientRef dest, index time) const + { + auto destPtr = dest.get().lock(); + if (!destPtr) return Error(NoDataSet); + destPtr->setDataSet(getSliceDataSet(time)); + + if(destPtr->size() == 0) return Error(EmptyDataSet); + + return OK(); + } + + MessageResult getIds(LabelSetClientRef dest) + { + auto destPtr = dest.get().lock(); + if (!destPtr) return Error(NoDataSet); + destPtr->setLabelSet(getIdsLabelSet()); + + return OK(); + } + + MessageResult clear() + { + mAlgorithm = DataSeries(0); + return OK(); + } + + MessageResult print() + { + return "DataSeries " + std::string(get()) + ": " + mAlgorithm.print(); + } + + const DataSeries getDataSeries() const { return mAlgorithm; } + void setDataSeries(DataSeries ds) { mAlgorithm = ds; } + + static auto getMessageDescriptors() + { + return defineMessages( + makeMessage("addFrame", &DataSeriesClient::addFrame), + makeMessage("addSeries", &DataSeriesClient::addSeries), + makeMessage("getFrame", &DataSeriesClient::getFrame), + makeMessage("getSeries", &DataSeriesClient::getSeries), + makeMessage("setFrame", &DataSeriesClient::setFrame), + makeMessage("setSeries", &DataSeriesClient::setSeries), + makeMessage("updateFrame", &DataSeriesClient::updateFrame), + makeMessage("updateSeries", &DataSeriesClient::updateSeries), + makeMessage("deleteFrame", &DataSeriesClient::deleteFrame), + makeMessage("deleteSeries", &DataSeriesClient::deleteSeries), + makeMessage("merge", &DataSeriesClient::merge), + makeMessage("dump", &DataSeriesClient::dump), + makeMessage("load", &DataSeriesClient::load), + makeMessage("print", &DataSeriesClient::print), + makeMessage("size", &DataSeriesClient::size), + makeMessage("cols", &DataSeriesClient::dims), + makeMessage("clear", &DataSeriesClient::clear), + makeMessage("write", &DataSeriesClient::write), + makeMessage("read", &DataSeriesClient::read), + makeMessage("getIds", &DataSeriesClient::getIds), + makeMessage("getDataSet", &DataSeriesClient::getDataSet) + ); + } + +private: + LabelSet getIdsLabelSet() + { + algorithm::DataSetIdSequence seq("", 0, 0); + FluidTensor newIds(mAlgorithm.size()); + FluidTensor labels(mAlgorithm.size(), 1); + labels.col(0) <<= mAlgorithm.getIds(); + seq.generate(newIds); + return LabelSet(newIds, labels); + }; + + DataSet getSliceDataSet(index time) const + { + DataSet ds(mAlgorithm.dims()); + decltype(mAlgorithm)::FrameType frame(mAlgorithm.dims()); + + for(auto id : mAlgorithm.getIds()) + { + bool ret = mAlgorithm.getFrame(id, time, frame); + if(ret) ds.add(id, frame); + } + + return ds; + } +}; + +} // namespace dataset + +using DataSeriesClientRef = SharedClientRef; +using InputDataSeriesClientRef = SharedClientRef; + +using NRTThreadedDataSeriesClient = + NRTThreadingAdaptor; + +} // namespace client +} // namespace fluid diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp new file mode 100644 index 000000000..dea7b2ed8 --- /dev/null +++ b/include/data/FluidDataSeries.hpp @@ -0,0 +1,353 @@ +#pragma once + +#include "data/FluidIndex.hpp" +#include "data/FluidTensor.hpp" +#include "data/TensorTypes.hpp" +#include +#include +#include +#include + +namespace fluid { + +template +class FluidDataSeries +{ + +public: + using FrameType = FluidTensor; + + explicit FluidDataSeries() = default; + ~FluidDataSeries() = default; + + // Construct from list of dimensions for each data point, + // e.g. FluidDataSet(2, 3) is a dataset of 2x3 tensors + template ()>> + FluidDataSeries(Dims... dims) + : mData(0, FluidTensor(0, dims...)), + mDim(dims...) + { + static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); + } + + // Construct from existing tensors of ids and data points + FluidDataSeries(FluidTensorView ids, + std::vector> points) + : mIds(ids), mData(points) + { + initFromData(); + } + + // Construct from existing tensors of ids and data points + // (from convertible type for data, typically float -> double) + template + FluidDataSeries(FluidTensorView ids, + std::vector> points, + std::enable_if_t::value>* = nullptr) + : mIds(ids), mData(points) + { + initFromData(); + } + + // Resize data point layout (if empty) + template ()>> + bool resize(Dims... dims) + { + static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); + if (size() == 0) + { + mData = std::vector>(); + mDim = FluidTensorSlice(dims...); + return true; + } + else + { + return false; + } + } + + template + bool addFrame(idType const& id, FluidTensorView frame) + { + static_assert(std::is_convertible::value, "Can't convert between types"); + assert(sameExtents(mDim, frame.descriptor())); + + auto pos = mIndex.find(id); + if (pos == mIndex.end()) + { + FluidTensorView newPoint(frame); + return addSeries(id, newPoint); + } + + mData[pos->second].resizeDim(0, 1); + mData[pos->second].row(mData[pos->second].rows() - 1) <<= frame; + + return true; + } + + template + bool addSeries(idType const& id, FluidTensorView series) + { + static_assert(std::is_convertible::value, "Can't convert between types"); + + // dont crete another view + auto result = mIndex.insert({id, mData.size()}); + if (!result.second) return false; + + mData.emplace_back(series); + + mIds.resizeDim(0, 1); + mIds(mIds.rows() - 1) = id; + + return true; + } + + bool getFrame(idType const& id, index time, FluidTensorView frame) const + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + + if (time >= mData[pos->second].rows()) return false; + + frame <<= mData[pos->second].row(time); + + return true; + } + + FluidTensorView getFrame(idType const& id, index time) const + { + auto pos = mIndex.find(id); + if(pos != mIndex.end()) + { + assert(time < mData[pos->second].rows()); + return mData[pos->second].row(time); + } + else { return FluidTensorView{nullptr, 0, 0}; } + } + + bool getSeries(idType const& id, FluidTensorView series) const + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + + series <<= mData[pos->second]; + + return true; + } + + FluidTensorView getSeries(idType const& id) const + { + auto pos = mIndex.find(id); + return pos != mIndex.end() + ? mData[pos->second] + : FluidTensorView{nullptr, 0, 0, 0}; + } + + template + bool updateFrame(idType const& id, index time, FluidTensorView frame) + { + static_assert(std::is_convertible::value, "Can't convert between types"); + + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + + if (time >= mData[pos->second].rows()) return false; + mData[pos->second].row(time) <<= frame; + + return true; + } + + template + bool updateSeries(idType const& id, FluidTensorView series) + { + static_assert(std::is_convertible::value, "Can't convert between types"); + + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + else + { + mData[pos->second].resizeDim(0, series.rows() - mData[pos->second].rows()); + mData[pos->second] <<= series; + } + + return true; + } + + bool removeFrame(idType const& id, index time) + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + + index current = pos->second; + if (time >= mData[current].rows()) return false; + + if(mData[current].rows() == 1) return removeSeries(id); + else mData[current].deleteRow(time); + + return true; + } + + bool removeSeries(idType const& id) + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return false; + + index current = pos->second; + mData.erase(mData.begin() + current); + mIds.deleteRow(current); + mIndex.erase(id); + + for (auto& point : mIndex) + { + if (point.second > current) point.second--; + } + + return true; + } + + index getIndex(idType const& id) const + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return -1; + else return pos->second; + } + + index getNumFrames(idType const& id) const + { + auto pos = mIndex.find(id); + if (pos == mIndex.end()) return -1; + else return mData[pos->second].rows(); + } + + std::vector> getData() + { + std::vector> viewVec(mData.size()); + + // hacky fix to force conversion of vector of tensors to vector of views of mData + // doesn't actually copy anything, it uses the FluidTensor ctor of FluidTensorView + // which creates a view/ref, so ends up creating what we want + std::copy(mData.begin(), mData.end(), std::back_inserter(viewVec)); + + return viewVec; + } + + const std::vector> getData() const + { + std::vector> viewVec; + + // hacky fix to force conversion of vector to views of mData + // doesn't actually copy anything, it uses the FluidTensor ctor of FluidTensorView + // which creates a view/ref, so ends up creating what we want + std::copy(mData.cbegin(), mData.cend(), std::back_inserter(viewVec)); + + return viewVec; + } + + FluidTensorView getIds() { return mIds; } + FluidTensorView getIds() const { return mIds; } + + index pointSize() const { return mDim.size; } + index dims() const { return mDim.size; } + index size() const { return mIds.size(); } + bool initialized() const { return (size() > 0); } + + std::string printFrame(FluidTensorView frame, + index maxCols) const + { + using namespace std; + ostringstream result; + + if (frame.size() < maxCols) + { + for (index c = 0; c < frame.size(); c++) + result << setw(10) << setprecision(5) << frame(c); + } + else + { + for (index c = 0; c < maxCols / 2; c++) + result << setw(10) << setprecision(5) << frame(c); + + result << setw(10) << "..."; + + for (index c = maxCols / 2; c > 0; c--) + result << setw(10) << setprecision(5) << frame(frame.size() - c); + } + + return result.str(); + } + + std::string printSeries(FluidTensorView series, + index maxFrames, index maxCols) const + { + using namespace std; + ostringstream result; + + if (series.rows() < maxFrames) + { + for (index t = 0; t < series.rows(); t++) + result << setw(10) << "t" << t << ": " << printFrame(series.row(t), maxCols) + << endl; + } + else + { + for (index t = 0; t < maxFrames / 2; t++) + result << setw(10) << "t" << t << ": " << printFrame(series.row(t), maxCols) + << endl; + + result << setw(10) << "..." << std::endl; + + for (index t = maxFrames / 2; t > 0; t--) + result << setw(10) << "t" << (series.rows() - t) << ": " + << printFrame(series.row(size() - t), maxCols) << endl; + } + + return result.str(); + } + + std::string print(index maxRows = 6, index maxFrames = 6, index maxCols = 6) const + { + using namespace std; + ostringstream result; + + if (size() == 0) return "{}"; + result << endl << "points: " << size() << endl << "frame size: " << pointSize() << endl; + + if (size() < maxRows) + { + for (index r = 0; r < size(); r++) + result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) + << endl; + } + else + { + for (index r = 0; r < maxRows / 2; r++) + result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) + << endl; + + result << setw(10) << "⋮" << endl; + + for (index r = maxRows / 2; r > 0; r--) + result << mIds(size() - r) << ":" << endl + << printSeries(mData[size() - r], maxFrames, maxCols) << endl; + } + + return result.str(); + } + +private: + void initFromData() + { + assert(mIds.rows() == mData.size()); + mDim = mData[0].cols(); + for (index i = 0; i < mIds.size(); i++) + mIndex.insert({mIds[i], i}); + } + + std::unordered_map mIndex; + FluidTensor mIds; + std::vector> mData; + FluidTensorSlice mDim; // dimensions for one frame +}; +} // namespace fluid diff --git a/include/data/FluidJSON.hpp b/include/data/FluidJSON.hpp index 0cdd5ede4..6020ba3c9 100644 --- a/include/data/FluidJSON.hpp +++ b/include/data/FluidJSON.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -133,6 +134,47 @@ void from_json(const nlohmann::json &j, FluidDataSet &ds) { } } +// FluidDataSeries +template +void to_json(nlohmann::json &j, const FluidDataSeries &ds) { + auto ids = ds.getIds(); + auto data = ds.getData(); + j["cols"] = ds.pointSize(); + for (index r = 0; r < ds.size(); r++) + { + auto series = data[r]; + for (index s = 0; s < series.rows(); s++) + j["data"][ids[r]]["t" + std::to_string(s)] = data[r].row(s); + } +} + +template +bool check_json(const nlohmann::json &j, + const FluidDataSeries &) { + return fluid::check_json(j, + {"cols", "data"}, + {JSONTypes::NUMBER, JSONTypes::OBJECT} + ); +} + +template +void from_json(const nlohmann::json &j, FluidDataSeries &ds) { + auto data = j.at("data"); + index pointSize = j.at("cols").get(); + FluidTensor tmp(pointSize); + + ds.resize(pointSize); + + for (auto r = data.begin(); r != data.end(); ++r) + { + for (auto s = r->begin(); s != r->end(); ++s) + { + s.value().get_to(tmp); + ds.addFrame(r.key(), FluidTensorView{tmp}); + } + } +} + namespace algorithm { // KDTree void to_json(nlohmann::json &j, const KDTree &tree) { From 1a5c2462d8eab1533fe4234b0bdba97446a38e1c Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 13:46:15 +0100 Subject: [PATCH 049/153] member matrix and helper distance function --- include/algorithms/public/DTW.hpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index c1b12c10f..47019f7a4 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -24,6 +24,10 @@ namespace algorithm { class DTW { + using MatrixXd = Eigen::MatrixXd; + using VectorXd = Eigen::VectorXd; + using ArrayXd = Eigen::ArrayXd; + public: explicit DTW() = default; ~DTW() = default; @@ -32,6 +36,16 @@ class DTW { } -} -} -} \ No newline at end of file + mutable MatrixXd distanceMetrics; + + inline static double euclidianDistToTheQ(const Eigen::Ref& in, const Eigen::Ref& out, index q) + { + double euclidianSquared = (in * out).value(); + if(q == 2) + return euclidianSquared; + return std::pow(euclidianSquared, 0.5 * q); // already squared, so (x^2)^(q/2) = x^q and _really_ optimises even values of q + } +}; + +} // namespace algorithm +} // namespace fluid \ No newline at end of file From fb4f7f90a5cae815fe152ca325b300ca3d572f0f Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 13:46:27 +0100 Subject: [PATCH 050/153] dtw brute-force algorithm --- include/algorithms/public/DTW.hpp | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 47019f7a4..3d21ef9c5 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -36,6 +36,40 @@ class DTW { } + + double process(InputRealMatrixView x1, InputRealMatrixView x2, index q) + { + distanceMetrics.conservativeResize(x1.rows(), x2.rows()); + // simple brute force DTW is very inefficient, see FastDTW + for (index i = 0; i < x1.rows(); i++) + { + for (index j = 0; j < x2.rows(); j++) + { + ArrayXd x1i = _impl::asEigen(x1.row(i)); + ArrayXd x2j = _impl::asEigen(x2.row(j)); + + distanceMetrics(i, j) = euclidianDistToTheQ(x1i, x2j, q); + + if (i > 0 || j > 0) + { + double minimum = std::numeric_limits::max(); + + if (i > 0 && j > 0) + minimum = std::min(minimum, distanceMetrics(i - 1, j - 1)); + if (i > 0) + minimum = std::min(minimum, distanceMetrics(i - 1, j)); + if (j > 0) + minimum = std::min(minimum, distanceMetrics(i, j - 1)); + + distanceMetrics(i, j) += minimum; + } + } + } + + return std::pow(distanceMetrics.bottomLeftCorner<1, 1>().value(), 1.0 / q); + } + +private: mutable MatrixXd distanceMetrics; inline static double euclidianDistToTheQ(const Eigen::Ref& in, const Eigen::Ref& out, index q) From eae785f43a0ba730d59994e06efabf6a9d6d9790 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 13:51:51 +0100 Subject: [PATCH 051/153] props and dataclient boilerplate --- include/algorithms/public/DTW.hpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 3d21ef9c5..d78f92a98 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -22,6 +22,10 @@ under the European Union’s Horizon 2020 research and innovation programme namespace fluid { namespace algorithm { + +// debt of gratitude to the wonderful article on https://rtavenar.github.io/blog/dtw.html +// a better explanation of DTW than any other algorithm explanation I've seen + class DTW { using MatrixXd = Eigen::MatrixXd; @@ -32,12 +36,15 @@ class DTW explicit DTW() = default; ~DTW() = default; - void init() - { + // functions so the DataClient doesnt have freak out + void init() {} + void clear() {} - } + constexpr index size() const { return 0; } + constexpr index dims() const { return 0; } + constexpr index initialized() const { return true; } - double process(InputRealMatrixView x1, InputRealMatrixView x2, index q) + double process(InputRealMatrixView x1, InputRealMatrixView x2, index q = 2) { distanceMetrics.conservativeResize(x1.rows(), x2.rows()); // simple brute force DTW is very inefficient, see FastDTW From c25836c2423e095cdcb2fb3a6eb58b1a701de1fb Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 14:05:14 +0100 Subject: [PATCH 052/153] member function constness --- include/algorithms/public/DTW.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index d78f92a98..1f067abb3 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -37,14 +37,14 @@ class DTW ~DTW() = default; // functions so the DataClient doesnt have freak out - void init() {} - void clear() {} + void init() const {} + void clear() const {} constexpr index size() const { return 0; } constexpr index dims() const { return 0; } constexpr index initialized() const { return true; } - double process(InputRealMatrixView x1, InputRealMatrixView x2, index q = 2) + double process(InputRealMatrixView x1, InputRealMatrixView x2, index q = 2) const { distanceMetrics.conservativeResize(x1.rows(), x2.rows()); // simple brute force DTW is very inefficient, see FastDTW From abdf5ce58ad5448a13026494d10a0658086bde40 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 14:05:25 +0100 Subject: [PATCH 053/153] fastdtw boilerplate --- include/algorithms/public/FastDTW.hpp | 66 +++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/include/algorithms/public/FastDTW.hpp b/include/algorithms/public/FastDTW.hpp index e69de29bb..64dfadd7b 100644 --- a/include/algorithms/public/FastDTW.hpp +++ b/include/algorithms/public/FastDTW.hpp @@ -0,0 +1,66 @@ +/* +Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) +Copyright University of Huddersfield. +Licensed under the BSD-3 License. +See license.md file in the project root for full license information. +This project has received funding from the European Research Council (ERC) +under the European Union’s Horizon 2020 research and innovation programme +(grant agreement No 725899). +*/ + +#pragma once + +#include "../util/FluidEigenMappings.hpp" +#include "../../data/FluidDataSet.hpp" +#include "../../data/FluidIndex.hpp" +#include "../../data/FluidTensor.hpp" +#include "../../data/TensorTypes.hpp" +#include "../../data/FluidMemory.hpp" +#include +#include + +namespace fluid { +namespace algorithm { + +// divide-and-conquer algorithm using the DTW algorithm +// and various constraints to reduce complexity +// down from O(N*M) (boooo) to O(N) (yayyy) +// props to the absolute units Stan Salvador and Philip Chan +// over at https://cs.fit.edu/~pkc/papers/tdm04.pdf + +class FastDTW +{ + using MatrixXd = Eigen::MatrixXd; + using VectorXd = Eigen::VectorXd; + using ArrayXd = Eigen::ArrayXd; + +public: + explicit FastDTW() = default; + ~FastDTW() = default; + + // functions so the DataClient doesnt have freak out + void init() + { + mInitialised = true; + } + + void clear() const {} + + index size() const { return mConstraintSize; } + index dims() const { return mConstraintSize; } + + bool initialized() const { return mInitialised; } + + void process() const + { + + } + +private: + bool mInitialised {false}; + index mConstraintSize {8}; + +}; + +} // namespace algorithm +} // namespace fluid \ No newline at end of file From 4d0bd5cea5fc3f433108893cb2d644710e3d3741 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 14:59:58 +0100 Subject: [PATCH 054/153] explicit eigen dot product --- include/algorithms/public/DTW.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 1f067abb3..01b1a9b73 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -81,7 +81,7 @@ class DTW inline static double euclidianDistToTheQ(const Eigen::Ref& in, const Eigen::Ref& out, index q) { - double euclidianSquared = (in * out).value(); + double euclidianSquared = in.dot(out); if(q == 2) return euclidianSquared; return std::pow(euclidianSquared, 0.5 * q); // already squared, so (x^2)^(q/2) = x^q and _really_ optimises even values of q From cdc8237979cb76fa2238f61e04cb4906a1d6035b Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 15:00:27 +0100 Subject: [PATCH 055/153] `kNearest` message for `DataSeries` --- include/clients/nrt/DataSeriesClient.hpp | 50 +++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index a2d5a30cd..020c30732 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -15,6 +15,7 @@ under the European Union’s Horizon 2020 research and innovation programme #include "NRTClient.hpp" #include "../common/SharedClientUtils.hpp" #include "../../algorithms/public/DataSetIdSequence.hpp" +#include "../../algorithms/public/DTW.hpp" #include "../../data/FluidDataSeries.hpp" #include #include @@ -255,6 +256,46 @@ class DataSeriesClient : public FluidBaseClient, return OK(); } + MessageResult> kNearest(InputBufferPtr data, + index nNeighbours) const + { + // check for nNeighbours > 0 and < size of DS + if (nNeighbours > mAlgorithm.size()) + return Error>(SmallDataSet); + if (nNeighbours <= 0) return Error>(SmallK); + + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error>(InvalidBuffer); + if (buf.numChans() < mAlgorithm.dims()) return Error>(WrongPointSize); + + FluidTensor series(buf.allFrames().transpose()); + + std::vector indices(asUnsigned(mAlgorithm.size())); + std::iota(indices.begin(), indices.end(), 0); + std::vector distances(asUnsigned(mAlgorithm.size())); + + auto ds = mAlgorithm.getData(); + + std::transform( + indices.begin(), indices.end(), distances.begin(), + [&series, &ds, this](index i) { return distance(series, ds[i]); }); + + std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { + return distances[asUnsigned(a)] < distances[asUnsigned(b)]; + }); + + FluidTensor labels(nNeighbours); + + std::transform( + indices.begin(), indices.begin() + nNeighbours, labels.begin(), + [this](index i) { + std::string const& id = mAlgorithm.getIds()[i]; + return rt::string{id, 0, id.size(), FluidDefaultAllocator()}; + }); + + return labels; + } + MessageResult clear() { mAlgorithm = DataSeries(0); @@ -292,7 +333,8 @@ class DataSeriesClient : public FluidBaseClient, makeMessage("write", &DataSeriesClient::write), makeMessage("read", &DataSeriesClient::read), makeMessage("getIds", &DataSeriesClient::getIds), - makeMessage("getDataSet", &DataSeriesClient::getDataSet) + makeMessage("getDataSet", &DataSeriesClient::getDataSet), + makeMessage("kNearest", &DataSeriesClient::kNearest) ); } @@ -320,6 +362,12 @@ class DataSeriesClient : public FluidBaseClient, return ds; } + + double distance(FluidTensorView x1, FluidTensorView x2) const + { + algorithm::DTW dtw; + return dtw.process(x1, x2); + } }; } // namespace dataset From 799374cadb6b2874171c43665edebd2eb4668300 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 24 Aug 2023 15:46:55 +0100 Subject: [PATCH 056/153] what was i thinking euclidian dist isnt remotely their dot product :| --- include/algorithms/public/DTW.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 01b1a9b73..b340081d2 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -81,7 +81,7 @@ class DTW inline static double euclidianDistToTheQ(const Eigen::Ref& in, const Eigen::Ref& out, index q) { - double euclidianSquared = in.dot(out); + double euclidianSquared = (in - out).dot(in - out); if(q == 2) return euclidianSquared; return std::pow(euclidianSquared, 0.5 * q); // already squared, so (x^2)^(q/2) = x^q and _really_ optimises even values of q From fe27129247485ed69112e4771f1c4a3dfae41ef2 Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 09:58:45 +0100 Subject: [PATCH 057/153] p-norm distance replaces euclidian --- include/algorithms/public/DTW.hpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index b340081d2..a85949578 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -44,7 +44,7 @@ class DTW constexpr index dims() const { return 0; } constexpr index initialized() const { return true; } - double process(InputRealMatrixView x1, InputRealMatrixView x2, index q = 2) const + double process(InputRealMatrixView x1, InputRealMatrixView x2, index p = 2) const { distanceMetrics.conservativeResize(x1.rows(), x2.rows()); // simple brute force DTW is very inefficient, see FastDTW @@ -55,7 +55,7 @@ class DTW ArrayXd x1i = _impl::asEigen(x1.row(i)); ArrayXd x2j = _impl::asEigen(x2.row(j)); - distanceMetrics(i, j) = euclidianDistToTheQ(x1i, x2j, q); + distanceMetrics(i, j) = differencePNormToTheP(x1i, x2j, p); if (i > 0 || j > 0) { @@ -73,18 +73,22 @@ class DTW } } - return std::pow(distanceMetrics.bottomLeftCorner<1, 1>().value(), 1.0 / q); + return std::pow(distanceMetrics.bottomLeftCorner<1, 1>().value(), 1.0 / p); } private: mutable MatrixXd distanceMetrics; - inline static double euclidianDistToTheQ(const Eigen::Ref& in, const Eigen::Ref& out, index q) + // P-Norm of the difference vector + // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) + // i.e., the 2-norm of a vector is the euclidian distance from the origin + // the 1-norm is the sum of the absolute value of the elements + // To the power P since we'll be summing multiple Norms together and they + // can combine into a single norm if you calculate the norm of multiple norms (normception) + inline static double differencePNormToTheP(const Eigen::Ref& v1, const Eigen::Ref& v2, index p) { - double euclidianSquared = (in - out).dot(in - out); - if(q == 2) - return euclidianSquared; - return std::pow(euclidianSquared, 0.5 * q); // already squared, so (x^2)^(q/2) = x^q and _really_ optimises even values of q + // assert(v1.size() == v2.size()); + return (v1 - v2).array().abs().pow(p).sum(); } }; From 062df21db2735f94a1a52f1ecafadc9dfa8a683a Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 09:59:24 +0100 Subject: [PATCH 058/153] `DTW` client boilerplate --- include/algorithms/public/DTW.hpp | 6 +- include/clients/nrt/DTWClient.hpp | 208 ++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 3 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index a85949578..a37e5b13a 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -62,11 +62,11 @@ class DTW double minimum = std::numeric_limits::max(); if (i > 0 && j > 0) - minimum = std::min(minimum, distanceMetrics(i - 1, j - 1)); + minimum = std::min(minimum, distanceMetrics(i-1, j-1)); if (i > 0) - minimum = std::min(minimum, distanceMetrics(i - 1, j)); + minimum = std::min(minimum, distanceMetrics(i-1, j )); if (j > 0) - minimum = std::min(minimum, distanceMetrics(i, j - 1)); + minimum = std::min(minimum, distanceMetrics(i , j-1)); distanceMetrics(i, j) += minimum; } diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index e69de29bb..65f3b9da8 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -0,0 +1,208 @@ +/* +Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) +Copyright University of Huddersfield. +Licensed under the BSD-3 License. +See license.md file in the project root for full license information. +This project has received funding from the European Research Council (ERC) +under the European Union’s Horizon 2020 research and innovation programme +(grant agreement No 725899). +*/ + +#pragma once + +#include "DataSetClient.hpp" +#include "DataSeriesClient.hpp" +#include "NRTClient.hpp" +#include "../../algorithms/public/DTW.hpp" +#include + +namespace fluid { +namespace client { +namespace dtw { + +constexpr auto DTWParams = defineParameters( + StringParam>("name", "Name"), + LongParam("p", "LpNorm power (distance weighting)", 2, Min(1))); + +class DTWClient : public FluidBaseClient, + OfflineIn, + OfflineOut, + ModelObject +{ + enum { kName, kQ }; + +public: + using string = std::string; + using BufferPtr = std::shared_ptr; + using InputBufferPtr = std::shared_ptr; + using StringVector = FluidTensor; + + using ParamDescType = decltype(DTWParams); + using ParamSetViewType = ParameterSetView; + using ParamValues = typename ParamSetViewType::ValueTuple; + + std::reference_wrapper mParams; + + void setParams(ParamSetViewType& p) { mParams = p; } + + template + auto& get() const + { + return mParams.get().template get(); + } + + static constexpr auto& getParameterDescriptors() { return DTWParams; } + + DTWClient(ParamSetViewType& p, FluidContext&) : mParams(p) + { + controlChannelsIn(1); + controlChannelsOut({1, 1}); + } + + template + Result process(FluidContext&) + { + return {}; + } + + MessageResult cost(InputDataSetClientRef datasetClient) + { + return OK(); + } + + MessageResult bufCost(InputDataSetClientRef datasetClient) + { + return OK(); + } + + static auto getMessageDescriptors() + { + return defineMessages( + makeMessage("cost", &DTWClient::cost) + makeMessage("bufCost", &DTWClient::bufCost) + ); + } +}; + +using DTWRef = SharedClientRef; + +constexpr auto DTWQueryParams = defineParameters( + DTWRef::makeParam("tree", "DTW"), + LongParam("p", "LpNorm power (distance weighting)", 2, Min(0)), + InputBufferParam("inputPointBuffer", "Input Point Buffer"), + BufferParam("predictionBuffer", "Prediction Buffer")); + +class DTWQuery : public FluidBaseClient, ControlIn, ControlOut +{ + enum { kTree, kNumNeighbors, kRadius, kDataSet, kInputBuffer, kOutputBuffer }; + +public: + using ParamDescType = decltype(DTWQueryParams); + using ParamSetViewType = ParameterSetView; + + std::reference_wrapper mParams; + + void setParams(ParamSetViewType& p) { mParams = p; } + + template + auto& get() const + { + return mParams.get().template get(); + } + + static constexpr auto& getParameterDescriptors() { return DTWQueryParams; } + + DTWQuery(ParamSetViewType& p, FluidContext& c) + : mParams(p), mRTBuffer(c.allocator()) + { + controlChannelsIn(1); + controlChannelsOut({1, 1}); + } + + index latency() { return 0; } + + template + void process(std::vector>& input, + std::vector>& output, FluidContext& c) + { + if (input[0](0) > 0) + { + auto kdtreeptr = get().get().lock(); + if (!kdtreeptr) + { + // c.reportError("FluidKDTree RT Query: No FluidKDTree found"); + return; + } + + if (!kdtreeptr->initialized()) + { + // c.reportError("FluidKDTree RT Query: tree not fitted"); + return; + } + + index k = get(); + if (k > kdtreeptr->size() || k < 0) + return; // c.reportError("FluidKDTree RT Query has wrong k size"); + index dims = kdtreeptr->dims(); + InOutBuffersCheck bufCheck(dims); + if (!bufCheck.checkInputs(get().get(), + get().get())) + return; // c.reportError("FluidKDTree RT Query i/o buffers are + // unavailable"); + auto datasetClientPtr = get().get().lock(); + if (!datasetClientPtr) + datasetClientPtr = kdtreeptr->getDataSet().get().lock(); + + if (!datasetClientPtr) + { + // c.reportError("Could not obtain reference FluidDataSet"); + return; + } + + auto dataset = datasetClientPtr->getDataSet(); + index pointSize = dataset.pointSize(); + auto outBuf = BufferAdaptor::Access(get().get()); + index maxK = outBuf.samps(0).size() / pointSize; + if (maxK <= 0) return; + index outputSize = maxK * pointSize; + + RealVector point(dims, c.allocator()); + point <<= BufferAdaptor::ReadAccess(get().get()) + .samps(0, dims, 0); + if (mRTBuffer.size() != outputSize) + { + mRTBuffer = RealVector(outputSize, c.allocator()); + mRTBuffer.fill(0); + } + + auto [dists, ids] = kdtreeptr->algorithm().kNearest( + point, k, get(), c.allocator()); + + mNumValidKs = std::min(asSigned(ids.size()), maxK); + + for (index i = 0; i < mNumValidKs; i++) + { + dataset.get(*ids[asUnsigned(i)], + mRTBuffer(Slice(i * pointSize, pointSize))); + } + outBuf.samps(0, outputSize, 0) <<= mRTBuffer; + } + + output[0](0) = mNumValidKs; + } + + +private: + RealVector mRTBuffer; + index mNumValidKs = 0; + InputDataSetClientRef mDataSetClient; +}; + +} // namespace DTW + +using NRTThreadedDTWClient = + NRTThreadingAdaptor; +using RTDTWQueryClient = ClientWrapper; + +} // namespace client +} // namespace fluid From bd09568a94810bf8ed52d2c7e56f3dd0528a3ed3 Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 10:00:40 +0100 Subject: [PATCH 059/153] add `DTW` to `libmanipulation` --- FlucomaClients.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FlucomaClients.cmake b/FlucomaClients.cmake index 2208b82b1..333d4d855 100644 --- a/FlucomaClients.cmake +++ b/FlucomaClients.cmake @@ -143,6 +143,7 @@ add_client(Transients clients/rt/TransientClient.hpp CLASS RTTransientClient ) #lib manipulation client group add_client(DataSet clients/nrt/DataSetClient.hpp CLASS NRTThreadedDataSetClient GROUP MANIPULATION) add_client(DataSetQuery clients/nrt/DataSetQueryClient.hpp CLASS NRTThreadedDataSetQueryClient GROUP MANIPULATION) +add_client(DataSeries clients/nrt/DataSeriesClient.hpp CLASS NRTThreadedDataSeriesClient GROUP MANIPULATION) add_client(LabelSet clients/nrt/LabelSetClient.hpp CLASS NRTThreadedLabelSetClient GROUP MANIPULATION) add_client(KDTree clients/nrt/KDTreeClient.hpp CLASS NRTThreadedKDTreeClient GROUP MANIPULATION) add_client(KMeans clients/nrt/KMeansClient.hpp CLASS NRTThreadedKMeansClient GROUP MANIPULATION) @@ -158,3 +159,4 @@ add_client(UMAP clients/nrt/UMAPClient.hpp CLASS NRTThreadedUMAPClient GROUP MAN add_client(MLPRegressor clients/nrt/MLPRegressorClient.hpp CLASS NRTThreadedMLPRegressorClient GROUP MANIPULATION) add_client(MLPClassifier clients/nrt/MLPClassifierClient.hpp CLASS NRTThreadedMLPClassifierClient GROUP MANIPULATION) add_client(Grid clients/nrt/GridClient.hpp CLASS NRTThreadedGridClient GROUP MANIPULATION) +add_client(DTW clients/nrt/DTWClient.hpp CLASS NRTThreadedDTWClient GROUP MANIPULATION) From 711ef37e4f0043390ce160ed3201458b7c01b6d4 Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 10:32:43 +0100 Subject: [PATCH 060/153] `dtw` templatifying shenanigains --- include/algorithms/public/DTW.hpp | 21 +++++++++++++-------- include/clients/nrt/DataSeriesClient.hpp | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index a37e5b13a..603396383 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -26,11 +26,12 @@ namespace algorithm { // debt of gratitude to the wonderful article on https://rtavenar.github.io/blog/dtw.html // a better explanation of DTW than any other algorithm explanation I've seen +template class DTW { - using MatrixXd = Eigen::MatrixXd; - using VectorXd = Eigen::VectorXd; - using ArrayXd = Eigen::ArrayXd; + using Matrix = Eigen::Matrix; + using Vector = Eigen::Vector; + using Array = Eigen::Array; public: explicit DTW() = default; @@ -44,16 +45,20 @@ class DTW constexpr index dims() const { return 0; } constexpr index initialized() const { return true; } - double process(InputRealMatrixView x1, InputRealMatrixView x2, index p = 2) const + template + dataType process(FluidTensorView x1, + FluidTensorView x2, index p = 2) const { + static_assert(std::is_convertible::value, "Can't convert between types"); + distanceMetrics.conservativeResize(x1.rows(), x2.rows()); // simple brute force DTW is very inefficient, see FastDTW for (index i = 0; i < x1.rows(); i++) { for (index j = 0; j < x2.rows(); j++) { - ArrayXd x1i = _impl::asEigen(x1.row(i)); - ArrayXd x2j = _impl::asEigen(x2.row(j)); + Array x1i = _impl::asEigen(x1.row(i)).cast(); + Array x2j = _impl::asEigen(x2.row(j)).cast(); distanceMetrics(i, j) = differencePNormToTheP(x1i, x2j, p); @@ -77,7 +82,7 @@ class DTW } private: - mutable MatrixXd distanceMetrics; + mutable Matrix distanceMetrics; // P-Norm of the difference vector // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) @@ -85,7 +90,7 @@ class DTW // the 1-norm is the sum of the absolute value of the elements // To the power P since we'll be summing multiple Norms together and they // can combine into a single norm if you calculate the norm of multiple norms (normception) - inline static double differencePNormToTheP(const Eigen::Ref& v1, const Eigen::Ref& v2, index p) + inline static dataType differencePNormToTheP(const Eigen::Ref& v1, const Eigen::Ref& v2, index p) { // assert(v1.size() == v2.size()); return (v1 - v2).array().abs().pow(p).sum(); diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 020c30732..1c1e25985 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -365,7 +365,7 @@ class DataSeriesClient : public FluidBaseClient, double distance(FluidTensorView x1, FluidTensorView x2) const { - algorithm::DTW dtw; + algorithm::DTW dtw; return dtw.process(x1, x2); } }; From 19de4a259270b0f1efc55109829dbb1038953aec Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 10:33:39 +0100 Subject: [PATCH 061/153] `dtw` `bufCost` message --- include/clients/nrt/DTWClient.hpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 65f3b9da8..a921ffd0d 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -43,6 +43,9 @@ class DTWClient : public FluidBaseClient, std::reference_wrapper mParams; + // stateless algorithm + static algorithm::DTW mAlgorithm; + void setParams(ParamSetViewType& p) { mParams = p; } template @@ -70,15 +73,25 @@ class DTWClient : public FluidBaseClient, return OK(); } - MessageResult bufCost(InputDataSetClientRef datasetClient) + MessageResult bufCost(InputBufferPtr data1, InputBufferPtr data2) { - return OK(); + if (!data1 || !data2) return Error(NoBuffer); + + BufferAdaptor::ReadAccess buf1(data1.get()), buf2(data2.get()); + + if (!buf1.exists() || !buf2.exists()) return Error(InvalidBuffer); + if (buf1.numChans() != buf2.numChans()) return Error(WrongPointSize); + + double cost = mAlgorithm.process(buf1.allFrames().transpose(), + buf2.allFrames().transpose()); + + return cost; } static auto getMessageDescriptors() { return defineMessages( - makeMessage("cost", &DTWClient::cost) + makeMessage("cost", &DTWClient::cost), makeMessage("bufCost", &DTWClient::bufCost) ); } From ee3490e216666d9d0d54102b3163091f06e91bef Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 11:06:26 +0100 Subject: [PATCH 062/153] make stateless `dtw` static --- include/algorithms/public/DTW.hpp | 18 ++++-------------- include/clients/nrt/DTWClient.hpp | 7 ++----- include/clients/nrt/DataSeriesClient.hpp | 3 +-- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 603396383..7b3416aa8 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -30,28 +30,20 @@ template class DTW { using Matrix = Eigen::Matrix; - using Vector = Eigen::Vector; + using Vector = Eigen::Matrix; using Array = Eigen::Array; public: explicit DTW() = default; ~DTW() = default; - // functions so the DataClient doesnt have freak out - void init() const {} - void clear() const {} - - constexpr index size() const { return 0; } - constexpr index dims() const { return 0; } - constexpr index initialized() const { return true; } - template - dataType process(FluidTensorView x1, - FluidTensorView x2, index p = 2) const + static dataType process(FluidTensorView x1, + FluidTensorView x2, index p = 2) { static_assert(std::is_convertible::value, "Can't convert between types"); - distanceMetrics.conservativeResize(x1.rows(), x2.rows()); + Matrix distanceMetrics(x1.rows(), x2.rows()); // simple brute force DTW is very inefficient, see FastDTW for (index i = 0; i < x1.rows(); i++) { @@ -82,8 +74,6 @@ class DTW } private: - mutable Matrix distanceMetrics; - // P-Norm of the difference vector // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) // i.e., the 2-norm of a vector is the euclidian distance from the origin diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index a921ffd0d..398cae690 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -43,9 +43,6 @@ class DTWClient : public FluidBaseClient, std::reference_wrapper mParams; - // stateless algorithm - static algorithm::DTW mAlgorithm; - void setParams(ParamSetViewType& p) { mParams = p; } template @@ -82,8 +79,8 @@ class DTWClient : public FluidBaseClient, if (!buf1.exists() || !buf2.exists()) return Error(InvalidBuffer); if (buf1.numChans() != buf2.numChans()) return Error(WrongPointSize); - double cost = mAlgorithm.process(buf1.allFrames().transpose(), - buf2.allFrames().transpose()); + double cost = algorithm::DTW::process(buf1.allFrames().transpose(), + buf2.allFrames().transpose()); return cost; } diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 1c1e25985..dbddb8bde 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -365,8 +365,7 @@ class DataSeriesClient : public FluidBaseClient, double distance(FluidTensorView x1, FluidTensorView x2) const { - algorithm::DTW dtw; - return dtw.process(x1, x2); + return algorithm::DTW::process(x1, x2); } }; From 40240331e0dcf1903952f44f179090865b4c5939 Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 11:44:40 +0100 Subject: [PATCH 063/153] added pNorm ui --- include/clients/nrt/DTWClient.hpp | 5 +++-- include/clients/nrt/DataSeriesClient.hpp | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 398cae690..80faad6c6 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -29,7 +29,7 @@ class DTWClient : public FluidBaseClient, OfflineOut, ModelObject { - enum { kName, kQ }; + enum { kName, kPNorm }; public: using string = std::string; @@ -80,7 +80,8 @@ class DTWClient : public FluidBaseClient, if (buf1.numChans() != buf2.numChans()) return Error(WrongPointSize); double cost = algorithm::DTW::process(buf1.allFrames().transpose(), - buf2.allFrames().transpose()); + buf2.allFrames().transpose(), + get()); return cost; } diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index dbddb8bde..ea352bae0 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -257,7 +257,8 @@ class DataSeriesClient : public FluidBaseClient, } MessageResult> kNearest(InputBufferPtr data, - index nNeighbours) const + index nNeighbours, + index p = 2) const { // check for nNeighbours > 0 and < size of DS if (nNeighbours > mAlgorithm.size()) @@ -278,7 +279,7 @@ class DataSeriesClient : public FluidBaseClient, std::transform( indices.begin(), indices.end(), distances.begin(), - [&series, &ds, this](index i) { return distance(series, ds[i]); }); + [&series, &ds, &p, this](index i) { return distance(series, ds[i], p); }); std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { return distances[asUnsigned(a)] < distances[asUnsigned(b)]; @@ -363,9 +364,9 @@ class DataSeriesClient : public FluidBaseClient, return ds; } - double distance(FluidTensorView x1, FluidTensorView x2) const + double distance(FluidTensorView x1, FluidTensorView x2, index p) const { - return algorithm::DTW::process(x1, x2); + return algorithm::DTW::process(x1, x2, p); } }; From 75287c7b7737c9ccacd34950ace0677feaeff5de Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 11:44:52 +0100 Subject: [PATCH 064/153] remove query object --- include/clients/nrt/DTWClient.hpp | 113 ------------------------------ 1 file changed, 113 deletions(-) diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 80faad6c6..fa8e15b3c 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -97,123 +97,10 @@ class DTWClient : public FluidBaseClient, using DTWRef = SharedClientRef; -constexpr auto DTWQueryParams = defineParameters( - DTWRef::makeParam("tree", "DTW"), - LongParam("p", "LpNorm power (distance weighting)", 2, Min(0)), - InputBufferParam("inputPointBuffer", "Input Point Buffer"), - BufferParam("predictionBuffer", "Prediction Buffer")); - -class DTWQuery : public FluidBaseClient, ControlIn, ControlOut -{ - enum { kTree, kNumNeighbors, kRadius, kDataSet, kInputBuffer, kOutputBuffer }; - -public: - using ParamDescType = decltype(DTWQueryParams); - using ParamSetViewType = ParameterSetView; - - std::reference_wrapper mParams; - - void setParams(ParamSetViewType& p) { mParams = p; } - - template - auto& get() const - { - return mParams.get().template get(); - } - - static constexpr auto& getParameterDescriptors() { return DTWQueryParams; } - - DTWQuery(ParamSetViewType& p, FluidContext& c) - : mParams(p), mRTBuffer(c.allocator()) - { - controlChannelsIn(1); - controlChannelsOut({1, 1}); - } - - index latency() { return 0; } - - template - void process(std::vector>& input, - std::vector>& output, FluidContext& c) - { - if (input[0](0) > 0) - { - auto kdtreeptr = get().get().lock(); - if (!kdtreeptr) - { - // c.reportError("FluidKDTree RT Query: No FluidKDTree found"); - return; - } - - if (!kdtreeptr->initialized()) - { - // c.reportError("FluidKDTree RT Query: tree not fitted"); - return; - } - - index k = get(); - if (k > kdtreeptr->size() || k < 0) - return; // c.reportError("FluidKDTree RT Query has wrong k size"); - index dims = kdtreeptr->dims(); - InOutBuffersCheck bufCheck(dims); - if (!bufCheck.checkInputs(get().get(), - get().get())) - return; // c.reportError("FluidKDTree RT Query i/o buffers are - // unavailable"); - auto datasetClientPtr = get().get().lock(); - if (!datasetClientPtr) - datasetClientPtr = kdtreeptr->getDataSet().get().lock(); - - if (!datasetClientPtr) - { - // c.reportError("Could not obtain reference FluidDataSet"); - return; - } - - auto dataset = datasetClientPtr->getDataSet(); - index pointSize = dataset.pointSize(); - auto outBuf = BufferAdaptor::Access(get().get()); - index maxK = outBuf.samps(0).size() / pointSize; - if (maxK <= 0) return; - index outputSize = maxK * pointSize; - - RealVector point(dims, c.allocator()); - point <<= BufferAdaptor::ReadAccess(get().get()) - .samps(0, dims, 0); - if (mRTBuffer.size() != outputSize) - { - mRTBuffer = RealVector(outputSize, c.allocator()); - mRTBuffer.fill(0); - } - - auto [dists, ids] = kdtreeptr->algorithm().kNearest( - point, k, get(), c.allocator()); - - mNumValidKs = std::min(asSigned(ids.size()), maxK); - - for (index i = 0; i < mNumValidKs; i++) - { - dataset.get(*ids[asUnsigned(i)], - mRTBuffer(Slice(i * pointSize, pointSize))); - } - outBuf.samps(0, outputSize, 0) <<= mRTBuffer; - } - - output[0](0) = mNumValidKs; - } - - -private: - RealVector mRTBuffer; - index mNumValidKs = 0; - InputDataSetClientRef mDataSetClient; -}; - } // namespace DTW using NRTThreadedDTWClient = NRTThreadingAdaptor; -using RTDTWQueryClient = ClientWrapper; } // namespace client } // namespace fluid From 652188460b116d67efd48f9dba05367dc610051f Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 11:55:04 +0100 Subject: [PATCH 065/153] returning the correct corner of the matrix might help --- include/algorithms/public/DTW.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 7b3416aa8..97ce69fcf 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -70,7 +70,7 @@ class DTW } } - return std::pow(distanceMetrics.bottomLeftCorner<1, 1>().value(), 1.0 / p); + return std::pow(distanceMetrics(x1.rows() - 1, x2.rows() - 1), 1.0 / p); } private: @@ -83,7 +83,7 @@ class DTW inline static dataType differencePNormToTheP(const Eigen::Ref& v1, const Eigen::Ref& v2, index p) { // assert(v1.size() == v2.size()); - return (v1 - v2).array().abs().pow(p).sum(); + return (v1.array() - v2.array()).abs().pow(p).sum(); } }; From 5869ebb680fc759e8246ceca1805903b1ad2b4ae Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 11:58:09 +0100 Subject: [PATCH 066/153] rm templating dipshittery i already have one i dont need a nested one --- include/algorithms/public/DTW.hpp | 11 ++++------- include/clients/nrt/DTWClient.hpp | 6 +++--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 97ce69fcf..ffff1f030 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -37,20 +37,17 @@ class DTW explicit DTW() = default; ~DTW() = default; - template - static dataType process(FluidTensorView x1, - FluidTensorView x2, index p = 2) + static dataType process(FluidTensorView x1, + FluidTensorView x2, index p = 2) { - static_assert(std::is_convertible::value, "Can't convert between types"); - Matrix distanceMetrics(x1.rows(), x2.rows()); // simple brute force DTW is very inefficient, see FastDTW for (index i = 0; i < x1.rows(); i++) { for (index j = 0; j < x2.rows(); j++) { - Array x1i = _impl::asEigen(x1.row(i)).cast(); - Array x2j = _impl::asEigen(x2.row(j)).cast(); + Array x1i = _impl::asEigen(x1.row(i)); + Array x2j = _impl::asEigen(x2.row(j)); distanceMetrics(i, j) = differencePNormToTheP(x1i, x2j, p); diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index fa8e15b3c..9d689bb2b 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -79,9 +79,9 @@ class DTWClient : public FluidBaseClient, if (!buf1.exists() || !buf2.exists()) return Error(InvalidBuffer); if (buf1.numChans() != buf2.numChans()) return Error(WrongPointSize); - double cost = algorithm::DTW::process(buf1.allFrames().transpose(), - buf2.allFrames().transpose(), - get()); + double cost = algorithm::DTW::process(buf1.allFrames().transpose(), + buf2.allFrames().transpose(), + get()); return cost; } From a8aa137f288c0fa44e785b07a4554a69efa1cf83 Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 12:15:41 +0100 Subject: [PATCH 067/153] `dtw` `cost` message with `dataseries` --- include/algorithms/public/DTW.hpp | 2 +- include/clients/nrt/DTWClient.hpp | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index ffff1f030..06654ef3f 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -53,7 +53,7 @@ class DTW if (i > 0 || j > 0) { - double minimum = std::numeric_limits::max(); + dataType minimum = std::numeric_limits::max(); if (i > 0 && j > 0) minimum = std::min(minimum, distanceMetrics(i-1, j-1)); diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 9d689bb2b..870e49cfb 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -65,9 +65,27 @@ class DTWClient : public FluidBaseClient, return {}; } - MessageResult cost(InputDataSetClientRef datasetClient) + MessageResult cost(InputDataSeriesClientRef dataseriesClient, + string id1, string id2) const { - return OK(); + auto dataseriesClientPtr = dataseriesClient.get().lock(); + if (!dataseriesClientPtr) return Error(NoDataSet); + + auto srcDataSeries = dataseriesClientPtr->getDataSeries(); + if (srcDataSeries.size() == 0) return Error(EmptyDataSet); + + index i1 = srcDataSeries.getIndex(id1), + i2 = srcDataSeries.getIndex(id2); + + if (i1 < 0 || i2 < 0) return Error(PointNotFound); + + InputRealMatrixView series1 = srcDataSeries.getSeries(id1), + series2 = srcDataSeries.getSeries(id2); + + double cost = algorithm::DTW::process(series1, series2, + get()); + + return cost; } MessageResult bufCost(InputBufferPtr data1, InputBufferPtr data2) From 69ba4a6a1e0d26907f6e60a42a07c72dddc16d5a Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 25 Aug 2023 15:33:43 +0100 Subject: [PATCH 068/153] private implementation of cost calculation --- include/algorithms/public/DTW.hpp | 47 +++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 06654ef3f..664c250d1 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -29,55 +29,72 @@ namespace algorithm { template class DTW { - using Matrix = Eigen::Matrix; - using Vector = Eigen::Matrix; - using Array = Eigen::Array; + using Matrix = Eigen::Matrix; + using Vector = Eigen::Matrix; + using Array = Eigen::Array; + using PathType = Eigen::Matrix; public: explicit DTW() = default; ~DTW() = default; static dataType process(FluidTensorView x1, - FluidTensorView x2, index p = 2) + FluidTensorView x2, + index p = 2) { Matrix distanceMetrics(x1.rows(), x2.rows()); + + return calculateDistanceMetrics( + _impl::asEigen(x1), + _impl::asEigen(x2) + distanceMetrics, + p + ); + } + +private: + static dataType calculateDistanceMetrics(Eigen::Ref x1, + Eigen::Ref x2, + Eigen::Ref distance, index p) + { + distance.conservativeResize(x1.rows(), x2.rows()); // simple brute force DTW is very inefficient, see FastDTW for (index i = 0; i < x1.rows(); i++) { for (index j = 0; j < x2.rows(); j++) { - Array x1i = _impl::asEigen(x1.row(i)); - Array x2j = _impl::asEigen(x2.row(j)); + Array x1i = x1.row(i); + Array x2j = x2.row(j); - distanceMetrics(i, j) = differencePNormToTheP(x1i, x2j, p); + distance(i, j) = differencePNormToTheP(x1i, x2j, p); if (i > 0 || j > 0) { dataType minimum = std::numeric_limits::max(); - if (i > 0 && j > 0) - minimum = std::min(minimum, distanceMetrics(i-1, j-1)); + if (i > 0 && j > 0) + minimum = std::min(minimum, distance(i-1, j-1)); if (i > 0) - minimum = std::min(minimum, distanceMetrics(i-1, j )); + minimum = std::min(minimum, distance(i-1, j )); if (j > 0) - minimum = std::min(minimum, distanceMetrics(i , j-1)); + minimum = std::min(minimum, distance(i , j-1)); - distanceMetrics(i, j) += minimum; + distance(i, j) += minimum; } } } - return std::pow(distanceMetrics(x1.rows() - 1, x2.rows() - 1), 1.0 / p); + return std::pow(distance(x1.rows() - 1, x2.rows() - 1), 1.0 / p); } -private: // P-Norm of the difference vector // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) // i.e., the 2-norm of a vector is the euclidian distance from the origin // the 1-norm is the sum of the absolute value of the elements // To the power P since we'll be summing multiple Norms together and they // can combine into a single norm if you calculate the norm of multiple norms (normception) - inline static dataType differencePNormToTheP(const Eigen::Ref& v1, const Eigen::Ref& v2, index p) + inline static dataType differencePNormToTheP(const Eigen::Ref& v1, + const Eigen::Ref& v2, index p) { // assert(v1.size() == v2.size()); return (v1.array() - v2.array()).abs().pow(p).sum(); From 740dbe8af68d40cb746c9bb196b8e876a0782626 Mon Sep 17 00:00:00 2001 From: lewardo Date: Sat, 26 Aug 2023 16:22:26 +0100 Subject: [PATCH 069/153] make algorithm stateful for consistency --- include/algorithms/public/DTW.hpp | 119 ++++++++++++++++------- include/clients/nrt/DTWClient.hpp | 21 ++-- include/clients/nrt/DataSeriesClient.hpp | 5 +- include/data/FluidDataSeries.hpp | 2 + 4 files changed, 101 insertions(+), 46 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 664c250d1..cc3cc47ce 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -26,75 +26,126 @@ namespace algorithm { // debt of gratitude to the wonderful article on https://rtavenar.github.io/blog/dtw.html // a better explanation of DTW than any other algorithm explanation I've seen -template class DTW { - using Matrix = Eigen::Matrix; - using Vector = Eigen::Matrix; - using Array = Eigen::Array; - using PathType = Eigen::Matrix; - public: explicit DTW() = default; ~DTW() = default; - static dataType process(FluidTensorView x1, - FluidTensorView x2, - index p = 2) + void init() const {} + void clear() { mCalculated = false; } + + constexpr index size() const { return 0; } + constexpr index dims() const { return 0; } + constexpr index initialized() const { return true; } + + double process(InputRealMatrixView x1, + InputRealMatrixView x2, + index p, + Allocator& alloc = FluidDefaultAllocator()) { - Matrix distanceMetrics(x1.rows(), x2.rows()); - - return calculateDistanceMetrics( - _impl::asEigen(x1), - _impl::asEigen(x2) - distanceMetrics, - p - ); + mCalculated = true; + + return calculateDistanceMetrics(x1, x2, p, alloc);; } - -private: - static dataType calculateDistanceMetrics(Eigen::Ref x1, - Eigen::Ref x2, - Eigen::Ref distance, index p) + + // bool getPath(InputRealMatrixView x1, + // InputRealMatrixView x2, + // RealMatrixView path, + // index p = 2, + // Allocator& alloc = FluidDefaultAllocator()) + // { + // calculateDistanceMetrics(x1, x2, p, alloc); + // return getPath(path); + // } + + // bool getPath(RealMatrixView to) + // { + // if (!mCalculated) return false; + + // index n = 0; + // index maxPathLength = mDistanceMetrics.rows() + // + mDistanceMetrics.cols() - 1; + // RealMatrix path(maxPathLength, 2); + + // for (index i = mDistanceMetrics.rows(), j = mDistanceMetrics.cols(); + // i > 0 || j > 0; ++n) + // { + // path.row(n) <<= {(double) i, (double) j}; + + // // if at one end of the matrix just go along that edge + // if (i == 0 && (--j, true)) continue; + // if (j == 0 && (--i, true)) continue; + + // double upVal = mDistanceMetrics(i-1, j ); + // double leftVal = mDistanceMetrics(i , j-1); + // double diagVal = mDistanceMetrics(i-1, j-1); + + // double minVal = std::min(std::min(upVal, leftVal), diagVal); + + // if (minVal == upVal) --i; + // else if (minVal == leftVal) --j; + // else if (minVal == upVal) (--i, --j); + // else return false; + // } + + // path.row(n++) <<= {0, 0}; + + // to <<= path; + // return true; + // } + + double calculateDistanceMetrics(InputRealMatrixView x1, + InputRealMatrixView x2, + index p, Allocator& alloc) { - distance.conservativeResize(x1.rows(), x2.rows()); + ScopedEigenMap x1r(x1.cols(), alloc), + x2r(x2.cols(), alloc); + + mDistanceMetrics.resize(x1.rows(), x2.rows()); + // simple brute force DTW is very inefficient, see FastDTW for (index i = 0; i < x1.rows(); i++) { for (index j = 0; j < x2.rows(); j++) { - Array x1i = x1.row(i); - Array x2j = x2.row(j); + x1r = _impl::asEigen(x1.row(i)); + x2r = _impl::asEigen(x2.row(j)); - distance(i, j) = differencePNormToTheP(x1i, x2j, p); + mDistanceMetrics(i, j) = differencePNormToTheP(x1r, x2r, p); if (i > 0 || j > 0) { - dataType minimum = std::numeric_limits::max(); + double minimum = std::numeric_limits::max(); if (i > 0 && j > 0) - minimum = std::min(minimum, distance(i-1, j-1)); + minimum = std::min(minimum, mDistanceMetrics(i-1, j-1)); if (i > 0) - minimum = std::min(minimum, distance(i-1, j )); + minimum = std::min(minimum, mDistanceMetrics(i-1, j )); if (j > 0) - minimum = std::min(minimum, distance(i , j-1)); + minimum = std::min(minimum, mDistanceMetrics(i , j-1)); - distance(i, j) += minimum; + mDistanceMetrics(i, j) += minimum; } } } - return std::pow(distance(x1.rows() - 1, x2.rows() - 1), 1.0 / p); + return std::pow(mDistanceMetrics(x1.rows() - 1, x2.rows() - 1), 1.0 / p); } +private: + RealMatrix mDistanceMetrics; + bool mCalculated {false}; + // P-Norm of the difference vector // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) // i.e., the 2-norm of a vector is the euclidian distance from the origin // the 1-norm is the sum of the absolute value of the elements // To the power P since we'll be summing multiple Norms together and they // can combine into a single norm if you calculate the norm of multiple norms (normception) - inline static dataType differencePNormToTheP(const Eigen::Ref& v1, - const Eigen::Ref& v2, index p) + inline static double differencePNormToTheP(const Eigen::Ref& v1, + const Eigen::Ref& v2, + index p) { // assert(v1.size() == v2.size()); return (v1.array() - v2.array()).abs().pow(p).sum(); diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 870e49cfb..3b7da4ed0 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -10,6 +10,7 @@ under the European Union’s Horizon 2020 research and innovation programme #pragma once +#include "DataClient.hpp" #include "DataSetClient.hpp" #include "DataSeriesClient.hpp" #include "NRTClient.hpp" @@ -27,7 +28,8 @@ constexpr auto DTWParams = defineParameters( class DTWClient : public FluidBaseClient, OfflineIn, OfflineOut, - ModelObject + ModelObject, + public DataClient { enum { kName, kPNorm }; @@ -66,7 +68,7 @@ class DTWClient : public FluidBaseClient, } MessageResult cost(InputDataSeriesClientRef dataseriesClient, - string id1, string id2) const + string id1, string id2) { auto dataseriesClientPtr = dataseriesClient.get().lock(); if (!dataseriesClientPtr) return Error(NoDataSet); @@ -82,10 +84,7 @@ class DTWClient : public FluidBaseClient, InputRealMatrixView series1 = srcDataSeries.getSeries(id1), series2 = srcDataSeries.getSeries(id2); - double cost = algorithm::DTW::process(series1, series2, - get()); - - return cost; + return mAlgorithm.process(series1, series2, get()); } MessageResult bufCost(InputBufferPtr data1, InputBufferPtr data2) @@ -97,11 +96,13 @@ class DTWClient : public FluidBaseClient, if (!buf1.exists() || !buf2.exists()) return Error(InvalidBuffer); if (buf1.numChans() != buf2.numChans()) return Error(WrongPointSize); - double cost = algorithm::DTW::process(buf1.allFrames().transpose(), - buf2.allFrames().transpose(), - get()); + RealMatrix buf1frames(buf1.numFrames(), buf1.numChans()), + buf2frames(buf2.numFrames(), buf2.numChans()); + + buf1frames <<= buf1.allFrames().transpose(); + buf2frames <<= buf2.allFrames().transpose(); - return cost; + return mAlgorithm.process(buf1frames, buf2frames, get()); } static auto getMessageDescriptors() diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index ea352bae0..19a26fc4d 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -364,9 +364,10 @@ class DataSeriesClient : public FluidBaseClient, return ds; } - double distance(FluidTensorView x1, FluidTensorView x2, index p) const + double distance(InputRealMatrixView x1, InputRealMatrixView x2, index p) const { - return algorithm::DTW::process(x1, x2, p); + algorithm::DTW dtw; + return dtw.process(x1, x2, p); } }; diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index dea7b2ed8..f321b7c3a 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -8,6 +8,8 @@ #include #include + +// TODO:: REMOVE TEMPLATE THINGY AND TURN IT INTO CONSISTNE ALLOCATION OF MATRIX namespace fluid { template From ce0efe865903d2a066b8e36beb67abd18565e289 Mon Sep 17 00:00:00 2001 From: lewardo Date: Mon, 28 Aug 2023 09:17:07 +0100 Subject: [PATCH 070/153] add `toBuffer` and `fromBuffer` message aliases --- include/clients/nrt/DataSeriesClient.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index a2d5a30cd..3cdaedfba 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -291,6 +291,8 @@ class DataSeriesClient : public FluidBaseClient, makeMessage("clear", &DataSeriesClient::clear), makeMessage("write", &DataSeriesClient::write), makeMessage("read", &DataSeriesClient::read), + makeMessage("toBuffer", &DataSeriesClient::getSeries), + makeMessage("fromBuffer", &DataSeriesClient::setSeries), makeMessage("getIds", &DataSeriesClient::getIds), makeMessage("getDataSet", &DataSeriesClient::getDataSet) ); From 8c71e918eb7f6754569a3dde88d1b1cc33c640f8 Mon Sep 17 00:00:00 2001 From: lewardo Date: Mon, 28 Aug 2023 10:39:36 +0100 Subject: [PATCH 071/153] run clang-format --- include/algorithms/public/DTW.hpp | 131 +++++++++++++------------- include/algorithms/public/FastDTW.hpp | 37 +++----- include/clients/nrt/DTWClient.hpp | 24 +++-- 3 files changed, 91 insertions(+), 101 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index cc3cc47ce..b106c591f 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -13,9 +13,9 @@ under the European Union’s Horizon 2020 research and innovation programme #include "../util/FluidEigenMappings.hpp" #include "../../data/FluidDataSet.hpp" #include "../../data/FluidIndex.hpp" +#include "../../data/FluidMemory.hpp" #include "../../data/FluidTensor.hpp" #include "../../data/TensorTypes.hpp" -#include "../../data/FluidMemory.hpp" #include #include @@ -23,31 +23,41 @@ namespace fluid { namespace algorithm { -// debt of gratitude to the wonderful article on https://rtavenar.github.io/blog/dtw.html -// a better explanation of DTW than any other algorithm explanation I've seen +// debt of gratitude to the wonderful article on +// https://rtavenar.github.io/blog/dtw.html a better explanation of DTW than any +// other algorithm explanation I've seen class DTW { public: - explicit DTW() = default; - ~DTW() = default; + explicit DTW() = default; + ~DTW() = default; - void init() const {} - void clear() { mCalculated = false; } + void init() const {} + void clear() { mCalculated = false; } - constexpr index size() const { return 0; } - constexpr index dims() const { return 0; } - constexpr index initialized() const { return true; } + constexpr index size() const { return 0; } + constexpr index dims() const { return 0; } + constexpr index initialized() const { return true; } - double process(InputRealMatrixView x1, - InputRealMatrixView x2, - index p, - Allocator& alloc = FluidDefaultAllocator()) - { - mCalculated = true; + double process(InputRealMatrixView x1, InputRealMatrixView x2, index p, + Allocator& alloc = FluidDefaultAllocator()) + { + mCalculated = true; - return calculateDistanceMetrics(x1, x2, p, alloc);; - } + return calculateDistanceMetrics(x1, x2, p, alloc); + ; + } + + + double calculateDistanceMetrics(InputRealMatrixView x1, + InputRealMatrixView x2, index p, + Allocator& alloc) + { + ScopedEigenMap x1r(x1.cols(), alloc), + x2r(x2.cols(), alloc); + + mDistanceMetrics.resize(x1.rows(), x2.rows()); // bool getPath(InputRealMatrixView x1, // InputRealMatrixView x2, @@ -94,62 +104,51 @@ class DTW // to <<= path; // return true; // } - - double calculateDistanceMetrics(InputRealMatrixView x1, - InputRealMatrixView x2, - index p, Allocator& alloc) + // simple brute force DTW is very inefficient, see FastDTW + for (index i = 0; i < x1.rows(); i++) { - ScopedEigenMap x1r(x1.cols(), alloc), - x2r(x2.cols(), alloc); + for (index j = 0; j < x2.rows(); j++) + { + x1r = _impl::asEigen(x1.row(i)); + x2r = _impl::asEigen(x2.row(j)); - mDistanceMetrics.resize(x1.rows(), x2.rows()); + mDistanceMetrics(i, j) = differencePNormToTheP(x1r, x2r, p); - // simple brute force DTW is very inefficient, see FastDTW - for (index i = 0; i < x1.rows(); i++) + if (i > 0 || j > 0) { - for (index j = 0; j < x2.rows(); j++) - { - x1r = _impl::asEigen(x1.row(i)); - x2r = _impl::asEigen(x2.row(j)); - - mDistanceMetrics(i, j) = differencePNormToTheP(x1r, x2r, p); - - if (i > 0 || j > 0) - { - double minimum = std::numeric_limits::max(); - - if (i > 0 && j > 0) - minimum = std::min(minimum, mDistanceMetrics(i-1, j-1)); - if (i > 0) - minimum = std::min(minimum, mDistanceMetrics(i-1, j )); - if (j > 0) - minimum = std::min(minimum, mDistanceMetrics(i , j-1)); - - mDistanceMetrics(i, j) += minimum; - } - } - } + double minimum = std::numeric_limits::max(); - return std::pow(mDistanceMetrics(x1.rows() - 1, x2.rows() - 1), 1.0 / p); + if (i > 0 && j > 0) + minimum = std::min(minimum, mDistanceMetrics(i - 1, j - 1)); + if (i > 0) minimum = std::min(minimum, mDistanceMetrics(i - 1, j)); + if (j > 0) minimum = std::min(minimum, mDistanceMetrics(i, j - 1)); + + mDistanceMetrics(i, j) += minimum; + } + } } + return std::pow(mDistanceMetrics(x1.rows() - 1, x2.rows() - 1), 1.0 / p); + } + private: - RealMatrix mDistanceMetrics; - bool mCalculated {false}; - - // P-Norm of the difference vector - // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) - // i.e., the 2-norm of a vector is the euclidian distance from the origin - // the 1-norm is the sum of the absolute value of the elements - // To the power P since we'll be summing multiple Norms together and they - // can combine into a single norm if you calculate the norm of multiple norms (normception) - inline static double differencePNormToTheP(const Eigen::Ref& v1, - const Eigen::Ref& v2, - index p) - { - // assert(v1.size() == v2.size()); - return (v1.array() - v2.array()).abs().pow(p).sum(); - } + RealMatrix mDistanceMetrics; + bool mCalculated{false}; + + // P-Norm of the difference vector + // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) + // i.e., the 2-norm of a vector is the euclidian distance from the origin + // the 1-norm is the sum of the absolute value of the elements + // To the power P since we'll be summing multiple Norms together and they + // can combine into a single norm if you calculate the norm of multiple norms + // (normception) + inline static double + differencePNormToTheP(const Eigen::Ref& v1, + const Eigen::Ref& v2, index p) + { + // assert(v1.size() == v2.size()); + return (v1.array() - v2.array()).abs().pow(p).sum(); + } }; } // namespace algorithm diff --git a/include/algorithms/public/FastDTW.hpp b/include/algorithms/public/FastDTW.hpp index 64dfadd7b..2ec2374fa 100644 --- a/include/algorithms/public/FastDTW.hpp +++ b/include/algorithms/public/FastDTW.hpp @@ -13,9 +13,9 @@ under the European Union’s Horizon 2020 research and innovation programme #include "../util/FluidEigenMappings.hpp" #include "../../data/FluidDataSet.hpp" #include "../../data/FluidIndex.hpp" +#include "../../data/FluidMemory.hpp" #include "../../data/FluidTensor.hpp" #include "../../data/TensorTypes.hpp" -#include "../../data/FluidMemory.hpp" #include #include @@ -30,36 +30,29 @@ namespace algorithm { class FastDTW { - using MatrixXd = Eigen::MatrixXd; - using VectorXd = Eigen::VectorXd; - using ArrayXd = Eigen::ArrayXd; + using MatrixXd = Eigen::MatrixXd; + using VectorXd = Eigen::VectorXd; + using ArrayXd = Eigen::ArrayXd; public: - explicit FastDTW() = default; - ~FastDTW() = default; + explicit FastDTW() = default; + ~FastDTW() = default; - // functions so the DataClient doesnt have freak out - void init() - { - mInitialised = true; - } + // functions so the DataClient doesnt have freak out + void init() { mInitialised = true; } - void clear() const {} + void clear() const {} - index size() const { return mConstraintSize; } - index dims() const { return mConstraintSize; } + index size() const { return mConstraintSize; } + index dims() const { return mConstraintSize; } - bool initialized() const { return mInitialised; } + bool initialized() const { return mInitialised; } - void process() const - { - - } + void process() const {} private: - bool mInitialised {false}; - index mConstraintSize {8}; - + bool mInitialised{false}; + index mConstraintSize{8}; }; } // namespace algorithm diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 3b7da4ed0..b475171c1 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -11,8 +11,8 @@ under the European Union’s Horizon 2020 research and innovation programme #pragma once #include "DataClient.hpp" -#include "DataSetClient.hpp" #include "DataSeriesClient.hpp" +#include "DataSetClient.hpp" #include "NRTClient.hpp" #include "../../algorithms/public/DTW.hpp" #include @@ -67,7 +67,7 @@ class DTWClient : public FluidBaseClient, return {}; } - MessageResult cost(InputDataSeriesClientRef dataseriesClient, + MessageResult cost(InputDataSeriesClientRef dataseriesClient, string id1, string id2) { auto dataseriesClientPtr = dataseriesClient.get().lock(); @@ -76,14 +76,13 @@ class DTWClient : public FluidBaseClient, auto srcDataSeries = dataseriesClientPtr->getDataSeries(); if (srcDataSeries.size() == 0) return Error(EmptyDataSet); - index i1 = srcDataSeries.getIndex(id1), - i2 = srcDataSeries.getIndex(id2); + index i1 = srcDataSeries.getIndex(id1), i2 = srcDataSeries.getIndex(id2); if (i1 < 0 || i2 < 0) return Error(PointNotFound); InputRealMatrixView series1 = srcDataSeries.getSeries(id1), series2 = srcDataSeries.getSeries(id2); - + return mAlgorithm.process(series1, series2, get()); } @@ -94,10 +93,11 @@ class DTWClient : public FluidBaseClient, BufferAdaptor::ReadAccess buf1(data1.get()), buf2(data2.get()); if (!buf1.exists() || !buf2.exists()) return Error(InvalidBuffer); - if (buf1.numChans() != buf2.numChans()) return Error(WrongPointSize); + if (buf1.numChans() != buf2.numChans()) + return Error(WrongPointSize); - RealMatrix buf1frames(buf1.numFrames(), buf1.numChans()), - buf2frames(buf2.numFrames(), buf2.numChans()); + RealMatrix buf1frames(buf1.numFrames(), buf1.numChans()), + buf2frames(buf2.numFrames(), buf2.numChans()); buf1frames <<= buf1.allFrames().transpose(); buf2frames <<= buf2.allFrames().transpose(); @@ -107,16 +107,14 @@ class DTWClient : public FluidBaseClient, static auto getMessageDescriptors() { - return defineMessages( - makeMessage("cost", &DTWClient::cost), - makeMessage("bufCost", &DTWClient::bufCost) - ); + return defineMessages(makeMessage("cost", &DTWClient::cost), + makeMessage("bufCost", &DTWClient::bufCost)); } }; using DTWRef = SharedClientRef; -} // namespace DTW +} // namespace dtw using NRTThreadedDTWClient = NRTThreadingAdaptor; From 54d32818d47addf4339d3de904379285fa7a6b78 Mon Sep 17 00:00:00 2001 From: lewardo Date: Mon, 28 Aug 2023 11:52:52 +0100 Subject: [PATCH 072/153] `knearestdist` message on `dataseries` with `dtw` and clang-format --- include/clients/nrt/DataSeriesClient.hpp | 174 +++++++++++++++-------- include/data/FluidDataSeries.hpp | 141 ++++++++++-------- 2 files changed, 194 insertions(+), 121 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 19a26fc4d..e96f1cd93 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -10,12 +10,12 @@ under the European Union’s Horizon 2020 research and innovation programme #pragma once #include "DataClient.hpp" -#include "LabelSetClient.hpp" #include "DataSetClient.hpp" +#include "LabelSetClient.hpp" #include "NRTClient.hpp" #include "../common/SharedClientUtils.hpp" -#include "../../algorithms/public/DataSetIdSequence.hpp" #include "../../algorithms/public/DTW.hpp" +#include "../../algorithms/public/DataSetIdSequence.hpp" #include "../../data/FluidDataSeries.hpp" #include #include @@ -27,13 +27,13 @@ namespace dataseries { enum { kName }; constexpr auto DataSeriesParams = defineParameters( - StringParam>("name", "Name of the DataSeries") -); + StringParam>("name", "Name of the DataSeries")); -class DataSeriesClient : public FluidBaseClient, - OfflineIn, - OfflineOut, - public DataClient> +class DataSeriesClient + : public FluidBaseClient, + OfflineIn, + OfflineOut, + public DataClient> { public: using string = std::string; @@ -76,9 +76,13 @@ class DataSeriesClient : public FluidBaseClient, if (mAlgorithm.size() == 0) { - if (mAlgorithm.dims() != buf.numFrames()) mAlgorithm = DataSeries(buf.numFrames()); + if (mAlgorithm.dims() != buf.numFrames()) + mAlgorithm = DataSeries(buf.numFrames()); + } + else if (buf.numFrames() != mAlgorithm.dims()) + { + return Error(WrongPointSize); } - else if (buf.numFrames() != mAlgorithm.dims()) { return Error(WrongPointSize); } mAlgorithm.addFrame(id, buf.samps(0, mAlgorithm.dims(), 0)); @@ -95,12 +99,17 @@ class DataSeriesClient : public FluidBaseClient, if (mAlgorithm.size() == 0) { - if (mAlgorithm.dims() != buf.numChans()) mAlgorithm = DataSeries(buf.numChans()); + if (mAlgorithm.dims() != buf.numChans()) + mAlgorithm = DataSeries(buf.numChans()); + } + else if (buf.numChans() != mAlgorithm.dims()) + { + return Error(WrongPointSize); } - else if (buf.numChans() != mAlgorithm.dims()) { return Error(WrongPointSize); } - return mAlgorithm.addSeries(id, buf.allFrames().transpose()) - ? OK() : Error(DuplicateIdentifier); + return mAlgorithm.addSeries(id, buf.allFrames().transpose()) + ? OK() + : Error(DuplicateIdentifier); } MessageResult getFrame(string id, index time, BufferPtr data) const @@ -133,7 +142,8 @@ class DataSeriesClient : public FluidBaseClient, BufferAdaptor::Access buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); - Result resizeResult = buf.resize(mAlgorithm.getNumFrames(id), mAlgorithm.dims(), buf.sampleRate()); + Result resizeResult = buf.resize(mAlgorithm.getNumFrames(id), + mAlgorithm.dims(), buf.sampleRate()); if (!resizeResult.ok()) return {resizeResult.status(), resizeResult.message()}; @@ -157,7 +167,9 @@ class DataSeriesClient : public FluidBaseClient, if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); - return mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)) ? OK() : Error(PointNotFound); + return mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)) + ? OK() + : Error(PointNotFound); } MessageResult updateSeries(string id, InputBufferPtr data) @@ -168,7 +180,9 @@ class DataSeriesClient : public FluidBaseClient, if (!buf.exists()) return Error(InvalidBuffer); if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); - return mAlgorithm.updateSeries(id, buf.allFrames().transpose()) ? OK() : Error(PointNotFound); + return mAlgorithm.updateSeries(id, buf.allFrames().transpose()) + ? OK() + : Error(PointNotFound); } MessageResult setFrame(string id, index time, InputBufferPtr data) @@ -180,7 +194,8 @@ class DataSeriesClient : public FluidBaseClient, if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); - bool result = mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)); + bool result = + mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)); if (result) return OK(); } @@ -213,8 +228,9 @@ class DataSeriesClient : public FluidBaseClient, return mAlgorithm.removeSeries(id) ? OK() : Error(PointNotFound); } - MessageResult merge(SharedClientRef dataseriesClient, - bool overwrite) + MessageResult + merge(SharedClientRef dataseriesClient, + bool overwrite) { auto dataseriesClientPtr = dataseriesClient.get().lock(); if (!dataseriesClientPtr) return Error(NoDataSet); @@ -224,12 +240,12 @@ class DataSeriesClient : public FluidBaseClient, if (srcDataSeries.pointSize() != mAlgorithm.pointSize()) return Error(WrongPointSize); - auto ids = srcDataSeries.getIds(); + auto ids = srcDataSeries.getIds(); for (index i = 0; i < srcDataSeries.size(); i++) { InputRealMatrixView series = srcDataSeries.getSeries(ids(i)); - bool added = mAlgorithm.addSeries(ids(i), series); + bool added = mAlgorithm.addSeries(ids(i), series); if (!added && overwrite) mAlgorithm.updateSeries(ids(i), series); } @@ -242,7 +258,7 @@ class DataSeriesClient : public FluidBaseClient, if (!destPtr) return Error(NoDataSet); destPtr->setDataSet(getSliceDataSet(time)); - if(destPtr->size() == 0) return Error(EmptyDataSet); + if (destPtr->size() == 0) return Error(EmptyDataSet); return OK(); } @@ -256,9 +272,8 @@ class DataSeriesClient : public FluidBaseClient, return OK(); } - MessageResult> kNearest(InputBufferPtr data, - index nNeighbours, - index p = 2) const + MessageResult> + kNearest(InputBufferPtr data, index nNeighbours, index p = 2) const { // check for nNeighbours > 0 and < size of DS if (nNeighbours > mAlgorithm.size()) @@ -267,7 +282,8 @@ class DataSeriesClient : public FluidBaseClient, BufferAdaptor::ReadAccess buf(data.get()); if (!buf.exists()) return Error>(InvalidBuffer); - if (buf.numChans() < mAlgorithm.dims()) return Error>(WrongPointSize); + if (buf.numChans() < mAlgorithm.dims()) + return Error>(WrongPointSize); FluidTensor series(buf.allFrames().transpose()); @@ -277,9 +293,10 @@ class DataSeriesClient : public FluidBaseClient, auto ds = mAlgorithm.getData(); - std::transform( - indices.begin(), indices.end(), distances.begin(), - [&series, &ds, &p, this](index i) { return distance(series, ds[i], p); }); + std::transform(indices.begin(), indices.end(), distances.begin(), + [&series, &ds, &p, this](index i) { + return distance(series, ds[i], p); + }); std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { return distances[asUnsigned(a)] < distances[asUnsigned(b)]; @@ -297,15 +314,55 @@ class DataSeriesClient : public FluidBaseClient, return labels; } + MessageResult> + kNearestDist(InputBufferPtr data, index nNeighbours, index p = 2) const + { + // check for nNeighbours > 0 and < size of DS + if (nNeighbours > mAlgorithm.size()) + return Error>(SmallDataSet); + if (nNeighbours <= 0) return Error>(SmallK); + + BufferAdaptor::ReadAccess buf(data.get()); + if (!buf.exists()) return Error>(InvalidBuffer); + if (buf.numChans() < mAlgorithm.dims()) + return Error>(WrongPointSize); + + FluidTensor series(buf.allFrames().transpose()); + + std::vector indices(asUnsigned(mAlgorithm.size())); + std::iota(indices.begin(), indices.end(), 0); + std::vector distances(asUnsigned(mAlgorithm.size())); + + auto ds = mAlgorithm.getData(); + + std::transform(indices.begin(), indices.end(), distances.begin(), + [&series, &ds, &p, this](index i) { + return distance(series, ds[i], p); + }); + + std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { + return distances[asUnsigned(a)] < distances[asUnsigned(b)]; + }); + + FluidTensor labels(nNeighbours); + + std::transform(indices.begin(), indices.begin() + nNeighbours, + labels.begin(), + [&distances](index i) { return distances[i]; }); + + return labels; + } + MessageResult clear() { mAlgorithm = DataSeries(0); return OK(); } - + MessageResult print() { - return "DataSeries " + std::string(get()) + ": " + mAlgorithm.print(); + return "DataSeries " + std::string(get()) + ": " + + mAlgorithm.print(); } const DataSeries getDataSeries() const { return mAlgorithm; } @@ -314,29 +371,29 @@ class DataSeriesClient : public FluidBaseClient, static auto getMessageDescriptors() { return defineMessages( - makeMessage("addFrame", &DataSeriesClient::addFrame), - makeMessage("addSeries", &DataSeriesClient::addSeries), - makeMessage("getFrame", &DataSeriesClient::getFrame), - makeMessage("getSeries", &DataSeriesClient::getSeries), - makeMessage("setFrame", &DataSeriesClient::setFrame), - makeMessage("setSeries", &DataSeriesClient::setSeries), - makeMessage("updateFrame", &DataSeriesClient::updateFrame), + makeMessage("addFrame", &DataSeriesClient::addFrame), + makeMessage("addSeries", &DataSeriesClient::addSeries), + makeMessage("getFrame", &DataSeriesClient::getFrame), + makeMessage("getSeries", &DataSeriesClient::getSeries), + makeMessage("setFrame", &DataSeriesClient::setFrame), + makeMessage("setSeries", &DataSeriesClient::setSeries), + makeMessage("updateFrame", &DataSeriesClient::updateFrame), makeMessage("updateSeries", &DataSeriesClient::updateSeries), - makeMessage("deleteFrame", &DataSeriesClient::deleteFrame), + makeMessage("deleteFrame", &DataSeriesClient::deleteFrame), makeMessage("deleteSeries", &DataSeriesClient::deleteSeries), - makeMessage("merge", &DataSeriesClient::merge), - makeMessage("dump", &DataSeriesClient::dump), - makeMessage("load", &DataSeriesClient::load), - makeMessage("print", &DataSeriesClient::print), - makeMessage("size", &DataSeriesClient::size), - makeMessage("cols", &DataSeriesClient::dims), - makeMessage("clear", &DataSeriesClient::clear), - makeMessage("write", &DataSeriesClient::write), - makeMessage("read", &DataSeriesClient::read), - makeMessage("getIds", &DataSeriesClient::getIds), - makeMessage("getDataSet", &DataSeriesClient::getDataSet), - makeMessage("kNearest", &DataSeriesClient::kNearest) - ); + makeMessage("merge", &DataSeriesClient::merge), + makeMessage("dump", &DataSeriesClient::dump), + makeMessage("load", &DataSeriesClient::load), + makeMessage("print", &DataSeriesClient::print), + makeMessage("size", &DataSeriesClient::size), + makeMessage("cols", &DataSeriesClient::dims), + makeMessage("clear", &DataSeriesClient::clear), + makeMessage("write", &DataSeriesClient::write), + makeMessage("read", &DataSeriesClient::read), + makeMessage("getIds", &DataSeriesClient::getIds), + makeMessage("getDataSet", &DataSeriesClient::getDataSet), + makeMessage("kNearest", &DataSeriesClient::kNearest), + makeMessage("kNearestDist", &DataSeriesClient::kNearestDist)); } private: @@ -352,13 +409,13 @@ class DataSeriesClient : public FluidBaseClient, DataSet getSliceDataSet(index time) const { - DataSet ds(mAlgorithm.dims()); + DataSet ds(mAlgorithm.dims()); decltype(mAlgorithm)::FrameType frame(mAlgorithm.dims()); - for(auto id : mAlgorithm.getIds()) + for (auto id : mAlgorithm.getIds()) { bool ret = mAlgorithm.getFrame(id, time, frame); - if(ret) ds.add(id, frame); + if (ret) ds.add(id, frame); } return ds; @@ -371,10 +428,11 @@ class DataSeriesClient : public FluidBaseClient, } }; -} // namespace dataset +} // namespace dataseries using DataSeriesClientRef = SharedClientRef; -using InputDataSeriesClientRef = SharedClientRef; +using InputDataSeriesClientRef = + SharedClientRef; using NRTThreadedDataSeriesClient = NRTThreadingAdaptor; diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index f321b7c3a..35173cb1e 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -26,9 +26,8 @@ class FluidDataSeries // e.g. FluidDataSet(2, 3) is a dataset of 2x3 tensors template ()>> - FluidDataSeries(Dims... dims) - : mData(0, FluidTensor(0, dims...)), - mDim(dims...) + FluidDataSeries(Dims... dims) + : mData(0, FluidTensor(0, dims...)), mDim(dims...) { static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); } @@ -44,8 +43,8 @@ class FluidDataSeries // Construct from existing tensors of ids and data points // (from convertible type for data, typically float -> double) template - FluidDataSeries(FluidTensorView ids, - std::vector> points, + FluidDataSeries(FluidTensorView ids, + std::vector> points, std::enable_if_t::value>* = nullptr) : mIds(ids), mData(points) { @@ -64,16 +63,14 @@ class FluidDataSeries mDim = FluidTensorSlice(dims...); return true; } - else - { - return false; - } + else { return false; } } template bool addFrame(idType const& id, FluidTensorView frame) { - static_assert(std::is_convertible::value, "Can't convert between types"); + static_assert(std::is_convertible::value, + "Can't convert between types"); assert(sameExtents(mDim, frame.descriptor())); auto pos = mIndex.find(id); @@ -81,7 +78,7 @@ class FluidDataSeries { FluidTensorView newPoint(frame); return addSeries(id, newPoint); - } + } mData[pos->second].resizeDim(0, 1); mData[pos->second].row(mData[pos->second].rows() - 1) <<= frame; @@ -92,9 +89,10 @@ class FluidDataSeries template bool addSeries(idType const& id, FluidTensorView series) { - static_assert(std::is_convertible::value, "Can't convert between types"); - - // dont crete another view + static_assert(std::is_convertible::value, + "Can't convert between types"); + + // dont crete another view auto result = mIndex.insert({id, mData.size()}); if (!result.second) return false; @@ -106,7 +104,8 @@ class FluidDataSeries return true; } - bool getFrame(idType const& id, index time, FluidTensorView frame) const + bool getFrame(idType const& id, index time, + FluidTensorView frame) const { auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; @@ -118,10 +117,11 @@ class FluidDataSeries return true; } - FluidTensorView getFrame(idType const& id, index time) const + FluidTensorView getFrame(idType const& id, + index time) const { auto pos = mIndex.find(id); - if(pos != mIndex.end()) + if (pos != mIndex.end()) { assert(time < mData[pos->second].rows()); return mData[pos->second].row(time); @@ -129,7 +129,8 @@ class FluidDataSeries else { return FluidTensorView{nullptr, 0, 0}; } } - bool getSeries(idType const& id, FluidTensorView series) const + bool getSeries(idType const& id, + FluidTensorView series) const { auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; @@ -150,8 +151,9 @@ class FluidDataSeries template bool updateFrame(idType const& id, index time, FluidTensorView frame) { - static_assert(std::is_convertible::value, "Can't convert between types"); - + static_assert(std::is_convertible::value, + "Can't convert between types"); + auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; @@ -164,13 +166,16 @@ class FluidDataSeries template bool updateSeries(idType const& id, FluidTensorView series) { - static_assert(std::is_convertible::value, "Can't convert between types"); + static_assert(std::is_convertible::value, + "Can't convert between types"); auto pos = mIndex.find(id); - if (pos == mIndex.end()) return false; - else + if (pos == mIndex.end()) + return false; + else { - mData[pos->second].resizeDim(0, series.rows() - mData[pos->second].rows()); + mData[pos->second].resizeDim(0, + series.rows() - mData[pos->second].rows()); mData[pos->second] <<= series; } @@ -185,8 +190,10 @@ class FluidDataSeries index current = pos->second; if (time >= mData[current].rows()) return false; - if(mData[current].rows() == 1) return removeSeries(id); - else mData[current].deleteRow(time); + if (mData[current].rows() == 1) + return removeSeries(id); + else + mData[current].deleteRow(time); return true; } @@ -212,43 +219,49 @@ class FluidDataSeries index getIndex(idType const& id) const { auto pos = mIndex.find(id); - if (pos == mIndex.end()) return -1; - else return pos->second; + if (pos == mIndex.end()) + return -1; + else + return pos->second; } - index getNumFrames(idType const& id) const + index getNumFrames(idType const& id) const { auto pos = mIndex.find(id); - if (pos == mIndex.end()) return -1; - else return mData[pos->second].rows(); + if (pos == mIndex.end()) + return -1; + else + return mData[pos->second].rows(); } - std::vector> getData() - { + std::vector> getData() + { std::vector> viewVec(mData.size()); - // hacky fix to force conversion of vector of tensors to vector of views of mData - // doesn't actually copy anything, it uses the FluidTensor ctor of FluidTensorView - // which creates a view/ref, so ends up creating what we want + // hacky fix to force conversion of vector of tensors to vector of views of + // mData doesn't actually copy anything, it uses the FluidTensor ctor of + // FluidTensorView which creates a view/ref, so ends up creating what we + // want std::copy(mData.begin(), mData.end(), std::back_inserter(viewVec)); - return viewVec; + return viewVec; } - const std::vector> getData() const - { + const std::vector> getData() const + { std::vector> viewVec; // hacky fix to force conversion of vector to views of mData - // doesn't actually copy anything, it uses the FluidTensor ctor of FluidTensorView - // which creates a view/ref, so ends up creating what we want + // doesn't actually copy anything, it uses the FluidTensor ctor of + // FluidTensorView which creates a view/ref, so ends up creating what we + // want std::copy(mData.cbegin(), mData.cend(), std::back_inserter(viewVec)); - return viewVec; + return viewVec; } - FluidTensorView getIds() { return mIds; } - FluidTensorView getIds() const { return mIds; } + FluidTensorView getIds() { return mIds; } + FluidTensorView getIds() const { return mIds; } index pointSize() const { return mDim.size; } index dims() const { return mDim.size; } @@ -256,7 +269,7 @@ class FluidDataSeries bool initialized() const { return (size() > 0); } std::string printFrame(FluidTensorView frame, - index maxCols) const + index maxCols) const { using namespace std; ostringstream result; @@ -268,7 +281,7 @@ class FluidDataSeries } else { - for (index c = 0; c < maxCols / 2; c++) + for (index c = 0; c < maxCols / 2; c++) result << setw(10) << setprecision(5) << frame(c); result << setw(10) << "..."; @@ -281,7 +294,7 @@ class FluidDataSeries } std::string printSeries(FluidTensorView series, - index maxFrames, index maxCols) const + index maxFrames, index maxCols) const { using namespace std; ostringstream result; @@ -289,44 +302,47 @@ class FluidDataSeries if (series.rows() < maxFrames) { for (index t = 0; t < series.rows(); t++) - result << setw(10) << "t" << t << ": " << printFrame(series.row(t), maxCols) - << endl; + result << setw(10) << "t" << t << ": " + << printFrame(series.row(t), maxCols) << endl; } else { for (index t = 0; t < maxFrames / 2; t++) - result << setw(10) << "t" << t << ": " << printFrame(series.row(t), maxCols) - << endl; + result << setw(10) << "t" << t << ": " + << printFrame(series.row(t), maxCols) << endl; result << setw(10) << "..." << std::endl; for (index t = maxFrames / 2; t > 0; t--) - result << setw(10) << "t" << (series.rows() - t) << ": " + result << setw(10) << "t" << (series.rows() - t) << ": " << printFrame(series.row(size() - t), maxCols) << endl; } return result.str(); } - std::string print(index maxRows = 6, index maxFrames = 6, index maxCols = 6) const + std::string print(index maxRows = 6, index maxFrames = 6, + index maxCols = 6) const { using namespace std; ostringstream result; if (size() == 0) return "{}"; - result << endl << "points: " << size() << endl << "frame size: " << pointSize() << endl; + result << endl + << "points: " << size() << endl + << "frame size: " << pointSize() << endl; if (size() < maxRows) { for (index r = 0; r < size(); r++) - result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) - << endl; + result << mIds(r) << ":" << endl + << printSeries(mData[r], maxFrames, maxCols) << endl; } else { for (index r = 0; r < maxRows / 2; r++) - result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) - << endl; + result << mIds(r) << ":" << endl + << printSeries(mData[r], maxFrames, maxCols) << endl; result << setw(10) << "⋮" << endl; @@ -343,13 +359,12 @@ class FluidDataSeries { assert(mIds.rows() == mData.size()); mDim = mData[0].cols(); - for (index i = 0; i < mIds.size(); i++) - mIndex.insert({mIds[i], i}); + for (index i = 0; i < mIds.size(); i++) mIndex.insert({mIds[i], i}); } - std::unordered_map mIndex; - FluidTensor mIds; + std::unordered_map mIndex; + FluidTensor mIds; std::vector> mData; - FluidTensorSlice mDim; // dimensions for one frame + FluidTensorSlice mDim; // dimensions for one frame }; } // namespace fluid From aaf04b12abeff38b9ce2fd156efecfb33e14ea90 Mon Sep 17 00:00:00 2001 From: lewardo Date: Mon, 28 Aug 2023 12:06:21 +0100 Subject: [PATCH 073/153] fix empty buffer crash --- include/clients/nrt/DTWClient.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index b475171c1..84c609e15 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -95,6 +95,8 @@ class DTWClient : public FluidBaseClient, if (!buf1.exists() || !buf2.exists()) return Error(InvalidBuffer); if (buf1.numChans() != buf2.numChans()) return Error(WrongPointSize); + if (buf1.numFrames() == 0 || buf2.numFrames() == 0) + return Error(EmptyBuffer); RealMatrix buf1frames(buf1.numFrames(), buf1.numChans()), buf2frames(buf2.numFrames(), buf2.numChans()); From acb3bbb9caa2f001a5b0e78e5f84727e87297a24 Mon Sep 17 00:00:00 2001 From: lewardo Date: Mon, 28 Aug 2023 13:35:25 +0100 Subject: [PATCH 074/153] made pNorm exponent a member --- include/algorithms/public/DTW.hpp | 75 ++++++++++++++++++++---- include/clients/nrt/DTWClient.hpp | 8 ++- include/clients/nrt/DataSeriesClient.hpp | 2 +- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index b106c591f..43a70dff1 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -33,26 +33,75 @@ class DTW explicit DTW() = default; ~DTW() = default; - void init() const {} + void init(index p = 2) { mPNorm = p; } void clear() { mCalculated = false; } - constexpr index size() const { return 0; } + index size() const { return mPNorm; } constexpr index dims() const { return 0; } constexpr index initialized() const { return true; } - double process(InputRealMatrixView x1, InputRealMatrixView x2, index p, + double process(InputRealMatrixView x1, InputRealMatrixView x2, Allocator& alloc = FluidDefaultAllocator()) { mCalculated = true; - return calculateDistanceMetrics(x1, x2, p, alloc); - ; + return calculateDistanceMetrics(x1, x2, alloc); } + bool getPath(InputRealMatrixView x1, InputRealMatrixView x2, + RealMatrixView path, Allocator& alloc = FluidDefaultAllocator()) + { + calculateDistanceMetrics(x1, x2, alloc); + return getPath(path); + } + + bool getPath(RealMatrixView path) + { + if (!mCalculated) return false; + + index n = 0, + maxPathLength = mDistanceMetrics.rows() + mDistanceMetrics.cols() - 1; + RealMatrix calculatedPath(2, maxPathLength); + + for (index i = mDistanceMetrics.rows() - 1, j = mDistanceMetrics.cols() - 1; + i > 0 || j > 0; ++n) + { + calculatedPath.col(n) <<= {(double) i, (double) j}; + + // if at one end of the matrix just go along that edge + if (i == 0 && (--j, true)) continue; + if (j == 0 && (--i, true)) continue; + + double upVal = mDistanceMetrics(i - 1, j); + double leftVal = mDistanceMetrics(i, j - 1); + double diagVal = mDistanceMetrics(i - 1, j - 1); + + double minVal = std::min(std::min(upVal, leftVal), diagVal); + + if (minVal == upVal) + --i; + else if (minVal == leftVal) + --j; + else if (minVal == upVal) + (--i, --j); + else + return false; + } + + calculatedPath.col(n++) <<= {0.0, 0.0}; + path <<= calculatedPath; + + return true; + } + + double calculateWindowedDistanceMetrics(InputRealMatrixView x1, + InputRealMatrixView x2, + InputRealMatrixView path, + Allocator& alloc) + {} double calculateDistanceMetrics(InputRealMatrixView x1, - InputRealMatrixView x2, index p, - Allocator& alloc) + InputRealMatrixView x2, Allocator& alloc) { ScopedEigenMap x1r(x1.cols(), alloc), x2r(x2.cols(), alloc); @@ -112,7 +161,7 @@ class DTW x1r = _impl::asEigen(x1.row(i)); x2r = _impl::asEigen(x2.row(j)); - mDistanceMetrics(i, j) = differencePNormToTheP(x1r, x2r, p); + mDistanceMetrics(i, j) = differencePNormToTheP(x1r, x2r); if (i > 0 || j > 0) { @@ -128,11 +177,13 @@ class DTW } } - return std::pow(mDistanceMetrics(x1.rows() - 1, x2.rows() - 1), 1.0 / p); + return std::pow(mDistanceMetrics(x1.rows() - 1, x2.rows() - 1), + 1.0 / mPNorm); } private: RealMatrix mDistanceMetrics; + index mPNorm{2}; bool mCalculated{false}; // P-Norm of the difference vector @@ -142,12 +193,12 @@ class DTW // To the power P since we'll be summing multiple Norms together and they // can combine into a single norm if you calculate the norm of multiple norms // (normception) - inline static double + inline double differencePNormToTheP(const Eigen::Ref& v1, - const Eigen::Ref& v2, index p) + const Eigen::Ref& v2) { // assert(v1.size() == v2.size()); - return (v1.array() - v2.array()).abs().pow(p).sum(); + return (v1.array() - v2.array()).abs().pow(mPNorm).sum(); } }; diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 84c609e15..6548b1bfa 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -83,7 +83,9 @@ class DTWClient : public FluidBaseClient, InputRealMatrixView series1 = srcDataSeries.getSeries(id1), series2 = srcDataSeries.getSeries(id2); - return mAlgorithm.process(series1, series2, get()); + mAlgorithm.init(get()); + + return mAlgorithm.process(series1, series2); } MessageResult bufCost(InputBufferPtr data1, InputBufferPtr data2) @@ -104,7 +106,9 @@ class DTWClient : public FluidBaseClient, buf1frames <<= buf1.allFrames().transpose(); buf2frames <<= buf2.allFrames().transpose(); - return mAlgorithm.process(buf1frames, buf2frames, get()); + mAlgorithm.init(get()); + + return mAlgorithm.process(buf1frames, buf2frames); } static auto getMessageDescriptors() diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index e96f1cd93..c261b1947 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -424,7 +424,7 @@ class DataSeriesClient double distance(InputRealMatrixView x1, InputRealMatrixView x2, index p) const { algorithm::DTW dtw; - return dtw.process(x1, x2, p); + return dtw.process(x1, x2); } }; From 1589b36b73ead7c65cbe7ee8a6a65ce0f8328375 Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 29 Aug 2023 13:47:27 +0100 Subject: [PATCH 075/153] remove `fastdtw` from this branch --- include/algorithms/public/FastDTW.hpp | 59 --------------------------- include/clients/nrt/FastDTWClient.hpp | 0 2 files changed, 59 deletions(-) delete mode 100644 include/algorithms/public/FastDTW.hpp delete mode 100644 include/clients/nrt/FastDTWClient.hpp diff --git a/include/algorithms/public/FastDTW.hpp b/include/algorithms/public/FastDTW.hpp deleted file mode 100644 index 2ec2374fa..000000000 --- a/include/algorithms/public/FastDTW.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* -Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) -Copyright University of Huddersfield. -Licensed under the BSD-3 License. -See license.md file in the project root for full license information. -This project has received funding from the European Research Council (ERC) -under the European Union’s Horizon 2020 research and innovation programme -(grant agreement No 725899). -*/ - -#pragma once - -#include "../util/FluidEigenMappings.hpp" -#include "../../data/FluidDataSet.hpp" -#include "../../data/FluidIndex.hpp" -#include "../../data/FluidMemory.hpp" -#include "../../data/FluidTensor.hpp" -#include "../../data/TensorTypes.hpp" -#include -#include - -namespace fluid { -namespace algorithm { - -// divide-and-conquer algorithm using the DTW algorithm -// and various constraints to reduce complexity -// down from O(N*M) (boooo) to O(N) (yayyy) -// props to the absolute units Stan Salvador and Philip Chan -// over at https://cs.fit.edu/~pkc/papers/tdm04.pdf - -class FastDTW -{ - using MatrixXd = Eigen::MatrixXd; - using VectorXd = Eigen::VectorXd; - using ArrayXd = Eigen::ArrayXd; - -public: - explicit FastDTW() = default; - ~FastDTW() = default; - - // functions so the DataClient doesnt have freak out - void init() { mInitialised = true; } - - void clear() const {} - - index size() const { return mConstraintSize; } - index dims() const { return mConstraintSize; } - - bool initialized() const { return mInitialised; } - - void process() const {} - -private: - bool mInitialised{false}; - index mConstraintSize{8}; -}; - -} // namespace algorithm -} // namespace fluid \ No newline at end of file diff --git a/include/clients/nrt/FastDTWClient.hpp b/include/clients/nrt/FastDTWClient.hpp deleted file mode 100644 index e69de29bb..000000000 From 2724898c922e723307bb9ba072c5e80e54b1a4db Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 29 Aug 2023 14:59:06 +0100 Subject: [PATCH 076/153] reorder member functions --- include/algorithms/public/DTW.hpp | 156 +++++++++--------------------- 1 file changed, 47 insertions(+), 109 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 43a70dff1..7999149da 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -17,6 +17,8 @@ under the European Union’s Horizon 2020 research and innovation programme #include "../../data/FluidTensor.hpp" #include "../../data/TensorTypes.hpp" #include +#include +#include #include namespace fluid { @@ -38,121 +40,75 @@ class DTW index size() const { return mPNorm; } constexpr index dims() const { return 0; } - constexpr index initialized() const { return true; } + constexpr index initialized() const { return mInitialized; } double process(InputRealMatrixView x1, InputRealMatrixView x2, Allocator& alloc = FluidDefaultAllocator()) { - mCalculated = true; - - return calculateDistanceMetrics(x1, x2, alloc); + return calculateDistanceMetrics(x1, x2, x1, alloc); } - bool getPath(InputRealMatrixView x1, InputRealMatrixView x2, - RealMatrixView path, Allocator& alloc = FluidDefaultAllocator()) +private: + RealMatrix mDistanceMetrics; + RealMatrix mPath; + index mPNorm{2}; + + bool mCalculated{false}; + const bool mInitialized{true}; + + // P-Norm of the difference vector + // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) + // i.e., the 2-norm of a vector is the euclidian distance from the origin + // the 1-norm is the sum of the absolute value of the elements + // To the power P since we'll be summing multiple Norms together and they + // can combine into a single norm if you calculate the norm of multiple norms + // (normception) + inline double + differencePNormToTheP(const Eigen::Ref& v1, + const Eigen::Ref& v2) { - calculateDistanceMetrics(x1, x2, alloc); - return getPath(path); + // assert(v1.size() == v2.size()); + return (v1.array() - v2.array()).abs().pow(mPNorm).sum(); } - bool getPath(RealMatrixView path) + static bool isValidPath(InputRealMatrixView p) { - if (!mCalculated) return false; + InputRealVectorView thisRow = p.row(0), nextRow = p.row(1); - index n = 0, - maxPathLength = mDistanceMetrics.rows() + mDistanceMetrics.cols() - 1; - RealMatrix calculatedPath(2, maxPathLength); + if (thisRow.size() != 2) return false; - for (index i = mDistanceMetrics.rows() - 1, j = mDistanceMetrics.cols() - 1; - i > 0 || j > 0; ++n) + for (index i = 0; i < p.rows() - 1; i++) { - calculatedPath.col(n) <<= {(double) i, (double) j}; - - // if at one end of the matrix just go along that edge - if (i == 0 && (--j, true)) continue; - if (j == 0 && (--i, true)) continue; - - double upVal = mDistanceMetrics(i - 1, j); - double leftVal = mDistanceMetrics(i, j - 1); - double diagVal = mDistanceMetrics(i - 1, j - 1); - - double minVal = std::min(std::min(upVal, leftVal), diagVal); - - if (minVal == upVal) - --i; - else if (minVal == leftVal) - --j; - else if (minVal == upVal) - (--i, --j); - else - return false; - } + thisRow = p.row(i); + nextRow = p.row(i + 1); - calculatedPath.col(n++) <<= {0.0, 0.0}; - path <<= calculatedPath; + if (nextRow.size() != 2) return false; + if (nextRow[0] < thisRow[0] || nextRow[1] < thisRow[1]) return false; + } return true; } - double calculateWindowedDistanceMetrics(InputRealMatrixView x1, - InputRealMatrixView x2, - InputRealMatrixView path, - Allocator& alloc) - {} + static bool isValidWindow(InputRealMatrixView w) + { + for (index i = 0; i < w.rows(); i++) + { + if (w.row(i)[0] >= w.row(i)[1]) return false; + } + + return true; + } double calculateDistanceMetrics(InputRealMatrixView x1, - InputRealMatrixView x2, Allocator& alloc) + InputRealMatrixView x2, + InputRealMatrixView window, + Allocator& alloc = FluidDefaultAllocator()) { ScopedEigenMap x1r(x1.cols(), alloc), x2r(x2.cols(), alloc); mDistanceMetrics.resize(x1.rows(), x2.rows()); - // bool getPath(InputRealMatrixView x1, - // InputRealMatrixView x2, - // RealMatrixView path, - // index p = 2, - // Allocator& alloc = FluidDefaultAllocator()) - // { - // calculateDistanceMetrics(x1, x2, p, alloc); - // return getPath(path); - // } - - // bool getPath(RealMatrixView to) - // { - // if (!mCalculated) return false; - - // index n = 0; - // index maxPathLength = mDistanceMetrics.rows() - // + mDistanceMetrics.cols() - 1; - // RealMatrix path(maxPathLength, 2); - - // for (index i = mDistanceMetrics.rows(), j = mDistanceMetrics.cols(); - // i > 0 || j > 0; ++n) - // { - // path.row(n) <<= {(double) i, (double) j}; - - // // if at one end of the matrix just go along that edge - // if (i == 0 && (--j, true)) continue; - // if (j == 0 && (--i, true)) continue; - - // double upVal = mDistanceMetrics(i-1, j ); - // double leftVal = mDistanceMetrics(i , j-1); - // double diagVal = mDistanceMetrics(i-1, j-1); - - // double minVal = std::min(std::min(upVal, leftVal), diagVal); - - // if (minVal == upVal) --i; - // else if (minVal == leftVal) --j; - // else if (minVal == upVal) (--i, --j); - // else return false; - // } - - // path.row(n++) <<= {0, 0}; - - // to <<= path; - // return true; - // } // simple brute force DTW is very inefficient, see FastDTW for (index i = 0; i < x1.rows(); i++) { @@ -177,29 +133,11 @@ class DTW } } + mCalculated = true; + return std::pow(mDistanceMetrics(x1.rows() - 1, x2.rows() - 1), 1.0 / mPNorm); } - -private: - RealMatrix mDistanceMetrics; - index mPNorm{2}; - bool mCalculated{false}; - - // P-Norm of the difference vector - // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) - // i.e., the 2-norm of a vector is the euclidian distance from the origin - // the 1-norm is the sum of the absolute value of the elements - // To the power P since we'll be summing multiple Norms together and they - // can combine into a single norm if you calculate the norm of multiple norms - // (normception) - inline double - differencePNormToTheP(const Eigen::Ref& v1, - const Eigen::Ref& v2) - { - // assert(v1.size() == v2.size()); - return (v1.array() - v2.array()).abs().pow(mPNorm).sum(); - } }; } // namespace algorithm From 14fccfc801c30f97c77341486be5272b60726602 Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 29 Aug 2023 14:59:42 +0100 Subject: [PATCH 077/153] remove window/path helper functions (`fastdtw` framework) --- include/algorithms/public/DTW.hpp | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 7999149da..eae6bfdf2 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -71,34 +71,6 @@ class DTW return (v1.array() - v2.array()).abs().pow(mPNorm).sum(); } - static bool isValidPath(InputRealMatrixView p) - { - InputRealVectorView thisRow = p.row(0), nextRow = p.row(1); - - if (thisRow.size() != 2) return false; - - for (index i = 0; i < p.rows() - 1; i++) - { - thisRow = p.row(i); - nextRow = p.row(i + 1); - - if (nextRow.size() != 2) return false; - if (nextRow[0] < thisRow[0] || nextRow[1] < thisRow[1]) return false; - } - - return true; - } - - static bool isValidWindow(InputRealMatrixView w) - { - for (index i = 0; i < w.rows(); i++) - { - if (w.row(i)[0] >= w.row(i)[1]) return false; - } - - return true; - } - double calculateDistanceMetrics(InputRealMatrixView x1, InputRealMatrixView x2, InputRealMatrixView window, From beb0ff16e048740516a22b419a92efa2c0b3cceb Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 29 Aug 2023 15:44:39 +0100 Subject: [PATCH 078/153] added Constraint object --- include/algorithms/public/DTW.hpp | 101 +++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 31 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index eae6bfdf2..7df40b486 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -25,12 +25,16 @@ namespace fluid { namespace algorithm { +enum class DTWConstraint { kUnconstrained, kIkatura, kSakoeChiba }; + // debt of gratitude to the wonderful article on // https://rtavenar.github.io/blog/dtw.html a better explanation of DTW than any // other algorithm explanation I've seen class DTW { + class Constraint; + public: explicit DTW() = default; ~DTW() = default; @@ -43,14 +47,48 @@ class DTW constexpr index initialized() const { return mInitialized; } double process(InputRealMatrixView x1, InputRealMatrixView x2, - Allocator& alloc = FluidDefaultAllocator()) + DTWConstraint c = DTWConstraint::kUnconstrained, + Allocator& alloc = FluidDefaultAllocator()) { - return calculateDistanceMetrics(x1, x2, x1, alloc); + Constraint constraint(c, x1.rows(), x2.rows()); + ScopedEigenMap x1r(x1.cols(), alloc), + x2r(x2.cols(), alloc); + + mDistanceMetrics.resize(x1.rows(), x2.rows()); + mDistanceMetrics.fill(std::numeric_limits::max()); + + // simple brute force DTW is very inefficient, see FastDTW + for (index i = 0; i < x1.rows(); i++) + { + for (index j = 0; j < x2.rows(); j++) + { + x1r = _impl::asEigen(x1.row(i)); + x2r = _impl::asEigen(x2.row(j)); + + mDistanceMetrics(i, j) = differencePNormToTheP(x1r, x2r); + + if (i > 0 || j > 0) + { + double minimum = std::numeric_limits::max(); + + if (i > 0) minimum = std::min(minimum, mDistanceMetrics(i - 1, j)); + if (j > 0) minimum = std::min(minimum, mDistanceMetrics(i, j - 1)); + if (i > 0 && j > 0) + minimum = std::min(minimum, mDistanceMetrics(i - 1, j - 1)); + + mDistanceMetrics(i, j) += minimum; + } + } + } + + mCalculated = true; + + return std::pow(mDistanceMetrics(x1.rows() - 1, x2.rows() - 1), + 1.0 / mPNorm); } private: RealMatrix mDistanceMetrics; - RealMatrix mPath; index mPNorm{2}; bool mCalculated{false}; @@ -71,45 +109,46 @@ class DTW return (v1.array() - v2.array()).abs().pow(mPNorm).sum(); } - double calculateDistanceMetrics(InputRealMatrixView x1, - InputRealMatrixView x2, - InputRealMatrixView window, - Allocator& alloc = FluidDefaultAllocator()) + class Constraint { - ScopedEigenMap x1r(x1.cols(), alloc), - x2r(x2.cols(), alloc); + Constraint(DTWConstraint c, index rows, index cols) + : mType{c}, mRows{rows}, mCols{cols} {}; - mDistanceMetrics.resize(x1.rows(), x2.rows()); + const index rowStart() const { return 0; }; + const index rowEnd() const { return mRows; }; - // simple brute force DTW is very inefficient, see FastDTW - for (index i = 0; i < x1.rows(); i++) + index colStart(index row) { - for (index j = 0; j < x2.rows(); j++) + switch (mType) { - x1r = _impl::asEigen(x1.row(i)); - x2r = _impl::asEigen(x2.row(j)); - - mDistanceMetrics(i, j) = differencePNormToTheP(x1r, x2r); + case DTWConstraint::kUnconstrained: return 0; - if (i > 0 || j > 0) - { - double minimum = std::numeric_limits::max(); + case DTWConstraint::kIkatura: break; - if (i > 0 && j > 0) - minimum = std::min(minimum, mDistanceMetrics(i - 1, j - 1)); - if (i > 0) minimum = std::min(minimum, mDistanceMetrics(i - 1, j)); - if (j > 0) minimum = std::min(minimum, mDistanceMetrics(i, j - 1)); + case DTWConstraint::kSakoeChiba: break; - mDistanceMetrics(i, j) += minimum; - } + default: return -1; } - } + }; - mCalculated = true; + index colEnd() + { + switch (mType) + { + case DTWConstraint::kUnconstrained: return mCols; - return std::pow(mDistanceMetrics(x1.rows() - 1, x2.rows() - 1), - 1.0 / mPNorm); - } + case DTWConstraint::kIkatura: break; + + case DTWConstraint::kSakoeChiba: break; + + default: return -1; + } + }; + + private: + DTWConstraint mType; + index mRows, mCols; + }; }; } // namespace algorithm From 1273cbf8852b8145451ebed5f7992e18cc6040b9 Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 29 Aug 2023 16:59:43 +0100 Subject: [PATCH 079/153] raster line range calculation --- include/algorithms/public/DTW.hpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 7df40b486..ec135ebd0 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -33,7 +33,7 @@ enum class DTWConstraint { kUnconstrained, kIkatura, kSakoeChiba }; class DTW { - class Constraint; + struct Constraint; public: explicit DTW() = default; @@ -50,17 +50,17 @@ class DTW DTWConstraint c = DTWConstraint::kUnconstrained, Allocator& alloc = FluidDefaultAllocator()) { - Constraint constraint(c, x1.rows(), x2.rows()); ScopedEigenMap x1r(x1.cols(), alloc), x2r(x2.cols(), alloc); + Constraint constraint(c, x1.rows(), x2.rows()); mDistanceMetrics.resize(x1.rows(), x2.rows()); mDistanceMetrics.fill(std::numeric_limits::max()); // simple brute force DTW is very inefficient, see FastDTW - for (index i = 0; i < x1.rows(); i++) + for (index i = constraint.rowStart(); i < constraint.rowEnd(); i++) { - for (index j = 0; j < x2.rows(); j++) + for (index j = constraint.colStart(i); j < constraint.colEnd(i); j++) { x1r = _impl::asEigen(x1.row(i)); x2r = _impl::asEigen(x2.row(j)); @@ -109,7 +109,7 @@ class DTW return (v1.array() - v2.array()).abs().pow(mPNorm).sum(); } - class Constraint + struct Constraint { Constraint(DTWConstraint c, index rows, index cols) : mType{c}, mRows{rows}, mCols{cols} {}; @@ -148,7 +148,22 @@ class DTW private: DTWConstraint mType; index mRows, mCols; - }; + + inline static index rasterLineMinY(float x1, float x2, float y1, float y2, + float x) + { + return y1 + (x - x1) * ((y2 - y1) / (x2 - x1)); + } + + inline static index rasterLineMaxY(float x1, float x2, float y1, float y2, + float x) + { + if (y2 + x1 > y1 + x2) + return y1 + (x - x1 + 1) * ((y2 - y1) / (x2 - x1)) - 1; + else + return y1 + (x - x1) * ((y2 - y1) / (x2 - x1)); + } + }; // struct Constraint }; } // namespace algorithm From 2ff212a1a24baf8717236b4652e88ad7a1b2c2f5 Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 29 Aug 2023 21:26:13 +0100 Subject: [PATCH 080/153] Sakoe-Chiba band constraint --- include/algorithms/public/DTW.hpp | 42 ++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index ec135ebd0..05e3f4190 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -47,20 +47,21 @@ class DTW constexpr index initialized() const { return mInitialized; } double process(InputRealMatrixView x1, InputRealMatrixView x2, - DTWConstraint c = DTWConstraint::kUnconstrained, + DTWConstraint constraint = DTWConstraint::kUnconstrained, + index constraintParam = 2, Allocator& alloc = FluidDefaultAllocator()) { ScopedEigenMap x1r(x1.cols(), alloc), x2r(x2.cols(), alloc); - Constraint constraint(c, x1.rows(), x2.rows()); + Constraint c(constraint, x1.rows(), x2.rows(), constraintParam); mDistanceMetrics.resize(x1.rows(), x2.rows()); mDistanceMetrics.fill(std::numeric_limits::max()); // simple brute force DTW is very inefficient, see FastDTW - for (index i = constraint.rowStart(); i < constraint.rowEnd(); i++) + for (index i = c.startRow(); i < c.endRow(); i++) { - for (index j = constraint.colStart(i); j < constraint.colEnd(i); j++) + for (index j = c.startCol(i); j < c.endCol(i); j++) { x1r = _impl::asEigen(x1.row(i)); x2r = _impl::asEigen(x2.row(j)); @@ -111,13 +112,14 @@ class DTW struct Constraint { - Constraint(DTWConstraint c, index rows, index cols) + Constraint(DTWConstraint c, index rows, index cols, index param) : mType{c}, mRows{rows}, mCols{cols} {}; - const index rowStart() const { return 0; }; - const index rowEnd() const { return mRows; }; + const index startRow() const { return 0; }; + const index endRow() const { return mRows; }; - index colStart(index row) + + index startCol(index row) { switch (mType) { @@ -125,13 +127,16 @@ class DTW case DTWConstraint::kIkatura: break; - case DTWConstraint::kSakoeChiba: break; + case DTWConstraint::kSakoeChiba: + index col = rasterLineMinY(mParam, -mParam, mRows - 1 + mParam, + mCols - 1 - mParam, row); + return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; default: return -1; } }; - index colEnd() + index endCol(index row) { switch (mType) { @@ -139,7 +144,10 @@ class DTW case DTWConstraint::kIkatura: break; - case DTWConstraint::kSakoeChiba: break; + case DTWConstraint::kSakoeChiba: + index col = rasterLineMaxY(-mParam, mParam, mRows - 1 - mParam, + mCols - 1 + mParam, row); + return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; default: return -1; } @@ -147,21 +155,21 @@ class DTW private: DTWConstraint mType; - index mRows, mCols; + index mRows, mCols, mParam; // mParam is either radius (SC) or gradient (Ik) - inline static index rasterLineMinY(float x1, float x2, float y1, float y2, + inline static index rasterLineMinY(float x1, float y1, float x2, float y2, float x) { - return y1 + (x - x1) * ((y2 - y1) / (x2 - x1)); + return std::round(y1 + (x - x1) * (y2 - y1) / (x2 - x1)); } - inline static index rasterLineMaxY(float x1, float x2, float y1, float y2, + inline static index rasterLineMaxY(float x1, float y1, float x2, float y2, float x) { if (y2 + x1 > y1 + x2) - return y1 + (x - x1 + 1) * ((y2 - y1) / (x2 - x1)) - 1; + return rasterLineMinY(x1, y1, x2, y2, x + 1) - 1; else - return y1 + (x - x1) * ((y2 - y1) / (x2 - x1)); + return rasterLineMinY(x1, y1, x2, y2, x); } }; // struct Constraint }; From 357410ec09f6cbcb7be70dc58193b8d1ca3009d8 Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 29 Aug 2023 21:34:07 +0100 Subject: [PATCH 081/153] Ikatura parallelogram constraint (WIP) --- include/algorithms/public/DTW.hpp | 38 ++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 05e3f4190..f57784e4f 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -125,7 +125,13 @@ class DTW { case DTWConstraint::kUnconstrained: return 0; - case DTWConstraint::kIkatura: break; + case DTWConstraint::kIkatura: + index colNorm = rasterLineMinY(mRows - 1, mCols - 1, mParam, row); + index colInv = rasterLineMinY(0, 0, 1 / mParam, row); + + index col = std::max(colNorm, colInv); + + return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; case DTWConstraint::kSakoeChiba: index col = rasterLineMinY(mParam, -mParam, mRows - 1 + mParam, @@ -140,9 +146,15 @@ class DTW { switch (mType) { - case DTWConstraint::kUnconstrained: return mCols; + case DTWConstraint::kUnconstrained: return mCols - 1; + + case DTWConstraint::kIkatura: + index colNorm = rasterLineMaxY(0, 0, mParam, row); + index colInv = rasterLineMaxY(mRows - 1, mCols - 1, 1 / mParam, row); - case DTWConstraint::kIkatura: break; + index col = std::min(colNorm, colInv); + + return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; case DTWConstraint::kSakoeChiba: index col = rasterLineMaxY(-mParam, mParam, mRows - 1 - mParam, @@ -157,19 +169,29 @@ class DTW DTWConstraint mType; index mRows, mCols, mParam; // mParam is either radius (SC) or gradient (Ik) + inline static index rasterLineMinY(float x1, float y1, float dydx, float x) + { + return std::round(y1 + (x - x1) * dydx); + } + inline static index rasterLineMinY(float x1, float y1, float x2, float y2, float x) { - return std::round(y1 + (x - x1) * (y2 - y1) / (x2 - x1)); + return rasterLineMinY(x1, y1, (y2 - y1) / (x2 - x1), x); + } + + inline static index rasterLineMaxY(float x1, float y1, float dydx, float x) + { + if (dydx > 1) + return rasterLineMinY(x1, y1, dydx, x + 1) - 1; + else + return rasterLineMinY(x1, y1, dydx, x); } inline static index rasterLineMaxY(float x1, float y1, float x2, float y2, float x) { - if (y2 + x1 > y1 + x2) - return rasterLineMinY(x1, y1, x2, y2, x + 1) - 1; - else - return rasterLineMinY(x1, y1, x2, y2, x); + return rasterLineMaxY(x1, y1, (y2 - y1) / (x2 - x1), x); } }; // struct Constraint }; From e9ac64bb6e483c7eaf78b7df6f57915d36e1c78d Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 29 Aug 2023 21:34:21 +0100 Subject: [PATCH 082/153] rename row/col functions --- include/algorithms/public/DTW.hpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index f57784e4f..cb76521d2 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -59,9 +59,9 @@ class DTW mDistanceMetrics.fill(std::numeric_limits::max()); // simple brute force DTW is very inefficient, see FastDTW - for (index i = c.startRow(); i < c.endRow(); i++) + for (index i = c.firstRow(); i <= c.lastRow(); i++) { - for (index j = c.startCol(i); j < c.endCol(i); j++) + for (index j = c.firstCol(i); j <= c.lastCol(i); j++) { x1r = _impl::asEigen(x1.row(i)); x2r = _impl::asEigen(x2.row(j)); @@ -115,11 +115,10 @@ class DTW Constraint(DTWConstraint c, index rows, index cols, index param) : mType{c}, mRows{rows}, mCols{cols} {}; - const index startRow() const { return 0; }; - const index endRow() const { return mRows; }; + const index firstRow() const { return 0; }; + const index lastRow() const { return mRows - 1; }; - - index startCol(index row) + index firstCol(index row) { switch (mType) { @@ -142,7 +141,7 @@ class DTW } }; - index endCol(index row) + index lastCol(index row) { switch (mType) { From b714b45c5fc3a2e6ee6af2f6cd00dd77465cdf6d Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 29 Aug 2023 21:49:16 +0100 Subject: [PATCH 083/153] add constraint interface --- include/algorithms/public/DTW.hpp | 12 ++++++++---- include/clients/nrt/DTWClient.hpp | 15 +++++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index cb76521d2..990420788 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -124,18 +124,20 @@ class DTW { case DTWConstraint::kUnconstrained: return 0; - case DTWConstraint::kIkatura: + case DTWConstraint::kIkatura: { index colNorm = rasterLineMinY(mRows - 1, mCols - 1, mParam, row); index colInv = rasterLineMinY(0, 0, 1 / mParam, row); index col = std::max(colNorm, colInv); return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; + } - case DTWConstraint::kSakoeChiba: + case DTWConstraint::kSakoeChiba: { index col = rasterLineMinY(mParam, -mParam, mRows - 1 + mParam, mCols - 1 - mParam, row); return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; + } default: return -1; } @@ -147,18 +149,20 @@ class DTW { case DTWConstraint::kUnconstrained: return mCols - 1; - case DTWConstraint::kIkatura: + case DTWConstraint::kIkatura: { index colNorm = rasterLineMaxY(0, 0, mParam, row); index colInv = rasterLineMaxY(mRows - 1, mCols - 1, 1 / mParam, row); index col = std::min(colNorm, colInv); return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; + } - case DTWConstraint::kSakoeChiba: + case DTWConstraint::kSakoeChiba: { index col = rasterLineMaxY(-mParam, mParam, mRows - 1 - mParam, mCols - 1 + mParam, row); return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; + } default: return -1; } diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 6548b1bfa..e8ae997e8 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -23,7 +23,10 @@ namespace dtw { constexpr auto DTWParams = defineParameters( StringParam>("name", "Name"), - LongParam("p", "LpNorm power (distance weighting)", 2, Min(1))); + LongParam("p", "LpNorm power (distance weighting)", 2, Min(1)), + EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", + "Sakoe-Chiba"), + LongParam("radius", "Constraint Value", 2, Min(0))); class DTWClient : public FluidBaseClient, OfflineIn, @@ -31,7 +34,7 @@ class DTWClient : public FluidBaseClient, ModelObject, public DataClient { - enum { kName, kPNorm }; + enum { kName, kPNorm, kConstraint, kRadius }; public: using string = std::string; @@ -85,7 +88,9 @@ class DTWClient : public FluidBaseClient, mAlgorithm.init(get()); - return mAlgorithm.process(series1, series2); + return mAlgorithm.process(series1, series2, + (algorithm::DTWConstraint) get(), + get()); } MessageResult bufCost(InputBufferPtr data1, InputBufferPtr data2) @@ -108,7 +113,9 @@ class DTWClient : public FluidBaseClient, mAlgorithm.init(get()); - return mAlgorithm.process(buf1frames, buf2frames); + return mAlgorithm.process(buf1frames, buf2frames, + (algorithm::DTWConstraint) get(), + get()); } static auto getMessageDescriptors() From 9dbc09f3893fa1ff3e49fffba4e060387fb4d59a Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 09:07:44 +0100 Subject: [PATCH 084/153] float param for fractional constraints --- include/algorithms/public/DTW.hpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 990420788..97458f142 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -112,7 +112,7 @@ class DTW struct Constraint { - Constraint(DTWConstraint c, index rows, index cols, index param) + Constraint(DTWConstraint c, index rows, index cols, float param) : mType{c}, mRows{rows}, mCols{cols} {}; const index firstRow() const { return 0; }; @@ -138,8 +138,6 @@ class DTW mCols - 1 - mParam, row); return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; } - - default: return -1; } }; @@ -163,27 +161,27 @@ class DTW mCols - 1 + mParam, row); return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; } - - default: return -1; } }; private: DTWConstraint mType; - index mRows, mCols, mParam; // mParam is either radius (SC) or gradient (Ik) + index mRows, mCols; + float mParam; // mParam is either radius (SC) or gradient (Ik) - inline static index rasterLineMinY(float x1, float y1, float dydx, float x) + inline static index rasterLineMinY(index x1, index y1, float dydx, index x) { return std::round(y1 + (x - x1) * dydx); } - inline static index rasterLineMinY(float x1, float y1, float x2, float y2, - float x) + inline static index rasterLineMinY(index x1, index y1, index x2, index y2, + index x) { - return rasterLineMinY(x1, y1, (y2 - y1) / (x2 - x1), x); + float dy = y2 - y1, dx = x2 - x1; + return rasterLineMinY(x1, y1, dy / dx, x); } - inline static index rasterLineMaxY(float x1, float y1, float dydx, float x) + inline static index rasterLineMaxY(index x1, index y1, float dydx, index x) { if (dydx > 1) return rasterLineMinY(x1, y1, dydx, x + 1) - 1; @@ -191,10 +189,11 @@ class DTW return rasterLineMinY(x1, y1, dydx, x); } - inline static index rasterLineMaxY(float x1, float y1, float x2, float y2, - float x) + inline static index rasterLineMaxY(index x1, index y1, index x2, index y2, + index x) { - return rasterLineMaxY(x1, y1, (y2 - y1) / (x2 - x1), x); + float dy = y2 - y1, dx = x2 - x1; + return rasterLineMaxY(x1, y1, dy / dx, x); } }; // struct Constraint }; From 93f6fdca2d7246cf6f5fcaf7df983400f64c9009 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 09:07:57 +0100 Subject: [PATCH 085/153] variadic minimum helpers --- include/algorithms/public/DTW.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 97458f142..288431b05 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -110,6 +110,22 @@ class DTW return (v1.array() - v2.array()).abs().pow(mPNorm).sum(); } + // fun little fold operation to do a variadic minimum + template + inline static auto min(Args&&... args) + { + auto m = (args, ...); + return ((m = std::min(m, args)), ...); + } + + // filter for minimum chaining, if cond evaluates to false then the value + // isn't used (never will be the minimum if its the numeric maximum) + template + inline static T useIf(bool cond, T val) + { + return cond ? val : std::numeric_limits::max(); + } + struct Constraint { Constraint(DTWConstraint c, index rows, index cols, float param) From 3a55c3bad809c1f5666951931adb25ec48b864dd Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 09:47:18 +0100 Subject: [PATCH 086/153] iterating lambda for main body to isolate range calculations --- include/algorithms/public/DTW.hpp | 117 ++++++++++++++++-------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 288431b05..79c3fe3b2 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -47,40 +47,35 @@ class DTW constexpr index initialized() const { return mInitialized; } double process(InputRealMatrixView x1, InputRealMatrixView x2, - DTWConstraint constraint = DTWConstraint::kUnconstrained, - index constraintParam = 2, - Allocator& alloc = FluidDefaultAllocator()) + DTWConstraint constr = DTWConstraint::kUnconstrained, + index param = 2, Allocator& alloc = FluidDefaultAllocator()) { ScopedEigenMap x1r(x1.cols(), alloc), x2r(x2.cols(), alloc); - Constraint c(constraint, x1.rows(), x2.rows(), constraintParam); + Constraint constraint(constr, x1.rows(), x2.rows(), param); mDistanceMetrics.resize(x1.rows(), x2.rows()); mDistanceMetrics.fill(std::numeric_limits::max()); - // simple brute force DTW is very inefficient, see FastDTW - for (index i = c.firstRow(); i <= c.lastRow(); i++) - { - for (index j = c.firstCol(i); j <= c.lastCol(i); j++) - { - x1r = _impl::asEigen(x1.row(i)); - x2r = _impl::asEigen(x2.row(j)); + constraint.iterate([&, this](index r, index c) { + x1r = _impl::asEigen(x1.row(r)); + x2r = _impl::asEigen(x2.row(c)); - mDistanceMetrics(i, j) = differencePNormToTheP(x1r, x2r); + mDistanceMetrics(r, c) = differencePNormToTheP(x1r, x2r); - if (i > 0 || j > 0) - { - double minimum = std::numeric_limits::max(); + if (r > 0 || c > 0) + { + double minimum = std::numeric_limits::max(); - if (i > 0) minimum = std::min(minimum, mDistanceMetrics(i - 1, j)); - if (j > 0) minimum = std::min(minimum, mDistanceMetrics(i, j - 1)); - if (i > 0 && j > 0) - minimum = std::min(minimum, mDistanceMetrics(i - 1, j - 1)); + if (r > 0) minimum = std::min(minimum, mDistanceMetrics(r - 1, c)); + if (c > 0) minimum = std::min(minimum, mDistanceMetrics(r, c - 1)); + if (r > 0 && c > 0) + minimum = std::min(minimum, mDistanceMetrics(r - 1, c - 1)); - mDistanceMetrics(i, j) += minimum; - } + if (minimum == std::numeric_limits::max()) return -1; + mDistanceMetrics(r, c) += minimum; } - } + }); mCalculated = true; @@ -131,8 +126,50 @@ class DTW Constraint(DTWConstraint c, index rows, index cols, float param) : mType{c}, mRows{rows}, mCols{cols} {}; - const index firstRow() const { return 0; }; - const index lastRow() const { return mRows - 1; }; + void iterate(std::function f) + { + index first, last; + + for (index r = 0; r < mRows; ++r) + { + first = firstCol(r); + last = lastCol(r); + + for (index c = first; c <= last; ++c) f(r, c); + } + }; + + private: + DTWConstraint mType; + index mRows, mCols; + float mParam; // mParam is either radius (SC) or gradient (Ik) + + inline static index rasterLineMinY(index x1, index y1, float dydx, index x) + { + return std::round(y1 + (x - x1) * dydx); + } + + inline static index rasterLineMinY(index x1, index y1, index x2, index y2, + index x) + { + float dy = y2 - y1, dx = x2 - x1; + return rasterLineMinY(x1, y1, dy / dx, x); + } + + inline static index rasterLineMaxY(index x1, index y1, float dydx, index x) + { + if (dydx > 1) + return rasterLineMinY(x1, y1, dydx, x + 1) - 1; + else + return rasterLineMinY(x1, y1, dydx, x); + } + + inline static index rasterLineMaxY(index x1, index y1, index x2, index y2, + index x) + { + float dy = y2 - y1, dx = x2 - x1; + return rasterLineMaxY(x1, y1, dy / dx, x); + } index firstCol(index row) { @@ -179,38 +216,6 @@ class DTW } } }; - - private: - DTWConstraint mType; - index mRows, mCols; - float mParam; // mParam is either radius (SC) or gradient (Ik) - - inline static index rasterLineMinY(index x1, index y1, float dydx, index x) - { - return std::round(y1 + (x - x1) * dydx); - } - - inline static index rasterLineMinY(index x1, index y1, index x2, index y2, - index x) - { - float dy = y2 - y1, dx = x2 - x1; - return rasterLineMinY(x1, y1, dy / dx, x); - } - - inline static index rasterLineMaxY(index x1, index y1, float dydx, index x) - { - if (dydx > 1) - return rasterLineMinY(x1, y1, dydx, x + 1) - 1; - else - return rasterLineMinY(x1, y1, dydx, x); - } - - inline static index rasterLineMaxY(index x1, index y1, index x2, index y2, - index x) - { - float dy = y2 - y1, dx = x2 - x1; - return rasterLineMaxY(x1, y1, dy / dx, x); - } }; // struct Constraint }; From 72d170e5d5c4b872a12963ff3a08ad0e655f308f Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 11:16:45 +0100 Subject: [PATCH 087/153] add Ikatura gradient catch --- include/algorithms/public/DTW.hpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 79c3fe3b2..63f0d3b4f 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -72,7 +72,6 @@ class DTW if (r > 0 && c > 0) minimum = std::min(minimum, mDistanceMetrics(r - 1, c - 1)); - if (minimum == std::numeric_limits::max()) return -1; mDistanceMetrics(r, c) += minimum; } }); @@ -124,7 +123,21 @@ class DTW struct Constraint { Constraint(DTWConstraint c, index rows, index cols, float param) - : mType{c}, mRows{rows}, mCols{cols} {}; + : mType{c}, mRows{rows}, mCols{cols}, mParam{param} + { + // ifn't gradient more than digonal set it to be the diagonal + // (sakoe-chiba with radius 0) + if (c == DTWConstraint::kIkatura) + { + float big = std::max(mRows, mCols), smol = std::min(mRows, mCols); + + if (mParam <= big / smol) + { + mType = DTWConstraint::kSakoeChiba; + mParam = 0; + } + } + }; void iterate(std::function f) { @@ -189,6 +202,7 @@ class DTW case DTWConstraint::kSakoeChiba: { index col = rasterLineMinY(mParam, -mParam, mRows - 1 + mParam, mCols - 1 - mParam, row); + return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; } } @@ -212,6 +226,7 @@ class DTW case DTWConstraint::kSakoeChiba: { index col = rasterLineMaxY(-mParam, mParam, mRows - 1 - mParam, mCols - 1 + mParam, row); + return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; } } From f075e9d32cf081a7e4608d08b0afe813de34de6c Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 11:17:08 +0100 Subject: [PATCH 088/153] separate constraint parameters by name --- include/clients/nrt/DTWClient.hpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index e8ae997e8..a91107211 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -26,7 +26,8 @@ constexpr auto DTWParams = defineParameters( LongParam("p", "LpNorm power (distance weighting)", 2, Min(1)), EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", "Sakoe-Chiba"), - LongParam("radius", "Constraint Value", 2, Min(0))); + LongParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), + FloatParam("gradient", "Ikatura Parallelogram max gradient", 1, Min(1))); class DTWClient : public FluidBaseClient, OfflineIn, @@ -34,7 +35,7 @@ class DTWClient : public FluidBaseClient, ModelObject, public DataClient { - enum { kName, kPNorm, kConstraint, kRadius }; + enum { kName, kPNorm, kConstraint, kRadius, kGradient }; public: using string = std::string; @@ -83,14 +84,16 @@ class DTWClient : public FluidBaseClient, if (i1 < 0 || i2 < 0) return Error(PointNotFound); + mAlgorithm.init(get()); + InputRealMatrixView series1 = srcDataSeries.getSeries(id1), series2 = srcDataSeries.getSeries(id2); - mAlgorithm.init(get()); + algorithm::DTWConstraint constraint = + (algorithm::DTWConstraint) get(); - return mAlgorithm.process(series1, series2, - (algorithm::DTWConstraint) get(), - get()); + return mAlgorithm.process(series1, series2, constraint, + constraintParam(constraint)); } MessageResult bufCost(InputBufferPtr data1, InputBufferPtr data2) @@ -123,6 +126,20 @@ class DTWClient : public FluidBaseClient, return defineMessages(makeMessage("cost", &DTWClient::cost), makeMessage("bufCost", &DTWClient::bufCost)); } + +private: + float constraintParam(algorithm::DTWConstraint constraint) + { + using namespace algorithm; + + switch (constraint) + { + case DTWConstraint::kIkatura: return get(); + case DTWConstraint::kSakoeChiba: return get(); + } + + return 0.0; + } }; using DTWRef = SharedClientRef; From 7f0378fd59f3ac1857ce4ba1ea138a21040c1bc5 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 11:24:28 +0100 Subject: [PATCH 089/153] float `radius` param --- include/clients/nrt/DTWClient.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index a91107211..17dc5e871 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -26,7 +26,7 @@ constexpr auto DTWParams = defineParameters( LongParam("p", "LpNorm power (distance weighting)", 2, Min(1)), EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", "Sakoe-Chiba"), - LongParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), + FloatParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), FloatParam("gradient", "Ikatura Parallelogram max gradient", 1, Min(1))); class DTWClient : public FluidBaseClient, From 067c9ad403c7887e1500ed4b382d574d356f5fc4 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 11:24:44 +0100 Subject: [PATCH 090/153] acc using the different params might be useful --- include/clients/nrt/DTWClient.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 17dc5e871..45a15308c 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -108,17 +108,19 @@ class DTWClient : public FluidBaseClient, if (buf1.numFrames() == 0 || buf2.numFrames() == 0) return Error(EmptyBuffer); + mAlgorithm.init(get()); + RealMatrix buf1frames(buf1.numFrames(), buf1.numChans()), buf2frames(buf2.numFrames(), buf2.numChans()); buf1frames <<= buf1.allFrames().transpose(); buf2frames <<= buf2.allFrames().transpose(); - mAlgorithm.init(get()); + algorithm::DTWConstraint constraint = + (algorithm::DTWConstraint) get(); - return mAlgorithm.process(buf1frames, buf2frames, - (algorithm::DTWConstraint) get(), - get()); + return mAlgorithm.process(buf1frames, buf2frames, constraint, + constraintParam(constraint)); } static auto getMessageDescriptors() From 9bb70236616c51c51b09ca50f62678343b32fe89 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 11:40:27 +0100 Subject: [PATCH 091/153] `dataSeries` distance messages now using sakoe-chiba --- include/clients/nrt/DataSeriesClient.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index c261b1947..0c09184b0 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -424,7 +424,8 @@ class DataSeriesClient double distance(InputRealMatrixView x1, InputRealMatrixView x2, index p) const { algorithm::DTW dtw; - return dtw.process(x1, x2); + return dtw.process(x1, x2, algorithm::DTWConstraint::kSakoeChiba, + std::min(x1.size(), x2.size()) / 4); } }; From 650035ba8a59b1ba5d9da955d2924527431cdf5a Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 14:31:33 +0100 Subject: [PATCH 092/153] cannibalise `knnclassifier` --- include/clients/nrt/DTWClassifierClient.hpp | 175 ++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 include/clients/nrt/DTWClassifierClient.hpp diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp new file mode 100644 index 000000000..f6393f36c --- /dev/null +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -0,0 +1,175 @@ +/* +Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) +Copyright University of Huddersfield. +Licensed under the BSD-3 License. +See license.md file in the project root for full license information. +This project has received funding from the European Research Council (ERC) +under the European Union’s Horizon 2020 research and innovation programme +(grant agreement No 725899). +*/ + +#pragma once + +#include "DataSeriesClient.hpp" +#include "DataSetClient.hpp" +#include "LabelSetClient.hpp" +#include "NRTClient.hpp" +#include "../../algorithms/public/DTW.hpp" +#include "../../data/FluidDataSeries.hpp" +#include "../../data/FluidDataSet.hpp" + +namespace fluid { +namespace client { +namespace dtwclassifier { + +struct DTWClassifierData +{ +}; + +void to_json(nlohmann::json& j, const DTWClassifierData& data) +{ +} + +bool check_json(const nlohmann::json& j, const DTWClassifierData&) +{ +} + +void from_json(const nlohmann::json& j, DTWClassifierData& data) +{ +} + +constexpr auto DTWClassifierParams = defineParameters( + StringParam>("name", "Name"), + LongParam("numNeighbours", "Number of Nearest Neighbours", 3, Min(1)), + EnumParam("weight", "Weight Neighbours by Distance", 1, "No", "Yes")); + +class DTWClassifierClient : public FluidBaseClient, + OfflineIn, + OfflineOut, + ModelObject, + public DataClient +{ + enum { kName, kNumNeighbors, kWeight }; + +public: + using string = std::string; + using BufferPtr = std::shared_ptr; + using InputBufferPtr = std::shared_ptr; + using LabelSet = FluidDataSet; + using DataSet = FluidDataSet; + using StringVector = FluidTensor; + + using ParamDescType = decltype(DTWClassifierParams); + + using ParamSetViewType = ParameterSetView; + std::reference_wrapper mParams; + + void setParams(ParamSetViewType& p) { mParams = p; } + + template + auto& get() const + { + return mParams.get().template get(); + } + + static constexpr auto& getParameterDescriptors() + { + return DTWClassifierParams; + } + + DTWClassifierClient(ParamSetViewType& p, FluidContext&) : mParams(p) {} + + template + Result process(FluidContext&) + { + return {}; + } + + MessageResult fit(InputDataSeriesClientRef dataSeriesClient, + InputLabelSetClientRef labelSetClient) + { + } + + MessageResult predictPoint(InputBufferPtr data) const + { + } + + MessageResult predict(InputDataSetClientRef source, + LabelSetClientRef dest) const + { + } + + + static auto getMessageDescriptors() + { + return defineMessages( + makeMessage("fit", &DTWClassifierClient::fit), + makeMessage("predict", &DTWClassifierClient::predict), + makeMessage("predictPoint", &DTWClassifierClient::predictPoint), + makeMessage("cols", &DTWClassifierClient::dims), + makeMessage("clear", &DTWClassifierClient::clear), + makeMessage("size", &DTWClassifierClient::size), + makeMessage("load", &DTWClassifierClient::load), + makeMessage("dump", &DTWClassifierClient::dump), + makeMessage("write", &DTWClassifierClient::write), + makeMessage("read", &DTWClassifierClient::read)); + } + +}; + +using DTWClassifierRef = SharedClientRef; + +constexpr auto DTWClassifierQueryParams = defineParameters( + DTWClassifierRef::makeParam("model", "Source model"), + LongParam("numNeighbours", "Number of Nearest Neighbours", 3, Min(1)), + EnumParam("weight", "Weight Neighbours by Distance", 1, "No", "Yes"), + InputBufferParam("inputPointBuffer", "Input Point Buffer"), + BufferParam("predictionBuffer", "Prediction Buffer")); + +class DTWClassifierQuery : public FluidBaseClient, ControlIn, ControlOut +{ + enum { kModel, kNumNeighbors, kWeight, kInputBuffer, kOutputBuffer }; + +public: + using ParamDescType = decltype(DTWClassifierQueryParams); + + using ParamSetViewType = ParameterSetView; + std::reference_wrapper mParams; + + void setParams(ParamSetViewType& p) { mParams = p; } + + template + auto& get() const + { + return mParams.get().template get(); + } + + static constexpr auto& getParameterDescriptors() + { + return DTWClassifierQueryParams; + } + + DTWClassifierQuery(ParamSetViewType& p, FluidContext&) : mParams(p) + { + controlChannelsIn(1); + controlChannelsOut({1, 1}); + } + + template + void process(std::vector>& input, + std::vector>& output, FluidContext& c) + { + } + + index latency() { return 0; } +}; + +} // namespace dtwclassifier + +using NRTThreadedDTWClassifierClient = + NRTThreadingAdaptor; + +using RTDTWClassifierQueryClient = + ClientWrapper; +} // namespace client +} // namespace fluid From ce7b783fccb8eaaaf85ee8b0bc09bee3ee9681ea Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 14:31:57 +0100 Subject: [PATCH 093/153] classifier data class --- include/clients/nrt/DTWClassifierClient.hpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index f6393f36c..6f69af31d 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -24,18 +24,38 @@ namespace dtwclassifier { struct DTWClassifierData { + algorithm::DTW dtw; + FluidDataSeries series{1}; + FluidDataSet labels{1}; + + index size() const { return labels.size(); } + index dims() const { return dtw.dims(); } + void clear() + { + labels = FluidDataSet(1); + series = FluidDataSeries(1); + + dtw.clear(); + } + bool initialized() const { return dtw.initialized(); } }; void to_json(nlohmann::json& j, const DTWClassifierData& data) { + j["labels"] = data.labels; + j["series"] = data.series; } bool check_json(const nlohmann::json& j, const DTWClassifierData&) { + return fluid::check_json(j, {"labels", "series"}, + {JSONTypes::OBJECT, JSONTypes::OBJECT}); } void from_json(const nlohmann::json& j, DTWClassifierData& data) { + data.series = j.at("series").get>(); + data.labels = j.at("labels").get>(); } constexpr auto DTWClassifierParams = defineParameters( From 49e3f2781499a4270655b972e27029533110bf4d Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 14:45:04 +0100 Subject: [PATCH 094/153] remove rt query object --- include/clients/nrt/DTWClassifierClient.hpp | 47 +-------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 6f69af31d..a5b3b9195 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -135,61 +135,16 @@ class DTWClassifierClient : public FluidBaseClient, makeMessage("read", &DTWClassifierClient::read)); } -}; - -using DTWClassifierRef = SharedClientRef; - -constexpr auto DTWClassifierQueryParams = defineParameters( - DTWClassifierRef::makeParam("model", "Source model"), - LongParam("numNeighbours", "Number of Nearest Neighbours", 3, Min(1)), - EnumParam("weight", "Weight Neighbours by Distance", 1, "No", "Yes"), - InputBufferParam("inputPointBuffer", "Input Point Buffer"), - BufferParam("predictionBuffer", "Prediction Buffer")); - -class DTWClassifierQuery : public FluidBaseClient, ControlIn, ControlOut -{ - enum { kModel, kNumNeighbors, kWeight, kInputBuffer, kOutputBuffer }; - -public: - using ParamDescType = decltype(DTWClassifierQueryParams); - - using ParamSetViewType = ParameterSetView; - std::reference_wrapper mParams; - - void setParams(ParamSetViewType& p) { mParams = p; } - - template - auto& get() const - { - return mParams.get().template get(); - } - - static constexpr auto& getParameterDescriptors() { - return DTWClassifierQueryParams; - } - DTWClassifierQuery(ParamSetViewType& p, FluidContext&) : mParams(p) - { - controlChannelsIn(1); - controlChannelsOut({1, 1}); - } - template - void process(std::vector>& input, - std::vector>& output, FluidContext& c) - { } - - index latency() { return 0; } }; + } // namespace dtwclassifier using NRTThreadedDTWClassifierClient = - NRTThreadingAdaptor; -using RTDTWClassifierQueryClient = - ClientWrapper; } // namespace client } // namespace fluid From a168e2fce6059cae6221d3e260348fb31b54c75a Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 15:27:36 +0100 Subject: [PATCH 095/153] hid pnorm power interface --- include/algorithms/public/DTW.hpp | 20 ++++++++------------ include/clients/nrt/DTWClient.hpp | 5 ----- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 63f0d3b4f..1a28fd667 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -39,16 +39,17 @@ class DTW explicit DTW() = default; ~DTW() = default; - void init(index p = 2) { mPNorm = p; } - void clear() { mCalculated = false; } + void init() {} + void clear() {} index size() const { return mPNorm; } constexpr index dims() const { return 0; } - constexpr index initialized() const { return mInitialized; } + constexpr index initialized() const { return true; } double process(InputRealMatrixView x1, InputRealMatrixView x2, DTWConstraint constr = DTWConstraint::kUnconstrained, - index param = 2, Allocator& alloc = FluidDefaultAllocator()) + index param = 2, + Allocator& alloc = FluidDefaultAllocator()) const { ScopedEigenMap x1r(x1.cols(), alloc), x2r(x2.cols(), alloc); @@ -76,18 +77,13 @@ class DTW } }); - mCalculated = true; - return std::pow(mDistanceMetrics(x1.rows() - 1, x2.rows() - 1), 1.0 / mPNorm); } private: - RealMatrix mDistanceMetrics; - index mPNorm{2}; - - bool mCalculated{false}; - const bool mInitialized{true}; + mutable RealMatrix mDistanceMetrics; + const index mPNorm{2}; // P-Norm of the difference vector // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) @@ -98,7 +94,7 @@ class DTW // (normception) inline double differencePNormToTheP(const Eigen::Ref& v1, - const Eigen::Ref& v2) + const Eigen::Ref& v2) const { // assert(v1.size() == v2.size()); return (v1.array() - v2.array()).abs().pow(mPNorm).sum(); diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 45a15308c..2bb68cf49 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -23,7 +23,6 @@ namespace dtw { constexpr auto DTWParams = defineParameters( StringParam>("name", "Name"), - LongParam("p", "LpNorm power (distance weighting)", 2, Min(1)), EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", "Sakoe-Chiba"), FloatParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), @@ -84,8 +83,6 @@ class DTWClient : public FluidBaseClient, if (i1 < 0 || i2 < 0) return Error(PointNotFound); - mAlgorithm.init(get()); - InputRealMatrixView series1 = srcDataSeries.getSeries(id1), series2 = srcDataSeries.getSeries(id2); @@ -108,8 +105,6 @@ class DTWClient : public FluidBaseClient, if (buf1.numFrames() == 0 || buf2.numFrames() == 0) return Error(EmptyBuffer); - mAlgorithm.init(get()); - RealMatrix buf1frames(buf1.numFrames(), buf1.numChans()), buf2frames(buf2.numFrames(), buf2.numChans()); From 892b3a600d9a3d74ac0c8eaba7910dde295c9b71 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 16:36:52 +0100 Subject: [PATCH 096/153] use custom allocator on std containers --- include/data/FluidDataSeries.hpp | 155 +++++++++++++++++-------------- include/data/FluidMemory.hpp | 3 + 2 files changed, 89 insertions(+), 69 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index dea7b2ed8..de89069c4 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -8,6 +8,8 @@ #include #include + +// TODO:: REMOVE TEMPLATE THINGY AND TURN IT INTO CONSISTNE ALLOCATION OF MATRIX namespace fluid { template @@ -24,16 +26,15 @@ class FluidDataSeries // e.g. FluidDataSet(2, 3) is a dataset of 2x3 tensors template ()>> - FluidDataSeries(Dims... dims) - : mData(0, FluidTensor(0, dims...)), - mDim(dims...) + FluidDataSeries(Dims... dims) + : mData(0, FluidTensor(0, dims...)), mDim(dims...) { static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); } // Construct from existing tensors of ids and data points - FluidDataSeries(FluidTensorView ids, - std::vector> points) + FluidDataSeries(FluidTensorView ids, + rt::vector> points) : mIds(ids), mData(points) { initFromData(); @@ -42,8 +43,8 @@ class FluidDataSeries // Construct from existing tensors of ids and data points // (from convertible type for data, typically float -> double) template - FluidDataSeries(FluidTensorView ids, - std::vector> points, + FluidDataSeries(FluidTensorView ids, + rt::vector> points, std::enable_if_t::value>* = nullptr) : mIds(ids), mData(points) { @@ -58,20 +59,18 @@ class FluidDataSeries static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); if (size() == 0) { - mData = std::vector>(); + mData = rt::vector>(); mDim = FluidTensorSlice(dims...); return true; } - else - { - return false; - } + else { return false; } } template bool addFrame(idType const& id, FluidTensorView frame) { - static_assert(std::is_convertible::value, "Can't convert between types"); + static_assert(std::is_convertible::value, + "Can't convert between types"); assert(sameExtents(mDim, frame.descriptor())); auto pos = mIndex.find(id); @@ -79,7 +78,7 @@ class FluidDataSeries { FluidTensorView newPoint(frame); return addSeries(id, newPoint); - } + } mData[pos->second].resizeDim(0, 1); mData[pos->second].row(mData[pos->second].rows() - 1) <<= frame; @@ -90,9 +89,10 @@ class FluidDataSeries template bool addSeries(idType const& id, FluidTensorView series) { - static_assert(std::is_convertible::value, "Can't convert between types"); - - // dont crete another view + static_assert(std::is_convertible::value, + "Can't convert between types"); + + // dont crete another view auto result = mIndex.insert({id, mData.size()}); if (!result.second) return false; @@ -104,7 +104,8 @@ class FluidDataSeries return true; } - bool getFrame(idType const& id, index time, FluidTensorView frame) const + bool getFrame(idType const& id, index time, + FluidTensorView frame) const { auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; @@ -116,10 +117,11 @@ class FluidDataSeries return true; } - FluidTensorView getFrame(idType const& id, index time) const + FluidTensorView getFrame(idType const& id, + index time) const { auto pos = mIndex.find(id); - if(pos != mIndex.end()) + if (pos != mIndex.end()) { assert(time < mData[pos->second].rows()); return mData[pos->second].row(time); @@ -127,7 +129,8 @@ class FluidDataSeries else { return FluidTensorView{nullptr, 0, 0}; } } - bool getSeries(idType const& id, FluidTensorView series) const + bool getSeries(idType const& id, + FluidTensorView series) const { auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; @@ -148,8 +151,9 @@ class FluidDataSeries template bool updateFrame(idType const& id, index time, FluidTensorView frame) { - static_assert(std::is_convertible::value, "Can't convert between types"); - + static_assert(std::is_convertible::value, + "Can't convert between types"); + auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; @@ -162,13 +166,16 @@ class FluidDataSeries template bool updateSeries(idType const& id, FluidTensorView series) { - static_assert(std::is_convertible::value, "Can't convert between types"); + static_assert(std::is_convertible::value, + "Can't convert between types"); auto pos = mIndex.find(id); - if (pos == mIndex.end()) return false; - else + if (pos == mIndex.end()) + return false; + else { - mData[pos->second].resizeDim(0, series.rows() - mData[pos->second].rows()); + mData[pos->second].resizeDim(0, + series.rows() - mData[pos->second].rows()); mData[pos->second] <<= series; } @@ -183,8 +190,10 @@ class FluidDataSeries index current = pos->second; if (time >= mData[current].rows()) return false; - if(mData[current].rows() == 1) return removeSeries(id); - else mData[current].deleteRow(time); + if (mData[current].rows() == 1) + return removeSeries(id); + else + mData[current].deleteRow(time); return true; } @@ -210,43 +219,49 @@ class FluidDataSeries index getIndex(idType const& id) const { auto pos = mIndex.find(id); - if (pos == mIndex.end()) return -1; - else return pos->second; + if (pos == mIndex.end()) + return -1; + else + return pos->second; } - index getNumFrames(idType const& id) const + index getNumFrames(idType const& id) const { auto pos = mIndex.find(id); - if (pos == mIndex.end()) return -1; - else return mData[pos->second].rows(); + if (pos == mIndex.end()) + return -1; + else + return mData[pos->second].rows(); } - std::vector> getData() - { - std::vector> viewVec(mData.size()); + rt::vector> getData() + { + rt::vector> viewVec(mData.size()); - // hacky fix to force conversion of vector of tensors to vector of views of mData - // doesn't actually copy anything, it uses the FluidTensor ctor of FluidTensorView - // which creates a view/ref, so ends up creating what we want + // hacky fix to force conversion of vector of tensors to vector of views of + // mData doesn't actually copy anything, it uses the FluidTensor ctor of + // FluidTensorView which creates a view/ref, so ends up creating what we + // want std::copy(mData.begin(), mData.end(), std::back_inserter(viewVec)); - return viewVec; + return viewVec; } - const std::vector> getData() const - { - std::vector> viewVec; + const rt::vector> getData() const + { + rt::vector> viewVec; // hacky fix to force conversion of vector to views of mData - // doesn't actually copy anything, it uses the FluidTensor ctor of FluidTensorView - // which creates a view/ref, so ends up creating what we want + // doesn't actually copy anything, it uses the FluidTensor ctor of + // FluidTensorView which creates a view/ref, so ends up creating what we + // want std::copy(mData.cbegin(), mData.cend(), std::back_inserter(viewVec)); - return viewVec; + return viewVec; } - FluidTensorView getIds() { return mIds; } - FluidTensorView getIds() const { return mIds; } + FluidTensorView getIds() { return mIds; } + FluidTensorView getIds() const { return mIds; } index pointSize() const { return mDim.size; } index dims() const { return mDim.size; } @@ -254,7 +269,7 @@ class FluidDataSeries bool initialized() const { return (size() > 0); } std::string printFrame(FluidTensorView frame, - index maxCols) const + index maxCols) const { using namespace std; ostringstream result; @@ -266,7 +281,7 @@ class FluidDataSeries } else { - for (index c = 0; c < maxCols / 2; c++) + for (index c = 0; c < maxCols / 2; c++) result << setw(10) << setprecision(5) << frame(c); result << setw(10) << "..."; @@ -279,7 +294,7 @@ class FluidDataSeries } std::string printSeries(FluidTensorView series, - index maxFrames, index maxCols) const + index maxFrames, index maxCols) const { using namespace std; ostringstream result; @@ -287,44 +302,47 @@ class FluidDataSeries if (series.rows() < maxFrames) { for (index t = 0; t < series.rows(); t++) - result << setw(10) << "t" << t << ": " << printFrame(series.row(t), maxCols) - << endl; + result << setw(10) << "t" << t << ": " + << printFrame(series.row(t), maxCols) << endl; } else { for (index t = 0; t < maxFrames / 2; t++) - result << setw(10) << "t" << t << ": " << printFrame(series.row(t), maxCols) - << endl; + result << setw(10) << "t" << t << ": " + << printFrame(series.row(t), maxCols) << endl; result << setw(10) << "..." << std::endl; for (index t = maxFrames / 2; t > 0; t--) - result << setw(10) << "t" << (series.rows() - t) << ": " + result << setw(10) << "t" << (series.rows() - t) << ": " << printFrame(series.row(size() - t), maxCols) << endl; } return result.str(); } - std::string print(index maxRows = 6, index maxFrames = 6, index maxCols = 6) const + std::string print(index maxRows = 6, index maxFrames = 6, + index maxCols = 6) const { using namespace std; ostringstream result; if (size() == 0) return "{}"; - result << endl << "points: " << size() << endl << "frame size: " << pointSize() << endl; + result << endl + << "points: " << size() << endl + << "frame size: " << pointSize() << endl; if (size() < maxRows) { for (index r = 0; r < size(); r++) - result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) - << endl; + result << mIds(r) << ":" << endl + << printSeries(mData[r], maxFrames, maxCols) << endl; } else { for (index r = 0; r < maxRows / 2; r++) - result << mIds(r) << ":" << endl << printSeries(mData[r], maxFrames, maxCols) - << endl; + result << mIds(r) << ":" << endl + << printSeries(mData[r], maxFrames, maxCols) << endl; result << setw(10) << "⋮" << endl; @@ -341,13 +359,12 @@ class FluidDataSeries { assert(mIds.rows() == mData.size()); mDim = mData[0].cols(); - for (index i = 0; i < mIds.size(); i++) - mIndex.insert({mIds[i], i}); + for (index i = 0; i < mIds.size(); i++) mIndex.insert({mIds[i], i}); } - std::unordered_map mIndex; - FluidTensor mIds; - std::vector> mData; - FluidTensorSlice mDim; // dimensions for one frame + rt::vector> mData; + rt::unordered_map mIndex; + FluidTensor mIds; + FluidTensorSlice mDim; // dimensions for one frame }; } // namespace fluid diff --git a/include/data/FluidMemory.hpp b/include/data/FluidMemory.hpp index 07768249e..748db1eb5 100644 --- a/include/data/FluidMemory.hpp +++ b/include/data/FluidMemory.hpp @@ -47,6 +47,9 @@ using deque = foonathan::memory::deque; template using queue = foonathan::memory::queue; + +template +using unordered_map = foonathan::memory::unordered_map; } // namespace rt inline Allocator& FluidDefaultAllocator() From c78368acda0238ce053ae4b4d8268f72c754289e Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 16:45:22 +0100 Subject: [PATCH 097/153] use custom allocator on std containers # Conflicts: # include/data/FluidDataSeries.hpp --- include/data/FluidDataSeries.hpp | 26 +++++++++++++------------- include/data/FluidMemory.hpp | 3 +++ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 35173cb1e..de89069c4 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -33,8 +33,8 @@ class FluidDataSeries } // Construct from existing tensors of ids and data points - FluidDataSeries(FluidTensorView ids, - std::vector> points) + FluidDataSeries(FluidTensorView ids, + rt::vector> points) : mIds(ids), mData(points) { initFromData(); @@ -43,8 +43,8 @@ class FluidDataSeries // Construct from existing tensors of ids and data points // (from convertible type for data, typically float -> double) template - FluidDataSeries(FluidTensorView ids, - std::vector> points, + FluidDataSeries(FluidTensorView ids, + rt::vector> points, std::enable_if_t::value>* = nullptr) : mIds(ids), mData(points) { @@ -59,7 +59,7 @@ class FluidDataSeries static_assert(sizeof...(dims) == N, "Number of dimensions doesn't match"); if (size() == 0) { - mData = std::vector>(); + mData = rt::vector>(); mDim = FluidTensorSlice(dims...); return true; } @@ -234,9 +234,9 @@ class FluidDataSeries return mData[pos->second].rows(); } - std::vector> getData() + rt::vector> getData() { - std::vector> viewVec(mData.size()); + rt::vector> viewVec(mData.size()); // hacky fix to force conversion of vector of tensors to vector of views of // mData doesn't actually copy anything, it uses the FluidTensor ctor of @@ -247,9 +247,9 @@ class FluidDataSeries return viewVec; } - const std::vector> getData() const + const rt::vector> getData() const { - std::vector> viewVec; + rt::vector> viewVec; // hacky fix to force conversion of vector to views of mData // doesn't actually copy anything, it uses the FluidTensor ctor of @@ -362,9 +362,9 @@ class FluidDataSeries for (index i = 0; i < mIds.size(); i++) mIndex.insert({mIds[i], i}); } - std::unordered_map mIndex; - FluidTensor mIds; - std::vector> mData; - FluidTensorSlice mDim; // dimensions for one frame + rt::vector> mData; + rt::unordered_map mIndex; + FluidTensor mIds; + FluidTensorSlice mDim; // dimensions for one frame }; } // namespace fluid diff --git a/include/data/FluidMemory.hpp b/include/data/FluidMemory.hpp index 07768249e..748db1eb5 100644 --- a/include/data/FluidMemory.hpp +++ b/include/data/FluidMemory.hpp @@ -47,6 +47,9 @@ using deque = foonathan::memory::deque; template using queue = foonathan::memory::queue; + +template +using unordered_map = foonathan::memory::unordered_map; } // namespace rt inline Allocator& FluidDefaultAllocator() From 066bc706d3fa97d0f38abc51c762228f0b97401f Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 16:47:05 +0100 Subject: [PATCH 098/153] `dtwClassifier` `fit` message --- include/clients/nrt/DTWClassifierClient.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index a5b3b9195..05a4bcc91 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -105,9 +105,28 @@ class DTWClassifierClient : public FluidBaseClient, return {}; } + // not fitting anything, you just set the input series and output labels MessageResult fit(InputDataSeriesClientRef dataSeriesClient, InputLabelSetClientRef labelSetClient) { + auto dataSeriesClientPtr = dataSeriesClient.get().lock(); + if (!dataSeriesClientPtr) return Error(NoDataSet); + + auto labelSetPtr = labelSetClient.get().lock(); + if (!labelSetPtr) return Error(NoLabelSet); + + auto dataSeries = dataSeriesClientPtr->getDataSeries(); + if (dataSeries.size() == 0) return Error(EmptyDataSet); + + auto labelSet = labelSetPtr->getLabelSet(); + if (labelSet.size() == 0) return Error(EmptyLabelSet); + + if (dataSeries.size() != labelSet.size()) return Error(SizesDontMatch); + + mAlgorithm.series = dataSeries; + mAlgorithm.labels = labelSet; + + return OK(); } MessageResult predictPoint(InputBufferPtr data) const From 746f312b466ee1f11cff5797572798a9bbe5ff3a Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 16:47:24 +0100 Subject: [PATCH 099/153] classifier parameters --- include/clients/nrt/DTWClassifierClient.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 05a4bcc91..86564f002 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -61,7 +61,11 @@ void from_json(const nlohmann::json& j, DTWClassifierData& data) constexpr auto DTWClassifierParams = defineParameters( StringParam>("name", "Name"), LongParam("numNeighbours", "Number of Nearest Neighbours", 3, Min(1)), - EnumParam("weight", "Weight Neighbours by Distance", 1, "No", "Yes")); + EnumParam("weight", "Weight Neighbours by Distance", 1, "No", "Yes"), + EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", + "Sakoe-Chiba"), + FloatParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), + FloatParam("gradient", "Ikatura Parallelogram max gradient", 1, Min(1))); class DTWClassifierClient : public FluidBaseClient, OfflineIn, @@ -69,7 +73,7 @@ class DTWClassifierClient : public FluidBaseClient, ModelObject, public DataClient { - enum { kName, kNumNeighbors, kWeight }; + enum { kName, kNumNeighbors, kWeight, kConstraint, kRadius, kGradient }; public: using string = std::string; @@ -145,7 +149,6 @@ class DTWClassifierClient : public FluidBaseClient, makeMessage("fit", &DTWClassifierClient::fit), makeMessage("predict", &DTWClassifierClient::predict), makeMessage("predictPoint", &DTWClassifierClient::predictPoint), - makeMessage("cols", &DTWClassifierClient::dims), makeMessage("clear", &DTWClassifierClient::clear), makeMessage("size", &DTWClassifierClient::size), makeMessage("load", &DTWClassifierClient::load), From 07973f6333f7378b715afd48e1a07cafbac53f5c Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 16:47:42 +0100 Subject: [PATCH 100/153] constraint getter --- include/clients/nrt/DTWClassifierClient.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 86564f002..74cf703e9 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -157,16 +157,27 @@ class DTWClassifierClient : public FluidBaseClient, makeMessage("read", &DTWClassifierClient::read)); } +private: + float constraintParam(algorithm::DTWConstraint constraint) const { + using namespace algorithm; + switch (constraint) + { + case DTWConstraint::kIkatura: return get(); + case DTWConstraint::kSakoeChiba: return get(); + } + return 0.0; } }; +using DTWClassifierRef = SharedClientRef; } // namespace dtwclassifier using NRTThreadedDTWClassifierClient = + NRTThreadingAdaptor; } // namespace client } // namespace fluid From 095b8adc794cdc2346d88973ae69be291a21d63b Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 16:49:00 +0100 Subject: [PATCH 101/153] assignment ordering and custom allocator --- include/clients/nrt/DataSeriesClient.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 0c09184b0..0118ce0ce 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -287,9 +287,10 @@ class DataSeriesClient FluidTensor series(buf.allFrames().transpose()); - std::vector indices(asUnsigned(mAlgorithm.size())); + rt::vector indices(asUnsigned(mAlgorithm.size())); + rt::vector distances(asUnsigned(mAlgorithm.size())); + std::iota(indices.begin(), indices.end(), 0); - std::vector distances(asUnsigned(mAlgorithm.size())); auto ds = mAlgorithm.getData(); @@ -329,10 +330,11 @@ class DataSeriesClient FluidTensor series(buf.allFrames().transpose()); - std::vector indices(asUnsigned(mAlgorithm.size())); - std::iota(indices.begin(), indices.end(), 0); + std::vector indices(asUnsigned(mAlgorithm.size())); std::vector distances(asUnsigned(mAlgorithm.size())); + std::iota(indices.begin(), indices.end(), 0); + auto ds = mAlgorithm.getData(); std::transform(indices.begin(), indices.end(), distances.begin(), From e8834dc410140ec3aa7591773ab95da56d259c4d Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 17:15:46 +0100 Subject: [PATCH 102/153] convert `dataseries` template operations for consistency --- include/clients/nrt/DataSeriesClient.hpp | 38 ++++++++++++++++-------- include/data/FluidDataSeries.hpp | 27 +++++------------ 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 0118ce0ce..c47f593ad 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -84,7 +84,10 @@ class DataSeriesClient return Error(WrongPointSize); } - mAlgorithm.addFrame(id, buf.samps(0, mAlgorithm.dims(), 0)); + RealVector frame(buf.numFrames()); + frame <<= buf.samps(0, mAlgorithm.dims(), 0); + + mAlgorithm.addFrame(id, frame); return OK(); } @@ -107,9 +110,10 @@ class DataSeriesClient return Error(WrongPointSize); } - return mAlgorithm.addSeries(id, buf.allFrames().transpose()) - ? OK() - : Error(DuplicateIdentifier); + RealMatrix series(buf.numFrames(), buf.numChans()); + series <<= buf.allFrames().transpose(); + + return mAlgorithm.addSeries(id, series) ? OK() : Error(DuplicateIdentifier); } MessageResult getFrame(string id, index time, BufferPtr data) const @@ -167,9 +171,11 @@ class DataSeriesClient if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); - return mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)) - ? OK() - : Error(PointNotFound); + RealVector frame(buf.numFrames()); + frame <<= buf.samps(0, mAlgorithm.dims(), 0); + + return mAlgorithm.updateFrame(id, time, frame) ? OK() + : Error(PointNotFound); } MessageResult updateSeries(string id, InputBufferPtr data) @@ -180,9 +186,10 @@ class DataSeriesClient if (!buf.exists()) return Error(InvalidBuffer); if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); - return mAlgorithm.updateSeries(id, buf.allFrames().transpose()) - ? OK() - : Error(PointNotFound); + RealMatrix series(buf.numFrames(), buf.numChans()); + series <<= buf.allFrames().transpose(); + + return mAlgorithm.updateSeries(id, series) ? OK() : Error(PointNotFound); } MessageResult setFrame(string id, index time, InputBufferPtr data) @@ -194,8 +201,10 @@ class DataSeriesClient if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); - bool result = - mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)); + RealVector frame(buf.numFrames()); + frame <<= buf.samps(0, mAlgorithm.dims(), 0); + + bool result = mAlgorithm.updateFrame(id, time, frame); if (result) return OK(); } @@ -211,7 +220,10 @@ class DataSeriesClient if (!buf.exists()) return Error(InvalidBuffer); if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); - bool result = mAlgorithm.updateSeries(id, buf.allFrames().transpose()); + RealMatrix series(buf.numFrames(), buf.numChans()); + series <<= buf.allFrames().transpose(); + + bool result = mAlgorithm.updateSeries(id, series); if (result) return OK(); } diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index de89069c4..55f323aa7 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -66,11 +66,8 @@ class FluidDataSeries else { return false; } } - template - bool addFrame(idType const& id, FluidTensorView frame) + bool addFrame(idType const& id, FluidTensorView frame) { - static_assert(std::is_convertible::value, - "Can't convert between types"); assert(sameExtents(mDim, frame.descriptor())); auto pos = mIndex.find(id); @@ -86,13 +83,9 @@ class FluidDataSeries return true; } - template - bool addSeries(idType const& id, FluidTensorView series) + bool addSeries(idType const& id, + FluidTensorView series) { - static_assert(std::is_convertible::value, - "Can't convert between types"); - - // dont crete another view auto result = mIndex.insert({id, mData.size()}); if (!result.second) return false; @@ -148,12 +141,9 @@ class FluidDataSeries : FluidTensorView{nullptr, 0, 0, 0}; } - template - bool updateFrame(idType const& id, index time, FluidTensorView frame) + bool updateFrame(idType const& id, index time, + FluidTensorView frame) { - static_assert(std::is_convertible::value, - "Can't convert between types"); - auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; @@ -163,12 +153,9 @@ class FluidDataSeries return true; } - template - bool updateSeries(idType const& id, FluidTensorView series) + bool updateSeries(idType const& id, + FluidTensorView series) { - static_assert(std::is_convertible::value, - "Can't convert between types"); - auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; From 1e82ed9de3d280bc1ab1b8d0977b65d5bdcd1767 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 17:18:13 +0100 Subject: [PATCH 103/153] formatting --- include/clients/nrt/DataSeriesClient.hpp | 119 +++++++++++++---------- 1 file changed, 68 insertions(+), 51 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 3cdaedfba..5b329b058 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -10,8 +10,8 @@ under the European Union’s Horizon 2020 research and innovation programme #pragma once #include "DataClient.hpp" -#include "LabelSetClient.hpp" #include "DataSetClient.hpp" +#include "LabelSetClient.hpp" #include "NRTClient.hpp" #include "../common/SharedClientUtils.hpp" #include "../../algorithms/public/DataSetIdSequence.hpp" @@ -26,13 +26,13 @@ namespace dataseries { enum { kName }; constexpr auto DataSeriesParams = defineParameters( - StringParam>("name", "Name of the DataSeries") -); + StringParam>("name", "Name of the DataSeries")); -class DataSeriesClient : public FluidBaseClient, - OfflineIn, - OfflineOut, - public DataClient> +class DataSeriesClient + : public FluidBaseClient, + OfflineIn, + OfflineOut, + public DataClient> { public: using string = std::string; @@ -75,9 +75,13 @@ class DataSeriesClient : public FluidBaseClient, if (mAlgorithm.size() == 0) { - if (mAlgorithm.dims() != buf.numFrames()) mAlgorithm = DataSeries(buf.numFrames()); + if (mAlgorithm.dims() != buf.numFrames()) + mAlgorithm = DataSeries(buf.numFrames()); + } + else if (buf.numFrames() != mAlgorithm.dims()) + { + return Error(WrongPointSize); } - else if (buf.numFrames() != mAlgorithm.dims()) { return Error(WrongPointSize); } mAlgorithm.addFrame(id, buf.samps(0, mAlgorithm.dims(), 0)); @@ -94,12 +98,17 @@ class DataSeriesClient : public FluidBaseClient, if (mAlgorithm.size() == 0) { - if (mAlgorithm.dims() != buf.numChans()) mAlgorithm = DataSeries(buf.numChans()); + if (mAlgorithm.dims() != buf.numChans()) + mAlgorithm = DataSeries(buf.numChans()); + } + else if (buf.numChans() != mAlgorithm.dims()) + { + return Error(WrongPointSize); } - else if (buf.numChans() != mAlgorithm.dims()) { return Error(WrongPointSize); } - return mAlgorithm.addSeries(id, buf.allFrames().transpose()) - ? OK() : Error(DuplicateIdentifier); + return mAlgorithm.addSeries(id, buf.allFrames().transpose()) + ? OK() + : Error(DuplicateIdentifier); } MessageResult getFrame(string id, index time, BufferPtr data) const @@ -132,7 +141,8 @@ class DataSeriesClient : public FluidBaseClient, BufferAdaptor::Access buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); - Result resizeResult = buf.resize(mAlgorithm.getNumFrames(id), mAlgorithm.dims(), buf.sampleRate()); + Result resizeResult = buf.resize(mAlgorithm.getNumFrames(id), + mAlgorithm.dims(), buf.sampleRate()); if (!resizeResult.ok()) return {resizeResult.status(), resizeResult.message()}; @@ -156,7 +166,9 @@ class DataSeriesClient : public FluidBaseClient, if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); - return mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)) ? OK() : Error(PointNotFound); + return mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)) + ? OK() + : Error(PointNotFound); } MessageResult updateSeries(string id, InputBufferPtr data) @@ -167,7 +179,9 @@ class DataSeriesClient : public FluidBaseClient, if (!buf.exists()) return Error(InvalidBuffer); if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); - return mAlgorithm.updateSeries(id, buf.allFrames().transpose()) ? OK() : Error(PointNotFound); + return mAlgorithm.updateSeries(id, buf.allFrames().transpose()) + ? OK() + : Error(PointNotFound); } MessageResult setFrame(string id, index time, InputBufferPtr data) @@ -179,7 +193,8 @@ class DataSeriesClient : public FluidBaseClient, if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); - bool result = mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)); + bool result = + mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)); if (result) return OK(); } @@ -212,8 +227,9 @@ class DataSeriesClient : public FluidBaseClient, return mAlgorithm.removeSeries(id) ? OK() : Error(PointNotFound); } - MessageResult merge(SharedClientRef dataseriesClient, - bool overwrite) + MessageResult + merge(SharedClientRef dataseriesClient, + bool overwrite) { auto dataseriesClientPtr = dataseriesClient.get().lock(); if (!dataseriesClientPtr) return Error(NoDataSet); @@ -223,12 +239,12 @@ class DataSeriesClient : public FluidBaseClient, if (srcDataSeries.pointSize() != mAlgorithm.pointSize()) return Error(WrongPointSize); - auto ids = srcDataSeries.getIds(); + auto ids = srcDataSeries.getIds(); for (index i = 0; i < srcDataSeries.size(); i++) { InputRealMatrixView series = srcDataSeries.getSeries(ids(i)); - bool added = mAlgorithm.addSeries(ids(i), series); + bool added = mAlgorithm.addSeries(ids(i), series); if (!added && overwrite) mAlgorithm.updateSeries(ids(i), series); } @@ -241,7 +257,7 @@ class DataSeriesClient : public FluidBaseClient, if (!destPtr) return Error(NoDataSet); destPtr->setDataSet(getSliceDataSet(time)); - if(destPtr->size() == 0) return Error(EmptyDataSet); + if (destPtr->size() == 0) return Error(EmptyDataSet); return OK(); } @@ -260,10 +276,11 @@ class DataSeriesClient : public FluidBaseClient, mAlgorithm = DataSeries(0); return OK(); } - + MessageResult print() { - return "DataSeries " + std::string(get()) + ": " + mAlgorithm.print(); + return "DataSeries " + std::string(get()) + ": " + + mAlgorithm.print(); } const DataSeries getDataSeries() const { return mAlgorithm; } @@ -272,30 +289,29 @@ class DataSeriesClient : public FluidBaseClient, static auto getMessageDescriptors() { return defineMessages( - makeMessage("addFrame", &DataSeriesClient::addFrame), - makeMessage("addSeries", &DataSeriesClient::addSeries), - makeMessage("getFrame", &DataSeriesClient::getFrame), - makeMessage("getSeries", &DataSeriesClient::getSeries), - makeMessage("setFrame", &DataSeriesClient::setFrame), - makeMessage("setSeries", &DataSeriesClient::setSeries), - makeMessage("updateFrame", &DataSeriesClient::updateFrame), + makeMessage("addFrame", &DataSeriesClient::addFrame), + makeMessage("addSeries", &DataSeriesClient::addSeries), + makeMessage("getFrame", &DataSeriesClient::getFrame), + makeMessage("getSeries", &DataSeriesClient::getSeries), + makeMessage("setFrame", &DataSeriesClient::setFrame), + makeMessage("setSeries", &DataSeriesClient::setSeries), + makeMessage("updateFrame", &DataSeriesClient::updateFrame), makeMessage("updateSeries", &DataSeriesClient::updateSeries), - makeMessage("deleteFrame", &DataSeriesClient::deleteFrame), + makeMessage("deleteFrame", &DataSeriesClient::deleteFrame), makeMessage("deleteSeries", &DataSeriesClient::deleteSeries), - makeMessage("merge", &DataSeriesClient::merge), - makeMessage("dump", &DataSeriesClient::dump), - makeMessage("load", &DataSeriesClient::load), - makeMessage("print", &DataSeriesClient::print), - makeMessage("size", &DataSeriesClient::size), - makeMessage("cols", &DataSeriesClient::dims), - makeMessage("clear", &DataSeriesClient::clear), - makeMessage("write", &DataSeriesClient::write), - makeMessage("read", &DataSeriesClient::read), - makeMessage("toBuffer", &DataSeriesClient::getSeries), - makeMessage("fromBuffer", &DataSeriesClient::setSeries), - makeMessage("getIds", &DataSeriesClient::getIds), - makeMessage("getDataSet", &DataSeriesClient::getDataSet) - ); + makeMessage("merge", &DataSeriesClient::merge), + makeMessage("dump", &DataSeriesClient::dump), + makeMessage("load", &DataSeriesClient::load), + makeMessage("print", &DataSeriesClient::print), + makeMessage("size", &DataSeriesClient::size), + makeMessage("cols", &DataSeriesClient::dims), + makeMessage("clear", &DataSeriesClient::clear), + makeMessage("write", &DataSeriesClient::write), + makeMessage("read", &DataSeriesClient::read), + makeMessage("toBuffer", &DataSeriesClient::getSeries), + makeMessage("fromBuffer", &DataSeriesClient::setSeries), + makeMessage("getIds", &DataSeriesClient::getIds), + makeMessage("getDataSet", &DataSeriesClient::getDataSet)); } private: @@ -311,23 +327,24 @@ class DataSeriesClient : public FluidBaseClient, DataSet getSliceDataSet(index time) const { - DataSet ds(mAlgorithm.dims()); + DataSet ds(mAlgorithm.dims()); decltype(mAlgorithm)::FrameType frame(mAlgorithm.dims()); - for(auto id : mAlgorithm.getIds()) + for (auto id : mAlgorithm.getIds()) { bool ret = mAlgorithm.getFrame(id, time, frame); - if(ret) ds.add(id, frame); + if (ret) ds.add(id, frame); } return ds; } }; -} // namespace dataset +} // namespace dataseries using DataSeriesClientRef = SharedClientRef; -using InputDataSeriesClientRef = SharedClientRef; +using InputDataSeriesClientRef = + SharedClientRef; using NRTThreadedDataSeriesClient = NRTThreadingAdaptor; From dde8232696d7e3f858e853e0dd612277b5c85e10 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 17:15:46 +0100 Subject: [PATCH 104/153] convert `dataseries` template operations for consistency --- include/clients/nrt/DataSeriesClient.hpp | 38 ++++++++++++++++-------- include/data/FluidDataSeries.hpp | 27 +++++------------ 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 5b329b058..036fed6f2 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -83,7 +83,10 @@ class DataSeriesClient return Error(WrongPointSize); } - mAlgorithm.addFrame(id, buf.samps(0, mAlgorithm.dims(), 0)); + RealVector frame(buf.numFrames()); + frame <<= buf.samps(0, mAlgorithm.dims(), 0); + + mAlgorithm.addFrame(id, frame); return OK(); } @@ -106,9 +109,10 @@ class DataSeriesClient return Error(WrongPointSize); } - return mAlgorithm.addSeries(id, buf.allFrames().transpose()) - ? OK() - : Error(DuplicateIdentifier); + RealMatrix series(buf.numFrames(), buf.numChans()); + series <<= buf.allFrames().transpose(); + + return mAlgorithm.addSeries(id, series) ? OK() : Error(DuplicateIdentifier); } MessageResult getFrame(string id, index time, BufferPtr data) const @@ -166,9 +170,11 @@ class DataSeriesClient if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); - return mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)) - ? OK() - : Error(PointNotFound); + RealVector frame(buf.numFrames()); + frame <<= buf.samps(0, mAlgorithm.dims(), 0); + + return mAlgorithm.updateFrame(id, time, frame) ? OK() + : Error(PointNotFound); } MessageResult updateSeries(string id, InputBufferPtr data) @@ -179,9 +185,10 @@ class DataSeriesClient if (!buf.exists()) return Error(InvalidBuffer); if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); - return mAlgorithm.updateSeries(id, buf.allFrames().transpose()) - ? OK() - : Error(PointNotFound); + RealMatrix series(buf.numFrames(), buf.numChans()); + series <<= buf.allFrames().transpose(); + + return mAlgorithm.updateSeries(id, series) ? OK() : Error(PointNotFound); } MessageResult setFrame(string id, index time, InputBufferPtr data) @@ -193,8 +200,10 @@ class DataSeriesClient if (!buf.exists()) return Error(InvalidBuffer); if (buf.numFrames() < mAlgorithm.dims()) return Error(WrongPointSize); - bool result = - mAlgorithm.updateFrame(id, time, buf.samps(0, mAlgorithm.dims(), 0)); + RealVector frame(buf.numFrames()); + frame <<= buf.samps(0, mAlgorithm.dims(), 0); + + bool result = mAlgorithm.updateFrame(id, time, frame); if (result) return OK(); } @@ -210,7 +219,10 @@ class DataSeriesClient if (!buf.exists()) return Error(InvalidBuffer); if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); - bool result = mAlgorithm.updateSeries(id, buf.allFrames().transpose()); + RealMatrix series(buf.numFrames(), buf.numChans()); + series <<= buf.allFrames().transpose(); + + bool result = mAlgorithm.updateSeries(id, series); if (result) return OK(); } diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index de89069c4..55f323aa7 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -66,11 +66,8 @@ class FluidDataSeries else { return false; } } - template - bool addFrame(idType const& id, FluidTensorView frame) + bool addFrame(idType const& id, FluidTensorView frame) { - static_assert(std::is_convertible::value, - "Can't convert between types"); assert(sameExtents(mDim, frame.descriptor())); auto pos = mIndex.find(id); @@ -86,13 +83,9 @@ class FluidDataSeries return true; } - template - bool addSeries(idType const& id, FluidTensorView series) + bool addSeries(idType const& id, + FluidTensorView series) { - static_assert(std::is_convertible::value, - "Can't convert between types"); - - // dont crete another view auto result = mIndex.insert({id, mData.size()}); if (!result.second) return false; @@ -148,12 +141,9 @@ class FluidDataSeries : FluidTensorView{nullptr, 0, 0, 0}; } - template - bool updateFrame(idType const& id, index time, FluidTensorView frame) + bool updateFrame(idType const& id, index time, + FluidTensorView frame) { - static_assert(std::is_convertible::value, - "Can't convert between types"); - auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; @@ -163,12 +153,9 @@ class FluidDataSeries return true; } - template - bool updateSeries(idType const& id, FluidTensorView series) + bool updateSeries(idType const& id, + FluidTensorView series) { - static_assert(std::is_convertible::value, - "Can't convert between types"); - auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; From 4c41387b9966fce18a4a0045e8b4c79d317d42b0 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 17:25:51 +0100 Subject: [PATCH 105/153] merge all relevant `data-series` into `dtime-warp` --- include/clients/nrt/DataSeriesClient.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index c47f593ad..caee8d0f1 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -404,10 +404,12 @@ class DataSeriesClient makeMessage("clear", &DataSeriesClient::clear), makeMessage("write", &DataSeriesClient::write), makeMessage("read", &DataSeriesClient::read), - makeMessage("getIds", &DataSeriesClient::getIds), - makeMessage("getDataSet", &DataSeriesClient::getDataSet), makeMessage("kNearest", &DataSeriesClient::kNearest), - makeMessage("kNearestDist", &DataSeriesClient::kNearestDist)); + makeMessage("kNearestDist", &DataSeriesClient::kNearestDist), + makeMessage("toBuffer", &DataSeriesClient::getSeries), + makeMessage("fromBuffer", &DataSeriesClient::setSeries), + makeMessage("getIds", &DataSeriesClient::getIds), + makeMessage("getDataSet", &DataSeriesClient::getDataSet)); } private: From 845b2182da3f2cf1601f5318155e6ce2e202b1ec Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 17:29:26 +0100 Subject: [PATCH 106/153] custom vector allocator for `knearestdist` --- include/clients/nrt/DataSeriesClient.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index caee8d0f1..161b78055 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -342,8 +342,8 @@ class DataSeriesClient FluidTensor series(buf.allFrames().transpose()); - std::vector indices(asUnsigned(mAlgorithm.size())); - std::vector distances(asUnsigned(mAlgorithm.size())); + rt::vector indices(asUnsigned(mAlgorithm.size())); + rt::vector distances(asUnsigned(mAlgorithm.size())); std::iota(indices.begin(), indices.end(), 0); From 48ef76e040b24639c09c34719bbc9963a70bc9f8 Mon Sep 17 00:00:00 2001 From: lewardo Date: Wed, 30 Aug 2023 17:45:59 +0100 Subject: [PATCH 107/153] classifier `predictpoint` message --- include/clients/nrt/DTWClassifierClient.hpp | 40 ++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 74cf703e9..22f0eeccc 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -61,7 +61,6 @@ void from_json(const nlohmann::json& j, DTWClassifierData& data) constexpr auto DTWClassifierParams = defineParameters( StringParam>("name", "Name"), LongParam("numNeighbours", "Number of Nearest Neighbours", 3, Min(1)), - EnumParam("weight", "Weight Neighbours by Distance", 1, "No", "Yes"), EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", "Sakoe-Chiba"), FloatParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), @@ -135,6 +134,45 @@ class DTWClassifierClient : public FluidBaseClient, MessageResult predictPoint(InputBufferPtr data) const { + index k = get(); + bool weight = get() > 0; + + if (k < 1) return Error(SmallK); + + BufferAdaptor::ReadAccess buf = data.get(); + RealMatrix series(buf.numFrames(), buf.numChans()); + rt::vector ds = mAlgorithm.series.getData(); + + if (buf.numChans() < mAlgorithm.series.dims()) + return Error(WrongPointSize); + + series <<= buf.allFrames().transpose(); + + rt::vector indices(asUnsigned(mAlgorithm.size())); + rt::vector distances(asUnsigned(mAlgorithm.size())); + + std::iota(indices.begin(), indices.end(), 0); + + algorithm::DTWConstraint constraint = + (algorithm::DTWConstraint) get(); + + std::transform(indices.begin(), indices.end(), distances.begin(), + [&](index i) { + return mAlgorithm.dtw.process(series, ds[i], constraint, + constraintParam(constraint)); + }); + + std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { + return distances[asUnsigned(a)] < distances[asUnsigned(b)]; + }); + + rt::unordered_map labels; + auto ids = mAlgorithm.series.getIds(); + + std::for_each(indices.begin(), indices.begin() + k, + [&](index i) { return labels[ids[i]]++; }); + + return; } MessageResult predict(InputDataSetClientRef source, From eb5838f5239bd6ccefff8dc96e6e002832f4cd73 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 09:48:06 +0100 Subject: [PATCH 108/153] fix templature correction --- include/data/FluidDataSeries.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 55f323aa7..3a2f344ca 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -73,7 +73,7 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) { - FluidTensorView newPoint(frame); + FluidTensorView newPoint(frame); return addSeries(id, newPoint); } From f1ede987b9738c02b6e30afdc395e25058593deb Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 09:48:06 +0100 Subject: [PATCH 109/153] fix templature correction --- include/data/FluidDataSeries.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 55f323aa7..3a2f344ca 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -73,7 +73,7 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) { - FluidTensorView newPoint(frame); + FluidTensorView newPoint(frame); return addSeries(id, newPoint); } From cdf7faacb521b74754acfa2ee0e673e7d7e8c92e Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 10:02:55 +0100 Subject: [PATCH 110/153] fix copy construction --- include/algorithms/public/DTW.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 1a28fd667..4d3edcc96 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -83,7 +83,7 @@ class DTW private: mutable RealMatrix mDistanceMetrics; - const index mPNorm{2}; + index mPNorm{2}; // P-Norm of the difference vector // Lp{vec} = (|vec[0]|^p + |vec[1]|^p + ... + |vec[n-1]|^p + |vec[n]|^p)^(1/p) From 3a237eeb7cc35e5cd811d286999b03553fa872c2 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 10:03:42 +0100 Subject: [PATCH 111/153] remove pnorm option (not a practically useful parameter) --- include/clients/nrt/DTWClient.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 2bb68cf49..59bd9c49a 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -25,8 +25,9 @@ constexpr auto DTWParams = defineParameters( StringParam>("name", "Name"), EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", "Sakoe-Chiba"), - FloatParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), - FloatParam("gradient", "Ikatura Parallelogram max gradient", 1, Min(1))); + LongParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), + FloatParam("gradient", "Ikatura Parallelogram max gradient", 1.0, + Min(1.0))); class DTWClient : public FluidBaseClient, OfflineIn, @@ -34,7 +35,7 @@ class DTWClient : public FluidBaseClient, ModelObject, public DataClient { - enum { kName, kPNorm, kConstraint, kRadius, kGradient }; + enum { kName, kConstraint, kRadius, kGradient }; public: using string = std::string; From 8ea77b4657304e301d09620f9864ab05fdbff22f Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 10:53:49 +0100 Subject: [PATCH 112/153] check if labels and series have matching ids --- include/clients/nrt/DTWClassifierClient.hpp | 23 +++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 22f0eeccc..ab8663d76 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -25,7 +25,7 @@ namespace dtwclassifier { struct DTWClassifierData { algorithm::DTW dtw; - FluidDataSeries series{1}; + FluidDataSeries series{0}; FluidDataSet labels{1}; index size() const { return labels.size(); } @@ -33,7 +33,7 @@ struct DTWClassifierData void clear() { labels = FluidDataSet(1); - series = FluidDataSeries(1); + series = FluidDataSeries(0); dtw.clear(); } @@ -72,7 +72,7 @@ class DTWClassifierClient : public FluidBaseClient, ModelObject, public DataClient { - enum { kName, kNumNeighbors, kWeight, kConstraint, kRadius, kGradient }; + enum { kName, kNumNeighbors, kConstraint, kRadius, kGradient }; public: using string = std::string; @@ -126,10 +126,21 @@ class DTWClassifierClient : public FluidBaseClient, if (dataSeries.size() != labelSet.size()) return Error(SizesDontMatch); - mAlgorithm.series = dataSeries; - mAlgorithm.labels = labelSet; + auto seriesIds = dataSeries.getIds(), labelIds = labelSet.getIds(); - return OK(); + // TODO: check if subset rather than all the same ids + bool everySeriesHasALabel = std::is_permutation( + seriesIds.begin(), seriesIds.end(), labelIds.begin()); + + if (everySeriesHasALabel) + { + mAlgorithm.series = dataSeries; + mAlgorithm.labels = labelSet; + + return OK(); + } + else + return Error(EmptyLabel); } MessageResult predictPoint(InputBufferPtr data) const From 5bc8aa9472cf3a828fa157b7fa2dc8ade474ce6d Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 11:31:09 +0100 Subject: [PATCH 113/153] `predictpoint` classifier message --- include/clients/nrt/DTWClassifierClient.hpp | 25 ++++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index ab8663d76..195c477b5 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -145,13 +145,12 @@ class DTWClassifierClient : public FluidBaseClient, MessageResult predictPoint(InputBufferPtr data) const { - index k = get(); - bool weight = get() > 0; - - if (k < 1) return Error(SmallK); + if (get() < 1) return Error(SmallK); + if (get() > mAlgorithm.size()) + return Error(LargeK); BufferAdaptor::ReadAccess buf = data.get(); - RealMatrix series(buf.numFrames(), buf.numChans()); + FluidTensor series(buf.numFrames(), buf.numChans()); rt::vector ds = mAlgorithm.series.getData(); if (buf.numChans() < mAlgorithm.series.dims()) @@ -168,7 +167,7 @@ class DTWClassifierClient : public FluidBaseClient, (algorithm::DTWConstraint) get(); std::transform(indices.begin(), indices.end(), distances.begin(), - [&](index i) { + [&series, &ds, &constraint, this](index i) { return mAlgorithm.dtw.process(series, ds[i], constraint, constraintParam(constraint)); }); @@ -177,13 +176,17 @@ class DTWClassifierClient : public FluidBaseClient, return distances[asUnsigned(a)] < distances[asUnsigned(b)]; }); - rt::unordered_map labels; - auto ids = mAlgorithm.series.getIds(); + rt::unordered_map labelCount; + FluidTensorView labels = mAlgorithm.labels.getData(); + + std::for_each(indices.begin(), indices.begin() + get(), + [&](index& i) { return labelCount[labels(i, 0)]++; }); - std::for_each(indices.begin(), indices.begin() + k, - [&](index i) { return labels[ids[i]]++; }); + auto result = std::max_element( + labelCount.begin(), labelCount.end(), + [](auto& left, auto& right) { return left.second < right.second; }); - return; + return result->first; } MessageResult predict(InputDataSetClientRef source, From e7193945f49bac37efa04454cac867de8c0a3710 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 11:51:32 +0100 Subject: [PATCH 114/153] private calculation member function --- include/clients/nrt/DTWClassifierClient.hpp | 55 +++++++++++++++++---- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 195c477b5..2783ecfcc 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -145,21 +145,16 @@ class DTWClassifierClient : public FluidBaseClient, MessageResult predictPoint(InputBufferPtr data) const { - if (get() < 1) return Error(SmallK); - if (get() > mAlgorithm.size()) - return Error(LargeK); - - BufferAdaptor::ReadAccess buf = data.get(); - FluidTensor series(buf.numFrames(), buf.numChans()); - rt::vector ds = mAlgorithm.series.getData(); + BufferAdaptor::ReadAccess buf = data.get(); + RealMatrix series(buf.numFrames(), buf.numChans()); if (buf.numChans() < mAlgorithm.series.dims()) return Error(WrongPointSize); series <<= buf.allFrames().transpose(); - rt::vector indices(asUnsigned(mAlgorithm.size())); - rt::vector distances(asUnsigned(mAlgorithm.size())); + return kNearestModeLabel(series); + } std::iota(indices.begin(), indices.end(), 0); @@ -222,6 +217,48 @@ class DTWClassifierClient : public FluidBaseClient, return 0.0; } + + std::string kNearestModeLabel(InputRealMatrixView series) const + { + index k = get(); + if (k < 1) return Error(SmallK); + if (k > mAlgorithm.size()) return Error(LargeK); + + rt::vector ds = mAlgorithm.series.getData(); + + if (series.cols() < mAlgorithm.series.dims()) + return Error(WrongPointSize); + + rt::vector indices(asUnsigned(mAlgorithm.size())); + rt::vector distances(asUnsigned(mAlgorithm.size())); + + std::iota(indices.begin(), indices.end(), 0); + + algorithm::DTWConstraint constraint = + (algorithm::DTWConstraint) get(); + + std::transform(indices.begin(), indices.end(), distances.begin(), + [&series, &ds, &constraint, this](index i) { + return mAlgorithm.dtw.process(series, ds[i], constraint, + constraintParam(constraint)); + }); + + std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { + return distances[asUnsigned(a)] < distances[asUnsigned(b)]; + }); + + rt::unordered_map labelCount; + FluidTensorView labels = mAlgorithm.labels.getData(); + + std::for_each(indices.begin(), indices.begin() + get(), + [&](index& i) { return labelCount[labels(i, 0)]++; }); + + auto result = std::max_element( + labelCount.begin(), labelCount.end(), + [](auto& left, auto& right) { return left.second < right.second; }); + + return result->first; + } }; using DTWClassifierRef = SharedClientRef; From 1160127fd2a60d872cac000bea4ceb8d6f232c01 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 11:56:04 +0100 Subject: [PATCH 115/153] `predict` message --- include/clients/nrt/DTWClassifierClient.hpp | 46 ++++++++++----------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 2783ecfcc..5e17f1662 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -156,37 +156,35 @@ class DTWClassifierClient : public FluidBaseClient, return kNearestModeLabel(series); } - std::iota(indices.begin(), indices.end(), 0); + MessageResult predict(InputDataSeriesClientRef source, + LabelSetClientRef dest) const + { + auto sourcePtr = source.get().lock(); + if (!sourcePtr) return Error(NoDataSet); - algorithm::DTWConstraint constraint = - (algorithm::DTWConstraint) get(); + auto destPtr = dest.get().lock(); + if (!destPtr) return Error(NoLabelSet); - std::transform(indices.begin(), indices.end(), distances.begin(), - [&series, &ds, &constraint, this](index i) { - return mAlgorithm.dtw.process(series, ds[i], constraint, - constraintParam(constraint)); - }); - - std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { - return distances[asUnsigned(a)] < distances[asUnsigned(b)]; - }); + auto dataSeries = sourcePtr->getDataSeries(); + if (dataSeries.size() == 0) return Error(EmptyDataSet); - rt::unordered_map labelCount; - FluidTensorView labels = mAlgorithm.labels.getData(); + if (dataSeries.pointSize() != mAlgorithm.series.dims()) + return Error(WrongPointSize); - std::for_each(indices.begin(), indices.begin() + get(), - [&](index& i) { return labelCount[labels(i, 0)]++; }); + if (mAlgorithm.size() == 0) return Error(NoDataFitted); - auto result = std::max_element( - labelCount.begin(), labelCount.end(), - [](auto& left, auto& right) { return left.second < right.second; }); + FluidTensorView ids = dataSeries.getIds(); + rt::vector data = dataSeries.getData(); + LabelSet result(1); - return result->first; - } + for (index i = 0; i < dataSeries.size(); i++) + { + StringVector label = {kNearestModeLabel(data[i])}; + result.add(ids(i), label); + } - MessageResult predict(InputDataSetClientRef source, - LabelSetClientRef dest) const - { + destPtr->setLabelSet(result); + return OK(); } From d6a2272905ee14aa7cdb204abfa72de0f58e9570 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 12:25:31 +0100 Subject: [PATCH 116/153] `predict` classifier message --- include/clients/nrt/DTWClassifierClient.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 5e17f1662..bd0653fc3 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -159,6 +159,7 @@ class DTWClassifierClient : public FluidBaseClient, MessageResult predict(InputDataSeriesClientRef source, LabelSetClientRef dest) const { + auto sourcePtr = source.get().lock(); if (!sourcePtr) return Error(NoDataSet); @@ -174,12 +175,11 @@ class DTWClassifierClient : public FluidBaseClient, if (mAlgorithm.size() == 0) return Error(NoDataFitted); FluidTensorView ids = dataSeries.getIds(); - rt::vector data = dataSeries.getData(); LabelSet result(1); for (index i = 0; i < dataSeries.size(); i++) { - StringVector label = {kNearestModeLabel(data[i])}; + StringVector label = {kNearestModeLabel(dataSeries.getSeries(ids[i]))}; result.add(ids(i), label); } @@ -216,7 +216,7 @@ class DTWClassifierClient : public FluidBaseClient, return 0.0; } - std::string kNearestModeLabel(InputRealMatrixView series) const + MessageResult kNearestModeLabel(InputRealMatrixView series) const { index k = get(); if (k < 1) return Error(SmallK); From d1cd2a097661a5b194314efe66c2b9ed8e0e406c Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 13:36:41 +0100 Subject: [PATCH 117/153] distance weighting --- include/clients/nrt/DTWClassifierClient.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index bd0653fc3..bd0352cc7 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -245,11 +245,13 @@ class DTWClassifierClient : public FluidBaseClient, return distances[asUnsigned(a)] < distances[asUnsigned(b)]; }); - rt::unordered_map labelCount; - FluidTensorView labels = mAlgorithm.labels.getData(); + rt::unordered_map labelCount; + FluidTensorView labels = mAlgorithm.labels.getData(); std::for_each(indices.begin(), indices.begin() + get(), - [&](index& i) { return labelCount[labels(i, 0)]++; }); + [&](index& i) { + return labelCount[labels(i, 0)] += 1.0 / distances[i]; + }); auto result = std::max_element( labelCount.begin(), labelCount.end(), From d84b2c9350f3e10a2fb978c0461c29dfabcde252 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 13:37:47 +0100 Subject: [PATCH 118/153] add `DTWClassifier` to clients --- FlucomaClients.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/FlucomaClients.cmake b/FlucomaClients.cmake index 333d4d855..ff602faa7 100644 --- a/FlucomaClients.cmake +++ b/FlucomaClients.cmake @@ -160,3 +160,4 @@ add_client(MLPRegressor clients/nrt/MLPRegressorClient.hpp CLASS NRTThreadedMLPR add_client(MLPClassifier clients/nrt/MLPClassifierClient.hpp CLASS NRTThreadedMLPClassifierClient GROUP MANIPULATION) add_client(Grid clients/nrt/GridClient.hpp CLASS NRTThreadedGridClient GROUP MANIPULATION) add_client(DTW clients/nrt/DTWClient.hpp CLASS NRTThreadedDTWClient GROUP MANIPULATION) +add_client(DTWClassifier clients/nrt/DTWClassifierClient.hpp CLASS NRTThreadedDTWClassifierClient GROUP MANIPULATION) From 5857e3abe9a275ed644ef4a35e7d25c45b78be81 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 13:38:04 +0100 Subject: [PATCH 119/153] ass `DTWRegressor` client --- FlucomaClients.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/FlucomaClients.cmake b/FlucomaClients.cmake index ff602faa7..9c6efcaaf 100644 --- a/FlucomaClients.cmake +++ b/FlucomaClients.cmake @@ -161,3 +161,4 @@ add_client(MLPClassifier clients/nrt/MLPClassifierClient.hpp CLASS NRTThreadedML add_client(Grid clients/nrt/GridClient.hpp CLASS NRTThreadedGridClient GROUP MANIPULATION) add_client(DTW clients/nrt/DTWClient.hpp CLASS NRTThreadedDTWClient GROUP MANIPULATION) add_client(DTWClassifier clients/nrt/DTWClassifierClient.hpp CLASS NRTThreadedDTWClassifierClient GROUP MANIPULATION) +add_client(DTWRegressor clients/nrt/DTWRegressorClient.hpp CLASS NRTThreadedDTWRegressorClient GROUP MANIPULATION) From ccc4342d921c3ce5a6ac4444a74ffcc1a3bc90d5 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 13:40:17 +0100 Subject: [PATCH 120/153] more logical `dims` forwarding --- include/clients/nrt/DTWClassifierClient.hpp | 11 +++++------ include/clients/nrt/DTWRegressorClient.hpp | 0 2 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 include/clients/nrt/DTWRegressorClient.hpp diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index bd0352cc7..63296ee9f 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -28,8 +28,8 @@ struct DTWClassifierData FluidDataSeries series{0}; FluidDataSet labels{1}; - index size() const { return labels.size(); } - index dims() const { return dtw.dims(); } + index size() const { return series.size(); } + index dims() const { return series.dims(); } void clear() { labels = FluidDataSet(1); @@ -148,7 +148,7 @@ class DTWClassifierClient : public FluidBaseClient, BufferAdaptor::ReadAccess buf = data.get(); RealMatrix series(buf.numFrames(), buf.numChans()); - if (buf.numChans() < mAlgorithm.series.dims()) + if (buf.numChans() < mAlgorithm.dims()) return Error(WrongPointSize); series <<= buf.allFrames().transpose(); @@ -169,7 +169,7 @@ class DTWClassifierClient : public FluidBaseClient, auto dataSeries = sourcePtr->getDataSeries(); if (dataSeries.size() == 0) return Error(EmptyDataSet); - if (dataSeries.pointSize() != mAlgorithm.series.dims()) + if (dataSeries.pointSize() != mAlgorithm.dims()) return Error(WrongPointSize); if (mAlgorithm.size() == 0) return Error(NoDataFitted); @@ -224,8 +224,7 @@ class DTWClassifierClient : public FluidBaseClient, rt::vector ds = mAlgorithm.series.getData(); - if (series.cols() < mAlgorithm.series.dims()) - return Error(WrongPointSize); + if (series.cols() < mAlgorithm.dims()) return Error(WrongPointSize); rt::vector indices(asUnsigned(mAlgorithm.size())); rt::vector distances(asUnsigned(mAlgorithm.size())); diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp new file mode 100644 index 000000000..e69de29bb From 1cc3c9237b829da7a93fb0c4ec73002b1cf62813 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 13:45:15 +0100 Subject: [PATCH 121/153] interface calls for no change --- include/clients/nrt/DTWClassifierClient.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 63296ee9f..591f1fc53 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -128,7 +128,6 @@ class DTWClassifierClient : public FluidBaseClient, auto seriesIds = dataSeries.getIds(), labelIds = labelSet.getIds(); - // TODO: check if subset rather than all the same ids bool everySeriesHasALabel = std::is_permutation( seriesIds.begin(), seriesIds.end(), labelIds.begin()); From dbc11e7fb3c8443e2851dca064f38b1ae0c28f48 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 14:17:09 +0100 Subject: [PATCH 122/153] boilerplate from classifier --- include/clients/nrt/DTWRegressorClient.hpp | 175 +++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index e69de29bb..6b1f6fba8 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -0,0 +1,175 @@ +/* +Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) +Copyright University of Huddersfield. +Licensed under the BSD-3 License. +See license.md file in the project root for full license information. +This project has received funding from the European Research Council (ERC) +under the European Union’s Horizon 2020 research and innovation programme +(grant agreement No 725899). +*/ + +#pragma once + +#include "DataSeriesClient.hpp" +#include "DataSetClient.hpp" +#include "NRTClient.hpp" +#include "../util/FluidEigenMappings.hpp" +#include "../../algorithms/public/DTW.hpp" +#include "../../data/FluidDataSeries.hpp" +#include "../../data/FluidDataSet.hpp" +#include "../../data/FluidMemory.hpp" +#include "../../data/FluidTensor.hpp" +#include "../../data/TensorTypes.hpp" + +namespace fluid { +namespace client { +namespace dtwclassifier { + +struct DTWRegressorData +{ + algorithm::DTW dtw; + FluidDataSeries series{0}; + FluidDataSet mappings{0}; + + index size() const { return series.size(); } + index dims() const { return series.dims(); } + + void clear() + { + mappings = FluidDataSet(0); + series = FluidDataSeries(0); + + dtw.clear(); + } + bool initialized() const { return dtw.initialized(); } +}; + +void to_json(nlohmann::json& j, const DTWRegressorData& data) +{ + j["mappings"] = data.mappings; + j["series"] = data.series; +} + +bool check_json(const nlohmann::json& j, const DTWRegressorData&) +{ + return fluid::check_json(j, {"mappings", "series"}, + {JSONTypes::OBJECT, JSONTypes::OBJECT}); +} + +void from_json(const nlohmann::json& j, DTWRegressorData& data) +{ + data.series = j.at("series").get>(); + data.mappings = j.at("mappings").get>(); +} + +constexpr auto DTWClassifierParams = defineParameters( + StringParam>("name", "Name"), + LongParam("numNeighbours", "Number of Nearest Neighbours", 3, Min(1)), + EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", + "Sakoe-Chiba"), + FloatParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), + FloatParam("gradient", "Ikatura Parallelogram max gradient", 1, Min(1))); + +class DTWRegressorClient : public FluidBaseClient, + OfflineIn, + OfflineOut, + ModelObject, + public DataClient +{ + enum { kName, kNumNeighbors, kConstraint, kRadius, kGradient }; + +public: + using string = std::string; + using BufferPtr = std::shared_ptr; + using InputBufferPtr = std::shared_ptr; + using LabelSet = FluidDataSet; + using DataSet = FluidDataSet; + using StringVector = FluidTensor; + + using ParamDescType = decltype(DTWClassifierParams); + + using ParamSetViewType = ParameterSetView; + std::reference_wrapper mParams; + + void setParams(ParamSetViewType& p) { mParams = p; } + + template + auto& get() const + { + return mParams.get().template get(); + } + + static constexpr auto& getParameterDescriptors() + { + return DTWClassifierParams; + } + + DTWRegressorClient(ParamSetViewType& p, FluidContext&) : mParams(p) {} + + template + Result process(FluidContext&) + { + return {}; + } + + // not fitting anything, you just set the input series and output labels + MessageResult fit(InputDataSeriesClientRef dataSeriesClient, + InputDataSetClientRef dataSetClient) + { + } + + MessageResult predictPoint(InputBufferPtr data) const + { + } + + MessageResult predict(InputDataSeriesClientRef source, + DataSetClientRef dest) const + { + return OK(); + } + + + static auto getMessageDescriptors() + { + return defineMessages( + makeMessage("fit", &DTWRegressorClient::fit), + makeMessage("predict", &DTWRegressorClient::predict), + makeMessage("predictPoint", &DTWRegressorClient::predictPoint), + makeMessage("clear", &DTWRegressorClient::clear), + makeMessage("size", &DTWRegressorClient::size), + makeMessage("load", &DTWRegressorClient::load), + makeMessage("dump", &DTWRegressorClient::dump), + makeMessage("write", &DTWRegressorClient::write), + makeMessage("read", &DTWRegressorClient::read)); + } + +private: + float constraintParam(algorithm::DTWConstraint constraint) const + { + using namespace algorithm; + + switch (constraint) + { + case DTWConstraint::kIkatura: return get(); + case DTWConstraint::kSakoeChiba: return get(); + } + + return 0.0; + } + + MessageResult + kNearestWeightedSum(InputRealMatrixView series, + Allocator& alloc = FluidDefaultAllocator()) const + { + } +}; + +using DTWRegressorRef = SharedClientRef; + +} // namespace dtwclassifier + +using NRTThreadedDTWRegressorClient = + NRTThreadingAdaptor; + +} // namespace client +} // namespace fluid From fd4de2106884a9d9d8607ec4b1b237bc36a8da90 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 14:17:21 +0100 Subject: [PATCH 123/153] weighted sum of k nearest points --- include/clients/nrt/DTWRegressorClient.hpp | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index 6b1f6fba8..a6751009d 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -161,6 +161,46 @@ class DTWRegressorClient : public FluidBaseClient, kNearestWeightedSum(InputRealMatrixView series, Allocator& alloc = FluidDefaultAllocator()) const { + using namespace algorithm::_impl; + + index k = get(); + if (k < 1) return Error(SmallK); + if (k > mAlgorithm.size()) return Error(LargeK); + + rt::vector ds = mAlgorithm.series.getData(); + + if (series.cols() < mAlgorithm.series.dims()) + return Error(WrongPointSize); + + rt::vector indices(asUnsigned(mAlgorithm.size())); + rt::vector distances(asUnsigned(mAlgorithm.size())); + + std::iota(indices.begin(), indices.end(), 0); + + algorithm::DTWConstraint constraint = + (algorithm::DTWConstraint) get(); + + std::transform(indices.begin(), indices.end(), distances.begin(), + [&series, &ds, &constraint, this](index i) { + return mAlgorithm.dtw.process(series, ds[i], constraint, + constraintParam(constraint)); + }); + + std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { + return distances[asUnsigned(a)] < distances[asUnsigned(b)]; + }); + + + ScopedEigenMap result(mAlgorithm.mappings.dims(), alloc); + InputRealMatrixView mappings = mAlgorithm.mappings.getData(); + + std::for_each(indices.begin(), indices.begin() + get(), + [&](index& i) { + return result += distances[i] * + asEigen(mappings.row(i)); + }); + + return RealVector(asFluid(result)); } }; From 649f4e022e19248706ce14a56a6baeb03259fe7b Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 14:17:29 +0100 Subject: [PATCH 124/153] predict dataset --- include/clients/nrt/DTWRegressorClient.hpp | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index a6751009d..c1a1bdd6a 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -125,6 +125,39 @@ class DTWRegressorClient : public FluidBaseClient, MessageResult predict(InputDataSeriesClientRef source, DataSetClientRef dest) const { + + auto sourcePtr = source.get().lock(); + if (!sourcePtr) return Error(NoDataSet); + + auto destPtr = dest.get().lock(); + if (!destPtr) return Error(NoLabelSet); + + auto dataSeries = sourcePtr->getDataSeries(); + if (dataSeries.size() == 0) return Error(EmptyDataSet); + + if (dataSeries.pointSize() != mAlgorithm.series.dims()) + return Error(WrongPointSize); + + if (mAlgorithm.size() == 0) return Error(NoDataFitted); + + FluidTensorView ids = dataSeries.getIds(); + DataSet result(mAlgorithm.mappings.dims()); + + for (index i = 0; i < dataSeries.size(); i++) + { + MessageResult point = + kNearestWeightedSum(dataSeries.getSeries(ids[i])); + + if (point.ok()) + { + RealVector pred = point; + result.add(ids(i), pred); + } + else + return MessageResult{Result::Status::kError, point.message()}; + } + + destPtr->setDataSet(result); return OK(); } From cc8199eab621573fa1b64089277c46470b615653 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 14:17:34 +0100 Subject: [PATCH 125/153] predict buffer --- include/clients/nrt/DTWRegressorClient.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index c1a1bdd6a..374e3fb30 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -120,6 +120,15 @@ class DTWRegressorClient : public FluidBaseClient, MessageResult predictPoint(InputBufferPtr data) const { + BufferAdaptor::ReadAccess buf = data.get(); + RealMatrix series(buf.numFrames(), buf.numChans()); + + if (buf.numChans() < mAlgorithm.series.dims()) + return Error(WrongPointSize); + + series <<= buf.allFrames().transpose(); + + return kNearestWeightedSum(series); } MessageResult predict(InputDataSeriesClientRef source, From 0e3920fb21a9dc131632584ee504258023ab8a6d Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 14:17:51 +0100 Subject: [PATCH 126/153] set mapping space (`fit`) --- include/clients/nrt/DTWRegressorClient.hpp | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index 374e3fb30..3c7b03651 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -116,6 +116,34 @@ class DTWRegressorClient : public FluidBaseClient, MessageResult fit(InputDataSeriesClientRef dataSeriesClient, InputDataSetClientRef dataSetClient) { + auto dataSeriesClientPtr = dataSeriesClient.get().lock(); + if (!dataSeriesClientPtr) return Error(NoDataSet); + + auto dataSetPtr = dataSetClient.get().lock(); + if (!dataSetPtr) return Error(NoDataSet); + + auto dataSeries = dataSeriesClientPtr->getDataSeries(); + if (dataSeries.size() == 0) return Error(EmptyDataSet); + + auto dataSet = dataSetPtr->getDataSet(); + if (dataSet.size() == 0) return Error(EmptyLabelSet); + + if (dataSeries.size() != dataSet.size()) return Error(SizesDontMatch); + + auto seriesIds = dataSeries.getIds(), mappingIds = dataSet.getIds(); + + bool everySeriesHasALabel = std::is_permutation( + seriesIds.begin(), seriesIds.end(), mappingIds.begin()); + + if (everySeriesHasALabel) + { + mAlgorithm.series = dataSeries; + mAlgorithm.mappings = dataSet; + + return OK(); + } + else + return Error(PointNotFound); } MessageResult predictPoint(InputBufferPtr data) const From 46db66be0f23863e086b1e977159365ea9987319 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 14:22:21 +0100 Subject: [PATCH 127/153] actually changing the name might avoid bugs --- include/clients/nrt/DTWRegressorClient.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index 3c7b03651..9b7613e6d 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -62,7 +62,7 @@ void from_json(const nlohmann::json& j, DTWRegressorData& data) data.mappings = j.at("mappings").get>(); } -constexpr auto DTWClassifierParams = defineParameters( +constexpr auto DTWRegressorParams = defineParameters( StringParam>("name", "Name"), LongParam("numNeighbours", "Number of Nearest Neighbours", 3, Min(1)), EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", @@ -86,7 +86,7 @@ class DTWRegressorClient : public FluidBaseClient, using DataSet = FluidDataSet; using StringVector = FluidTensor; - using ParamDescType = decltype(DTWClassifierParams); + using ParamDescType = decltype(DTWRegressorParams); using ParamSetViewType = ParameterSetView; std::reference_wrapper mParams; @@ -101,7 +101,7 @@ class DTWRegressorClient : public FluidBaseClient, static constexpr auto& getParameterDescriptors() { - return DTWClassifierParams; + return DTWRegressorParams; } DTWRegressorClient(ParamSetViewType& p, FluidContext&) : mParams(p) {} From b24ad380d043d3df521c8a730f03ac495ab28283 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 14:22:54 +0100 Subject: [PATCH 128/153] ohh so you actually have to give the correct path for includes --- include/clients/nrt/DTWRegressorClient.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index 9b7613e6d..12271226c 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -13,8 +13,8 @@ under the European Union’s Horizon 2020 research and innovation programme #include "DataSeriesClient.hpp" #include "DataSetClient.hpp" #include "NRTClient.hpp" -#include "../util/FluidEigenMappings.hpp" #include "../../algorithms/public/DTW.hpp" +#include "../../algorithms/util/FluidEigenMappings.hpp" #include "../../data/FluidDataSeries.hpp" #include "../../data/FluidDataSet.hpp" #include "../../data/FluidMemory.hpp" From e94dd03c07bdc700113f1a208386b4cb1e49cd18 Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 14:40:12 +0100 Subject: [PATCH 129/153] prevent distance 0 from losing sanity --- include/clients/nrt/DTWClassifierClient.hpp | 3 ++- include/clients/nrt/DTWRegressorClient.hpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 591f1fc53..d0b136c32 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -235,7 +235,8 @@ class DTWClassifierClient : public FluidBaseClient, std::transform(indices.begin(), indices.end(), distances.begin(), [&series, &ds, &constraint, this](index i) { - return mAlgorithm.dtw.process(series, ds[i], constraint, + return std::numeric_limits::epsilon() + + mAlgorithm.dtw.process(series, ds[i], constraint, constraintParam(constraint)); }); diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index 12271226c..bfdd97aab 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -252,7 +252,8 @@ class DTWRegressorClient : public FluidBaseClient, std::transform(indices.begin(), indices.end(), distances.begin(), [&series, &ds, &constraint, this](index i) { - return mAlgorithm.dtw.process(series, ds[i], constraint, + return std::numeric_limits::epsilon() + + mAlgorithm.dtw.process(series, ds[i], constraint, constraintParam(constraint)); }); From 91c9f6b8a4d8ab159825a9b47c94d4ff7c6e1f4b Mon Sep 17 00:00:00 2001 From: lewardo Date: Thu, 31 Aug 2023 14:40:27 +0100 Subject: [PATCH 130/153] perhaps weighting in the corrent direction may be helpful --- include/clients/nrt/DTWRegressorClient.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index bfdd97aab..ca1dcafc4 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -261,16 +261,19 @@ class DTWRegressorClient : public FluidBaseClient, return distances[asUnsigned(a)] < distances[asUnsigned(b)]; }); - ScopedEigenMap result(mAlgorithm.mappings.dims(), alloc); InputRealMatrixView mappings = mAlgorithm.mappings.getData(); + double totalWeight = 0.0; std::for_each(indices.begin(), indices.begin() + get(), [&](index& i) { - return result += distances[i] * - asEigen(mappings.row(i)); + double weight = 1.0 / distances[i]; + + totalWeight += weight; + result += asEigen(mappings.row(i)) * weight; }); + result.noalias() = result * (1 / totalWeight); return RealVector(asFluid(result)); } }; From 3a59eaea19a2b37d41f82c1d3dd4d4efc811e132 Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 1 Sep 2023 08:37:42 +0100 Subject: [PATCH 131/153] return max of distance and epsilon, rather than sum thereof --- include/clients/nrt/DTWClassifierClient.hpp | 13 +++++++------ include/clients/nrt/DTWRegressorClient.hpp | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index d0b136c32..9c7bd0d90 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -233,12 +233,13 @@ class DTWClassifierClient : public FluidBaseClient, algorithm::DTWConstraint constraint = (algorithm::DTWConstraint) get(); - std::transform(indices.begin(), indices.end(), distances.begin(), - [&series, &ds, &constraint, this](index i) { - return std::numeric_limits::epsilon() + - mAlgorithm.dtw.process(series, ds[i], constraint, - constraintParam(constraint)); - }); + std::transform( + indices.begin(), indices.end(), distances.begin(), + [&series, &ds, &constraint, this](index i) { + double dist = mAlgorithm.dtw.process(series, ds[i], constraint, + constraintParam(constraint)); + return std::max(std::numeric_limits::epsilon(), dist); + }); std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { return distances[asUnsigned(a)] < distances[asUnsigned(b)]; diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index ca1dcafc4..38b6724fd 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -250,12 +250,13 @@ class DTWRegressorClient : public FluidBaseClient, algorithm::DTWConstraint constraint = (algorithm::DTWConstraint) get(); - std::transform(indices.begin(), indices.end(), distances.begin(), - [&series, &ds, &constraint, this](index i) { - return std::numeric_limits::epsilon() + - mAlgorithm.dtw.process(series, ds[i], constraint, - constraintParam(constraint)); - }); + std::transform( + indices.begin(), indices.end(), distances.begin(), + [&series, &ds, &constraint, this](index i) { + double dist = mAlgorithm.dtw.process(series, ds[i], constraint, + constraintParam(constraint)); + return std::max(std::numeric_limits::epsilon(), dist); + }); std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { return distances[asUnsigned(a)] < distances[asUnsigned(b)]; From b84749405910a5aad1486639a430a16d25583929 Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 5 Sep 2023 15:46:21 +0100 Subject: [PATCH 132/153] consistent printing terminology --- include/data/FluidDataSeries.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 3a2f344ca..f343faa50 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -316,8 +316,8 @@ class FluidDataSeries if (size() == 0) return "{}"; result << endl - << "points: " << size() << endl - << "frame size: " << pointSize() << endl; + << "series: " << size() << endl + << "cols: " << pointSize() << endl; if (size() < maxRows) { From c372c223c1b24f792e15dcecc52a70f73a437ce3 Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 5 Sep 2023 15:46:53 +0100 Subject: [PATCH 133/153] check if id exists before resizing buffer --- include/clients/nrt/DataSeriesClient.hpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 036fed6f2..f6f9abd62 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -145,15 +145,16 @@ class DataSeriesClient BufferAdaptor::Access buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); - Result resizeResult = buf.resize(mAlgorithm.getNumFrames(id), - mAlgorithm.dims(), buf.sampleRate()); + index numFrames = mAlgorithm.getNumFrames(id); + if (numFrames < 0) return Error(PointNotFound); + + Result resizeResult = + buf.resize(numFrames, mAlgorithm.dims(), buf.sampleRate()); if (!resizeResult.ok()) return {resizeResult.status(), resizeResult.message()}; - RealMatrix point(mAlgorithm.getNumFrames(id), mAlgorithm.dims()); - point <<= buf.allFrames().transpose(); - - bool result = mAlgorithm.getSeries(id, point); + RealMatrix point(numFrames, mAlgorithm.dims()); + bool result = mAlgorithm.getSeries(id, point); if (result) { buf.allFrames() <<= point.transpose(); From dc4c2d3174becf5b153c2092edb1827f483ea2af Mon Sep 17 00:00:00 2001 From: lewardo Date: Tue, 5 Sep 2023 15:48:01 +0100 Subject: [PATCH 134/153] squash merge `data-series` into `dtime-warp` --- include/clients/nrt/DataSeriesClient.hpp | 13 +++++++------ include/data/FluidDataSeries.hpp | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 161b78055..d4930988e 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -146,15 +146,16 @@ class DataSeriesClient BufferAdaptor::Access buf(data.get()); if (!buf.exists()) return Error(InvalidBuffer); - Result resizeResult = buf.resize(mAlgorithm.getNumFrames(id), - mAlgorithm.dims(), buf.sampleRate()); + index numFrames = mAlgorithm.getNumFrames(id); + if (numFrames < 0) return Error(PointNotFound); + + Result resizeResult = + buf.resize(numFrames, mAlgorithm.dims(), buf.sampleRate()); if (!resizeResult.ok()) return {resizeResult.status(), resizeResult.message()}; - RealMatrix point(mAlgorithm.getNumFrames(id), mAlgorithm.dims()); - point <<= buf.allFrames().transpose(); - - bool result = mAlgorithm.getSeries(id, point); + RealMatrix point(numFrames, mAlgorithm.dims()); + bool result = mAlgorithm.getSeries(id, point); if (result) { buf.allFrames() <<= point.transpose(); diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 3a2f344ca..f343faa50 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -316,8 +316,8 @@ class FluidDataSeries if (size() == 0) return "{}"; result << endl - << "points: " << size() << endl - << "frame size: " << pointSize() << endl; + << "series: " << size() << endl + << "cols: " << pointSize() << endl; if (size() < maxRows) { From b8eb7863fd45cd4c6d1bb805d26c9efca96f58cd Mon Sep 17 00:00:00 2001 From: lewardo Date: Fri, 8 Sep 2023 09:35:05 +0100 Subject: [PATCH 135/153] buffer `predictpoint` output --- include/clients/nrt/DTWRegressorClient.hpp | 30 +++++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index 38b6724fd..a0f957343 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -146,17 +146,33 @@ class DTWRegressorClient : public FluidBaseClient, return Error(PointNotFound); } - MessageResult predictPoint(InputBufferPtr data) const + MessageResult predictPoint(InputBufferPtr in, BufferPtr out) const { - BufferAdaptor::ReadAccess buf = data.get(); - RealMatrix series(buf.numFrames(), buf.numChans()); + if (!in || !out) return Error(NoBuffer); - if (buf.numChans() < mAlgorithm.series.dims()) - return Error(WrongPointSize); + BufferAdaptor::ReadAccess inBuf = in.get(); + BufferAdaptor::Access outBuf = out.get(); + + if (!inBuf.exists()) return Error(InvalidBuffer); + if (!outBuf.exists()) return Error(InvalidBuffer); + + if (inBuf.numChans() < mAlgorithm.series.dims()) + return Error(WrongPointSize); - series <<= buf.allFrames().transpose(); + Result resizeResult = + outBuf.resize(mAlgorithm.mappings.dims(), 1, inBuf.sampleRate()); + if (!resizeResult.ok()) return Error(BufferAlloc); - return kNearestWeightedSum(series); + RealMatrix series(inBuf.numFrames(), inBuf.numChans()); + series <<= inBuf.allFrames().transpose(); + + MessageResult result = kNearestWeightedSum(series); + if (result.ok()) + outBuf.samps(0, result.value().size(), 0) <<= result.value(); + else + return Error(result.message()); + + return OK(); } MessageResult predict(InputDataSeriesClientRef source, From 1e2bd33e15a4fbedaf432e00a927c0cd79bd0c3a Mon Sep 17 00:00:00 2001 From: lewardo Date: Sat, 9 Sep 2023 13:23:16 +0100 Subject: [PATCH 136/153] remove lpnorm argument to fix docs --- include/clients/nrt/DataSeriesClient.hpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index d4930988e..18aa0ed5a 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -285,8 +285,8 @@ class DataSeriesClient return OK(); } - MessageResult> - kNearest(InputBufferPtr data, index nNeighbours, index p = 2) const + MessageResult> kNearest(InputBufferPtr data, + index nNeighbours) const { // check for nNeighbours > 0 and < size of DS if (nNeighbours > mAlgorithm.size()) @@ -307,10 +307,9 @@ class DataSeriesClient auto ds = mAlgorithm.getData(); - std::transform(indices.begin(), indices.end(), distances.begin(), - [&series, &ds, &p, this](index i) { - return distance(series, ds[i], p); - }); + std::transform( + indices.begin(), indices.end(), distances.begin(), + [&series, &ds, this](index i) { return distance(series, ds[i], 2); }); std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { return distances[asUnsigned(a)] < distances[asUnsigned(b)]; @@ -328,8 +327,8 @@ class DataSeriesClient return labels; } - MessageResult> - kNearestDist(InputBufferPtr data, index nNeighbours, index p = 2) const + MessageResult> kNearestDist(InputBufferPtr data, + index nNeighbours) const { // check for nNeighbours > 0 and < size of DS if (nNeighbours > mAlgorithm.size()) @@ -350,10 +349,9 @@ class DataSeriesClient auto ds = mAlgorithm.getData(); - std::transform(indices.begin(), indices.end(), distances.begin(), - [&series, &ds, &p, this](index i) { - return distance(series, ds[i], p); - }); + std::transform( + indices.begin(), indices.end(), distances.begin(), + [&series, &ds, this](index i) { return distance(series, ds[i], 2); }); std::sort(indices.begin(), indices.end(), [&distances](index a, index b) { return distances[asUnsigned(a)] < distances[asUnsigned(b)]; From bdc9fa4515f9a56e9b2a83e427826cc3464992e1 Mon Sep 17 00:00:00 2001 From: lewardo Date: Sat, 9 Sep 2023 13:50:16 +0100 Subject: [PATCH 137/153] remove `toBuffer` and `fromBuffer` aliases to unconfuse --- include/clients/nrt/DataSeriesClient.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 18aa0ed5a..b351be8e5 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -405,8 +405,6 @@ class DataSeriesClient makeMessage("read", &DataSeriesClient::read), makeMessage("kNearest", &DataSeriesClient::kNearest), makeMessage("kNearestDist", &DataSeriesClient::kNearestDist), - makeMessage("toBuffer", &DataSeriesClient::getSeries), - makeMessage("fromBuffer", &DataSeriesClient::setSeries), makeMessage("getIds", &DataSeriesClient::getIds), makeMessage("getDataSet", &DataSeriesClient::getDataSet)); } From 6173b9c1d632a892c9f32dd5a9fb5abbcad76ee7 Mon Sep 17 00:00:00 2001 From: lewardo Date: Sat, 9 Sep 2023 13:50:16 +0100 Subject: [PATCH 138/153] remove `toBuffer` and `fromBuffer` aliases to unconfuse --- include/clients/nrt/DataSeriesClient.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index f6f9abd62..b8435cc4e 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -321,8 +321,6 @@ class DataSeriesClient makeMessage("clear", &DataSeriesClient::clear), makeMessage("write", &DataSeriesClient::write), makeMessage("read", &DataSeriesClient::read), - makeMessage("toBuffer", &DataSeriesClient::getSeries), - makeMessage("fromBuffer", &DataSeriesClient::setSeries), makeMessage("getIds", &DataSeriesClient::getIds), makeMessage("getDataSet", &DataSeriesClient::getDataSet)); } From c800a96cf94f0ae3c1286eef32297c1a2b78b88b Mon Sep 17 00:00:00 2001 From: lewardo Date: Sat, 9 Sep 2023 18:37:48 +0100 Subject: [PATCH 139/153] added proper dataseries error messages --- include/clients/nrt/CommonResults.hpp | 2 ++ include/clients/nrt/DataSeriesClient.hpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/CommonResults.hpp b/include/clients/nrt/CommonResults.hpp index 6195fa6ba..7d719c07b 100644 --- a/include/clients/nrt/CommonResults.hpp +++ b/include/clients/nrt/CommonResults.hpp @@ -28,8 +28,10 @@ static const std::string LargeK{"k is too large"}; static const std::string SmallDim{"Number of dimensions is too small"}; static const std::string LargeDim{"Number of dimensions is too large"}; static const std::string EmptyDataSet{"DataSet is empty"}; +static const std::string EmptyDataSeries{"DataSeries is empty"}; static const std::string EmptyLabelSet{"LabelSet is empty"}; static const std::string NoDataSet{"DataSet does not exist"}; +static const std::string NoDataSeries{"DataSeries does not exist"}; static const std::string NoLabelSet{"LabelSet does not exist"}; static const std::string NoDataFitted{"No data fitted"}; static const std::string NotEnoughData{"Not enough data"}; diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index b8435cc4e..d458ee1cb 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -245,10 +245,10 @@ class DataSeriesClient bool overwrite) { auto dataseriesClientPtr = dataseriesClient.get().lock(); - if (!dataseriesClientPtr) return Error(NoDataSet); + if (!dataseriesClientPtr) return Error(NoDataSeries); auto srcDataSeries = dataseriesClientPtr->getDataSeries(); - if (srcDataSeries.size() == 0) return Error(EmptyDataSet); + if (srcDataSeries.size() == 0) return Error(EmptyDataSeries); if (srcDataSeries.pointSize() != mAlgorithm.pointSize()) return Error(WrongPointSize); @@ -278,7 +278,7 @@ class DataSeriesClient MessageResult getIds(LabelSetClientRef dest) { auto destPtr = dest.get().lock(); - if (!destPtr) return Error(NoDataSet); + if (!destPtr) return Error(NoLabelSet); destPtr->setLabelSet(getIdsLabelSet()); return OK(); From 8a9a059c5a07564124770a56cf8e9005c736fc73 Mon Sep 17 00:00:00 2001 From: lewardo Date: Sat, 9 Sep 2023 18:37:48 +0100 Subject: [PATCH 140/153] added proper dataseries error messages --- include/clients/nrt/CommonResults.hpp | 2 ++ include/clients/nrt/DataSeriesClient.hpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/clients/nrt/CommonResults.hpp b/include/clients/nrt/CommonResults.hpp index 6195fa6ba..7d719c07b 100644 --- a/include/clients/nrt/CommonResults.hpp +++ b/include/clients/nrt/CommonResults.hpp @@ -28,8 +28,10 @@ static const std::string LargeK{"k is too large"}; static const std::string SmallDim{"Number of dimensions is too small"}; static const std::string LargeDim{"Number of dimensions is too large"}; static const std::string EmptyDataSet{"DataSet is empty"}; +static const std::string EmptyDataSeries{"DataSeries is empty"}; static const std::string EmptyLabelSet{"LabelSet is empty"}; static const std::string NoDataSet{"DataSet does not exist"}; +static const std::string NoDataSeries{"DataSeries does not exist"}; static const std::string NoLabelSet{"LabelSet does not exist"}; static const std::string NoDataFitted{"No data fitted"}; static const std::string NotEnoughData{"Not enough data"}; diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index b351be8e5..7fc94fd14 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -246,10 +246,10 @@ class DataSeriesClient bool overwrite) { auto dataseriesClientPtr = dataseriesClient.get().lock(); - if (!dataseriesClientPtr) return Error(NoDataSet); + if (!dataseriesClientPtr) return Error(NoDataSeries); auto srcDataSeries = dataseriesClientPtr->getDataSeries(); - if (srcDataSeries.size() == 0) return Error(EmptyDataSet); + if (srcDataSeries.size() == 0) return Error(EmptyDataSeries); if (srcDataSeries.pointSize() != mAlgorithm.pointSize()) return Error(WrongPointSize); @@ -279,7 +279,7 @@ class DataSeriesClient MessageResult getIds(LabelSetClientRef dest) { auto destPtr = dest.get().lock(); - if (!destPtr) return Error(NoDataSet); + if (!destPtr) return Error(NoLabelSet); destPtr->setLabelSet(getIdsLabelSet()); return OK(); From 4cfc4f7520a780bf78dca663f55ae256fe376173 Mon Sep 17 00:00:00 2001 From: lewardo Date: Sat, 9 Sep 2023 18:41:24 +0100 Subject: [PATCH 141/153] proper dataseries error messages --- include/clients/nrt/DTWClassifierClient.hpp | 8 ++++---- include/clients/nrt/DTWClient.hpp | 4 ++-- include/clients/nrt/DTWRegressorClient.hpp | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 9c7bd0d90..dc750c71d 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -113,13 +113,13 @@ class DTWClassifierClient : public FluidBaseClient, InputLabelSetClientRef labelSetClient) { auto dataSeriesClientPtr = dataSeriesClient.get().lock(); - if (!dataSeriesClientPtr) return Error(NoDataSet); + if (!dataSeriesClientPtr) return Error(NoDataSeries); auto labelSetPtr = labelSetClient.get().lock(); if (!labelSetPtr) return Error(NoLabelSet); auto dataSeries = dataSeriesClientPtr->getDataSeries(); - if (dataSeries.size() == 0) return Error(EmptyDataSet); + if (dataSeries.size() == 0) return Error(EmptyDataSeries); auto labelSet = labelSetPtr->getLabelSet(); if (labelSet.size() == 0) return Error(EmptyLabelSet); @@ -160,13 +160,13 @@ class DTWClassifierClient : public FluidBaseClient, { auto sourcePtr = source.get().lock(); - if (!sourcePtr) return Error(NoDataSet); + if (!sourcePtr) return Error(NoDataSeries); auto destPtr = dest.get().lock(); if (!destPtr) return Error(NoLabelSet); auto dataSeries = sourcePtr->getDataSeries(); - if (dataSeries.size() == 0) return Error(EmptyDataSet); + if (dataSeries.size() == 0) return Error(EmptyDataSeries); if (dataSeries.pointSize() != mAlgorithm.dims()) return Error(WrongPointSize); diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 59bd9c49a..067fa3aaa 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -75,10 +75,10 @@ class DTWClient : public FluidBaseClient, string id1, string id2) { auto dataseriesClientPtr = dataseriesClient.get().lock(); - if (!dataseriesClientPtr) return Error(NoDataSet); + if (!dataseriesClientPtr) return Error(NoDataSeries); auto srcDataSeries = dataseriesClientPtr->getDataSeries(); - if (srcDataSeries.size() == 0) return Error(EmptyDataSet); + if (srcDataSeries.size() == 0) return Error(EmptyDataSeries); index i1 = srcDataSeries.getIndex(id1), i2 = srcDataSeries.getIndex(id2); diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index a0f957343..bcadfa061 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -117,16 +117,16 @@ class DTWRegressorClient : public FluidBaseClient, InputDataSetClientRef dataSetClient) { auto dataSeriesClientPtr = dataSeriesClient.get().lock(); - if (!dataSeriesClientPtr) return Error(NoDataSet); + if (!dataSeriesClientPtr) return Error(NoDataSeries); auto dataSetPtr = dataSetClient.get().lock(); if (!dataSetPtr) return Error(NoDataSet); auto dataSeries = dataSeriesClientPtr->getDataSeries(); - if (dataSeries.size() == 0) return Error(EmptyDataSet); + if (dataSeries.size() == 0) return Error(EmptyDataSeries); auto dataSet = dataSetPtr->getDataSet(); - if (dataSet.size() == 0) return Error(EmptyLabelSet); + if (dataSet.size() == 0) return Error(EmptyDataSet); if (dataSeries.size() != dataSet.size()) return Error(SizesDontMatch); @@ -180,13 +180,13 @@ class DTWRegressorClient : public FluidBaseClient, { auto sourcePtr = source.get().lock(); - if (!sourcePtr) return Error(NoDataSet); + if (!sourcePtr) return Error(NoDataSeries); auto destPtr = dest.get().lock(); if (!destPtr) return Error(NoLabelSet); auto dataSeries = sourcePtr->getDataSeries(); - if (dataSeries.size() == 0) return Error(EmptyDataSet); + if (dataSeries.size() == 0) return Error(EmptyDataSeries); if (dataSeries.pointSize() != mAlgorithm.series.dims()) return Error(WrongPointSize); From 1740ee71eaa34e1ceaeb01bbfcdbf47f7e2f7305 Mon Sep 17 00:00:00 2001 From: lewardo Date: Sun, 10 Sep 2023 10:56:32 +0100 Subject: [PATCH 142/153] merge constraint parameters into once conceptual value --- include/clients/nrt/DTWClassifierClient.hpp | 23 ++++-------------- include/clients/nrt/DTWClient.hpp | 26 ++++----------------- include/clients/nrt/DTWRegressorClient.hpp | 23 ++++-------------- 3 files changed, 15 insertions(+), 57 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index dc750c71d..47f0fe8d7 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -63,8 +63,8 @@ constexpr auto DTWClassifierParams = defineParameters( LongParam("numNeighbours", "Number of Nearest Neighbours", 3, Min(1)), EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", "Sakoe-Chiba"), - FloatParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), - FloatParam("gradient", "Ikatura Parallelogram max gradient", 1, Min(1))); + FloatParam("constraintParam", "Sakoe-Chiba radius or Ikatura max gradient", + 3, Min(0))); class DTWClassifierClient : public FluidBaseClient, OfflineIn, @@ -72,7 +72,7 @@ class DTWClassifierClient : public FluidBaseClient, ModelObject, public DataClient { - enum { kName, kNumNeighbors, kConstraint, kRadius, kGradient }; + enum { kName, kNumNeighbors, kConstraint, kParam }; public: using string = std::string; @@ -202,19 +202,6 @@ class DTWClassifierClient : public FluidBaseClient, } private: - float constraintParam(algorithm::DTWConstraint constraint) const - { - using namespace algorithm; - - switch (constraint) - { - case DTWConstraint::kIkatura: return get(); - case DTWConstraint::kSakoeChiba: return get(); - } - - return 0.0; - } - MessageResult kNearestModeLabel(InputRealMatrixView series) const { index k = get(); @@ -236,8 +223,8 @@ class DTWClassifierClient : public FluidBaseClient, std::transform( indices.begin(), indices.end(), distances.begin(), [&series, &ds, &constraint, this](index i) { - double dist = mAlgorithm.dtw.process(series, ds[i], constraint, - constraintParam(constraint)); + double dist = + mAlgorithm.dtw.process(series, ds[i], constraint, get()); return std::max(std::numeric_limits::epsilon(), dist); }); diff --git a/include/clients/nrt/DTWClient.hpp b/include/clients/nrt/DTWClient.hpp index 067fa3aaa..b34c84dcb 100644 --- a/include/clients/nrt/DTWClient.hpp +++ b/include/clients/nrt/DTWClient.hpp @@ -25,9 +25,8 @@ constexpr auto DTWParams = defineParameters( StringParam>("name", "Name"), EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", "Sakoe-Chiba"), - LongParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), - FloatParam("gradient", "Ikatura Parallelogram max gradient", 1.0, - Min(1.0))); + FloatParam("constraintParam", "Sakoe-Chiba radius or Ikatura max gradient", + 3, Min(0))); class DTWClient : public FluidBaseClient, OfflineIn, @@ -35,7 +34,7 @@ class DTWClient : public FluidBaseClient, ModelObject, public DataClient { - enum { kName, kConstraint, kRadius, kGradient }; + enum { kName, kConstraint, kParam }; public: using string = std::string; @@ -90,8 +89,7 @@ class DTWClient : public FluidBaseClient, algorithm::DTWConstraint constraint = (algorithm::DTWConstraint) get(); - return mAlgorithm.process(series1, series2, constraint, - constraintParam(constraint)); + return mAlgorithm.process(series1, series2, constraint, get()); } MessageResult bufCost(InputBufferPtr data1, InputBufferPtr data2) @@ -116,7 +114,7 @@ class DTWClient : public FluidBaseClient, (algorithm::DTWConstraint) get(); return mAlgorithm.process(buf1frames, buf2frames, constraint, - constraintParam(constraint)); + get()); } static auto getMessageDescriptors() @@ -124,20 +122,6 @@ class DTWClient : public FluidBaseClient, return defineMessages(makeMessage("cost", &DTWClient::cost), makeMessage("bufCost", &DTWClient::bufCost)); } - -private: - float constraintParam(algorithm::DTWConstraint constraint) - { - using namespace algorithm; - - switch (constraint) - { - case DTWConstraint::kIkatura: return get(); - case DTWConstraint::kSakoeChiba: return get(); - } - - return 0.0; - } }; using DTWRef = SharedClientRef; diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index bcadfa061..1daf43718 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -67,8 +67,8 @@ constexpr auto DTWRegressorParams = defineParameters( LongParam("numNeighbours", "Number of Nearest Neighbours", 3, Min(1)), EnumParam("constraint", "Constraint Type", 0, "Unconstrained", "Ikatura", "Sakoe-Chiba"), - FloatParam("radius", "Sakoe-Chiba Constraint Radius", 2, Min(0)), - FloatParam("gradient", "Ikatura Parallelogram max gradient", 1, Min(1))); + FloatParam("constraintParam", "Sakoe-Chiba radius or Ikatura max gradient", + 3, Min(0))); class DTWRegressorClient : public FluidBaseClient, OfflineIn, @@ -76,7 +76,7 @@ class DTWRegressorClient : public FluidBaseClient, ModelObject, public DataClient { - enum { kName, kNumNeighbors, kConstraint, kRadius, kGradient }; + enum { kName, kNumNeighbors, kConstraint, kParam }; public: using string = std::string; @@ -230,19 +230,6 @@ class DTWRegressorClient : public FluidBaseClient, } private: - float constraintParam(algorithm::DTWConstraint constraint) const - { - using namespace algorithm; - - switch (constraint) - { - case DTWConstraint::kIkatura: return get(); - case DTWConstraint::kSakoeChiba: return get(); - } - - return 0.0; - } - MessageResult kNearestWeightedSum(InputRealMatrixView series, Allocator& alloc = FluidDefaultAllocator()) const @@ -269,8 +256,8 @@ class DTWRegressorClient : public FluidBaseClient, std::transform( indices.begin(), indices.end(), distances.begin(), [&series, &ds, &constraint, this](index i) { - double dist = mAlgorithm.dtw.process(series, ds[i], constraint, - constraintParam(constraint)); + double dist = + mAlgorithm.dtw.process(series, ds[i], constraint, get()); return std::max(std::numeric_limits::epsilon(), dist); }); From df9ccd97d38cf1c72e3e7203448f9b7821d52b2b Mon Sep 17 00:00:00 2001 From: lewardo Date: Sun, 10 Sep 2023 10:56:43 +0100 Subject: [PATCH 143/153] fallback control path --- include/algorithms/public/DTW.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/algorithms/public/DTW.hpp b/include/algorithms/public/DTW.hpp index 4d3edcc96..f929a5faf 100644 --- a/include/algorithms/public/DTW.hpp +++ b/include/algorithms/public/DTW.hpp @@ -202,6 +202,8 @@ class DTW return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; } } + + return 0; }; index lastCol(index row) @@ -226,6 +228,8 @@ class DTW return col < 0 ? 0 : col > mCols - 1 ? mCols - 1 : col; } } + + return mCols - 1; }; }; // struct Constraint }; From 8f19fef964ca371b0c14810e9c4d6b9e0e7cf8f9 Mon Sep 17 00:00:00 2001 From: lewardo Date: Sun, 10 Sep 2023 11:20:08 +0100 Subject: [PATCH 144/153] proper knearest checks --- include/clients/nrt/DataSeriesClient.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index 7fc94fd14..dc6b76f3b 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -289,8 +289,10 @@ class DataSeriesClient index nNeighbours) const { // check for nNeighbours > 0 and < size of DS + if (mAlgorithm.size() == 0) + return Error>(EmptyDataSeries); if (nNeighbours > mAlgorithm.size()) - return Error>(SmallDataSet); + return Error>(LargeK); if (nNeighbours <= 0) return Error>(SmallK); BufferAdaptor::ReadAccess buf(data.get()); @@ -331,8 +333,10 @@ class DataSeriesClient index nNeighbours) const { // check for nNeighbours > 0 and < size of DS + if (mAlgorithm.size() == 0) + return Error>(EmptyDataSeries); if (nNeighbours > mAlgorithm.size()) - return Error>(SmallDataSet); + return Error>(LargeK); if (nNeighbours <= 0) return Error>(SmallK); BufferAdaptor::ReadAccess buf(data.get()); From 69265acc9d0fac5119263e4f789af226ec298243 Mon Sep 17 00:00:00 2001 From: lewardo Date: Sun, 10 Sep 2023 11:35:34 +0100 Subject: [PATCH 145/153] negative frame indexing --- include/data/FluidDataSeries.hpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index f343faa50..a2c9b9381 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -104,6 +104,9 @@ class FluidDataSeries if (pos == mIndex.end()) return false; if (time >= mData[pos->second].rows()) return false; + if (time < -mData[pos->second].rows()) return false; + + time += time < 0 ? mData[pos->second].rows() : 0; frame <<= mData[pos->second].row(time); @@ -116,7 +119,10 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos != mIndex.end()) { - assert(time < mData[pos->second].rows()); + assert(time < mData[pos->second].rows() || + time >= -mData[pos->second].rows()); + + time += time < 0 ? mData[pos->second].rows() : 0; return mData[pos->second].row(time); } else { return FluidTensorView{nullptr, 0, 0}; } @@ -148,6 +154,9 @@ class FluidDataSeries if (pos == mIndex.end()) return false; if (time >= mData[pos->second].rows()) return false; + if (time < -mData[pos->second].rows()) return false; + + time += time < 0 ? mData[pos->second].rows() : 0; mData[pos->second].row(time) <<= frame; return true; @@ -174,13 +183,15 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - index current = pos->second; - if (time >= mData[current].rows()) return false; + if (time >= mData[pos->second].rows()) return false; + if (time < -mData[pos->second].rows()) return false; + + time += time < 0 ? mData[pos->second].rows() : 0; - if (mData[current].rows() == 1) + if (mData[pos->second].rows() == 1) return removeSeries(id); else - mData[current].deleteRow(time); + mData[pos->second].deleteRow(time); return true; } @@ -190,14 +201,13 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - index current = pos->second; - mData.erase(mData.begin() + current); - mIds.deleteRow(current); + mData.erase(mData.begin() + pos->second); + mIds.deleteRow(pos->second); mIndex.erase(id); for (auto& point : mIndex) { - if (point.second > current) point.second--; + if (point.second > pos->second) point.second--; } return true; From 035638a8251e133449261026b9e324ae19772803 Mon Sep 17 00:00:00 2001 From: lewardo Date: Sun, 10 Sep 2023 11:35:49 +0100 Subject: [PATCH 146/153] `getdataset` argument order --- include/clients/nrt/DataSeriesClient.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index d458ee1cb..7ab05224a 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -264,7 +264,7 @@ class DataSeriesClient return OK(); } - MessageResult getDataSet(DataSetClientRef dest, index time) const + MessageResult getDataSet(index time, DataSetClientRef dest) const { auto destPtr = dest.get().lock(); if (!destPtr) return Error(NoDataSet); From aa2e5d995e6a049654730620dc3a822b741d7fff Mon Sep 17 00:00:00 2001 From: lewardo Date: Sun, 10 Sep 2023 11:35:34 +0100 Subject: [PATCH 147/153] negative frame indexing --- include/data/FluidDataSeries.hpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index f343faa50..a2c9b9381 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -104,6 +104,9 @@ class FluidDataSeries if (pos == mIndex.end()) return false; if (time >= mData[pos->second].rows()) return false; + if (time < -mData[pos->second].rows()) return false; + + time += time < 0 ? mData[pos->second].rows() : 0; frame <<= mData[pos->second].row(time); @@ -116,7 +119,10 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos != mIndex.end()) { - assert(time < mData[pos->second].rows()); + assert(time < mData[pos->second].rows() || + time >= -mData[pos->second].rows()); + + time += time < 0 ? mData[pos->second].rows() : 0; return mData[pos->second].row(time); } else { return FluidTensorView{nullptr, 0, 0}; } @@ -148,6 +154,9 @@ class FluidDataSeries if (pos == mIndex.end()) return false; if (time >= mData[pos->second].rows()) return false; + if (time < -mData[pos->second].rows()) return false; + + time += time < 0 ? mData[pos->second].rows() : 0; mData[pos->second].row(time) <<= frame; return true; @@ -174,13 +183,15 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - index current = pos->second; - if (time >= mData[current].rows()) return false; + if (time >= mData[pos->second].rows()) return false; + if (time < -mData[pos->second].rows()) return false; + + time += time < 0 ? mData[pos->second].rows() : 0; - if (mData[current].rows() == 1) + if (mData[pos->second].rows() == 1) return removeSeries(id); else - mData[current].deleteRow(time); + mData[pos->second].deleteRow(time); return true; } @@ -190,14 +201,13 @@ class FluidDataSeries auto pos = mIndex.find(id); if (pos == mIndex.end()) return false; - index current = pos->second; - mData.erase(mData.begin() + current); - mIds.deleteRow(current); + mData.erase(mData.begin() + pos->second); + mIds.deleteRow(pos->second); mIndex.erase(id); for (auto& point : mIndex) { - if (point.second > current) point.second--; + if (point.second > pos->second) point.second--; } return true; From 8981f98542716445ab03247e7e0149fd10c0c32f Mon Sep 17 00:00:00 2001 From: lewardo Date: Sun, 10 Sep 2023 11:35:49 +0100 Subject: [PATCH 148/153] `getdataset` argument order --- include/clients/nrt/DataSeriesClient.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clients/nrt/DataSeriesClient.hpp b/include/clients/nrt/DataSeriesClient.hpp index dc6b76f3b..97128733c 100644 --- a/include/clients/nrt/DataSeriesClient.hpp +++ b/include/clients/nrt/DataSeriesClient.hpp @@ -265,7 +265,7 @@ class DataSeriesClient return OK(); } - MessageResult getDataSet(DataSetClientRef dest, index time) const + MessageResult getDataSet(index time, DataSetClientRef dest) const { auto destPtr = dest.get().lock(); if (!destPtr) return Error(NoDataSet); From ac1293ee3037c55badeee210fb77be8e5305993a Mon Sep 17 00:00:00 2001 From: tremblap Date: Mon, 25 Sep 2023 13:47:31 +0100 Subject: [PATCH 149/153] modify the interface name - we predict from a buffer containing a Series, not a Point --- include/clients/nrt/DTWClassifierClient.hpp | 4 ++-- include/clients/nrt/DTWRegressorClient.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/clients/nrt/DTWClassifierClient.hpp b/include/clients/nrt/DTWClassifierClient.hpp index 47f0fe8d7..4afd580f7 100644 --- a/include/clients/nrt/DTWClassifierClient.hpp +++ b/include/clients/nrt/DTWClassifierClient.hpp @@ -142,7 +142,7 @@ class DTWClassifierClient : public FluidBaseClient, return Error(EmptyLabel); } - MessageResult predictPoint(InputBufferPtr data) const + MessageResult predictSeries(InputBufferPtr data) const { BufferAdaptor::ReadAccess buf = data.get(); RealMatrix series(buf.numFrames(), buf.numChans()); @@ -192,7 +192,7 @@ class DTWClassifierClient : public FluidBaseClient, return defineMessages( makeMessage("fit", &DTWClassifierClient::fit), makeMessage("predict", &DTWClassifierClient::predict), - makeMessage("predictPoint", &DTWClassifierClient::predictPoint), + makeMessage("predictSeries", &DTWClassifierClient::predictSeries), makeMessage("clear", &DTWClassifierClient::clear), makeMessage("size", &DTWClassifierClient::size), makeMessage("load", &DTWClassifierClient::load), diff --git a/include/clients/nrt/DTWRegressorClient.hpp b/include/clients/nrt/DTWRegressorClient.hpp index 1daf43718..7d257e3a4 100644 --- a/include/clients/nrt/DTWRegressorClient.hpp +++ b/include/clients/nrt/DTWRegressorClient.hpp @@ -146,7 +146,7 @@ class DTWRegressorClient : public FluidBaseClient, return Error(PointNotFound); } - MessageResult predictPoint(InputBufferPtr in, BufferPtr out) const + MessageResult predictSeries(InputBufferPtr in, BufferPtr out) const { if (!in || !out) return Error(NoBuffer); @@ -220,7 +220,7 @@ class DTWRegressorClient : public FluidBaseClient, return defineMessages( makeMessage("fit", &DTWRegressorClient::fit), makeMessage("predict", &DTWRegressorClient::predict), - makeMessage("predictPoint", &DTWRegressorClient::predictPoint), + makeMessage("predictSeries", &DTWRegressorClient::predictSeries), makeMessage("clear", &DTWRegressorClient::clear), makeMessage("size", &DTWRegressorClient::size), makeMessage("load", &DTWRegressorClient::load), From 890985f781667bac5d023d22743d6169e1e68cfc Mon Sep 17 00:00:00 2001 From: tremblap Date: Mon, 8 Jan 2024 13:06:16 +0000 Subject: [PATCH 150/153] temporary fix to reload error - json is alphabetical - TODO: potentially check the max length dynamically zero pad ceil(log10(maxlen)) zero padding would do but maybe we should store a new "maxlength" key --- include/data/FluidJSON.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/data/FluidJSON.hpp b/include/data/FluidJSON.hpp index 6020ba3c9..fe0b1b071 100644 --- a/include/data/FluidJSON.hpp +++ b/include/data/FluidJSON.hpp @@ -140,11 +140,13 @@ void to_json(nlohmann::json &j, const FluidDataSeries &ds) { auto ids = ds.getIds(); auto data = ds.getData(); j["cols"] = ds.pointSize(); - for (index r = 0; r < ds.size(); r++) - { + char timestring[6]; + for (index r = 0; r < ds.size(); r++) { auto series = data[r]; - for (index s = 0; s < series.rows(); s++) - j["data"][ids[r]]["t" + std::to_string(s)] = data[r].row(s); + for (index s = 0; s < series.rows(); s++) { + std::snprintf(timestring,6,"T%04ld",s); + j["data"][ids[r]][timestring] = data[r].row(s); + } } } From c4a80fe1bfa5f942fccdffe9ea34c3191415ac7c Mon Sep 17 00:00:00 2001 From: tremblap Date: Thu, 29 Feb 2024 15:12:31 +0000 Subject: [PATCH 151/153] printseries fix print 2nd boundary counter --- include/data/FluidDataSeries.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index a2c9b9381..6a2e3a69f 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -311,8 +311,11 @@ class FluidDataSeries result << setw(10) << "..." << std::endl; for (index t = maxFrames / 2; t > 0; t--) - result << setw(10) << "t" << (series.rows() - t) << ": " - << printFrame(series.row(size() - t), maxCols) << endl; + { + index rownum = series.rows() - t; + result << setw(10) << "t" << (rownum) << ": " + << printFrame(series.row(rownum), maxCols) << endl; + } } return result.str(); From 05af721a4531d18d7087d124f18b3ee947439299 Mon Sep 17 00:00:00 2001 From: tremblap Date: Thu, 7 Mar 2024 15:49:00 +0000 Subject: [PATCH 152/153] remove the T in dataset printing and json dumping --- include/data/FluidDataSeries.hpp | 6 +++--- include/data/FluidJSON.hpp | 14 +++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/data/FluidDataSeries.hpp b/include/data/FluidDataSeries.hpp index 6a2e3a69f..a4baefc44 100644 --- a/include/data/FluidDataSeries.hpp +++ b/include/data/FluidDataSeries.hpp @@ -299,13 +299,13 @@ class FluidDataSeries if (series.rows() < maxFrames) { for (index t = 0; t < series.rows(); t++) - result << setw(10) << "t" << t << ": " + result << setw(10) << t << ": " << printFrame(series.row(t), maxCols) << endl; } else { for (index t = 0; t < maxFrames / 2; t++) - result << setw(10) << "t" << t << ": " + result << setw(10) << t << ": " << printFrame(series.row(t), maxCols) << endl; result << setw(10) << "..." << std::endl; @@ -313,7 +313,7 @@ class FluidDataSeries for (index t = maxFrames / 2; t > 0; t--) { index rownum = series.rows() - t; - result << setw(10) << "t" << (rownum) << ": " + result << setw(10) << (rownum) << ": " << printFrame(series.row(rownum), maxCols) << endl; } } diff --git a/include/data/FluidJSON.hpp b/include/data/FluidJSON.hpp index fe0b1b071..66808bdf3 100644 --- a/include/data/FluidJSON.hpp +++ b/include/data/FluidJSON.hpp @@ -140,12 +140,20 @@ void to_json(nlohmann::json &j, const FluidDataSeries &ds) { auto ids = ds.getIds(); auto data = ds.getData(); j["cols"] = ds.pointSize(); - char timestring[6]; + index maxlength = 0; + for (index r = 0; r < ds.size(); r++) { + auto length = data[r].rows(); + maxlength = std::max(maxlength, length); + } + index stringlen = std::ceil(std::log10(maxlength-1)); + std::stringstream timestring; for (index r = 0; r < ds.size(); r++) { auto series = data[r]; for (index s = 0; s < series.rows(); s++) { - std::snprintf(timestring,6,"T%04ld",s); - j["data"][ids[r]][timestring] = data[r].row(s); + timestring.str(""); + timestring.clear(); + timestring << std::setw(stringlen) << std::setfill('0') << s; + j["data"][ids[r]][timestring.str()] = data[r].row(s); } } } From 023ed8574ee39bc13f6c5a9c2dbc24ef3f1311d5 Mon Sep 17 00:00:00 2001 From: tremblap Date: Thu, 7 Mar 2024 15:57:35 +0000 Subject: [PATCH 153/153] correct padding per series instead --- include/data/FluidJSON.hpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/include/data/FluidJSON.hpp b/include/data/FluidJSON.hpp index 66808bdf3..4687f7de1 100644 --- a/include/data/FluidJSON.hpp +++ b/include/data/FluidJSON.hpp @@ -140,18 +140,13 @@ void to_json(nlohmann::json &j, const FluidDataSeries &ds) { auto ids = ds.getIds(); auto data = ds.getData(); j["cols"] = ds.pointSize(); - index maxlength = 0; - for (index r = 0; r < ds.size(); r++) { - auto length = data[r].rows(); - maxlength = std::max(maxlength, length); - } - index stringlen = std::ceil(std::log10(maxlength-1)); std::stringstream timestring; for (index r = 0; r < ds.size(); r++) { auto series = data[r]; + index stringlen = std::ceil(std::log10(series.rows() - 1)); for (index s = 0; s < series.rows(); s++) { - timestring.str(""); - timestring.clear(); + timestring.str(""); + timestring.clear(); timestring << std::setw(stringlen) << std::setfill('0') << s; j["data"][ids[r]][timestring.str()] = data[r].row(s); }