diff --git a/.gitignore b/.gitignore
index d1ad80c..eeb9501 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,9 @@ EvenPerfectBitScanner.ResultsParser/bin/*
EvenPerfectBitScanner.ResultsParser/obj/*
EvenPerfectBitScanner.Tests/bin/*
EvenPerfectBitScanner.Tests/obj/*
+BuildDivisorTransitions/bin/*
+BuildDivisorTransitions/obj/*
PerfectNumbers.Core.Tests/bin/*
PerfectNumbers.Core.Tests/obj/*
.DS_Store
+.vs/*
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 21e35ff..38a0589 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -8,7 +8,8 @@
"options": [
"EvenPerfectBitScanner",
"EvenPerfectBitScanner.ResultsParser",
- "EvenPerfectBitScanner.Benchmarks"
+ "EvenPerfectBitScanner.Benchmarks",
+ "BuildDivisorTransitions"
],
"default": "EvenPerfectBitScanner"
}
@@ -68,6 +69,33 @@
"internalConsoleOptions": "neverOpen",
"justMyCode": false
},
+ {
+ "name": "Run BuildDivisorTransitions",
+ "type": "coreclr",
+ "request": "launch",
+ "noDebug": true,
+ "preLaunchTask": "Build BuildDivisorTransitions Release",
+ "program": "${workspaceFolder}/BuildDivisorTransitions/bin/Release/net8.0/BuildDivisorTransitions.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "integratedTerminal",
+ "internalConsoleOptions": "neverOpen",
+ "justMyCode": false
+ },
+ {
+ "name": "Debug BuildDivisorTransitions",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "Build BuildDivisorTransitions Debug",
+ "program": "${workspaceFolder}/BuildDivisorTransitions/bin/Debug/net8.0/BuildDivisorTransitions.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "integratedTerminal",
+ "internalConsoleOptions": "neverOpen",
+ "justMyCode": false
+ },
{
"name": "Run EvenPerfectBitScanner.ResultsParser",
"type": "coreclr",
@@ -136,4 +164,4 @@
"justMyCode": false
}
]
-}
\ No newline at end of file
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 90e6505..e42a24f 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -94,6 +94,36 @@
"group": "build",
"problemMatcher": "$msCompile"
},
+ {
+ "label": "Build BuildDivisorTransitions Debug",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/BuildDivisorTransitions/BuildDivisorTransitions.csproj",
+ "--configuration",
+ "Debug",
+ "--framework",
+ "net8.0"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "Build BuildDivisorTransitions Release",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/BuildDivisorTransitions/BuildDivisorTransitions.csproj",
+ "--configuration",
+ "Release",
+ "--framework",
+ "net8.0"
+ ],
+ "group": "build",
+ "problemMatcher": "$msCompile"
+ },
{
"label": "Run EvenPerfectBitScanner",
"type": "process",
@@ -124,6 +154,21 @@
],
"problemMatcher": []
},
+ {
+ "label": "Run BuildDivisorTransitions",
+ "type": "process",
+ "command": "dotnet",
+ "args": [
+ "run",
+ "--project",
+ "${workspaceFolder}/BuildDivisorTransitions/BuildDivisorTransitions.csproj",
+ "--configuration",
+ "Release",
+ "--framework",
+ "net8.0"
+ ],
+ "problemMatcher": []
+ },
{
"label": "Run Release (no debug)",
"type": "process",
@@ -183,4 +228,4 @@
"problemMatcher": []
}
]
-}
\ No newline at end of file
+}
diff --git a/BuildDivisorTransitions/BuildDivisorTransitions.csproj b/BuildDivisorTransitions/BuildDivisorTransitions.csproj
new file mode 100644
index 0000000..48b2e9e
--- /dev/null
+++ b/BuildDivisorTransitions/BuildDivisorTransitions.csproj
@@ -0,0 +1,8 @@
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
diff --git a/BuildDivisorTransitions/Program.cs b/BuildDivisorTransitions/Program.cs
new file mode 100644
index 0000000..c4a474f
--- /dev/null
+++ b/BuildDivisorTransitions/Program.cs
@@ -0,0 +1,211 @@
+using System.Globalization;
+using System.Numerics;
+using System.Text;
+
+namespace BuildDivisorTransitions;
+
+internal static class Program
+{
+ private const string OutputFileName = "NextKForecastTraning.csv";
+
+ private static int Main(string[] args)
+ {
+ if (args.Length != 2)
+ {
+ PrintUsage();
+ return 1;
+ }
+
+ string candidatesPath = args[0];
+ string divisorsPath = args[1];
+
+ if (!File.Exists(candidatesPath))
+ {
+ Console.Error.WriteLine($"Candidates file not found: {candidatesPath}");
+ return 1;
+ }
+
+ if (!File.Exists(divisorsPath))
+ {
+ Console.Error.WriteLine($"Divisors file not found: {divisorsPath}");
+ return 1;
+ }
+
+ List candidatePs = LoadCandidates(candidatesPath);
+ Dictionary divisors = LoadDivisors(divisorsPath);
+ List transitions = BuildTransitions(candidatePs, divisors);
+
+ WriteOutput(transitions);
+
+ Console.WriteLine($"Saved {transitions.Count} transitions to {OutputFileName}.");
+ return 0;
+ }
+
+ private static void PrintUsage()
+ {
+ Console.WriteLine("Usage: BuildDivisorTransitions ");
+ Console.WriteLine(" candidatesCsv: CSV with columns p,searchedMersenne,detailedCheck,passedAllTests");
+ Console.WriteLine(" divisorsCsv: CSV with columns p,divisor");
+ }
+
+ private static List LoadCandidates(string path)
+ {
+ var result = new List();
+ var seen = new HashSet();
+
+ using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+ using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
+
+ while (!reader.EndOfStream)
+ {
+ string? line = reader.ReadLine();
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+
+ string trimmedLine = line.Trim();
+ if (trimmedLine.StartsWith("p", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ string[] parts = trimmedLine.Split(',', StringSplitOptions.TrimEntries);
+ if (parts.Length == 0)
+ {
+ continue;
+ }
+
+ if (!long.TryParse(parts[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out long p))
+ {
+ throw new FormatException($"Invalid p value '{parts[0]}' in {path}.");
+ }
+
+ if (seen.Add(p))
+ {
+ result.Add(p);
+ }
+ }
+
+ return result;
+ }
+
+ private static Dictionary LoadDivisors(string path)
+ {
+ var result = new Dictionary();
+
+ using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+ using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
+
+ while (!reader.EndOfStream)
+ {
+ string? line = reader.ReadLine();
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+
+ string trimmedLine = line.Trim();
+ if (trimmedLine.StartsWith("p", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ string[] parts = trimmedLine.Split(',', StringSplitOptions.TrimEntries);
+ if (parts.Length < 2)
+ {
+ throw new FormatException($"Line '{line}' in {path} does not contain both p and divisor columns.");
+ }
+
+ if (!long.TryParse(parts[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out long p))
+ {
+ throw new FormatException($"Invalid p value '{parts[0]}' in {path}.");
+ }
+
+ if (!BigInteger.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out BigInteger divisor))
+ {
+ throw new FormatException($"Invalid divisor value '{parts[1]}' for p={p} in {path}.");
+ }
+
+ result[p] = divisor;
+ }
+
+ return result;
+ }
+
+ private static List BuildTransitions(IReadOnlyList candidatePs, IReadOnlyDictionary divisors)
+ {
+ var transitions = new List();
+
+ for (int i = 0; i < candidatePs.Count - 1; i++)
+ {
+ long currentP = candidatePs[i];
+ long nextP = candidatePs[i + 1];
+
+ if (!divisors.TryGetValue(currentP, out BigInteger currentDivisor))
+ {
+ continue;
+ }
+
+ if (!divisors.TryGetValue(nextP, out BigInteger nextDivisor))
+ {
+ continue;
+ }
+
+ BigInteger currentK = CalculateK(currentP, currentDivisor);
+ BigInteger nextK = CalculateK(nextP, nextDivisor);
+ bool isNextKHigher = nextK > currentK;
+ transitions.Add(new Transition(currentP, currentDivisor, LastDigit(currentP), currentK, LastMersenneDigit(currentP), isNextKHigher));
+ }
+
+ return transitions;
+ }
+
+ private static BigInteger CalculateK(long p, BigInteger divisor)
+ {
+ BigInteger numerator = divisor - BigInteger.One;
+ BigInteger denominator = new BigInteger(p) << 1;
+
+ if (denominator <= BigInteger.Zero)
+ {
+ throw new InvalidOperationException($"Computed 2p is not positive for p={p}.");
+ }
+
+ BigInteger remainder = numerator % denominator;
+ if (remainder != BigInteger.Zero)
+ {
+ throw new InvalidOperationException($"Divisor {divisor} is not congruent to 1 mod 2p for p={p}.");
+ }
+
+ BigInteger k = numerator / denominator;
+ if (k < BigInteger.One)
+ {
+ throw new InvalidOperationException($"Calculated k={k} is less than 1 for p={p}, divisor={divisor}.");
+ }
+
+ return k;
+ }
+
+ private static void WriteOutput(IReadOnlyCollection transitions)
+ {
+ using var writer = new StreamWriter(OutputFileName, false, Encoding.UTF8);
+ writer.WriteLine("p,divisor,lastDigit,k,lastMersenneDigit,isNextKHigher");
+
+ foreach (Transition transition in transitions)
+ {
+ writer.WriteLine($"{transition.P.ToString(CultureInfo.InvariantCulture)},{FormatBigInteger(transition.Divisor)},{transition.LastDigit},{FormatBigInteger(transition.CurrentK)},{transition.LastMersenneDigit},{(transition.IsNextKHigher ? "1" : "0")}");
+ }
+ }
+
+ private static int LastDigit(long p) => (int)(Math.Abs(p) % 10);
+
+ private static int LastMersenneDigit(long p)
+ {
+ long mod = p & 3;
+ return mod == 1 ? 1 : 7; // odd primes give p%4 == 1 or 3; 2^p mod 10 cycles -> 1 or 7 after subtracting 1
+ }
+
+ private static string FormatBigInteger(in BigInteger value) => value.ToString(CultureInfo.InvariantCulture);
+
+ private sealed record Transition(long P, BigInteger Divisor, int LastDigit, BigInteger CurrentK, int LastMersenneDigit, bool IsNextKHigher);
+}
diff --git a/EvenPerfectBitScanner/EvenPerfectBitScanner.csproj b/EvenPerfectBitScanner/EvenPerfectBitScanner.csproj
index c1a0f2a..fcebb40 100644
--- a/EvenPerfectBitScanner/EvenPerfectBitScanner.csproj
+++ b/EvenPerfectBitScanner/EvenPerfectBitScanner.csproj
@@ -13,7 +13,16 @@
+
+
+
+
-
+
+
+ PreserveNewest
+
+
+
\ No newline at end of file
diff --git a/EvenPerfectBitScanner/MLModel1.consumption.cs b/EvenPerfectBitScanner/MLModel1.consumption.cs
new file mode 100644
index 0000000..7fd3a37
--- /dev/null
+++ b/EvenPerfectBitScanner/MLModel1.consumption.cs
@@ -0,0 +1,103 @@
+// This file was auto-generated by ML.NET Model Builder.
+using Microsoft.ML;
+using Microsoft.ML.Data;
+using System;
+using System.Linq;
+using System.IO;
+using System.Collections.Generic;
+namespace EvenPerfectBitScanner
+{
+ public partial class MLModel1
+ {
+ ///
+ /// model input class for MLModel1.
+ ///
+ #region model input class
+ public class ModelInput
+ {
+ [LoadColumn(0)]
+ [ColumnName(@"p")]
+ public float P { get; set; }
+
+ [LoadColumn(1)]
+ [ColumnName(@"divisor")]
+ public float Divisor { get; set; }
+
+ [LoadColumn(2)]
+ [ColumnName(@"lastDigit")]
+ public float LastDigit { get; set; }
+
+ [LoadColumn(3)]
+ [ColumnName(@"k")]
+ public float K { get; set; }
+
+ [LoadColumn(4)]
+ [ColumnName(@"lastMersenneDigit")]
+ public float LastMersenneDigit { get; set; }
+
+ [LoadColumn(5)]
+ [ColumnName(@"isNextKHigher")]
+ public float IsNextKHigher { get; set; }
+
+ }
+
+ #endregion
+
+ ///
+ /// model output class for MLModel1.
+ ///
+ #region model output class
+ public class ModelOutput
+ {
+ [ColumnName(@"p")]
+ public float P { get; set; }
+
+ [ColumnName(@"divisor")]
+ public float Divisor { get; set; }
+
+ [ColumnName(@"lastDigit")]
+ public float LastDigit { get; set; }
+
+ [ColumnName(@"k")]
+ public float K { get; set; }
+
+ [ColumnName(@"lastMersenneDigit")]
+ public float LastMersenneDigit { get; set; }
+
+ [ColumnName(@"isNextKHigher")]
+ public float IsNextKHigher { get; set; }
+
+ [ColumnName(@"Features")]
+ public float[] Features { get; set; }
+
+ [ColumnName(@"Score")]
+ public float Score { get; set; }
+
+ }
+
+ #endregion
+
+ private static string MLNetModelPath = Path.GetFullPath("MLModel1.mlnet");
+
+ public static readonly Lazy> PredictEngine = new Lazy>(() => CreatePredictEngine(), true);
+
+
+ private static PredictionEngine CreatePredictEngine()
+ {
+ var mlContext = new MLContext();
+ ITransformer mlModel = mlContext.Model.Load(MLNetModelPath, out var _);
+ return mlContext.Model.CreatePredictionEngine(mlModel);
+ }
+
+ ///
+ /// Use this method to predict on .
+ ///
+ /// model input.
+ ///
+ public static ModelOutput Predict(ModelInput input)
+ {
+ var predEngine = PredictEngine.Value;
+ return predEngine.Predict(input);
+ }
+ }
+}
diff --git a/EvenPerfectBitScanner/MLModel1.evaluate.cs b/EvenPerfectBitScanner/MLModel1.evaluate.cs
new file mode 100644
index 0000000..d46ae96
--- /dev/null
+++ b/EvenPerfectBitScanner/MLModel1.evaluate.cs
@@ -0,0 +1,125 @@
+// This file was auto-generated by ML.NET Model Builder.
+
+using Microsoft.ML.Data;
+using Microsoft.ML;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Plotly.NET;
+using Plotly.NET.LayoutObjects;
+
+namespace EvenPerfectBitScanner
+{
+ public partial class MLModel1
+ {
+ ///
+ /// Permutation feature importance (PFI) is a technique to determine the importance
+ /// of features in a trained machine learning model. PFI works by taking a labeled dataset,
+ /// choosing a feature, and permuting the values for that feature across all the examples,
+ /// so that each example now has a random value for the feature and the original values for all other features.
+ /// The evaluation metric (e.g. R-squared) is then calculated for this modified dataset,
+ /// and the change in the evaluation metric from the original dataset is computed.
+ /// The larger the change in the evaluation metric, the more important the feature is to the model.
+ ///
+ /// PFI typically takes a long time to compute, as the evaluation metric is calculated
+ /// many times to determine the importance of each feature.
+ ///
+ ///
+ /// The common context for all ML.NET operations.
+ /// IDataView used to evaluate the model.
+ /// Model to evaluate.
+ /// Label column being predicted.
+ /// A list of each feature and its importance.
+ public static List> CalculatePFI(MLContext mlContext, IDataView trainData, ITransformer model, string labelColumnName)
+ {
+ var preprocessedTrainData = model.Transform(trainData);
+
+ var permutationFeatureImportance =
+ mlContext.Regression
+ .PermutationFeatureImportance(
+ model,
+ preprocessedTrainData,
+ labelColumnName: labelColumnName);
+
+ var featureImportanceMetrics =
+ permutationFeatureImportance
+ .Select((kvp) => new { kvp.Key, kvp.Value.RSquared })
+ .OrderByDescending(myFeatures => Math.Abs(myFeatures.RSquared.Mean));
+
+ var featurePFI = new List>();
+ foreach (var feature in featureImportanceMetrics)
+ {
+ var pfiValue = Math.Abs(feature.RSquared.Mean);
+ featurePFI.Add(new Tuple(feature.Key, pfiValue));
+ }
+
+ return featurePFI;
+ }
+
+ ///
+ /// R Squared is a measure of variation between the values predicted by the model and the true values.
+ /// In a "perfect" model, there would be no variation between predictions and true values.
+ ///
+ /// Here we will plot the predicted values vs the true values for the trained model. This RegressionChart.html
+ /// is then saved to the location specified by .
+ ///
+ /// See more information on R Squared at https://en.wikipedia.org/wiki/Coefficient_of_determination.
+ ///
+ /// IDataView used to train the model.
+ /// Model used for predictions.
+ /// Name of the predicted label column.
+ /// Folder path to save the RegressionChart.html file into.
+ public static void PlotRSquaredValues(IDataView trainData, ITransformer model, string labelColumnName, string folderPath)
+ {
+ // Number of rows to display in charts.
+ int numberOfRows = 1000;
+ // Use the model to make batch predictions on training data
+ var testResults = model.Transform(trainData);
+
+ // Get the actual values from the dataset
+ var trueValues = testResults.GetColumn(labelColumnName).Take(numberOfRows); ;
+
+ // Get the predicted values from the test results
+ var predictedValues = testResults.GetColumn("Score").Take(numberOfRows);
+
+ // Setup what the graph looks like
+ var title = Title.init(Text: "R-Squared Plot");
+ var layout = Layout.init(Title: title, PlotBGColor: Plotly.NET.Color.fromString("#e5ecf6"));
+ var xAxis = LinearAxis.init(
+ Title: Title.init("True Values"),
+ ZeroLineColor: Plotly.NET.Color.fromString("#ffff"),
+ GridColor: Plotly.NET.Color.fromString("#ffff"),
+ ZeroLineWidth: 2);
+ var yAxis = LinearAxis.init(
+ Title: Title.init("Predicted Values"),
+ ZeroLineColor: Plotly.NET.Color.fromString("#ffff"),
+ GridColor: Plotly.NET.Color.fromString("#ffff"),
+ ZeroLineWidth: 2);
+
+ // We will plot the line that shows the perfect result. Setup that line here.
+ var maximumValue = Math.Max(trueValues.Max(), predictedValues.Max());
+ var perfectX = new[] { 0, maximumValue };
+ var perfectY = new[] { 0, maximumValue };
+
+
+
+ // Create the scatterplot that shows the true values vs the predicted values
+ var trueAndPredictedValues = Chart2D.Chart.Scatter(x: trueValues, y: predictedValues, mode: StyleParam.Mode.Markers)
+ .WithLayout(layout)
+ .WithXAxis(xAxis)
+ .WithYAxis(yAxis);
+
+ // Setup the line that shows what a perfect prediction would look like
+ var perfectLineGraph = Chart2D.Chart.Line(x: perfectX, y: perfectY)
+ .WithLayout(layout)
+ .WithLine(Line.init(Width: 1.5));
+
+ var chartWithValuesAndIdealLine = Chart.Combine(new[] { trueAndPredictedValues, perfectLineGraph });
+ var chartFilePath = folderPath + "\\RegressionChart.html";
+
+ chartWithValuesAndIdealLine.SaveHtml(chartFilePath);
+ }
+ }
+}
+
+
diff --git a/EvenPerfectBitScanner/MLModel1.mbconfig b/EvenPerfectBitScanner/MLModel1.mbconfig
new file mode 100644
index 0000000..e3b174d
--- /dev/null
+++ b/EvenPerfectBitScanner/MLModel1.mbconfig
@@ -0,0 +1,337 @@
+{
+ "Scenario": "Regression",
+ "DataSource": {
+ "Version": 3,
+ "EscapeCharacter": "\"",
+ "ReadMultiLines": false,
+ "KeepDiacritics": false,
+ "KeepPunctuations": false,
+ "AllowQuoting": false,
+ "Type": "TabularFile",
+ "FilePath": "C:\\Users\\marek\\source\\repos\\even-perfect-numbers-scanner\\BuildDivisorTransitions\\bin\\Release\\net8.0\\NextKForecastTraning.csv",
+ "Delimiter": ",",
+ "DecimalMarker": ".",
+ "HasHeader": true,
+ "ColumnProperties": [
+ {
+ "ColumnName": "p",
+ "ColumnPurpose": "Feature",
+ "ColumnDataFormat": "Single",
+ "IsCategorical": false,
+ "Type": "Column",
+ "Version": 5
+ },
+ {
+ "ColumnName": "divisor",
+ "ColumnPurpose": "Feature",
+ "ColumnDataFormat": "Single",
+ "IsCategorical": false,
+ "Type": "Column",
+ "Version": 5
+ },
+ {
+ "ColumnName": "lastDigit",
+ "ColumnPurpose": "Feature",
+ "ColumnDataFormat": "Single",
+ "IsCategorical": true,
+ "Type": "Column",
+ "Version": 5
+ },
+ {
+ "ColumnName": "k",
+ "ColumnPurpose": "Feature",
+ "ColumnDataFormat": "Single",
+ "IsCategorical": false,
+ "Type": "Column",
+ "Version": 5
+ },
+ {
+ "ColumnName": "lastMersenneDigit",
+ "ColumnPurpose": "Feature",
+ "ColumnDataFormat": "Single",
+ "IsCategorical": true,
+ "Type": "Column",
+ "Version": 5
+ },
+ {
+ "ColumnName": "isNextKHigher",
+ "ColumnPurpose": "Label",
+ "ColumnDataFormat": "Single",
+ "IsCategorical": true,
+ "Type": "Column",
+ "Version": 5
+ }
+ ]
+ },
+ "Environment": {
+ "Type": "LocalCPU",
+ "Version": 1
+ },
+ "RunHistory": {
+ "Version": 3,
+ "Type": "Result",
+ "Trials": [
+ {
+ "Version": 1,
+ "Type": "Trial",
+ "TrainerName": "FastTreeRegression",
+ "Score": 0.30006118613190746,
+ "RuntimeInSeconds": 1.489,
+ "Parameter": {
+ "_SCHEMA_": "e0 * e1 * e2",
+ "e0": {
+ "OutputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ],
+ "InputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ]
+ },
+ "e1": {
+ "InputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ],
+ "OutputColumnName": "Features"
+ },
+ "e2": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 9,
+ "NumberOfTrees": 8,
+ "MaximumBinCountPerFeature": 317,
+ "FeatureFraction": 0.98434331463123226,
+ "LearningRate": 0.3857390791447961,
+ "LabelColumnName": "isNextKHigher",
+ "FeatureColumnName": "Features",
+ "DiskTranspose": false
+ }
+ }
+ },
+ {
+ "Version": 1,
+ "Type": "Trial",
+ "TrainerName": "FastTreeRegression",
+ "Score": 0.299969238277351,
+ "RuntimeInSeconds": 1.496,
+ "Parameter": {
+ "_SCHEMA_": "e0 * e1 * e2",
+ "e0": {
+ "OutputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ],
+ "InputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ]
+ },
+ "e1": {
+ "InputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ],
+ "OutputColumnName": "Features"
+ },
+ "e2": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 9,
+ "NumberOfTrees": 8,
+ "MaximumBinCountPerFeature": 317,
+ "FeatureFraction": 0.98444606904931908,
+ "LearningRate": 0.38567641892007121,
+ "LabelColumnName": "isNextKHigher",
+ "FeatureColumnName": "Features",
+ "DiskTranspose": false
+ }
+ }
+ },
+ {
+ "Version": 1,
+ "Type": "Trial",
+ "TrainerName": "FastTreeRegression",
+ "Score": 0.29992424440705012,
+ "RuntimeInSeconds": 0.993,
+ "Parameter": {
+ "_SCHEMA_": "e0 * e1 * e2",
+ "e0": {
+ "OutputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ],
+ "InputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ]
+ },
+ "e1": {
+ "InputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ],
+ "OutputColumnName": "Features"
+ },
+ "e2": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 9,
+ "NumberOfTrees": 8,
+ "MaximumBinCountPerFeature": 317,
+ "FeatureFraction": 0.98445819026817927,
+ "LearningRate": 0.38613186537816252,
+ "LabelColumnName": "isNextKHigher",
+ "FeatureColumnName": "Features",
+ "DiskTranspose": false
+ }
+ }
+ },
+ {
+ "Version": 1,
+ "Type": "Trial",
+ "TrainerName": "FastTreeRegression",
+ "Score": 0.29992046748552242,
+ "RuntimeInSeconds": 1.389,
+ "Parameter": {
+ "_SCHEMA_": "e0 * e1 * e2",
+ "e0": {
+ "OutputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ],
+ "InputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ]
+ },
+ "e1": {
+ "InputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ],
+ "OutputColumnName": "Features"
+ },
+ "e2": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 9,
+ "NumberOfTrees": 8,
+ "MaximumBinCountPerFeature": 317,
+ "FeatureFraction": 0.984395476164661,
+ "LearningRate": 0.3858463322510558,
+ "LabelColumnName": "isNextKHigher",
+ "FeatureColumnName": "Features",
+ "DiskTranspose": false
+ }
+ }
+ },
+ {
+ "Version": 1,
+ "Type": "Trial",
+ "TrainerName": "FastTreeRegression",
+ "Score": 0.29984566377505206,
+ "RuntimeInSeconds": 1.512,
+ "Parameter": {
+ "_SCHEMA_": "e0 * e1 * e2",
+ "e0": {
+ "OutputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ],
+ "InputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ]
+ },
+ "e1": {
+ "InputColumnNames": [
+ "p",
+ "divisor",
+ "lastDigit",
+ "k",
+ "lastMersenneDigit"
+ ],
+ "OutputColumnName": "Features"
+ },
+ "e2": {
+ "NumberOfLeaves": 4,
+ "MinimumExampleCountPerLeaf": 8,
+ "NumberOfTrees": 8,
+ "MaximumBinCountPerFeature": 317,
+ "FeatureFraction": 0.98439134546037266,
+ "LearningRate": 0.38521733136295422,
+ "LabelColumnName": "isNextKHigher",
+ "FeatureColumnName": "Features",
+ "DiskTranspose": false
+ }
+ }
+ }
+ ],
+ "Estimators": {
+ "e0": "ReplaceMissingValues",
+ "e1": "Concatenate",
+ "e2": "FastTreeRegression",
+ "e3": "FastForestRegression",
+ "e4": "LightGbmRegression",
+ "e5": "LbfgsPoissonRegressionRegression",
+ "e6": "SdcaRegression"
+ },
+ "Schema": "e0 * e1 * (e2 + e3 + e4 + e5 + e6)",
+ "MetricName": "RSquared",
+ "ModelFilePath": "C:\\Users\\marek\\source\\repos\\even-perfect-numbers-scanner\\EvenPerfectBitScanner\\MLModel1.mlnet"
+ },
+ "Type": "TrainingConfig",
+ "Version": 5,
+ "TrainingOption": {
+ "Version": 2,
+ "Subsampling": false,
+ "Type": "RegressionTrainingOption",
+ "TrainingTime": 3600,
+ "LabelColumn": "isNextKHigher",
+ "ValidationOption": {
+ "Version": 0,
+ "Type": "CrossValidationValidationOption",
+ "NumberOfFolds": 10
+ }
+ }
+}
\ No newline at end of file
diff --git a/EvenPerfectBitScanner/MLModel1.mlnet b/EvenPerfectBitScanner/MLModel1.mlnet
new file mode 100644
index 0000000..c913388
Binary files /dev/null and b/EvenPerfectBitScanner/MLModel1.mlnet differ
diff --git a/EvenPerfectBitScanner/MLModel1.training.cs b/EvenPerfectBitScanner/MLModel1.training.cs
new file mode 100644
index 0000000..2e2a801
--- /dev/null
+++ b/EvenPerfectBitScanner/MLModel1.training.cs
@@ -0,0 +1,100 @@
+// This file was auto-generated by ML.NET Model Builder.
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.ML;
+using Microsoft.ML.Data;
+using Microsoft.ML.Trainers;
+using Microsoft.ML.Trainers.FastTree;
+
+namespace EvenPerfectBitScanner
+{
+ public partial class MLModel1
+ {
+ public const string RetrainFilePath = @"C:\Users\marek\source\repos\even-perfect-numbers-scanner\BuildDivisorTransitions\bin\Release\net8.0\NextKForecastTraning.csv";
+ public const char RetrainSeparatorChar = ',';
+ public const bool RetrainHasHeader = true;
+ public const bool RetrainAllowQuoting = false;
+
+ ///
+ /// Train a new model with the provided dataset.
+ ///
+ /// File path for saving the model. Should be similar to "C:\YourPath\ModelName.mlnet"
+ /// Path to the data file for training.
+ /// Separator character for delimited training file.
+ /// Boolean if training file has a header.
+ public static void Train(string outputModelPath, string inputDataFilePath = RetrainFilePath, char separatorChar = RetrainSeparatorChar, bool hasHeader = RetrainHasHeader, bool allowQuoting = RetrainAllowQuoting)
+ {
+ var mlContext = new MLContext();
+
+ var data = LoadIDataViewFromFile(mlContext, inputDataFilePath, separatorChar, hasHeader, allowQuoting);
+ var model = RetrainModel(mlContext, data);
+ SaveModel(mlContext, model, data, outputModelPath);
+ }
+
+ ///
+ /// Load an IDataView from a file path.
+ ///
+ /// The common context for all ML.NET operations.
+ /// Path to the data file for training.
+ /// Separator character for delimited training file.
+ /// Boolean if training file has a header.
+ /// IDataView with loaded training data.
+ public static IDataView LoadIDataViewFromFile(MLContext mlContext, string inputDataFilePath, char separatorChar, bool hasHeader, bool allowQuoting)
+ {
+ return mlContext.Data.LoadFromTextFile(inputDataFilePath, separatorChar, hasHeader, allowQuoting: allowQuoting);
+ }
+
+
+ ///
+ /// Save a model at the specified path.
+ ///
+ /// The common context for all ML.NET operations.
+ /// Model to save.
+ /// IDataView used to train the model.
+ /// File path for saving the model. Should be similar to "C:\YourPath\ModelName.mlnet.
+ public static void SaveModel(MLContext mlContext, ITransformer model, IDataView data, string modelSavePath)
+ {
+ // Pull the data schema from the IDataView used for training the model
+ DataViewSchema dataViewSchema = data.Schema;
+
+ using (var fs = File.Create(modelSavePath))
+ {
+ mlContext.Model.Save(model, dataViewSchema, fs);
+ }
+ }
+
+
+ ///
+ /// Retrain model using the pipeline generated as part of the training process.
+ ///
+ ///
+ ///
+ ///
+ public static ITransformer RetrainModel(MLContext mlContext, IDataView trainData)
+ {
+ var pipeline = BuildPipeline(mlContext);
+ var model = pipeline.Fit(trainData);
+
+ return model;
+ }
+
+ ///
+ /// build the pipeline that is used from model builder. Use this function to retrain model.
+ ///
+ ///
+ ///
+ public static IEstimator BuildPipeline(MLContext mlContext)
+ {
+ // Data process configuration with pipeline data transformations
+ var pipeline = mlContext.Transforms.ReplaceMissingValues(new []{new InputOutputColumnPair(@"p", @"p"),new InputOutputColumnPair(@"divisor", @"divisor"),new InputOutputColumnPair(@"lastDigit", @"lastDigit"),new InputOutputColumnPair(@"k", @"k"),new InputOutputColumnPair(@"lastMersenneDigit", @"lastMersenneDigit")})
+ .Append(mlContext.Transforms.Concatenate(@"Features", new []{@"p",@"divisor",@"lastDigit",@"k",@"lastMersenneDigit"}))
+ .Append(mlContext.Regression.Trainers.FastTree(new FastTreeRegressionTrainer.Options(){NumberOfLeaves=4,MinimumExampleCountPerLeaf=9,NumberOfTrees=8,MaximumBinCountPerFeature=317,FeatureFraction=0.9843433146312323,LearningRate=0.3857390791447961,LabelColumnName=@"isNextKHigher",FeatureColumnName=@"Features",DiskTranspose=false}));
+
+ return pipeline;
+ }
+ }
+ }
diff --git a/EvenPerfectScanner.sln b/EvenPerfectScanner.sln
index 6ec9da2..e727561 100644
--- a/EvenPerfectScanner.sln
+++ b/EvenPerfectScanner.sln
@@ -14,6 +14,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EvenPerfectBitScanner.Resul
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EvenPerfectBitScanner.Benchmarks", "EvenPerfectBitScanner.Benchmarks\EvenPerfectBitScanner.Benchmarks.csproj", "{83551FCC-41FA-4162-B541-AFCB5666A29F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildDivisorTransitions", "BuildDivisorTransitions\BuildDivisorTransitions.csproj", "{0A936837-5082-4C0B-AC3B-840AD2E11057}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -47,5 +49,9 @@ Global
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0A936837-5082-4C0B-AC3B-840AD2E11057}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0A936837-5082-4C0B-AC3B-840AD2E11057}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0A936837-5082-4C0B-AC3B-840AD2E11057}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0A936837-5082-4C0B-AC3B-840AD2E11057}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal