From 639df99746e4f3db0d52f8e9c73d340791a30a7e Mon Sep 17 00:00:00 2001 From: Michael ZBYSZYNSKI Date: Wed, 14 May 2025 06:12:03 +0200 Subject: [PATCH 1/6] Fix jasoncpp UB --- .gitignore | 2 +- dependencies/jsoncpp.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 03d7f89..da7b0c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ - +.DS_STORE /wekiLib/ # Created by https://www.gitignore.io/api/emacs diff --git a/dependencies/jsoncpp.cpp b/dependencies/jsoncpp.cpp index 4c1b04c..1956e02 100644 --- a/dependencies/jsoncpp.cpp +++ b/dependencies/jsoncpp.cpp @@ -1767,8 +1767,8 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { ++current; // TODO: Help the compiler do the div and mod at compile time or get rid of them. Value::LargestUInt maxIntegerValue = - isNegative ? Value::LargestUInt(-Value::minLargestInt) - : Value::maxLargestUInt; + isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 + : Value::maxLargestUInt; Value::LargestUInt threshold = maxIntegerValue / 10; Value::LargestUInt value = 0; while (current < token.end_) { From e7297c4038f2bd7114b0452951f6e59337fdb115 Mon Sep 17 00:00:00 2001 From: Michael ZBYSZYNSKI Date: Wed, 2 Jul 2025 16:17:34 +0100 Subject: [PATCH 2/6] Clean up, modernize --- src/classification.cpp | 32 +++-- src/dtw.cpp | 96 +++++++------ src/fastDTW.cpp | 93 +++++++------ src/knnClassification.cpp | 280 +++++++++++++++++++------------------- src/knnClassification.h | 150 ++++++++++---------- src/rapidStream.cpp | 67 +++++---- src/searchWindow.cpp | 103 ++++++++------ src/searchWindow.h | 23 ++-- src/warpPath.h | 4 +- 9 files changed, 440 insertions(+), 408 deletions(-) diff --git a/src/classification.cpp b/src/classification.cpp index cf8c1db..614d03d 100644 --- a/src/classification.cpp +++ b/src/classification.cpp @@ -69,36 +69,39 @@ bool classificationTemplate::train(const std::vector 0) { //create model(s) here - modelSet::numInputs = int(training_set[0].input.size()); - modelSet::numOutputs = int(training_set[0].output.size()); - - for (int i = 0; i < modelSet::numInputs; ++i) + modelSet::numInputs = static_cast(training_set[0].input.size()); + modelSet::numOutputs = static_cast(training_set[0].output.size()); + + for (int i {}; i < modelSet::numInputs; ++i) { modelSet::inputNames.push_back("inputs-" + std::to_string(i + 1)); } - modelSet::numOutputs = int(training_set[0].output.size()); - - for ( auto example : training_set) + + modelSet::numOutputs = static_cast(training_set[0].output.size()); + + for (const auto& example : training_set) { if (example.input.size() != modelSet::numInputs) { throw std::length_error("unequal feature vectors in input."); return false; } + if (example.output.size() != modelSet::numOutputs) { throw std::length_error("unequal output vectors."); return false; } } - std::vector whichInputs; - - for (int j = 0; j < modelSet::numInputs; ++j) + + std::vector whichInputs {}; + + for (int inputNum {}; inputNum < modelSet::numInputs; ++inputNum) { - whichInputs.push_back(j); + whichInputs.push_back(inputNum); } - for (int i = 0; i < modelSet::numOutputs; ++i) + for (int outputNum {}; outputNum < modelSet::numOutputs; ++outputNum) { if (classificationType == svm) { @@ -118,12 +121,13 @@ bool classificationTemplate::train(const std::vector std::vector classificationTemplate::getK() { - std::vector kVector; - + std::vector kVector {}; + for (const baseModel* model : modelSet::myModelSet) { kVector.push_back(dynamic_cast*>(model)->getK()); //FIXME: I really dislike this design } + return kVector; } diff --git a/src/dtw.cpp b/src/dtw.cpp index c86ca45..e8a9f28 100644 --- a/src/dtw.cpp +++ b/src/dtw.cpp @@ -22,26 +22,28 @@ dtw::~dtw() {}; template inline T dtw::distanceFunction(const std::vector &x, const std::vector &y) { - double euclidianDistance = 0; + double euclidianDistance {}; + if (x.size() != y.size()) { throw std::length_error("comparing different length series"); } else { - for (std::size_t i = 0; i < x.size(); ++i) + for (std::size_t i {}; i < x.size(); ++i) { - euclidianDistance = euclidianDistance + pow((x[i] - y[i]), 2); + euclidianDistance += std::pow((x[i] - y[i]), 2); } euclidianDistance = sqrt(euclidianDistance); } - return (T)euclidianDistance; + + return static_cast(euclidianDistance); }; /* Just returns the cost, doesn't calculate the path */ template -T dtw::getCost(const std::vector > &seriesX, const std::vector > &seriesY) +T dtw::getCost(const std::vector>& seriesX, const std::vector>& seriesY) { if (seriesX.size() < seriesY.size()) return getCost(seriesY, seriesX); @@ -53,22 +55,23 @@ T dtw::getCost(const std::vector > &seriesX, const std::vector //Calculate values for the first column costMatrix[0][0] = distanceFunction(seriesX[0], seriesY[0]); - for (int j = 1; j <= maxY; ++j) + for (int y { 1 }; y <= maxY; ++y) { - costMatrix[0][j] = costMatrix[0][j - 1] + distanceFunction(seriesX[0], seriesY[j]); + costMatrix[0][y] = costMatrix[0][y - 1] + distanceFunction(seriesX[0], seriesY[y]); } - for (std::size_t i = 1; i <= maxX; ++i) + for (std::size_t x { 1 }; x <= maxX; ++x) { //Bottom row of current column - costMatrix[i][0] = costMatrix[i - 1][0] + distanceFunction(seriesX[i], seriesY[0]); - - for (std::size_t j = 1; j <= maxY; ++j) + costMatrix[x][0] = costMatrix[x - 1][0] + distanceFunction(seriesX[x], seriesY[0]); + + for (std::size_t y { 1 }; y <= maxY; ++y) { - T minGlobalCost = fmin(costMatrix[i-1][j-1], costMatrix[i][j-1]); - costMatrix[i][j] = minGlobalCost + distanceFunction(seriesX[i], seriesY[j]); + T minGlobalCost { fmin(costMatrix[x - 1][y - 1], costMatrix[x][y - 1]) }; + costMatrix[x][y] = minGlobalCost + distanceFunction(seriesX[x], seriesY[y]); } } + return costMatrix[maxX][maxY]; }; @@ -76,38 +79,38 @@ template warpPath dtw::calculatePath(std::size_t seriesXsize, std::size_t seriesYsize) const { warpPath warpPath; - std::size_t i { seriesXsize - 1 }; - std::size_t j { seriesYsize - 1 }; - warpPath.add(i, j); - - while ((i > 0) || (j > 0)) + std::size_t x { seriesXsize - 1 }; + std::size_t y { seriesYsize - 1 }; + warpPath.add(x, y); + + while ((x > 0) || (y > 0)) { - T diagonalCost = ((i > 0) && (j > 0)) ? costMatrix[i - 1][j - 1] : std::numeric_limits::infinity(); - T leftCost = (i > 0) ? costMatrix[i - 1][j] : std::numeric_limits::infinity(); - T downCost = (j > 0) ? costMatrix[i][j - 1] : std::numeric_limits::infinity(); - + T diagonalCost { ((x > 0) && (y > 0)) ? costMatrix[x - 1][y - 1] : std::numeric_limits::infinity() }; + T leftCost { (x > 0) ? costMatrix[x - 1][y] : std::numeric_limits::infinity() }; + T downCost { (y > 0) ? costMatrix[x][y - 1] : std::numeric_limits::infinity() }; + if ((diagonalCost <= leftCost) && (diagonalCost <= downCost)) { - if (i > 0) --i; - if (j > 0) --j; + if (x > 0) --x; + if (y > 0) --y; } else if ((leftCost < diagonalCost) && (leftCost < downCost)) { - --i; + --x; } else if ((downCost < diagonalCost) && (downCost < leftCost)) { - --j; + --y; } - else if (i <= j) + else if (x <= y) { - --j; + --y; } else { - --i; + --x; } - warpPath.add(i, j); + warpPath.add(x, y); } return warpPath; @@ -115,9 +118,10 @@ warpPath dtw::calculatePath(std::size_t seriesXsize, std::size_t seriesYsize) /* calculates both the cost and the warp path*/ template -warpInfo dtw::dynamicTimeWarp(const std::vector > &seriesX, const std::vector > &seriesY) +warpInfo dtw::dynamicTimeWarp(const std::vector>& seriesX, const std::vector>& seriesY) { - warpInfo info; + warpInfo info {}; + //calculate cost matrix info.cost = getCost(seriesX, seriesY); info.path = calculatePath(seriesX.size(), seriesY.size()); @@ -132,26 +136,34 @@ warpInfo dtw::constrainedDTW(const std::vector > &seriesX, costMatrix.clear(); std::vector tempVector(seriesY.size(), std::numeric_limits::max()); costMatrix.assign(seriesX.size(), tempVector); //TODO: this could be smaller, since most cells are unused - std::size_t maxX = seriesX.size() - 1; - std::size_t maxY = seriesY.size() - 1; - + std::size_t maxX { seriesX.size() - 1 }; + std::size_t maxY { seriesY.size() - 1 }; + //fill cost matrix cells based on window - for (std::size_t currentX = 0; currentX < window.minMaxValues.size(); ++currentX) + for (std::size_t currentX {}; currentX < window.minMaxValues.size(); ++currentX) { - for (std::size_t currentY = window.minMaxValues[currentX].first; currentY <= window.minMaxValues[currentX].second; ++currentY) //FIXME: should be <= ? + for (std::size_t currentY { window.minMaxValues[currentX].first }; currentY <= window.minMaxValues[currentX].second; ++currentY) //FIXME: should be <= ? { - if (currentX == 0 && currentY == 0) { //bottom left cell + if (currentX == 0 && currentY == 0) //bottom left cell + { costMatrix[0][0] = distanceFunction(seriesX[0], seriesY[0]); - } else if (currentX == 0) { //first column + } + else if (currentX == 0) //first column + { costMatrix[0][currentY] = distanceFunction(seriesX[0], seriesY[currentY]) + costMatrix[0][currentY - 1]; - } else if (currentY == 0) { //first row + } + else if (currentY == 0) //first row + { costMatrix[currentX][0] = distanceFunction(seriesX[currentX], seriesY[0]) + costMatrix[currentX - 1][0]; - } else { - T minGlobalCost = fmin(costMatrix[currentX - 1][currentY], fmin(costMatrix[currentX-1][currentY-1], costMatrix[currentX][currentY-1])); + } + else + { + T minGlobalCost { fmin(costMatrix[currentX - 1][currentY], fmin(costMatrix[currentX-1][currentY-1], costMatrix[currentX][currentY-1])) }; costMatrix[currentX][currentY] = distanceFunction(seriesX[currentX], seriesY[currentY]) + minGlobalCost; } } } + warpInfo info; info.cost = costMatrix[maxX][maxY]; info.path = calculatePath(seriesX.size(), seriesY.size()); diff --git a/src/fastDTW.cpp b/src/fastDTW.cpp index 49d92a0..a976b59 100644 --- a/src/fastDTW.cpp +++ b/src/fastDTW.cpp @@ -17,64 +17,73 @@ template fastDTW::~fastDTW() {}; template -warpInfo fastDTW::fullFastDTW(const std::vector> &seriesX, const std::vector > &seriesY, int searchRadius) +warpInfo fastDTW::fullFastDTW(const std::vector>& seriesX, const std::vector>& seriesY, int searchRadius) { - + #ifndef EMSCRIPTEN - if (seriesY.size() > seriesX.size()) - { - return fullFastDTW(seriesY, seriesX, searchRadius); //TODO: I'm not sure why I need this. Also, not sure why it fails with Emscripten. - } + if (seriesY.size() > seriesX.size()) + { + return fullFastDTW(seriesY, seriesX, searchRadius); //TODO: I'm not sure why I need this. Also, not sure why it fails with Emscripten. + } #endif - - dtw dtw; - searchRadius = (searchRadius < 0) ? 0 : searchRadius; - int minSeries = searchRadius + 2; - if (seriesX.size() <= minSeries || seriesY.size() <= minSeries) - { - return dtw.dynamicTimeWarp(seriesX, seriesY); - } - - T resolution = 2.0;//TODO: Just hardcode this? - std::vector> shrunkenX = downsample(seriesX, resolution); - std::vector> shrunkenY = downsample(seriesY, resolution); - - //some nice recursion here - searchWindow window(int(seriesX.size()), int(seriesY.size()), getWarpPath(shrunkenX, shrunkenY, searchRadius), searchRadius); - return dtw.constrainedDTW(seriesX, seriesY, window); + + dtw dtw; + searchRadius = std::max(0, searchRadius); + const int minSeries { searchRadius + 2 }; + + if (seriesX.size() <= minSeries || seriesY.size() <= minSeries) + { + return dtw.dynamicTimeWarp(seriesX, seriesY); + } + + T resolution { 2.0 };//TODO: Just hardcode this? + std::vector> shrunkenX { downsample(seriesX, resolution) }; + std::vector> shrunkenY { downsample(seriesY, resolution) }; + + //some nice recursion here + searchWindow window(int(seriesX.size()), int(seriesY.size()), getWarpPath(shrunkenX, shrunkenY, searchRadius), searchRadius); + + return dtw.constrainedDTW(seriesX, seriesY, window); }; template -T fastDTW::getCost(const std::vector> &seriesX, const std::vector > &seriesY, int searchRadius) +T fastDTW::getCost(const std::vector>& seriesX, const std::vector>& seriesY, int searchRadius) { - warpInfo info = fullFastDTW(seriesX, seriesY, searchRadius); - return info.cost; + warpInfo info { fullFastDTW(seriesX, seriesY, searchRadius) }; + return info.cost; }; template -warpPath fastDTW::getWarpPath(const std::vector> &seriesX, const std::vector > &seriesY, int searchRadius) +warpPath fastDTW::getWarpPath(const std::vector>& seriesX, const std::vector>& seriesY, int searchRadius) { - warpInfo info = fullFastDTW(seriesX, seriesY, searchRadius); - return info.path; + warpInfo info { fullFastDTW(seriesX, seriesY, searchRadius) }; + return info.path; }; template -inline std::vector > fastDTW::downsample(const std::vector> &series, T resolution) +inline std::vector > fastDTW::downsample(const std::vector>& series, T resolution) { - std::vector > shrunkenSeries; - - for (std::size_t i = 0; i < series.size(); ++i) { - if (i % 2 == 0) { - shrunkenSeries.push_back(series[i]); - } else { - int shrunkIndex = int(i * 0.5); - for (std::size_t j = 0; j < series[i].size(); ++j) { - shrunkenSeries[shrunkIndex][j] = (shrunkenSeries[shrunkIndex][j] + series[i][j]) * (T)0.5; - } - } + std::vector > shrunkenSeries {}; + + for (std::size_t i {}; i < series.size(); ++i) + { + if (i % 2 == 0) + { + shrunkenSeries.push_back(series[i]); + } + else + { + const int shrunkIndex { static_cast(i * 0.5) }; + + for (std::size_t j {}; j < series[i].size(); ++j) + { + shrunkenSeries[shrunkIndex][j] = (shrunkenSeries[shrunkIndex][j] + series[i][j]) * (T)0.5; + } } - //TODO: implement downsampling by resolution - return shrunkenSeries; + } + + //TODO: implement downsampling by resolution + return shrunkenSeries; } //explicit instantiation diff --git a/src/knnClassification.cpp b/src/knnClassification.cpp index 13a57b1..31d0deb 100644 --- a/src/knnClassification.cpp +++ b/src/knnClassification.cpp @@ -6,202 +6,196 @@ // Copyright © 2016 Goldsmiths. All rights reserved. // +#include "knnClassification.h" +#include +#include #include #include -#include #include -#include -#include "knnClassification.h" #ifdef EMSCRIPTEN #include "emscripten/knnEmbindings.h" #endif -template -knnClassification::knnClassification(const int &num_inputs, - const std::vector &which_inputs, - const std::vector > &_neighbours, - const int k) : - numInputs(num_inputs), - whichInputs(which_inputs), - whichOutput(0), - neighbours(_neighbours), - desiredK(k), - currentK(k) +template +knnClassification::knnClassification( + const int &num_inputs, + const std::vector &which_inputs, + const std::vector> &_neighbours, + const int k) + : numInputs(num_inputs), + whichInputs(which_inputs), + whichOutput(0), + neighbours(_neighbours), + desiredK(k), + currentK(k) +{} + +template knnClassification::~knnClassification() {} + +template void knnClassification::reset() { + // TODO: implement this } -template -knnClassification::~knnClassification() {} - -template -void knnClassification::reset() +template size_t knnClassification::getNumInputs() const { - //TODO: implement this + return numInputs; } -template -size_t knnClassification::getNumInputs() const +template +std::vector knnClassification::getWhichInputs() const { - return numInputs; + return whichInputs; } -template -std::vector knnClassification::getWhichInputs() const +template int knnClassification::getK() const { - return whichInputs; + return currentK; } -template -int knnClassification::getK() const +template inline void knnClassification::updateK() { - return currentK; + if (currentK != desiredK) currentK = std::min(desiredK, (int)neighbours.size()); } -template -inline void knnClassification::updateK() +template void knnClassification::setK(int newK) { - if (currentK != desiredK) currentK = std::min(desiredK, (int) neighbours.size()); + desiredK = newK; + updateK(); } -template -void knnClassification::setK(int newK) +template +void knnClassification::addNeighbour(const int classNum, const std::vector& features) { - desiredK = newK; - updateK(); -} - -template -void knnClassification::addNeighbour(const int &classNum, const std::vector &features) -{ - std::vector classVec; - classVec.push_back(T(classNum)); - trainingExampleTemplate newNeighbour = {features, classVec}; - neighbours.push_back(newNeighbour); - updateK(); + std::vector classVec; + classVec.push_back(T(classNum)); + trainingExampleTemplate newNeighbour = {features, classVec}; + neighbours.push_back(newNeighbour); + updateK(); }; -template -void knnClassification::train(const std::vector >& trainingSet) +template +void knnClassification::train(const std::vector>& trainingSet) { - train(trainingSet, 0); + train(trainingSet, 0); } -// FIXME: Not paying attention to whichOutput. -template -void knnClassification::train(const std::vector > &trainingSet, const std::size_t which_output) //FIXME: Does numInputs need to be reset here? -MZ -{ - neighbours.clear(); - neighbours = trainingSet; - updateK(); - whichOutput = which_output; +// FIXME: Not paying attention to whichOutput. +template +void knnClassification::train(const std::vector>& trainingSet, const std::size_t which_output) // FIXME: Does numInputs need to be reset here? -MZ +{ + neighbours.clear(); + neighbours = trainingSet; + updateK(); + whichOutput = which_output; }; -template -T knnClassification::run(const std::vector &inputVector) +template +T knnClassification::run(const std::vector &inputVector) { - std::vector> nearestNeighbours; //These are our k nearest neighbours - - for (size_t i { 0 }; i < currentK; ++i) + std::vector> nearestNeighbours { static_cast(currentK), std::make_pair(0, 0.0) }; // These are our k nearest neighbours + std::pair farthestNN{ 0, 0.0 }; // This one will be replaced if there's a closer one + std::vector pattern {}; // This is what we're trying to match + + for (const auto input : whichInputs) + { + pattern.push_back(inputVector[input]); + } + + // Find k nearest neighbours + for (size_t index {}; const auto &neighbour : neighbours) + { + // find Euclidian distance for this neighbor + T euclidianDistance {}; + + for (size_t j {}; j < numInputs; ++j) { - nearestNeighbours.push_back( std::make_pair(0, 0.) ); - }; - std::pair farthestNN {0, 0.}; //This one will be replaced if there's a closer one - - std::vector pattern; //This is what we're trying to match - for (size_t h { 0 }; h < numInputs; ++h) + euclidianDistance += (T)std::pow((pattern[j] - neighbour.input[j]), 2); + } + + euclidianDistance = std::sqrt(euclidianDistance); + + if (index < currentK) { - pattern.push_back(inputVector[whichInputs[h]]); + // save the first k neighbours + nearestNeighbours[index] = {index, euclidianDistance}; + if (euclidianDistance > farthestNN.second) farthestNN = { index, euclidianDistance }; } - - //Find k nearest neighbours - size_t index { 0 }; - for (auto it = neighbours.cbegin(); it != neighbours.cend(); ++it) + else if (euclidianDistance < farthestNN.second) { - //find Euclidian distance for this neighbor - T euclidianDistance { 0 }; - for (size_t j = 0; j < numInputs ; ++j) + // replace farthest, if new neighbour is closer + nearestNeighbours[farthestNN.first] = {index, euclidianDistance}; + size_t currentFarthest{0}; + T currentFarthestDistance{0.0}; + + for (size_t n {}; n < currentK; ++n) + { + if (nearestNeighbours[n].second > currentFarthestDistance) { - euclidianDistance += (T)pow((pattern[j] - it->input[j]), 2); + currentFarthest = n; + currentFarthestDistance = nearestNeighbours[n].second; } - euclidianDistance = sqrt(euclidianDistance); + } + farthestNN = { currentFarthest, currentFarthestDistance }; + } + ++index; + } - if (index < currentK) - { - //save the first k neighbours - nearestNeighbours[index] = {index, euclidianDistance}; - if (euclidianDistance > farthestNN.second) farthestNN = {index, euclidianDistance}; - } - else if (euclidianDistance < farthestNN.second) - { - //replace farthest, if new neighbour is closer - nearestNeighbours[farthestNN.first] = {index, euclidianDistance}; - size_t currentFarthest { 0 }; - T currentFarthestDistance { 0.0 }; - - for (size_t n { 0 }; n < currentK; ++n) - { - if (nearestNeighbours[n].second > currentFarthestDistance) - { - currentFarthest = n; - currentFarthestDistance = nearestNeighbours[n].second; - } - } - farthestNN = { currentFarthest, currentFarthestDistance} ; - } - ++index; + // majority vote on nearest neighbours + std::map classVoteMap; + using classVotePair = std::pair; + + for (size_t i {}; i < currentK; ++i) + { + T classNum{ (T)round(neighbours[nearestNeighbours[i].first].output[whichOutput]) }; + + if (classVoteMap.find(classNum) == classVoteMap.end()) + { + classVoteMap.insert(classVotePair(classNum, 1)); } - - //majority vote on nearest neighbours - std::map classVoteMap; - using classVotePair = std::pair; - for (size_t i = 0; i < currentK; ++i) + else { - T classNum { (T)round(neighbours[nearestNeighbours[i].first].output[whichOutput]) }; - if ( classVoteMap.find(classNum) == classVoteMap.end() ) - { - classVoteMap.insert(classVotePair(classNum, 1)); - } - else - { - ++classVoteMap[classNum]; - } + ++classVoteMap[classNum]; } + } - T foundClass = 0; - int mostVotes = 0; - for (auto p = classVoteMap.cbegin(); p != classVoteMap.cend(); ++p) + T foundClass {}; + int mostVotes {}; + + for (const auto& classVote : classVoteMap) + { + if (classVote.second > mostVotes) { - if (p->second > mostVotes) - { - mostVotes = p->second; - foundClass = p->first; - } + mostVotes = classVote.second; + foundClass = classVote.first; } - return foundClass; + } + + return foundClass; } #ifndef EMSCRIPTEN -template -void knnClassification::getJSONDescription(Json::Value &jsonModelDescription) +template +void knnClassification::getJSONDescription(Json::Value &jsonModelDescription) { - jsonModelDescription["modelType"] = "kNN Classificiation"; - jsonModelDescription["numInputs"] = numInputs; - jsonModelDescription["whichInputs"] = this->vector2json(whichInputs); - jsonModelDescription["k"] = desiredK; - Json::Value examples; - - for (auto it = neighbours.cbegin(); it != neighbours.cend(); ++it) - { - Json::Value oneExample; - oneExample["class"] = it->output[whichOutput]; - oneExample["features"] = this->vector2json(it->input); - examples.append(oneExample); - } - - jsonModelDescription["examples"] = examples; + jsonModelDescription["modelType"] = "kNN Classificiation"; + jsonModelDescription["numInputs"] = numInputs; + jsonModelDescription["whichInputs"] = this->vector2json(whichInputs); + jsonModelDescription["k"] = desiredK; + Json::Value examples; + + for (const auto& neighbour : neighbours) + { + Json::Value oneExample; + oneExample["class"] = neighbour.output[whichOutput]; + oneExample["features"] = this->vector2json(neighbour.input); + examples.append(oneExample); + } + + jsonModelDescription["examples"] = examples; } #endif -//explicit instantiation +// explicit instantiation template class knnClassification; template class knnClassification; diff --git a/src/knnClassification.h b/src/knnClassification.h index bc27d31..efc601d 100644 --- a/src/knnClassification.h +++ b/src/knnClassification.h @@ -18,82 +18,84 @@ /** Class for implementing a knn classifier */ template -class knnClassification final : public baseModel -{ -public: - /** Constructor that takes training examples in - * @param int Number of inputs expected in the training and input vectors - * @param vector of input numbers to be fed into the classifer. - * @param vector of training examples - * @param int how many near neighbours to evaluate - */ - knnClassification(const int &num_inputs, - const std::vector &which_inputs, - const std::vector > &trainingSet, - const int k); - ~knnClassification(); - - /** add another example to the existing training set - * @param class number of example - * @param feature vector of example - */ - void addNeighbour(const int &classNum, const std::vector &features); - - /** Generate an output value from a single input vector. - * @param A standard vector of type T to be evaluated. - * @return A single value of type T: the nearest class as determined by k-nearest neighbor. - */ - T run(const std::vector &inputVector) override; - - /** Fill the model with a vector of examples. - * - * @param The training set is a vector of training examples that contain both a vector of input values and a value specifying desired output class. - * - */ - void train(const std::vector > &trainingSet) override; - - /** Fill the model with a vector of examples. Use this when the model is part of a modelSet. - * - * @param The training set is a vector of training examples that contain both a vector of input values and a value specifying desired output class. - * - */ - void train(const std::vector >& trainingSet, const std::size_t whichOutput) override; - - /** Reset the model to its empty state. */ - void reset() override; - - /** Find out how many inputs the model expects - * @return Integer number of intpus - */ - size_t getNumInputs() const override; - - /** Find out which inputs in a vector will be used - * @return Vector of ints, specifying input indices. - */ - std::vector getWhichInputs() const override; - - /** Get the number of nearest neighbours used by the kNN algorithm. */ - int getK() const; - /** Change the number of nearest neighbours used by the kNN algorithm. - * @param new value for k - */ - void setK(int newK); - +class knnClassification final : public baseModel +{ + public: + /** Constructor that takes training examples in + * @param int Number of inputs expected in the training and input vectors + * @param vector of input numbers to be fed into the classifer. + * @param vector of training examples + * @param int how many near neighbours to evaluate + */ + knnClassification(const int &num_inputs, + const std::vector &which_inputs, + const std::vector > &trainingSet, + const int k); + + ~knnClassification(); + + /** add another example to the existing training set + * @param class number of example + * @param feature vector of example + */ + void addNeighbour(const int classNum, const std::vector& features); + + /** Generate an output value from a single input vector. + * @param A standard vector of type T to be evaluated. + * @return A single value of type T: the nearest class as determined by k-nearest neighbor. + */ + T run(const std::vector& inputVector) override; + + /** Fill the model with a vector of examples. + * + * @param The training set is a vector of training examples that contain both a vector of input values and a value specifying desired output class. + * + */ + void train(const std::vector >& trainingSet) override; + + /** Fill the model with a vector of examples. Use this when the model is part of a modelSet. + * + * @param The training set is a vector of training examples that contain both a vector of input values and a value specifying desired output class. + * + */ + void train(const std::vector>& trainingSet, const std::size_t whichOutput) override; + + /** Reset the model to its empty state. */ + void reset() override; + + /** Find out how many inputs the model expects + * @return Integer number of intpus + */ + size_t getNumInputs() const override; + + /** Find out which inputs in a vector will be used + * @return Vector of ints, specifying input indices. + */ + std::vector getWhichInputs() const override; + + /** Get the number of nearest neighbours used by the kNN algorithm. */ + int getK() const; + + /** Change the number of nearest neighbours used by the kNN algorithm. + * @param new value for k + */ + void setK(int newK); + #ifndef EMSCRIPTEN - /** Populate a JSON value with a description of the current model - * @param A JSON value to be populated - */ - void getJSONDescription(Json::Value ¤tModel) override; + /** Populate a JSON value with a description of the current model + * @param A JSON value to be populated + */ + void getJSONDescription(Json::Value& currentModel) override; #endif - -private: - int numInputs; - std::vector whichInputs; - std::size_t whichOutput; - std::vector> neighbours; - int desiredK; //K that user asked for might be limited but number of examples - int currentK; //K minimum of desiredK or neighbours.size() - inline void updateK(); + + private: + int numInputs {}; + std::vector whichInputs {}; + std::size_t whichOutput {}; + std::vector> neighbours {}; + int desiredK {}; //K that user asked for might be limited but number of examples + int currentK {}; //K minimum of desiredK or neighbours.size() + inline void updateK(); }; #endif diff --git a/src/rapidStream.cpp b/src/rapidStream.cpp index 07a5e7d..6cf126e 100644 --- a/src/rapidStream.cpp +++ b/src/rapidStream.cpp @@ -19,32 +19,28 @@ namespace rapidLib { template -rapidStream::rapidStream(std::size_t window_size) : -windowSize(window_size), -windowIndex(0) +rapidStream::rapidStream(std::size_t window_size) : windowSize(window_size), windowIndex(0) { circularWindow.resize(windowSize); std::fill(circularWindow.begin(), circularWindow.end(), 0); - + //Baysian Filter setup - bayesFilt.diffusion = powf(10., -2); - bayesFilt.jump_rate = powf(10., -10); - bayesFilt.mvc[0] = 1.; + bayesFilt.diffusion = powf(10.0f, -2); + bayesFilt.jump_rate = powf(10.0f, -10); + bayesFilt.mvc[0] = 1.0f; bayesFilt.init(); } template -rapidStream::rapidStream() : -windowSize(3), -windowIndex(0) +rapidStream::rapidStream() : windowSize(3), windowIndex(0) { circularWindow.resize(windowSize); std::fill(circularWindow.begin(), circularWindow.end(), 0); - + //Baysian Filter setup - bayesFilt.diffusion = powf(10., -2); - bayesFilt.jump_rate = powf(10., -10); - bayesFilt.mvc[0] = 1.; + bayesFilt.diffusion = powf(10.0f, -2); + bayesFilt.jump_rate = powf(10.0f, -10); + bayesFilt.mvc[0] = 1.0; bayesFilt.init(); } @@ -103,9 +99,10 @@ template uint32_t rapidStream::numZeroCrossings() const { uint32_t zeroCrossings {}; - + //Is the begininng positive, negative, or 0? int previous { 1 }; + if (circularWindow[windowIndex] < 0) { previous = -1; @@ -115,11 +112,11 @@ uint32_t rapidStream::numZeroCrossings() const ++zeroCrossings; previous = 0; } - + for (std::size_t i { 1 }; i < windowSize; ++i) { const std::size_t index { (windowIndex + i) % windowSize}; - + if (circularWindow[index] < 0 && previous >= 0) //Transition to negative { ++zeroCrossings; @@ -135,14 +132,14 @@ uint32_t rapidStream::numZeroCrossings() const previous = 0; } } + return zeroCrossings; } - template T rapidStream::sum() const { - return std::accumulate(circularWindow.begin(), circularWindow.end(), 0); + return std::reduce(circularWindow.begin(), circularWindow.end()); } template @@ -156,26 +153,28 @@ T rapidStream::standardDeviation() const { const T newMean { mean() }; T standardDeviation {}; - - for (auto value : circularWindow) + + for (const auto value : circularWindow) { - standardDeviation += static_cast(pow(value - newMean, 2)); + standardDeviation += static_cast(std::pow(value - newMean, 2)); } - return sqrt(standardDeviation / windowSize); + + return std::sqrt(standardDeviation / windowSize); } template T rapidStream::rms() const { T rms {}; - + for (auto value:circularWindow) { rms += value * value; } - + rms = rms / windowSize; - return sqrt(rms); + + return std::sqrt(rms); } template @@ -211,13 +210,13 @@ template T rapidStream::minVelocity() const { T minVel { std::numeric_limits::infinity() }; - + for (std::size_t i {}; i < windowSize; ++i) { const T currentVel { calcCurrentVel(i) }; if (currentVel < minVel) minVel = currentVel; } - + return minVel; } @@ -225,13 +224,13 @@ template T rapidStream::maxVelocity() const { T maxVel { std::numeric_limits::lowest() }; - + for (std::size_t i {}; i < windowSize; ++i) { const T currentVel { calcCurrentVel(i) }; if (currentVel > maxVel) maxVel = currentVel; } - + return maxVel; } @@ -240,7 +239,7 @@ T rapidStream::minAcceleration() const { T minAccel { std::numeric_limits::infinity() }; T lastVel { calcCurrentVel(1) }; - + for (std::size_t i { 2 }; i < windowSize; ++i) { const T currentVel { calcCurrentVel(i) }; @@ -248,7 +247,7 @@ T rapidStream::minAcceleration() const lastVel = currentVel; if (currentAccel < minAccel) minAccel = currentAccel; } - + return minAccel; } @@ -257,7 +256,7 @@ T rapidStream::maxAcceleration() const { T maxAccel { std::numeric_limits::lowest() }; T lastVel { calcCurrentVel(1) }; - + for (std::size_t i { 2 }; i < windowSize; ++i) { const T currentVel { calcCurrentVel(i) }; @@ -265,7 +264,7 @@ T rapidStream::maxAcceleration() const lastVel = currentVel; if (currentAccel > maxAccel) maxAccel = currentAccel; } - + return maxAccel; } diff --git a/src/searchWindow.cpp b/src/searchWindow.cpp index 0fd6a47..85c58c9 100644 --- a/src/searchWindow.cpp +++ b/src/searchWindow.cpp @@ -11,18 +11,19 @@ #include "searchWindow.h" template -searchWindow::searchWindow(const std::size_t seriesXSize, const std::size_t seriesYSize, const warpPath &shrunkenWarpPath, const int searchRadius) : minMaxValues(seriesXSize, std::make_pair(-1, 0)), maxY(seriesYSize - 1) +searchWindow::searchWindow(const std::size_t seriesXSize, const std::size_t seriesYSize, const warpPath& shrunkenWarpPath, const int searchRadius) : minMaxValues(seriesXSize, std::make_pair(-1, 0)), + maxY(static_cast(seriesYSize - 1)) { //Current location of higher resolution path - std::pair currentIndex = shrunkenWarpPath.indices[0]; - + std::pair currentIndex { shrunkenWarpPath.indices[0] }; + //Last evaluated part of low resolution path - std::pair lastWarped = std::make_pair(std::numeric_limits::max(), std::numeric_limits::max()); - - int blockSize = 2; //TODO: something other than 2? Different for x and y? - + std::pair lastWarped { std::make_pair(std::numeric_limits::max(), std::numeric_limits::max()) }; + + const int blockSize { 2 }; //TODO: something other than 2? Different for x and y? + //project each part of the low-res path to high res cells - for ( auto &xyIndex : shrunkenWarpPath.indices) + for (const auto &xyIndex : shrunkenWarpPath.indices) { if (xyIndex.first > lastWarped.first) currentIndex.first += blockSize; if (xyIndex.second > lastWarped.second) currentIndex.second += blockSize; @@ -32,7 +33,7 @@ searchWindow::searchWindow(const std::size_t seriesXSize, const std::size_t s markVisited(currentIndex.first, currentIndex.second - 1); } - for (int j = 0; j < blockSize; ++j) + for (int j {}; j < blockSize; ++j) { markVisited(currentIndex.first + j, currentIndex.second); markVisited(currentIndex.first + j, currentIndex.second + blockSize - 1); //TODO: These are redundant? @@ -57,9 +58,13 @@ inline void searchWindow::markVisited(std::size_t col, std::size_t row) { minMaxValues[col].first = row; minMaxValues[col].second = row; - } else if (minMaxValues[col].first > row) { + } + else if (minMaxValues[col].first > row) + { minMaxValues[col].first = row; - } else if (minMaxValues[col].second < row) { + } + else if (minMaxValues[col].second < row) + { minMaxValues[col].second = row; } } @@ -71,133 +76,141 @@ void searchWindow::expandWindow(int radius) if (radius > 0) { //Add all cells in the current window to a vector. - std::vector> windowCells; + std::vector> windowCells {}; + for (int currentX = 0; currentX < minMaxValues.size(); ++currentX) { - for (std::size_t currentY = minMaxValues[currentX].first; currentY <= minMaxValues[currentX].second; ++currentY) + for (std::size_t currentY { minMaxValues[currentX].first}; currentY <= minMaxValues[currentX].second; ++currentY) { windowCells.push_back(std::make_pair(currentX, currentY)); } } - int maxX = int(minMaxValues.size() - 1); - - for (auto ¤tCell : windowCells) + const int maxX { static_cast(minMaxValues.size() - 1) }; + + for (const auto ¤tCell : windowCells) { if (currentCell.first != 0 && currentCell.second != maxY) //move to upper left if possible { //expand until edges are met - int targetX = currentCell.first - radius; - int targetY = currentCell.second + radius; - + const int targetX { currentCell.first - radius }; + const int targetY { currentCell.second + radius }; + if (targetX >= 0 && targetY <= maxY) { markVisited(targetX, targetY); } else { - int cellsPastEdge = std::max(0 - targetX, targetY - int(maxY)); //FIXME: ints? size_t? + const int cellsPastEdge { std::max(0 - targetX, targetY - maxY) }; markVisited(targetX + cellsPastEdge, targetY + cellsPastEdge); } } if (currentCell.second != maxY) //move up if possible { - int targetX = currentCell.first; - int targetY = currentCell.second + radius; + const int targetX { currentCell.first }; + const int targetY { currentCell.second + radius }; if (targetY <= maxY) { markVisited(targetX, targetY); } else { - int cellsPastEdge = targetY - int(maxY); + const int cellsPastEdge { targetY - int(maxY) }; markVisited(targetX, targetY - cellsPastEdge); } } if (currentCell.first != maxX && currentCell.second != maxY) //move upper right if possible { - std::size_t targetX = currentCell.first + radius; - std::size_t targetY = currentCell.second + radius; + const int targetX { currentCell.first + radius }; + const int targetY { currentCell.second + radius }; if (targetX <= maxX && targetY <= maxY) { markVisited(targetX, targetY); } else { - std::size_t cellsPastEdge = std::max(targetX - maxX, targetY - maxY); + const int cellsPastEdge { std::max(targetX - maxX, targetY - maxY) }; markVisited(targetX - cellsPastEdge, targetY - cellsPastEdge); } } if (currentCell.first != 0) //move left if possible { - int targetX = currentCell.first - radius; - std::size_t targetY = currentCell.second; + const int targetX { currentCell.first - radius }; + const int targetY { currentCell.second }; + if (targetX != 0) { markVisited(targetX, targetY); } else { - int cellsPastEdge = (0 - targetX); + const int cellsPastEdge { 0 - targetX }; markVisited(targetX + cellsPastEdge, targetY); } } - if (currentCell.first != maxX) { //move right if possible - int targetX = currentCell.first + radius; - int targetY = currentCell.second; + if (currentCell.first != maxX) //move right if possible + { + const int targetX { currentCell.first + radius}; + const int targetY { currentCell.second }; if (targetX <= maxX) { markVisited(targetX, targetY); } else { - int cellsPastEdge = (targetX - maxX); + const int cellsPastEdge { targetX - maxX }; markVisited(targetX - cellsPastEdge, targetY); } } if (currentCell.first != 0 && currentCell.second != 0) { //move to lower left if possible - int targetX = currentCell.first - radius; - int targetY = currentCell.second - radius; - + const int targetX { currentCell.first - radius }; + const int targetY { currentCell.second - radius }; + if (targetX >= 0 && targetY >= 0) { markVisited(targetX, targetY); } else { - int cellsPastEdge = std::max(0 - targetX, 0 - targetY); + const int cellsPastEdge { std::max(0 - targetX, 0 - targetY) }; markVisited(targetX + cellsPastEdge, targetY + cellsPastEdge); } } if (currentCell.second != 0) //move down if possible { - int targetX = currentCell.first; - int targetY = currentCell.second - radius; + const int targetX { currentCell.first }; + const int targetY { currentCell.second - radius }; + if (targetY >= 0) { markVisited(targetX, targetY); } else { - int cellsPastEdge = 0 - targetY; + const int cellsPastEdge { 0 - targetY }; markVisited(targetX, targetY + cellsPastEdge); } } if (currentCell.first != maxX && currentCell.second != 0) //move lower right if possible { - int targetX = currentCell.first + radius; - int targetY = currentCell.second - radius; - if (targetX <= maxX && targetY >= 0) { + const int targetX { currentCell.first + radius }; + const int targetY { currentCell.second - radius }; + + if (targetX <= maxX && targetY >= 0) + { markVisited(targetX, targetY); - } else { - int cellsPastEdge = std::max(targetX - maxX, 0 - targetY); + } + else + { + const int cellsPastEdge { std::max(targetX - maxX, 0 - targetY) }; markVisited(targetX - cellsPastEdge, targetY + cellsPastEdge); } } diff --git a/src/searchWindow.h b/src/searchWindow.h index a3a219b..ce43215 100644 --- a/src/searchWindow.h +++ b/src/searchWindow.h @@ -19,17 +19,18 @@ template class searchWindow { public: - searchWindow(const std::size_t seriesXSize, - const std::size_t seriesYSize, - const warpPath &shrunkenWarpPath, - const int searchRadius); - - std::vector< std::pair > minMaxValues; - + searchWindow(const std::size_t seriesXSize, + const std::size_t seriesYSize, + const warpPath &shrunkenWarpPath, + const int searchRadius); + + std::vector> minMaxValues {}; + private: - std::size_t maxY; - inline void markVisited(std::size_t col, std::size_t row); - void expandWindow(int searchRadius); + inline void markVisited(std::size_t col, std::size_t row); + void expandWindow(int searchRadius); + + int maxY {}; }; -#endif \ No newline at end of file +#endif diff --git a/src/warpPath.h b/src/warpPath.h index 1caa7ce..ebebba3 100644 --- a/src/warpPath.h +++ b/src/warpPath.h @@ -24,17 +24,15 @@ class warpPath */ void add(std::size_t x, std::size_t y); - std::vector< std::pair > indices; + std::vector> indices; }; /** return struct holding a warp path and the cost of that path */ template struct warpInfo { -public: warpPath path; T cost; - }; #endif From d2f4d1a4816612ae574c97243901bfec7e140227 Mon Sep 17 00:00:00 2001 From: Michael ZBYSZYNSKI Date: Wed, 2 Jul 2025 16:23:48 +0100 Subject: [PATCH 3/6] Change to std::min --- src/dtw.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dtw.cpp b/src/dtw.cpp index e8a9f28..99d319e 100644 --- a/src/dtw.cpp +++ b/src/dtw.cpp @@ -67,7 +67,7 @@ T dtw::getCost(const std::vector>& seriesX, const std::vector< for (std::size_t y { 1 }; y <= maxY; ++y) { - T minGlobalCost { fmin(costMatrix[x - 1][y - 1], costMatrix[x][y - 1]) }; + T minGlobalCost { (std::min(costMatrix[x - 1][y - 1], costMatrix[x][y - 1])) }; costMatrix[x][y] = minGlobalCost + distanceFunction(seriesX[x], seriesY[y]); } } @@ -158,7 +158,7 @@ warpInfo dtw::constrainedDTW(const std::vector > &seriesX, } else { - T minGlobalCost { fmin(costMatrix[currentX - 1][currentY], fmin(costMatrix[currentX-1][currentY-1], costMatrix[currentX][currentY-1])) }; + T minGlobalCost { std::min(costMatrix[currentX - 1][currentY], std::min(costMatrix[currentX-1][currentY-1], costMatrix[currentX][currentY-1])) }; costMatrix[currentX][currentY] = distanceFunction(seriesX[currentX], seriesY[currentY]) + minGlobalCost; } } From 16ae819f3ea2a6a2873b7bd961e4bf37b4de6a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Sun, 27 Jul 2025 10:43:10 -0400 Subject: [PATCH 4/6] Allow to disable jsoncpp If a software tries to integrate rapidlib but already uses jsoncpp, then this is undefined behaviour which gets flagged by addresssanitizer for instance. I encountered this while trying to integrate RapidLib with https://ossia.io : ossia integrates sh4lt which is a completely different package for sharing video frames across processes on Linux. sh4lt also uses jsoncpp as part of its implementation thus jsoncpp symbols are defined twice (and the unlucky software crashes on startup). --- CMakeLists.txt | 16 +++++++++++++--- src/baseModel.h | 4 ++-- src/fastDTW.cpp | 2 +- src/knnClassification.cpp | 6 ++++-- src/knnClassification.h | 4 ++-- src/modelSet.cpp | 8 +++++--- src/modelSet.h | 4 ++-- src/neuralNetwork.cpp | 2 +- src/neuralNetwork.h | 4 ++-- src/svmClassification.cpp | 2 +- src/svmClassification.h | 2 +- 11 files changed, 34 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a967538..2f033e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,11 @@ else() add_compile_options(-std=c++20 -O2) endif() +option(RAPIDLIB_DISABLE_JSONCPP "Disable jsoncpp integration." OFF) +if(EMSCRIPTEN) + set(RAPIDLIB_DISABLE_JSONCPP ON) +endif() + # Threads set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) @@ -28,16 +33,21 @@ file(GLOB JSON_SRC "${PROJECT_SOURCE_DIR}/dependencies/jsoncpp.cpp") file(GLOB BAYES_SRC "${PROJECT_SOURCE_DIR}/dependencies/bayesfilter/src/*.cpp") # Set the source for the main library, using the groups defined above -set(RAPIDLIB_FULL_SRC +set(RAPIDLIB_FULL_SRC ${RAPIDLIB_SRC} ${RAPIDLIB_DEP} - ${JSON_SRC} ${BAYES_SRC} - ) + ) add_library(${PROJECT_NAME} SHARED ${RAPIDLIB_FULL_SRC}) add_executable(rapidLibTest test/rapidLibTest.cpp) +if(RAPIDLIB_DISABLE_JSONCPP) + target_compile_definitions(${PROJECT_NAME} PUBLIC RAPIDLIB_DISABLE_JSONCPP) +else() + target_sources(${PROJECT_NAME} PUBLIC ${JSON_SRC}) +endif() + #include(GenerateExportHeader) #generate_export_header(${PROJECT_NAME}) diff --git a/src/baseModel.h b/src/baseModel.h index 2abcfeb..7c3e566 100644 --- a/src/baseModel.h +++ b/src/baseModel.h @@ -13,7 +13,7 @@ #include #include "trainingExample.h" -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP #include "../dependencies/json/json.h" #endif @@ -49,7 +49,7 @@ class baseModel virtual size_t getNumInputs() const = 0; virtual std::vector getWhichInputs() const = 0; -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP virtual void getJSONDescription(Json::Value& currentModel) = 0; protected: diff --git a/src/fastDTW.cpp b/src/fastDTW.cpp index a976b59..ef110ac 100644 --- a/src/fastDTW.cpp +++ b/src/fastDTW.cpp @@ -20,7 +20,7 @@ template warpInfo fastDTW::fullFastDTW(const std::vector>& seriesX, const std::vector>& seriesY, int searchRadius) { -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP if (seriesY.size() > seriesX.size()) { return fullFastDTW(seriesY, seriesX, searchRadius); //TODO: I'm not sure why I need this. Also, not sure why it fails with Emscripten. diff --git a/src/knnClassification.cpp b/src/knnClassification.cpp index 31d0deb..6ea5760 100644 --- a/src/knnClassification.cpp +++ b/src/knnClassification.cpp @@ -7,9 +7,11 @@ // #include "knnClassification.h" + +#include + #include #include -#include #include #include #ifdef EMSCRIPTEN @@ -174,7 +176,7 @@ T knnClassification::run(const std::vector &inputVector) return foundClass; } -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP template void knnClassification::getJSONDescription(Json::Value &jsonModelDescription) { diff --git a/src/knnClassification.h b/src/knnClassification.h index efc601d..64a8881 100644 --- a/src/knnClassification.h +++ b/src/knnClassification.h @@ -12,7 +12,7 @@ #include #include "baseModel.h" -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP #include "../dependencies/json/json.h" #endif @@ -81,7 +81,7 @@ class knnClassification final : public baseModel */ void setK(int newK); -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP /** Populate a JSON value with a description of the current model * @param A JSON value to be populated */ diff --git a/src/modelSet.cpp b/src/modelSet.cpp index 64968a8..8d99a78 100644 --- a/src/modelSet.cpp +++ b/src/modelSet.cpp @@ -15,9 +15,11 @@ #include #include "modelSet.h" -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP #include "../dependencies/json/json.h" -#else +#endif + +#if defined(EMSCRIPTEN) #include "emscripten/modelSetEmbindings.h" #endif @@ -129,7 +131,7 @@ std::vector modelSet::run(const std::vector &inputVector) -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP //In emscripten, we do the JSON parsing with native JavaScript template std::vector json2vector(Json::Value json) diff --git a/src/modelSet.h b/src/modelSet.h index 8a73a2b..7ceacdc 100644 --- a/src/modelSet.h +++ b/src/modelSet.h @@ -15,7 +15,7 @@ #include "neuralNetwork.h" #include "knnClassification.h" #include "svmClassification.h" -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP #include "../dependencies/json/json.h" #endif @@ -48,7 +48,7 @@ class modelSet { bool isTrained; void threadTrain(std::size_t i, const std::vector >& training_set); -#ifndef EMSCRIPTEN //The javascript code will do its own JSON parsing +#ifndef RAPIDLIB_DISABLE_JSONCPP //The javascript code will do its own JSON parsing public: /** Get a JSON representation of the model diff --git a/src/neuralNetwork.cpp b/src/neuralNetwork.cpp index b6aed03..643a819 100644 --- a/src/neuralNetwork.cpp +++ b/src/neuralNetwork.cpp @@ -328,7 +328,7 @@ T neuralNetwork::getOutBase() const return outBase; } -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP template void neuralNetwork::getJSONDescription(Json::Value& jsonModelDescription) { diff --git a/src/neuralNetwork.h b/src/neuralNetwork.h index 5307096..0c06e15 100644 --- a/src/neuralNetwork.h +++ b/src/neuralNetwork.h @@ -12,7 +12,7 @@ #include #include "baseModel.h" -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP #include "../dependencies/json/json.h" #endif @@ -81,7 +81,7 @@ class neuralNetwork final : public baseModel T getOutRange() const; T getOutBase() const; -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP void getJSONDescription(Json::Value& currentModel) override; #endif diff --git a/src/svmClassification.cpp b/src/svmClassification.cpp index 3e42b47..fa745be 100644 --- a/src/svmClassification.cpp +++ b/src/svmClassification.cpp @@ -261,7 +261,7 @@ std::vector svmClassification::getWhichInputs() const { return returnVec; }; -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP template void svmClassification::getJSONDescription(Json::Value ¤tModel){ diff --git a/src/svmClassification.h b/src/svmClassification.h index 55c5e39..168b93f 100644 --- a/src/svmClassification.h +++ b/src/svmClassification.h @@ -113,7 +113,7 @@ class svmClassification final : public baseModel size_t getNumInputs() const override; std::vector getWhichInputs() const override; -#ifndef EMSCRIPTEN +#ifndef RAPIDLIB_DISABLE_JSONCPP void getJSONDescription(Json::Value ¤tModel) override; #endif From 3b5e85082674e24f8192258f504687c8fa0a1229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Sun, 27 Jul 2025 11:45:50 -0400 Subject: [PATCH 5/6] Add a missing header for clang on linux --- src/classification.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/classification.cpp b/src/classification.cpp index 614d03d..0b475d0 100644 --- a/src/classification.cpp +++ b/src/classification.cpp @@ -6,9 +6,10 @@ // Copyright © 2016 Goldsmiths. All rights reserved. // +#include "classification.h" +#include #include -#include "classification.h" #ifdef EMSCRIPTEN #include "emscripten/classificationEmbindings.h" #endif From 905d1b95a78e25012e639c0def21384187bd843e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Tue, 12 Aug 2025 10:02:26 -0400 Subject: [PATCH 6/6] Fix target_sources PUBLIC -> PRIVATE --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f033e5..70e67de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ add_executable(rapidLibTest test/rapidLibTest.cpp) if(RAPIDLIB_DISABLE_JSONCPP) target_compile_definitions(${PROJECT_NAME} PUBLIC RAPIDLIB_DISABLE_JSONCPP) else() - target_sources(${PROJECT_NAME} PUBLIC ${JSON_SRC}) + target_sources(${PROJECT_NAME} PRIVATE ${JSON_SRC}) endif() #include(GenerateExportHeader) @@ -56,3 +56,4 @@ target_link_libraries(rapidLibTest Threads::Threads) enable_testing() add_test(rapidLibTest rapidLibTest) +