add: Generator

This commit is contained in:
h z
2025-01-22 09:46:30 +00:00
parent 081863bde2
commit f428c6f3a5
6 changed files with 137 additions and 28 deletions

View File

@@ -3,7 +3,9 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>disable</Nullable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>0.0.2</Version>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -20,8 +20,8 @@ public class Board
public required int Height { get; init; } public required int Height { get; init; }
public required bool QuotientX { get; init; } public required bool QuotientX { get; init; }
public required bool QuotientY { get; init; } public required bool QuotientY { get; init; }
public HashSet<(int, int)> Lives { get; init; } public HashSet<(int, int)> Lives { get; internal set; }
public CellTracer? Tracer { get; init; } public CellTracer Tracer { get; init; }
public void Toggle(int x, int y, bool initialConfig=false) public void Toggle(int x, int y, bool initialConfig=false)
{ {

View File

@@ -292,7 +292,7 @@ public class Gene
return res; 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) if (a.Resolution != b.Resolution)
throw new ArgumentException("Resolutions of both genes must match."); throw new ArgumentException("Resolutions of both genes must match.");
@@ -312,7 +312,9 @@ public class Gene
{ {
byte[] ra = a[i]; byte[] ra = a[i];
byte[] rb = b[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; res[i] = random.NextDouble() < ratio ? ra : rb;
} }

62
src/Generator.cs Normal file
View 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;
}
}

View File

@@ -1,3 +1,5 @@
using System.Diagnostics.CodeAnalysis;
namespace InverseOfLife; namespace InverseOfLife;
public class Solver public class Solver
@@ -6,12 +8,14 @@ public class Solver
{ {
Target = target; Target = target;
Steps = steps; Steps = steps;
YS = target.ToString();
} }
public Board Target { get; init; } public Board Target { get; init; }
private int Steps { get; set; } 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; int c = best.Length-1;
while (c != 0) 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() List<(Board, Gene)> currentGeneration = GetInitialGeneration()
.Select(x => (x, new Gene(withResolution, x))) .Select(x => (x, new Gene(withResolution, x)))
.ToList(); .ToList();
Gene res; Gene res;
Random random = new(); Random random = new();
(int, int) selectParent() => (random.Next() % topN, random.Next() % topN); (int, int) SelectParent() => (random.Next() % topN, random.Next() % topN);
(Board? b, Gene? gene, double score)[] best = new (Board?, Gene?, double)[topN + 1]; (Board b, Gene gene, double score)[] best = new (Board, Gene, double)[topN + 1];
for (int i = 0; i < topN + 1; i++) for (int i = 0; i < topN + 1; i++)
best[i] = (null, null, 0); best[i] = (null, null, 0);
for (int gen = 0; gen < maxGenerations; gen++) for (int gen = 0; gen < maxGenerations; gen++)
@@ -42,23 +54,31 @@ public class Solver
foreach ((Board b, Gene g) in currentGeneration) foreach ((Board b, Gene g) in currentGeneration)
{ {
b.Evaluate(Steps); b.Evaluate(Steps);
double score = Summarizer.Score(b, Target, false); double score = Summarizer.Score(b, Target, mode);
best[topN] = (b, g, score); best[topN] = (b, g, score);
SortBest(best); SortBest(best);
} }
for (int i = 0; i < maxGenerations; i++) 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[] weightA = Summarizer.GetWeight(best[pa].b, Target, withResolution, mode);
double[] weightB = Summarizer.GetWeight(best[pb].b!, Target, withResolution, true); double[] weightB = Summarizer.GetWeight(best[pb].b, Target, withResolution, mode);
Gene parentA = best[pa].gene!.Mutate(weightA, mutationRate); Gene parentA = best[pa].gene.Mutate(weightA, mutationRate);
Gene parentB = best[pb].gene!.Mutate(weightB, mutationRate); Gene parentB = best[pb].gene.Mutate(weightB, mutationRate);
Gene newGene = Gene.Hybrid(parentA, parentB, weightA, weightB); Gene newGene = Gene.Hybrid(parentA, parentB, weightA, weightB);
Board b = newGene.Restore(Target.Width, Target.Height, Target.QuotientX, Target.QuotientY, true); Board b = newGene.Restore(Target.Width, Target.Height, Target.QuotientX, Target.QuotientY, true);
nextGeneration.Add((b, newGene)); 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}"); Console.WriteLine($"--------{best[0].score}");
currentGeneration = nextGeneration.ToList(); currentGeneration = nextGeneration.ToList();
} }

View File

@@ -33,31 +33,48 @@ public static class Summarizer
return res; 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 || if (yAct.Height != yGnd.Height || yAct.Width != yGnd.Width || yAct.QuotientX != yGnd.QuotientX ||
yAct.QuotientY != yGnd.QuotientY) yAct.QuotientY != yGnd.QuotientY)
throw new Exception(); 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; double tp = 0;
HashSet<(int, int)> allAlives = yAct.Lives.Union(yGnd.Lives).ToHashSet(); 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) foreach ((int, int) cell in yAct.Lives)
{ {
if (yGnd.Lives.Contains(cell)) if (yGnd.Lives.Contains(cell))
tp += 1d; tp += 1d;
} }
double txp = yGnd.Lives.Count > 0 ? tp / p : 1d;
if (tpOnly) if (tpOnly)
return txp;
double txn = tn / n;
if (tnOnly)
return txn;
if (dc)
{ {
if (yGnd.Lives.Count > 0) double rp = n / sx;
return tp / yGnd.Lives.Count; double rn = p / sx;
return 1d; return txp * rp + txn * rn;
} }
return (tp + tn) / (yGnd.Height * yGnd.Width); 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 || if (yAct.Height != yGnd.Height || yAct.Width != yGnd.Width || yAct.QuotientX != yGnd.QuotientX ||
yAct.QuotientY != yGnd.QuotientY) 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 subGridY = 0; subGridY < subGridHeight; subGridY++)
{ {
for (int subGridX = 0; subGridX < subGridWidth; subGridX++) for (int subGridX = 0; subGridX < subGridWidth; subGridX++)
@@ -106,16 +126,19 @@ public static class Summarizer
int subGridStartY = subGridY * resolution; int subGridStartY = subGridY * resolution;
int subGridEndX = Math.Min(subGridStartX + resolution, yAct.Width); int subGridEndX = Math.Min(subGridStartX + resolution, yAct.Width);
int subGridEndY = Math.Min(subGridStartY + resolution, yAct.Height); int subGridEndY = Math.Min(subGridStartY + resolution, yAct.Height);
int cellsInSubGrid = (subGridEndX - subGridStartX) * (subGridEndY - subGridStartY); int cellsInSubGrid = (subGridEndX - subGridStartX) * (subGridEndY - subGridStartY);
double tn = cellsInSubGrid - actAlive[subGridIdx].Union(gndAlive[subGridIdx]).Count(); double tn = cellsInSubGrid - actAlive[subGridIdx].Union(gndAlive[subGridIdx]).Count();
double tp = tps[subGridIdx]; double tp = tps[subGridIdx];
double txp = gndAlive[subGridIdx].Count > 0 ? tp / gndAlive[subGridIdx].Count : 1d;
double txn = tn / (cellsInSubGrid - gndAlive[subGridIdx].Count);
if(tpOnly) 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 else
res[subGridIdx] = (tp + tn) / cellsInSubGrid; res[subGridIdx] = (tp + tn) / cellsInSubGrid;
@@ -124,12 +147,12 @@ public static class Summarizer
return res; 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) if (yAct.Tracer is null)
throw new Exception("tracer was not enabled"); throw new Exception("tracer was not enabled");
Dictionary<int, Dictionary<int, double>> contribution = ContributionBySubGrid(yAct, resolution); 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 subgridWidth = (yAct.Width + resolution - 1) / resolution;
int subgridHeight = (yAct.Height + resolution - 1) / resolution; int subgridHeight = (yAct.Height + resolution - 1) / resolution;
double[] res = new double[subgridWidth * subgridHeight]; double[] res = new double[subgridWidth * subgridHeight];