Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: CI

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Setup .NET
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # 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
32 changes: 32 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Publish

on:
push:
tags:
- 'v*'

permissions:
id-token: write

jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Setup .NET
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # 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@8d196754b4036150537f80ac539e15c2f1028841 # 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
46 changes: 46 additions & 0 deletions .github/workflows/sonar.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: SonarCloud

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
sonar:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5
with:
dotnet-version: 8.0.x
- name: Setup Java (required by SonarScanner)
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
with:
distribution: temurin
java-version: 21
- name: Cache SonarCloud packages
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
- 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" \
/o:"pfalkowski" \
/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
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: dotnet sonarscanner end /d:sonar.token="$SONAR_TOKEN"
58 changes: 56 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,57 @@
# 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.

## Install

```bash
dotnet add package SimpleML.GeneticAlgorithm
```

## Usage

```csharp
// Implement IFitnessFunction for your problem
public class MyFitness : IFitnessFunction
{
public double Evaluate(Genotype genotype) => /* compute fitness */;
public Task<double> 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.
2 changes: 1 addition & 1 deletion SimpleML.GeneticAlgorithm/GeneticAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
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)
{
Expand Down Expand Up @@ -40,7 +40,7 @@
}
else
{
throw new ApplicationException("GenePool not initialized.");

Check warning on line 43 in SimpleML.GeneticAlgorithm/GeneticAlgorithm.cs

View workflow job for this annotation

GitHub Actions / sonar

'System.ApplicationException' should not be thrown by user code.
}
}

Expand Down
4 changes: 2 additions & 2 deletions SimpleML.GeneticAlgorithm/GeneticAlgorithmSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
FittestSelectionAlgorithm = new EliteSelection();
FitnessFunction = fitnessFunction;
}
public Random Rng { get; set; } = new Random();

Check warning on line 15 in SimpleML.GeneticAlgorithm/GeneticAlgorithmSettings.cs

View workflow job for this annotation

GitHub Actions / sonar

Make sure that using this pseudorandom number generator is safe here.

Check warning on line 15 in SimpleML.GeneticAlgorithm/GeneticAlgorithmSettings.cs

View workflow job for this annotation

GitHub Actions / sonar

Make sure that using this pseudorandom number generator is safe here.
public int PopulationSize { get; set; } = 10000;
public int SurvivorsCount => (int)(SurvivalRate * PopulationSize);
public double SurvivalRate { get; set; } = 0.1;
Expand All @@ -25,8 +25,8 @@
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);
}
}
4 changes: 2 additions & 2 deletions SimpleML.GeneticAlgorithm/Population.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
private readonly object _syncRoot = new object();

public Random Rng { get; protected set; }
public List<Genotype> GenePool { get; protected set; }
public Genotype BestFit { get; protected set; }
public List<Genotype> 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; }
Expand Down Expand Up @@ -105,7 +105,7 @@
{
if (Settings.PopulationSize < Settings.NewOrganismsCount + Settings.SurvivorsCount)
{
throw new ApplicationException("Incorrect state. Population over 100%.");

Check warning on line 108 in SimpleML.GeneticAlgorithm/Population.cs

View workflow job for this annotation

GitHub Actions / sonar

'System.ApplicationException' should not be thrown by user code.
}
var parents = GenePool;
GenePool = new List<Genotype>(Settings.PopulationSize);
Expand All @@ -125,7 +125,7 @@
}
else
{
child = parents.First();

Check warning on line 128 in SimpleML.GeneticAlgorithm/Population.cs

View workflow job for this annotation

GitHub Actions / sonar

Indexing at 0 should be used instead of the "Enumerable" extension method "First"
}

if (Rng.NextDouble() < Settings.MutationRate)
Expand Down
4 changes: 2 additions & 2 deletions SimpleML.GeneticAlgorithm/RunMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
{
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; }
Expand All @@ -22,7 +22,7 @@
CurrentEpochBest = genotype;
}
else if (CurrentEpochBest.Fitness < genotype.Fitness)
{

Check warning on line 25 in SimpleML.GeneticAlgorithm/RunMetadata.cs

View workflow job for this annotation

GitHub Actions / sonar

Either merge this branch with the identical one on line 21 or change one of the implementations.
CurrentEpochBest = genotype;
}

Expand All @@ -31,7 +31,7 @@
BestOverall = genotype;
}
else if (BestOverall.Fitness < genotype.Fitness)
{

Check warning on line 34 in SimpleML.GeneticAlgorithm/RunMetadata.cs

View workflow job for this annotation

GitHub Actions / sonar

Either merge this branch with the identical one on line 30 or change one of the implementations.
BestOverall = genotype;
}
}
Expand Down
29 changes: 18 additions & 11 deletions SimpleML.GeneticAlgorithm/SimpleML.GeneticAlgorithm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,33 @@

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Authors>Piotr Falkowski</Authors>
<Description>Simple Genetic Algorithm implementation</Description>
<Copyright>Piotr Falkowski © 2023</Copyright>
<Copyright>Piotr Falkowski © 2026</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/PFalkowski/SimpleML</PackageProjectUrl>
<RepositoryUrl>https://github.com/PFalkowski/SimpleML</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Title>SimpleML.GeneticAlgorithm</Title>
<Company>Piotr Falkowski</Company>
<Product>SimpleML</Product>
<Version>4.0.0</Version>
<PackageReleaseNotes>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. </PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<Version>4.1.0</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="LoggerLite" Version="4.0.0" />
<PackageReference Include="LoggerLite" Version="5.0.0" />
<PackageReference Include="StrongRandom" Version="2.0.0" />
<PackageReference Include="System.Collections" Version="4.3.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>

</Project>
Loading