234 lines
6.4 KiB
C#
234 lines
6.4 KiB
C#
// Copyright (C) 2016 Maxim Gumin, The MIT License (MIT)
|
|
|
|
using System;
|
|
|
|
abstract class Model
|
|
{
|
|
protected bool[][] wave;
|
|
|
|
protected int[][][] propagator;
|
|
int[][][] compatible;
|
|
protected int[] observed;
|
|
|
|
(int, int)[] stack;
|
|
int stacksize, observedSoFar;
|
|
|
|
protected int MX, MY, T, N;
|
|
protected bool periodic, ground;
|
|
|
|
protected double[] weights;
|
|
double[] weightLogWeights, distribution;
|
|
|
|
protected int[] sumsOfOnes;
|
|
double sumOfWeights, sumOfWeightLogWeights, startingEntropy;
|
|
protected double[] sumsOfWeights, sumsOfWeightLogWeights, entropies;
|
|
|
|
public enum Heuristic { Entropy, MRV, Scanline };
|
|
Heuristic heuristic;
|
|
|
|
protected Model(int width, int height, int N, bool periodic, Heuristic heuristic)
|
|
{
|
|
MX = width;
|
|
MY = height;
|
|
this.N = N;
|
|
this.periodic = periodic;
|
|
this.heuristic = heuristic;
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
wave = new bool[MX * MY][];
|
|
compatible = new int[wave.Length][][];
|
|
for (int i = 0; i < wave.Length; i++)
|
|
{
|
|
wave[i] = new bool[T];
|
|
compatible[i] = new int[T][];
|
|
for (int t = 0; t < T; t++) compatible[i][t] = new int[4];
|
|
}
|
|
distribution = new double[T];
|
|
observed = new int[MX * MY];
|
|
|
|
weightLogWeights = new double[T];
|
|
sumOfWeights = 0;
|
|
sumOfWeightLogWeights = 0;
|
|
|
|
for (int t = 0; t < T; t++)
|
|
{
|
|
weightLogWeights[t] = weights[t] * Math.Log(weights[t]);
|
|
sumOfWeights += weights[t];
|
|
sumOfWeightLogWeights += weightLogWeights[t];
|
|
}
|
|
|
|
startingEntropy = Math.Log(sumOfWeights) - sumOfWeightLogWeights / sumOfWeights;
|
|
|
|
sumsOfOnes = new int[MX * MY];
|
|
sumsOfWeights = new double[MX * MY];
|
|
sumsOfWeightLogWeights = new double[MX * MY];
|
|
entropies = new double[MX * MY];
|
|
|
|
stack = new (int, int)[wave.Length * T];
|
|
stacksize = 0;
|
|
}
|
|
|
|
public bool Run(int seed, int limit)
|
|
{
|
|
if (wave == null) Init();
|
|
|
|
Clear();
|
|
Random random = new(seed);
|
|
|
|
for (int l = 0; l < limit || limit < 0; l++)
|
|
{
|
|
int node = NextUnobservedNode(random);
|
|
if (node >= 0)
|
|
{
|
|
Observe(node, random);
|
|
bool success = Propagate();
|
|
if (!success) return false;
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < wave.Length; i++) for (int t = 0; t < T; t++) if (wave[i][t]) { observed[i] = t; break; }
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int NextUnobservedNode(Random random)
|
|
{
|
|
if (heuristic == Heuristic.Scanline)
|
|
{
|
|
for (int i = observedSoFar; i < wave.Length; i++)
|
|
{
|
|
if (!periodic && (i % MX + N > MX || i / MX + N > MY)) continue;
|
|
if (sumsOfOnes[i] > 1)
|
|
{
|
|
observedSoFar = i + 1;
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
double min = 1E+4;
|
|
int argmin = -1;
|
|
for (int i = 0; i < wave.Length; i++)
|
|
{
|
|
if (!periodic && (i % MX + N > MX || i / MX + N > MY)) continue;
|
|
int remainingValues = sumsOfOnes[i];
|
|
double entropy = heuristic == Heuristic.Entropy ? entropies[i] : remainingValues;
|
|
if (remainingValues > 1 && entropy <= min)
|
|
{
|
|
double noise = 1E-6 * random.NextDouble();
|
|
if (entropy + noise < min)
|
|
{
|
|
min = entropy + noise;
|
|
argmin = i;
|
|
}
|
|
}
|
|
}
|
|
return argmin;
|
|
}
|
|
|
|
void Observe(int node, Random random)
|
|
{
|
|
bool[] w = wave[node];
|
|
for (int t = 0; t < T; t++) distribution[t] = w[t] ? weights[t] : 0.0;
|
|
int r = distribution.Random(random.NextDouble());
|
|
for (int t = 0; t < T; t++) if (w[t] != (t == r)) Ban(node, t);
|
|
}
|
|
|
|
bool Propagate()
|
|
{
|
|
while (stacksize > 0)
|
|
{
|
|
(int i1, int t1) = stack[stacksize - 1];
|
|
stacksize--;
|
|
|
|
int x1 = i1 % MX;
|
|
int y1 = i1 / MX;
|
|
|
|
for (int d = 0; d < 4; d++)
|
|
{
|
|
int x2 = x1 + dx[d];
|
|
int y2 = y1 + dy[d];
|
|
if (!periodic && (x2 < 0 || y2 < 0 || x2 + N > MX || y2 + N > MY)) continue;
|
|
|
|
if (x2 < 0) x2 += MX;
|
|
else if (x2 >= MX) x2 -= MX;
|
|
if (y2 < 0) y2 += MY;
|
|
else if (y2 >= MY) y2 -= MY;
|
|
|
|
int i2 = x2 + y2 * MX;
|
|
int[] p = propagator[d][t1];
|
|
int[][] compat = compatible[i2];
|
|
|
|
for (int l = 0; l < p.Length; l++)
|
|
{
|
|
int t2 = p[l];
|
|
int[] comp = compat[t2];
|
|
|
|
comp[d]--;
|
|
if (comp[d] == 0) Ban(i2, t2);
|
|
}
|
|
}
|
|
}
|
|
|
|
return sumsOfOnes[0] > 0;
|
|
}
|
|
|
|
void Ban(int i, int t)
|
|
{
|
|
wave[i][t] = false;
|
|
|
|
int[] comp = compatible[i][t];
|
|
for (int d = 0; d < 4; d++) comp[d] = 0;
|
|
stack[stacksize] = (i, t);
|
|
stacksize++;
|
|
|
|
sumsOfOnes[i] -= 1;
|
|
sumsOfWeights[i] -= weights[t];
|
|
sumsOfWeightLogWeights[i] -= weightLogWeights[t];
|
|
|
|
double sum = sumsOfWeights[i];
|
|
entropies[i] = Math.Log(sum) - sumsOfWeightLogWeights[i] / sum;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
for (int i = 0; i < wave.Length; i++)
|
|
{
|
|
for (int t = 0; t < T; t++)
|
|
{
|
|
wave[i][t] = true;
|
|
for (int d = 0; d < 4; d++) compatible[i][t][d] = propagator[opposite[d]][t].Length;
|
|
}
|
|
|
|
sumsOfOnes[i] = weights.Length;
|
|
sumsOfWeights[i] = sumOfWeights;
|
|
sumsOfWeightLogWeights[i] = sumOfWeightLogWeights;
|
|
entropies[i] = startingEntropy;
|
|
observed[i] = -1;
|
|
}
|
|
observedSoFar = 0;
|
|
|
|
if (ground)
|
|
{
|
|
for (int x = 0; x < MX; x++)
|
|
{
|
|
for (int t = 0; t < T - 1; t++) Ban(x + (MY - 1) * MX, t);
|
|
for (int y = 0; y < MY - 1; y++) Ban(x + y * MX, T - 1);
|
|
}
|
|
Propagate();
|
|
}
|
|
}
|
|
|
|
public abstract void Save(string filename);
|
|
|
|
protected static int[] dx = { -1, 0, 1, 0 };
|
|
protected static int[] dy = { 0, 1, 0, -1 };
|
|
static int[] opposite = { 2, 3, 0, 1 };
|
|
}
|