From 990a4044890a605bccc1e187a63dd40b84972ba9 Mon Sep 17 00:00:00 2001 From: PFalkowski Date: Mon, 15 Jun 2026 10:37:22 +0200 Subject: [PATCH 1/5] chore: modernize GeneticAlgorithm to net8.0, nullable, SourceLink, CI/CD/Sonar (v4.1.0) Co-Authored-By: Claude Sonnet 4.6 --- .gitattributes | 1 + .github/workflows/ci.yml | 21 ++++++++ .github/workflows/publish.yml | 32 ++++++++++++ .github/workflows/sonar.yml | 42 +++++++++++++++ README.md | 52 ++++++++++++++++++- SimpleML.GeneticAlgorithm/GeneticAlgorithm.cs | 2 +- .../GeneticAlgorithmSettings.cs | 4 +- SimpleML.GeneticAlgorithm/Population.cs | 4 +- SimpleML.GeneticAlgorithm/RunMetadata.cs | 4 +- .../SimpleML.GeneticAlgorithm.csproj | 29 +++++++---- 10 files changed, 171 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/sonar.yml diff --git a/.gitattributes b/.gitattributes index 1ff0c42..5b65514 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,6 +2,7 @@ # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto +*.yml text eol=lf ############################################################################### # Set default behavior for command prompt diff. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4ebd34a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 8.0.x + - name: Restore + run: dotnet restore SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj + - name: Build + run: dotnet build SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj --no-restore diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..c2b7b60 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,32 @@ +name: Publish + +on: + push: + tags: + - 'v*' + +permissions: + id-token: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 8.0.x + - name: Restore + run: dotnet restore SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj + - name: Build + run: dotnet build SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj --no-restore -c Release + - name: Pack + run: dotnet pack SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj --no-build -c Release -o nupkgs + - name: Login to NuGet + uses: NuGet/login@v1 + with: + usernameVar: NUGET_USER + tokenVar: NUGET_TOKEN + - name: Push to NuGet + run: dotnet nuget push nupkgs/*.nupkg --source https://api.nuget.org/v3/index.json --skip-duplicate diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml new file mode 100644 index 0000000..44d7dda --- /dev/null +++ b/.github/workflows/sonar.yml @@ -0,0 +1,42 @@ +name: SonarCloud + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + sonar: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 8.0.x + - name: Setup Java (required by SonarScanner) + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + - name: Cache SonarCloud packages + uses: actions/cache@v4 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + - name: Install dotnet-sonarscanner + run: dotnet tool install --global dotnet-sonarscanner + - name: SonarCloud begin + run: | + dotnet sonarscanner begin \ + /k:"PFalkowski_SimpleML" \ + /o:"pfalkowski" \ + /d:sonar.token="${{ secrets.SONAR_TOKEN }}" \ + /d:sonar.host.url="https://sonarcloud.io" + - name: Build + run: dotnet build SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj --no-incremental + - name: SonarCloud end + run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" diff --git a/README.md b/README.md index cda4c3d..2109ad5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,51 @@ -# SimpleML +# SimpleML.GeneticAlgorithm -The idea of the project is to implement various popular ML algorithms in a simple yet efficient way, conforming to best patterns and practices of modern OO programming. PR are very welcomed. +[![CI](https://github.com/PFalkowski/SimpleML/actions/workflows/ci.yml/badge.svg)](https://github.com/PFalkowski/SimpleML/actions/workflows/ci.yml) +[![NuGet version](https://img.shields.io/nuget/v/SimpleML.GeneticAlgorithm.svg)](https://www.nuget.org/packages/SimpleML.GeneticAlgorithm/) +[![NuGet downloads](https://img.shields.io/nuget/dt/SimpleML.GeneticAlgorithm.svg)](https://www.nuget.org/packages/SimpleML.GeneticAlgorithm/) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=PFalkowski_SimpleML&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=PFalkowski_SimpleML) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://choosealicense.com/licenses/mit/) +[![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-support-yellow.svg)](https://www.buymeacoffee.com/piotrfalkowski) + +A simple, extensible Genetic Algorithm implementation for .NET 8. + +## Usage + +```csharp +// Implement IFitnessFunction for your problem +public class MyFitness : IFitnessFunction +{ + public double Evaluate(Genotype genotype) => /* compute fitness */; + public Task EvaluateAsync(Genotype genotype) => Task.FromResult(Evaluate(genotype)); +} + +// Configure and run +var settings = new GeneticAlgorithmSettings(new MyFitness(), problemSize: 100) +{ + PopulationSize = 5000, + SurvivalRate = 0.1, + MutationRate = 0.05, + StopFunction = new BasicStopFunction { MaxEpochs = 500, MinFitness = 95 } +}; + +var ga = new GeneticAlgorithm(settings); +await ga.Run(); + +Console.WriteLine($"Best fitness: {ga.RunInfo.BestFitnessSoFar}"); +``` + +## Key types + +| Type | Description | +|------|-------------| +| `GeneticAlgorithm` | Orchestrates the evolution loop | +| `GeneticAlgorithmSettings` | Population size, survival/mutation rates, stop condition | +| `Population` | Gene pool management, selection, and breeding | +| `Genotype` | Individual solution encoded as a boolean array | +| `BasicStopFunction` | Stops after max epochs, target fitness, or fitness plateau | +| `EliteSelection` | Keeps the top N organisms | +| `BinaryTournamentSelection` | Random pairwise tournament selection | + +--- + +The idea of the project is to implement various popular ML algorithms in a simple yet efficient way, conforming to best patterns and practices of modern OO programming. PRs are very welcome. diff --git a/SimpleML.GeneticAlgorithm/GeneticAlgorithm.cs b/SimpleML.GeneticAlgorithm/GeneticAlgorithm.cs index a3da94e..20f15b9 100644 --- a/SimpleML.GeneticAlgorithm/GeneticAlgorithm.cs +++ b/SimpleML.GeneticAlgorithm/GeneticAlgorithm.cs @@ -12,7 +12,7 @@ public class GeneticAlgorithm public GeneticAlgorithmSettings Settings { get; protected set; } public IStopFunction StopFunction { get; protected set; } public RunMetadata RunInfo { get; protected set; } = new RunMetadata(); - public ILoggerLite Logger { get; protected set; } + public ILogger? Logger { get; protected set; } public GeneticAlgorithm(GeneticAlgorithmSettings settings) { diff --git a/SimpleML.GeneticAlgorithm/GeneticAlgorithmSettings.cs b/SimpleML.GeneticAlgorithm/GeneticAlgorithmSettings.cs index f024060..b9ef6b6 100644 --- a/SimpleML.GeneticAlgorithm/GeneticAlgorithmSettings.cs +++ b/SimpleML.GeneticAlgorithm/GeneticAlgorithmSettings.cs @@ -25,8 +25,8 @@ public GeneticAlgorithmSettings(IFitnessFunction fitnessFunction, int problemSiz public IStopFunction StopFunction { get; set; } = new BasicStopFunction(); public ISelectionAlgorithm FittestSelectionAlgorithm { get; set; } public IFitnessFunction FitnessFunction { get; protected set; } - public ILoggerLite Logger { get; protected set; } = new ConsoleLogger(); - public FileInfo ContinueFile { get; set; } + public ILogger? Logger { get; protected set; } = new ConsoleLogger(); + public FileInfo? ContinueFile { get; set; } public int MaxDegreeOfParallelism => Math.Min(PopulationSize, 500); } } \ No newline at end of file diff --git a/SimpleML.GeneticAlgorithm/Population.cs b/SimpleML.GeneticAlgorithm/Population.cs index 4cef3b7..795b34d 100644 --- a/SimpleML.GeneticAlgorithm/Population.cs +++ b/SimpleML.GeneticAlgorithm/Population.cs @@ -11,8 +11,8 @@ public class Population private readonly object _syncRoot = new object(); public Random Rng { get; protected set; } - public List GenePool { get; protected set; } - public Genotype BestFit { get; protected set; } + public List GenePool { get; protected set; } = null!; + public Genotype? BestFit { get; protected set; } public IFitnessFunction FitnessFunction { get; protected set; } public ISelectionAlgorithm FittestSelectionAlgorithm { get; protected set; } public GeneticAlgorithmSettings Settings { get; protected set; } diff --git a/SimpleML.GeneticAlgorithm/RunMetadata.cs b/SimpleML.GeneticAlgorithm/RunMetadata.cs index 10f0267..ec00354 100644 --- a/SimpleML.GeneticAlgorithm/RunMetadata.cs +++ b/SimpleML.GeneticAlgorithm/RunMetadata.cs @@ -7,8 +7,8 @@ public class RunMetadata { public ulong Epochs { get; set; } public ulong SimulationsCount { get; set; } - public Genotype CurrentEpochBest { get; set; } - public Genotype BestOverall { get; set; } + public Genotype? CurrentEpochBest { get; set; } + public Genotype? BestOverall { get; set; } public double CurrentFitness => CurrentEpochBest?.Fitness ?? 0; public double BestFitnessSoFar => BestOverall?.Fitness ?? 0; public DateTime StartTime { get; set; } diff --git a/SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj b/SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj index f3846e1..972c956 100644 --- a/SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj +++ b/SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj @@ -2,26 +2,33 @@ net8.0 - True + latest + enable Piotr Falkowski Simple Genetic Algorithm implementation - Piotr Falkowski © 2023 + Piotr Falkowski © 2026 + MIT https://github.com/PFalkowski/SimpleML https://github.com/PFalkowski/SimpleML git - SimpleML.GeneticAlgorithm - Piotr Falkowski - SimpleML - 4.0.0 - Improved conditions in best fit calculation. Config changes of default values. Add MaxEpochs configurable form IStopFunction. Fix not randomized new organism genes added to populations. Fix incorrect number of epochs to wait when no delta change. - MIT - True + 4.1.0 + true + $(NoWarn);CS1591 + README.md + true + true + true + snupkg - + - + + + + + From 77d01846f680dd360924c4855e46891c4ef9073c Mon Sep 17 00:00:00 2001 From: PFalkowski Date: Mon, 15 Jun 2026 14:27:03 +0200 Subject: [PATCH 2/5] docs: add install snippet to README --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2109ad5..26d70e9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SimpleML.GeneticAlgorithm +# SimpleML.GeneticAlgorithm [![CI](https://github.com/PFalkowski/SimpleML/actions/workflows/ci.yml/badge.svg)](https://github.com/PFalkowski/SimpleML/actions/workflows/ci.yml) [![NuGet version](https://img.shields.io/nuget/v/SimpleML.GeneticAlgorithm.svg)](https://www.nuget.org/packages/SimpleML.GeneticAlgorithm/) @@ -9,6 +9,12 @@ A simple, extensible Genetic Algorithm implementation for .NET 8. +## Install + +```bash +dotnet add package SimpleML.GeneticAlgorithm +``` + ## Usage ```csharp From 039047bb1d01a981af52e7909e8b42b6b500ca62 Mon Sep 17 00:00:00 2001 From: PFalkowski Date: Mon, 15 Jun 2026 23:32:24 +0200 Subject: [PATCH 3/5] fix(ci): pin GitHub Actions to full commit SHAs (supply-chain hardening) Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 4 ++-- .github/workflows/publish.yml | 6 +++--- .github/workflows/sonar.yml | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ebd34a..2328cee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - name: Setup .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5 with: dotnet-version: 8.0.x - name: Restore diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c2b7b60..da38c3e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,9 +12,9 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - name: Setup .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5 with: dotnet-version: 8.0.x - name: Restore @@ -24,7 +24,7 @@ jobs: - name: Pack run: dotnet pack SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj --no-build -c Release -o nupkgs - name: Login to NuGet - uses: NuGet/login@v1 + uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1 with: usernameVar: NUGET_USER tokenVar: NUGET_TOKEN diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 44d7dda..5844c56 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -10,20 +10,20 @@ jobs: sonar: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 with: fetch-depth: 0 - name: Setup .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5 with: dotnet-version: 8.0.x - name: Setup Java (required by SonarScanner) - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: distribution: temurin java-version: 21 - name: Cache SonarCloud packages - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar From 0e9bca4a350ab9bba884c9ee5532e9302299f43b Mon Sep 17 00:00:00 2001 From: PFalkowski Date: Mon, 15 Jun 2026 23:37:17 +0200 Subject: [PATCH 4/5] fix(ci): use SONAR_TOKEN env var instead of inline secret expansion Avoids the SonarCloud 'Avoid expanding secrets in a run block' hotspot. The secret is already mapped to the env var on each step. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/sonar.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 5844c56..ff16e04 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -34,9 +34,9 @@ jobs: dotnet sonarscanner begin \ /k:"PFalkowski_SimpleML" \ /o:"pfalkowski" \ - /d:sonar.token="${{ secrets.SONAR_TOKEN }}" \ + /d:sonar.token="$SONAR_TOKEN" \ /d:sonar.host.url="https://sonarcloud.io" - name: Build run: dotnet build SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj --no-incremental - name: SonarCloud end - run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + run: dotnet sonarscanner end /d:sonar.token="$SONAR_TOKEN" From 815d07f6687f81ee95307d656fc6d59d1bc5a7b6 Mon Sep 17 00:00:00 2001 From: PFalkowski Date: Mon, 15 Jun 2026 23:45:33 +0200 Subject: [PATCH 5/5] fix(ci): correctly wire SONAR_TOKEN secret to env block Previously the sed replacement incorrectly changed the env mapping line (SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}) to a bare var reference, or the env block was missing entirely. The scanner received an empty/literal token and authentication failed. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/sonar.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index ff16e04..5c5e5b4 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -30,6 +30,8 @@ jobs: - name: Install dotnet-sonarscanner run: dotnet tool install --global dotnet-sonarscanner - name: SonarCloud begin + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | dotnet sonarscanner begin \ /k:"PFalkowski_SimpleML" \ @@ -39,4 +41,6 @@ jobs: - name: Build run: dotnet build SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj --no-incremental - name: SonarCloud end + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: dotnet sonarscanner end /d:sonar.token="$SONAR_TOKEN"