add: Generator
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Nullable>disable</Nullable>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Version>0.0.2</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -20,8 +20,8 @@ public class Board
|
||||
public required int Height { get; init; }
|
||||
public required bool QuotientX { get; init; }
|
||||
public required bool QuotientY { get; init; }
|
||||
public HashSet<(int, int)> Lives { get; init; }
|
||||
public CellTracer? Tracer { get; init; }
|
||||
public HashSet<(int, int)> Lives { get; internal set; }
|
||||
public CellTracer Tracer { get; init; }
|
||||
|
||||
public void Toggle(int x, int y, bool initialConfig=false)
|
||||
{
|
||||
|
||||
@@ -292,7 +292,7 @@ public class Gene
|
||||
return res;
|
||||
}
|
||||
|
||||
public static Gene Hybrid(Gene a, Gene b, double[]? weightA = null, double[]? weightB = null)
|
||||
public static Gene Hybrid(Gene a, Gene b, double[] weightA = null, double[] weightB = null)
|
||||
{
|
||||
if (a.Resolution != b.Resolution)
|
||||
throw new ArgumentException("Resolutions of both genes must match.");
|
||||
@@ -312,7 +312,9 @@ public class Gene
|
||||
{
|
||||
byte[] ra = a[i];
|
||||
byte[] rb = b[i];
|
||||
double ratio = weightA[i] / (weightA[i] + weightB[i] + 1E-5d);
|
||||
double ratio = 0.5d;
|
||||
if(weightA[i] + weightB[i] != 0)
|
||||
ratio = weightA[i] / (weightA[i] + weightB[i]);
|
||||
res[i] = random.NextDouble() < ratio ? ra : rb;
|
||||
}
|
||||
|
||||
|
||||
62
src/Generator.cs
Normal file
62
src/Generator.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
namespace InverseOfLife;
|
||||
|
||||
public class Generator
|
||||
{
|
||||
|
||||
public static Dictionary<(int, int), float>[] Generate(Board target, int layers, int steps, int split=1, string mode="tp_only")
|
||||
{
|
||||
HashSet<(int, int)>[] targetCell = new HashSet<(int, int)> [split];
|
||||
Board[] targets = new Board[split];
|
||||
HashSet<(int, int)> alives = target.Lives;
|
||||
for (int i = 0; i < split; i++)
|
||||
targetCell[i] = new HashSet<(int, int)>();
|
||||
int idx = 0;
|
||||
foreach ((int, int) cell in alives)
|
||||
{
|
||||
idx = (idx + 1) % split;
|
||||
targetCell[idx].Add(cell);
|
||||
}
|
||||
for (int i = 0; i < split; i++)
|
||||
{
|
||||
targets[i] = new Board(target.Width, target.Height, target.QuotientX, target.QuotientY);
|
||||
targets[i].Lives = targetCell[i];
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
Board[] res = new Board[layers * split];
|
||||
foreach (Board t in targets)
|
||||
{
|
||||
|
||||
Solver s = new Solver(t, steps);
|
||||
for (int i = 0; i < layers; i++)
|
||||
{
|
||||
if (idx > layers * split)
|
||||
break;
|
||||
(Gene g, double x) = s.Solve(mode: mode);
|
||||
res[idx] = g.Restore(target.Width, target.Height, target.QuotientX, target.QuotientY);
|
||||
idx += 1;
|
||||
Console.WriteLine($"Progress: {idx}/{layers * split}");
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<(int, int), float>[] result = new Dictionary<(int, int), float>[steps];
|
||||
for (int i = 0; i < steps; i++)
|
||||
{
|
||||
result[i] = new Dictionary<(int, int), float>();
|
||||
foreach (Board b in res)
|
||||
{
|
||||
foreach ((int, int) cell in b.Lives)
|
||||
{
|
||||
if(!result[i].Keys.Contains(cell))
|
||||
result[i][cell] = 0f;
|
||||
result[i][cell] += 1f/(layers * split);
|
||||
}
|
||||
b.Evaluate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace InverseOfLife;
|
||||
|
||||
public class Solver
|
||||
@@ -6,12 +8,14 @@ public class Solver
|
||||
{
|
||||
Target = target;
|
||||
Steps = steps;
|
||||
YS = target.ToString();
|
||||
}
|
||||
|
||||
public Board Target { get; init; }
|
||||
private int Steps { get; set; }
|
||||
private string YS { get; set; }
|
||||
|
||||
private static void SortBest((Board? b, Gene? g, double s)[] best)
|
||||
private static void SortBest((Board b, Gene g, double s)[] best)
|
||||
{
|
||||
int c = best.Length-1;
|
||||
while (c != 0)
|
||||
@@ -25,15 +29,23 @@ public class Solver
|
||||
|
||||
}
|
||||
|
||||
public (Gene, double) Solve(int withResolution=2, int maxGenerations=50, int topN=10, float mutationRate=0.1f)
|
||||
private static byte[] RandomBytes(int length)
|
||||
{
|
||||
byte[] res = new byte[length];
|
||||
Random.Shared.NextBytes(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper.DPA", "DPA0002: Excessive memory allocations in SOH", MessageId = "type: Entry[System.Int32,System.Double][]; size: 14391MB")]
|
||||
public (Gene, double) Solve(int withResolution=1, int maxGenerations=50, int topN=10, float mutationRate=0.1f, string mode="tp_only")
|
||||
{
|
||||
List<(Board, Gene)> currentGeneration = GetInitialGeneration()
|
||||
.Select(x => (x, new Gene(withResolution, x)))
|
||||
.ToList();
|
||||
Gene res;
|
||||
Random random = new();
|
||||
(int, int) selectParent() => (random.Next() % topN, random.Next() % topN);
|
||||
(Board? b, Gene? gene, double score)[] best = new (Board?, Gene?, double)[topN + 1];
|
||||
(int, int) SelectParent() => (random.Next() % topN, random.Next() % topN);
|
||||
(Board b, Gene gene, double score)[] best = new (Board, Gene, double)[topN + 1];
|
||||
for (int i = 0; i < topN + 1; i++)
|
||||
best[i] = (null, null, 0);
|
||||
for (int gen = 0; gen < maxGenerations; gen++)
|
||||
@@ -42,23 +54,31 @@ public class Solver
|
||||
foreach ((Board b, Gene g) in currentGeneration)
|
||||
{
|
||||
b.Evaluate(Steps);
|
||||
double score = Summarizer.Score(b, Target, false);
|
||||
double score = Summarizer.Score(b, Target, mode);
|
||||
best[topN] = (b, g, score);
|
||||
SortBest(best);
|
||||
}
|
||||
|
||||
for (int i = 0; i < maxGenerations; i++)
|
||||
{
|
||||
(int pa, int pb) = selectParent();
|
||||
(int pa, int pb) = SelectParent();
|
||||
|
||||
double[] weightA = Summarizer.GetWeight(best[pa].b!, Target, withResolution, true);
|
||||
double[] weightB = Summarizer.GetWeight(best[pb].b!, Target, withResolution, true);
|
||||
Gene parentA = best[pa].gene!.Mutate(weightA, mutationRate);
|
||||
Gene parentB = best[pb].gene!.Mutate(weightB, mutationRate);
|
||||
double[] weightA = Summarizer.GetWeight(best[pa].b, Target, withResolution, mode);
|
||||
double[] weightB = Summarizer.GetWeight(best[pb].b, Target, withResolution, mode);
|
||||
Gene parentA = best[pa].gene.Mutate(weightA, mutationRate);
|
||||
Gene parentB = best[pb].gene.Mutate(weightB, mutationRate);
|
||||
Gene newGene = Gene.Hybrid(parentA, parentB, weightA, weightB);
|
||||
Board b = newGene.Restore(Target.Width, Target.Height, Target.QuotientX, Target.QuotientY, true);
|
||||
nextGeneration.Add((b, newGene));
|
||||
}
|
||||
|
||||
for (int i = 0; i < maxGenerations / 5; i++)
|
||||
{
|
||||
Gene ng = new Gene(withResolution, RandomBytes(best[0].gene.RiboseSequence.Length));
|
||||
Board b = ng.Restore(Target.Width, Target.Height, Target.QuotientX, Target.QuotientY, true);
|
||||
nextGeneration.Add((b, ng));
|
||||
}
|
||||
|
||||
Console.WriteLine($"--------{best[0].score}");
|
||||
currentGeneration = nextGeneration.ToList();
|
||||
}
|
||||
|
||||
@@ -33,31 +33,48 @@ public static class Summarizer
|
||||
return res;
|
||||
}
|
||||
|
||||
public static double Score(Board yAct, Board yGnd, bool tpOnly = false)
|
||||
public static double Score(Board yAct, Board yGnd, string mode = "tp_only")
|
||||
{
|
||||
if (yAct.Height != yGnd.Height || yAct.Width != yGnd.Width || yAct.QuotientX != yGnd.QuotientX ||
|
||||
yAct.QuotientY != yGnd.QuotientY)
|
||||
throw new Exception();
|
||||
bool tpOnly = mode == "tp_only";
|
||||
bool tnOnly = mode == "tn_only";
|
||||
bool dc = mode == "dynamic_combine";
|
||||
|
||||
double sx = yGnd.Width * yGnd.Height;
|
||||
double p = yGnd.Lives.Count;
|
||||
double n = sx - p;
|
||||
|
||||
double tp = 0;
|
||||
HashSet<(int, int)> allAlives = yAct.Lives.Union(yGnd.Lives).ToHashSet();
|
||||
double tn = yGnd.Height * yGnd.Width - allAlives.Count;
|
||||
double tn = sx - allAlives.Count;
|
||||
foreach ((int, int) cell in yAct.Lives)
|
||||
{
|
||||
if (yGnd.Lives.Contains(cell))
|
||||
tp += 1d;
|
||||
}
|
||||
|
||||
double txp = yGnd.Lives.Count > 0 ? tp / p : 1d;
|
||||
if (tpOnly)
|
||||
return txp;
|
||||
|
||||
|
||||
double txn = tn / n;
|
||||
if (tnOnly)
|
||||
return txn;
|
||||
if (dc)
|
||||
{
|
||||
if (yGnd.Lives.Count > 0)
|
||||
return tp / yGnd.Lives.Count;
|
||||
return 1d;
|
||||
double rp = n / sx;
|
||||
double rn = p / sx;
|
||||
return txp * rp + txn * rn;
|
||||
}
|
||||
|
||||
return (tp + tn) / (yGnd.Height * yGnd.Width);
|
||||
}
|
||||
|
||||
|
||||
public static double[] ScoreBySubGrid(Board yAct, Board yGnd, int resolution, bool tpOnly = false)
|
||||
public static double[] ScoreBySubGrid(Board yAct, Board yGnd, int resolution, string mode="tp_only")
|
||||
{
|
||||
if (yAct.Height != yGnd.Height || yAct.Width != yGnd.Width || yAct.QuotientX != yGnd.QuotientX ||
|
||||
yAct.QuotientY != yGnd.QuotientY)
|
||||
@@ -96,6 +113,9 @@ public static class Summarizer
|
||||
|
||||
}
|
||||
|
||||
bool tpOnly = mode == "tp_only";
|
||||
bool tnOnly = mode == "tn_only";
|
||||
bool dc = mode == "dynamic_combine";
|
||||
for (int subGridY = 0; subGridY < subGridHeight; subGridY++)
|
||||
{
|
||||
for (int subGridX = 0; subGridX < subGridWidth; subGridX++)
|
||||
@@ -107,15 +127,18 @@ public static class Summarizer
|
||||
int subGridEndX = Math.Min(subGridStartX + resolution, yAct.Width);
|
||||
int subGridEndY = Math.Min(subGridStartY + resolution, yAct.Height);
|
||||
|
||||
|
||||
int cellsInSubGrid = (subGridEndX - subGridStartX) * (subGridEndY - subGridStartY);
|
||||
double tn = cellsInSubGrid - actAlive[subGridIdx].Union(gndAlive[subGridIdx]).Count();
|
||||
double tp = tps[subGridIdx];
|
||||
double txp = gndAlive[subGridIdx].Count > 0 ? tp / gndAlive[subGridIdx].Count : 1d;
|
||||
double txn = tn / (cellsInSubGrid - gndAlive[subGridIdx].Count);
|
||||
if(tpOnly)
|
||||
res[subGridIdx] = txp;
|
||||
else if (tnOnly)
|
||||
res[subGridIdx] = txn;
|
||||
else if (dc)
|
||||
{
|
||||
if (gndAlive[subGridIdx].Count > 0)
|
||||
res[subGridIdx] = tp / gndAlive[subGridIdx].Count;
|
||||
else
|
||||
res[subGridIdx] = 1d;
|
||||
}
|
||||
else
|
||||
res[subGridIdx] = (tp + tn) / cellsInSubGrid;
|
||||
@@ -124,12 +147,12 @@ public static class Summarizer
|
||||
return res;
|
||||
}
|
||||
|
||||
public static double[] GetWeight(Board yAct, Board yGnd, int resolution, bool tpOnly=false)
|
||||
public static double[] GetWeight(Board yAct, Board yGnd, int resolution, string mode = "tp_only")
|
||||
{
|
||||
if (yAct.Tracer is null)
|
||||
throw new Exception("tracer was not enabled");
|
||||
Dictionary<int, Dictionary<int, double>> contribution = ContributionBySubGrid(yAct, resolution);
|
||||
double[] score = ScoreBySubGrid(yAct, yGnd, resolution, tpOnly);
|
||||
double[] score = ScoreBySubGrid(yAct, yGnd, resolution, mode);
|
||||
int subgridWidth = (yAct.Width + resolution - 1) / resolution;
|
||||
int subgridHeight = (yAct.Height + resolution - 1) / resolution;
|
||||
double[] res = new double[subgridWidth * subgridHeight];
|
||||
|
||||
Reference in New Issue
Block a user