// The resulting lambda processes N samples, using buffers provided for Input and Output:
// void Process(int N, double t0, double T, double[] Input0 ..., double[] Output0 ...)
// { ... }
private Delegate DefineProcess()
{
// Map expressions to identifiers in the syntax tree.
List <KeyValuePair <Expression, LinqExpr> > inputs = new List <KeyValuePair <Expression, LinqExpr> >();
List <KeyValuePair <Expression, LinqExpr> > outputs = new List <KeyValuePair <Expression, LinqExpr> >();
// Lambda code generator.
CodeGen code = new CodeGen();
// Create parameters for the basic simulation info (N, t, Iterations).
ParamExpr SampleCount = code.Decl <int>(Scope.Parameter, "SampleCount");
ParamExpr t = code.Decl(Scope.Parameter, Simulation.t);
// Create buffer parameters for each input...
foreach (Expression i in Input)
{
inputs.Add(new KeyValuePair <Expression, LinqExpr>(i, code.Decl <double[]>(Scope.Parameter, i.ToString())));
}
// ... and output.
foreach (Expression i in Output)
{
outputs.Add(new KeyValuePair <Expression, LinqExpr>(i, code.Decl <double[]>(Scope.Parameter, i.ToString())));
}
// Create globals to store previous values of inputs.
foreach (Expression i in Input.Distinct())
{
AddGlobal(i.Evaluate(t_t0));
}
// Define lambda body.
// int Zero = 0
LinqExpr Zero = LinqExpr.Constant(0);
// double h = T / Oversample
LinqExpr h = LinqExpr.Constant(TimeStep / (double)Oversample);
// Load the globals to local variables and add them to the map.
foreach (KeyValuePair <Expression, GlobalExpr <double> > i in globals)
{
code.Add(LinqExpr.Assign(code.Decl(i.Key), i.Value));
}
foreach (KeyValuePair <Expression, LinqExpr> i in inputs)
{
code.Add(LinqExpr.Assign(code.Decl(i.Key), code[i.Key.Evaluate(t_t0)]));
}
// Create arrays for linear systems.
int M = Solution.Solutions.OfType <NewtonIteration>().Max(i => i.Equations.Count(), 0);
int N = Solution.Solutions.OfType <NewtonIteration>().Max(i => i.UnknownDeltas.Count(), 0) + 1;
LinqExpr JxF = code.DeclInit <double[][]>("JxF", LinqExpr.NewArrayBounds(typeof(double[]), LinqExpr.Constant(M)));
for (int j = 0; j < M; ++j)
{
code.Add(LinqExpr.Assign(LinqExpr.ArrayAccess(JxF, LinqExpr.Constant(j)), LinqExpr.NewArrayBounds(typeof(double), LinqExpr.Constant(N))));
}
// for (int n = 0; n < SampleCount; ++n)
ParamExpr n = code.Decl <int>("n");
code.For(
() => code.Add(LinqExpr.Assign(n, Zero)),
LinqExpr.LessThan(n, SampleCount),
() => code.Add(LinqExpr.PreIncrementAssign(n)),
() =>
{
// Prepare input samples for oversampling interpolation.
Dictionary <Expression, LinqExpr> dVi = new Dictionary <Expression, LinqExpr>();
foreach (Expression i in Input.Distinct())
{
LinqExpr Va = code[i];
// Sum all inputs with this key.
IEnumerable <LinqExpr> Vbs = inputs.Where(j => j.Key.Equals(i)).Select(j => j.Value);
LinqExpr Vb = LinqExpr.ArrayAccess(Vbs.First(), n);
foreach (LinqExpr j in Vbs.Skip(1))
{
Vb = LinqExpr.Add(Vb, LinqExpr.ArrayAccess(j, n));
}
// dVi = (Vb - Va) / Oversample
code.Add(LinqExpr.Assign(
Decl <double>(code, dVi, i, "d" + i.ToString().Replace("[t]", "")),
LinqExpr.Multiply(LinqExpr.Subtract(Vb, Va), LinqExpr.Constant(1.0 / (double)Oversample))));
}
// Prepare output sample accumulators for low pass filtering.
Dictionary <Expression, LinqExpr> Vo = new Dictionary <Expression, LinqExpr>();
foreach (Expression i in Output.Distinct())
{
code.Add(LinqExpr.Assign(
Decl <double>(code, Vo, i, i.ToString().Replace("[t]", "")),
LinqExpr.Constant(0.0)));
}
// int ov = Oversample;
// do { -- ov; } while(ov > 0)
ParamExpr ov = code.Decl <int>("ov");
code.Add(LinqExpr.Assign(ov, LinqExpr.Constant(Oversample)));
code.DoWhile(() =>
{
// t += h
code.Add(LinqExpr.AddAssign(t, h));
// Interpolate the input samples.
foreach (Expression i in Input.Distinct())
{
code.Add(LinqExpr.AddAssign(code[i], dVi[i]));
}
// Compile all of the SolutionSets in the solution.
foreach (SolutionSet ss in Solution.Solutions)
{
if (ss is LinearSolutions)
{
// Linear solutions are easy.
LinearSolutions S = (LinearSolutions)ss;
foreach (Arrow i in S.Solutions)
{
code.DeclInit(i.Left, i.Right);
}
}
else if (ss is NewtonIteration)
{
NewtonIteration S = (NewtonIteration)ss;
// Start with the initial guesses from the solution.
foreach (Arrow i in S.Guesses)
{
code.DeclInit(i.Left, i.Right);
}
// int it = iterations
LinqExpr it = code.ReDeclInit <int>("it", Iterations);
// do { ... --it } while(it > 0)
code.DoWhile((Break) =>
{
// Solve the un-solved system.
Solve(code, JxF, S.Equations, S.UnknownDeltas);
// Compile the pre-solved solutions.
if (S.KnownDeltas != null)
{
foreach (Arrow i in S.KnownDeltas)
{
code.DeclInit(i.Left, i.Right);
}
}
// bool done = true
LinqExpr done = code.ReDeclInit("done", true);
foreach (Expression i in S.Unknowns)
{
LinqExpr v = code[i];
LinqExpr dv = code[NewtonIteration.Delta(i)];
// done &= (|dv| < |v|*epsilon)
code.Add(LinqExpr.AndAssign(done, LinqExpr.LessThan(LinqExpr.Multiply(Abs(dv), LinqExpr.Constant(1e4)), LinqExpr.Add(Abs(v), LinqExpr.Constant(1e-6)))));
// v += dv
code.Add(LinqExpr.AddAssign(v, dv));
}
// if (done) break
code.Add(LinqExpr.IfThen(done, Break));
// --it;
code.Add(LinqExpr.PreDecrementAssign(it));
}, LinqExpr.GreaterThan(it, Zero));
//// bool failed = false
//LinqExpr failed = Decl(code, code, "failed", LinqExpr.Constant(false));
//for (int i = 0; i < eqs.Length; ++i)
// // failed |= |JxFi| > epsilon
// code.Add(LinqExpr.OrAssign(failed, LinqExpr.GreaterThan(Abs(eqs[i].ToExpression().Compile(map)), LinqExpr.Constant(1e-3))));
//code.Add(LinqExpr.IfThen(failed, ThrowSimulationDiverged(n)));
}
}
// Update the previous timestep variables.
foreach (SolutionSet S in Solution.Solutions)
{
foreach (Expression i in S.Unknowns.Where(i => globals.Keys.Contains(i.Evaluate(t_t0))))
{
code.Add(LinqExpr.Assign(code[i.Evaluate(t_t0)], code[i]));
}
}
// Vo += i
foreach (Expression i in Output.Distinct())
{
LinqExpr Voi = LinqExpr.Constant(0.0);
try
{
Voi = code.Compile(i);
}
catch (Exception Ex)
{
Log.WriteLine(MessageType.Warning, Ex.Message);
}
code.Add(LinqExpr.AddAssign(Vo[i], Voi));
}
// Vi_t0 = Vi
foreach (Expression i in Input.Distinct())
{
code.Add(LinqExpr.Assign(code[i.Evaluate(t_t0)], code[i]));
}
// --ov;
code.Add(LinqExpr.PreDecrementAssign(ov));
}, LinqExpr.GreaterThan(ov, Zero));
// Output[i][n] = Vo / Oversample
foreach (KeyValuePair <Expression, LinqExpr> i in outputs)
{
code.Add(LinqExpr.Assign(LinqExpr.ArrayAccess(i.Value, n), LinqExpr.Multiply(Vo[i.Key], LinqExpr.Constant(1.0 / (double)Oversample))));
}
// Every 256 samples, check for divergence.
if (Vo.Any())
{
code.Add(LinqExpr.IfThen(LinqExpr.Equal(LinqExpr.And(n, LinqExpr.Constant(0xFF)), Zero),
LinqExpr.Block(Vo.Select(i => LinqExpr.IfThenElse(IsNotReal(i.Value),
ThrowSimulationDiverged(n),
LinqExpr.Assign(i.Value, RoundDenormToZero(i.Value)))))));
}
});
// Copy the global state variables back to the globals.
foreach (KeyValuePair <Expression, GlobalExpr <double> > i in globals)
{
code.Add(LinqExpr.Assign(i.Value, code[i.Key]));
}
LinqExprs.LambdaExpression lambda = code.Build();
Delegate ret = lambda.Compile();
return(ret);
}