public void Test_FunctionRecognitionModuleSave(int MinNoiseForPrediction, int MaxNoiseForPrediction)
        {
            #region Train and Save
            var batch = 100;

            var funcData = FunctionGenerator.CreateFunction(500, 2, 2 * Math.PI / 100);

            LearningApi api = new LearningApi();
            api.UseActionModule <object, double[][]>((notUsed, ctx) =>
            {
                var similarFuncData = FunctionGenerator.CreateSimilarFromReferenceFunc(funcData.ToArray(), 7, 10);

                double[][] formattedData = formatData(similarFuncData);

                return(formattedData);
            });

            double[][] initCentroids = new double[4][];
            initCentroids[0] = new double[] { 1.53, 0.63 };
            initCentroids[1] = new double[] { 4.68, -0.63 };
            initCentroids[2] = new double[] { 7.85, 0.62 };
            initCentroids[3] = new double[] { 10.99, -0.64 };

            ClusteringSettings settings = new ClusteringSettings(0, numClusters: 4, numDims: 2, KmeansAlgorithm: 2, initialCentroids: initCentroids, tolerance: 0)
            {
                KmeansMaxIterations = 1000
            };

            api.UseKMeansFunctionRecognitionModule(settings);

            KMeansFunctionRecognitonScore res;

            while (batch-- > 0)
            {
                res = api.RunBatch() as KMeansFunctionRecognitonScore;
            }

            api.Save("sinusmodel");
            #endregion

            #region Load And Predict
            var api2       = LearningApi.Load("sinusmodel");
            var noisedFunc = FunctionGenerator.CreateSimilarFromReferenceFunc(funcData.ToArray(), MinNoiseForPrediction, MaxNoiseForPrediction);

            double[][] data = formatData(noisedFunc);

            var predictionResult = api2.Algorithm.Predict(data, null) as KMeansFunctionRecognitionResult;

            // TRUE positives
            if (MaxNoiseForPrediction <= 10)
            {
                Assert.True(predictionResult.Loss == 1.0);
            }
            // TRUE negatives
            else if (MaxNoiseForPrediction >= 25)
            {
                Assert.False(predictionResult.Loss == 1.0);
            }
            #endregion
        }
        /// <summary>
        /// Provide settings for K-Means algorithm.
        /// </summary>
        public MouseGestureRecognizer()
        {
            m_LearningApi = new LearningApi();

            m_LearningApi.UseActionModule <object, double[][]>((notUsed, ctx) =>
            {
                return(m_CurrentData);
            });

            double[][] initCentroids = new double[2][];
            initCentroids[0] = new double[] { 0.0, 0.0, 0.0 };
            initCentroids[1] = new double[] { 400.0, 0.0, 0.0 };

            m_Settings = new ClusteringSettings(KmeansMaxIterations: 1000, numClusters: 2, numDims: 3, KmeansAlgorithm: 2, initialCentroids: initCentroids, tolerance: 0);

            m_LearningApi.UseKMeansFunctionRecognitionModule(m_Settings);
        }
        public void Test_FunctionRecognitionModule_CustomFunc()
        {
            #region Training
            int points = 22;
            var batch  = 100;

            /// If 2 dimensions are used, then data is formatted as:
            /// ret[dim1] = {x1,x2,..xN},
            /// ret[dim2] = {y1,y2,..,yN},
            /// Every dimension is returned as a row. Poinst of dimension are cells.
            var funcData = FunctionGenerator.CreateFunction(points, 2, 1, customFunc1);

            LearningApi api = new LearningApi();
            api.UseActionModule <object, double[][]>((notUsed, ctx) =>
            {
                var similarFuncData = FunctionGenerator.CreateSimilarFromReferenceFunc(funcData.ToArray(), 7, 10);

                // Formats the data to mulitidimensional array.
                double[][] formattedData = formatData(similarFuncData);

                return(formattedData);
            });

            double[][] initCentroids = new double[2][];
            initCentroids[0] = new double[] { 5.0, 10.0 };
            initCentroids[1] = new double[] { 15.0, 20.0 };

            ClusteringSettings settings = new ClusteringSettings(0, numClusters: 2, numDims: 2, KmeansAlgorithm: 2, initialCentroids: initCentroids, tolerance: 0)
            {
                KmeansMaxIterations = 1000
            };

            api.UseKMeansFunctionRecognitionModule(settings);

            KMeansFunctionRecognitonScore res;

            while (batch-- > 0)
            {
                res = api.RunBatch() as KMeansFunctionRecognitonScore;
            }
            #endregion

            #region Prediction
            int[] noises = new int[] { 5, 7, 9, 10, 3, 15, 20, 25, 30, 35, 40, 22, 15, 17, 12, 48, 61 };
            int   numOfAnomalliesDetected = 0;

            foreach (var noiseForPrediction in noises)
            {
                var noisedFunc = FunctionGenerator.CreateSimilarFromReferenceFunc(funcData.ToArray(), noiseForPrediction - 2, noiseForPrediction);

                m_CustFncIndx = 0;

                double[][] data = formatData(noisedFunc);

                var predictionResult = api.Algorithm.Predict(data, null) as KMeansFunctionRecognitionResult;

                //// TRUE positives
                if (noiseForPrediction <= 10)
                {
                    Assert.True(predictionResult.Loss == 1.0);
                    Debug.WriteLine($"Recognized: noise: {noiseForPrediction} - {predictionResult.Result} - {predictionResult.Loss}");
                }
                // TRUE negatives
                else if (noiseForPrediction >= 10)
                {
                    if (predictionResult.Result == false)
                    {
                        numOfAnomalliesDetected++;

                        // Result can be statistically true positive or true negative. This is because similar functions are genarated in +/- range to specified noise.
                        // If model is trained to 10% and sinilar function is created to 25%, it means that values of similar function
                        // fit range from 0-25% of referenced value. If it is below 10% (used for training in this test) then resut will be true positive.
                        Debug.WriteLine($"Anomally detected: Noise: {noiseForPrediction} - {predictionResult.Result} - {predictionResult.Loss}");
                    }
                }
            }

            // This is a statistical value. Test might theoretically fail.
            Assert.True(numOfAnomalliesDetected > 2, $"Num of anomallies detected = {numOfAnomalliesDetected}. Expected at least 2.");
            #endregion
        }
        public void Test_FunctionRecognitionModule(int points, int MinNoiseForPrediction, int MaxNoiseForPrediction)
        {
            #region Training
            var batch = 100;

            /// If 2 dimensions are used, then data is formatted as:
            /// ret[dim1] = {x1,x2,..xN},
            /// ret[dim2] = {y1,y2,..,yN},
            /// Every dimension is returned as a row. Poinst of dimension are cells.
            var funcData = FunctionGenerator.CreateFunction(points, 2, 2 * Math.PI / 100);

            LearningApi api = new LearningApi();
            api.UseActionModule <object, double[][]>((notUsed, ctx) =>
            {
                var similarFuncData = FunctionGenerator.CreateSimilarFromReferenceFunc(funcData.ToArray(), 7, 10);

                // Formats the data to mlitidimensional array.
                double[][] formattedData = formatData(similarFuncData);

                return(formattedData);
            });

            double[][] initCentroids = new double[4][];
            initCentroids[0] = new double[] { 1.53, 0.63 };
            initCentroids[1] = new double[] { 4.68, -0.63 };
            initCentroids[2] = new double[] { 7.85, 0.62 };
            initCentroids[3] = new double[] { 10.99, -0.64 };

            ClusteringSettings settings = new ClusteringSettings(0, numClusters: 4, numDims: 2, KmeansAlgorithm: 2, initialCentroids: initCentroids, tolerance: 0, funcRecogMethod: 2)
            {
                KmeansMaxIterations = 1000
            };

            api.UseKMeansFunctionRecognitionModule(settings);

            KMeansFunctionRecognitonScore res;

            while (batch-- > 0)
            {
                res = api.RunBatch() as KMeansFunctionRecognitonScore;
            }
            #endregion

            #region Prediction
            var noisedFunc = FunctionGenerator.CreateSimilarFromReferenceFunc(funcData.ToArray(), MinNoiseForPrediction, MaxNoiseForPrediction);

            double[][] data = formatData(noisedFunc);

            var predictionResult = api.Algorithm.Predict(data, null) as KMeansFunctionRecognitionResult;

            // TRUE positives
            if (MaxNoiseForPrediction <= 10)
            {
                Assert.True(predictionResult.Loss == 1.0);
            }
            // TRUE negatives
            else if (MaxNoiseForPrediction >= 25)
            {
                Assert.False(predictionResult.Loss == 1.0);
            }
            #endregion
        }
        public void Test_FunctionRecognition()
        {
            int numAttributes = 3;
            int numClusters   = 2;
            int maxCount      = 300;


            // a value in % representing the tolerance to possible outliers
            double tolerance = 0;
            // directory to load
            string loadDirectory = rootFolder + "Functions\\";
            // directory to save
            string saveDirectory = rootFolder + "Function Recognition\\";


            string TrainedFuncName = "SIN_SIN X";

            /*
             * // functions' paths
             * string[] FunctionPaths = new string[]
             * {
             *  loadDirectory + TrainedFuncName + "\\NRP5-10\\" + TrainedFuncName + " SimilarFunctions Normalized NRP5-10.csv",
             *  loadDirectory + "COS X\\NRP5-10\\COS X SimilarFunctions Normalized NRP5-10.csv",
             *  loadDirectory + "Triangular\\NRP5-10\\Triangular SimilarFunctions Normalized NRP5-10.csv",
             *  loadDirectory + "Square\\NRP5-10\\Square SimilarFunctions Normalized NRP5-10.csv",
             *  loadDirectory + "Line -X\\NRP5-10\\Line -X SimilarFunctions Normalized NRP5-10.csv"
             * };*/

            /*
             * // functions' paths
             * string[] FunctionPaths = new string[]
             * {
             *  loadDirectory + TrainedFuncName + "\\NRP5-10\\" + TrainedFuncName + " SimilarFunctions NRP5-10.csv",
             *  loadDirectory + "5 SIN 2X\\NRP5-10\\5 SIN 2X SimilarFunctions NRP5-10.csv"
             * };*/


            /*
             * // functions' paths
             * string[] FunctionPaths = new string[]
             * {
             *  loadDirectory + TrainedFuncName + "\\NRP5-10\\" + TrainedFuncName + " SimilarFunctions Normalized NRP5-10.csv",
             *  loadDirectory + "SIN 1.5X\\NRP5-10\\SIN 1.5X SimilarFunctions Normalized NRP5-10.csv",
             *  loadDirectory + "SIN 2X\\NRP5-10\\SIN 2X SimilarFunctions Normalized NRP5-10.csv"
             * };*/


            // functions' paths
            string[] FunctionPaths = new string[]
            {
                loadDirectory + TrainedFuncName + "\\NRP5-10\\" + TrainedFuncName + " SimilarFunctions Normalized NRP5-10.csv",
                loadDirectory + "SIN_COS X\\NRP5-10\\SIN_COS X SimilarFunctions Normalized NRP5-10.csv",
                loadDirectory + "COS_COS X\\NRP5-10\\COS_COS X SimilarFunctions Normalized NRP5-10.csv",
                loadDirectory + "COS_SIN X\\NRP5-10\\COS_SIN X SimilarFunctions Normalized NRP5-10.csv"
            };

            int numTrainFun = 800;
            int numTestFun  = 200;

            double[][] loadedSimFunctions = Helpers.LoadFunctionData(FunctionPaths[0]);

            int numLoadedFunc = 0;

            ClusteringSettings settings = new ClusteringSettings(maxCount, numClusters, numAttributes, KmeansAlgorithm: 2);
            LearningApi        api      = new LearningApi();

            api.UseActionModule <object, double[][]>((funcData, ctx) =>
            {
                numLoadedFunc++;
                return(KMeansAlgorithm.transposeFunction(KMeansAlgorithm.selectFunction(loadedSimFunctions, numLoadedFunc, numAttributes)));
            });

            api.UseKMeansFunctionRecognitionModule(settings);

            KMeansFunctionRecognitonScore res = new KMeansFunctionRecognitonScore();

            //train
            for (int i = 0; i < numTrainFun; i++)
            {
                res = api.Run() as KMeansFunctionRecognitonScore;
            }

            // save the formed clusters (just for plotting the function recognition results)
            Helpers.Write2CSVFile(res.Centroids, saveDirectory + "Calculated Centroids.csv");
            double[][] tempMaxDistance = new double[1][];
            tempMaxDistance[0] = res.InClusterMaxDistance;
            Helpers.Write2CSVFile(tempMaxDistance, saveDirectory + "Calculated Max Distance.csv");

            // save the trained clusters in a persistant location (just for plotting the clusters)
            Helpers.Write2CSVFile(res.Centroids, saveDirectory + TrainedFuncName + "\\Calculated Centroids C" + numClusters + ".csv");
            Helpers.Write2CSVFile(tempMaxDistance, saveDirectory + TrainedFuncName + "\\Calculated Max Distance C" + numClusters + ".csv");

            // start testing for function recognition

            double[]   testingResults = new double[numTestFun * FunctionPaths.Length];
            double[][] data;
            for (int l = 0; l < FunctionPaths.Length; l++)
            {
                loadedSimFunctions = Helpers.LoadFunctionData(FunctionPaths[l]);
                for (int i = 0; i < numTestFun; i++)
                {
                    data = KMeansAlgorithm.transposeFunction(KMeansAlgorithm.selectFunction(loadedSimFunctions, numTrainFun + i + 1, numAttributes));

                    var predictionResult = api.Algorithm.Predict(data, null) as KMeansFunctionRecognitionResult;

                    if (predictionResult.Result)
                    {
                        testingResults[i + l * numTestFun] = 1;
                    }
                    else
                    {
                        testingResults[i + l * numTestFun] = 0;
                    }
                }
            }

            // save results
            double[][] tempFunResults = new double[1][];
            tempFunResults[0] = testingResults;
            Helpers.Write2CSVFile(tempFunResults, saveDirectory + "Results.csv");

            double[][] TFMat = createTrueFalseMatrix(testingResults, FunctionPaths.Length);
            Helpers.Write2CSVFile(TFMat, saveDirectory + "TrueFalseMatrix.csv");
        }