using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using HeuristicLab.Common; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; namespace HeuristicLab.Problems.DataAnalysis.Symbolic { public abstract class Interpreter where T : IAlgebraicType { public struct Instruction { public byte opcode; public ushort narg; public int childIndex; public double dblVal; public object data; // any kind of data you want to store in instructions public T value; } public T Evaluate(Instruction[] code) { for (int i = code.Length - 1; i >= 0; --i) { var instr = code[i]; var c = instr.childIndex; var n = instr.narg; switch (instr.opcode) { case OpCodes.Variable: { LoadVariable(instr); break; } case OpCodes.Constant: { break; } // we initialize constants in Compile. The value never changes afterwards case OpCodes.Add: { instr.value.Assign(code[c].value); for (int j = 1; j < n; ++j) { instr.value.Add(code[c + j].value); } break; } case OpCodes.Sub: { if (n == 1) { instr.value.AssignNeg(code[c].value); } else { instr.value.Assign(code[c].value); for (int j = 1; j < n; ++j) { instr.value.Sub(code[c + j].value); } } break; } case OpCodes.Mul: { instr.value.Assign(code[c].value); for (int j = 1; j < n; ++j) { instr.value.Mul(code[c + j].value); } break; } case OpCodes.Div: { if (n == 1) { instr.value.AssignInv(code[c].value); } else { instr.value.Assign(code[c].value); for (int j = 1; j < n; ++j) { instr.value.Div(code[c + j].value); } } break; } case OpCodes.Square: { instr.value.AssignIntPower(code[c].value, 2); break; } case OpCodes.SquareRoot: { instr.value.AssignIntRoot(code[c].value, 2); break; } case OpCodes.Cube: { instr.value.AssignIntPower(code[c].value, 3); break; } case OpCodes.CubeRoot: { instr.value.AssignIntRoot(code[c].value, 3); break; } case OpCodes.Exp: { instr.value.AssignExp(code[c].value); break; } case OpCodes.Log: { instr.value.AssignLog(code[c].value); break; } case OpCodes.Sin: { instr.value.AssignSin(code[c].value); break; } case OpCodes.Cos: { instr.value.AssignCos(code[c].value); break; } case OpCodes.Absolute: { instr.value.AssignAbs(code[c].value); break; } case OpCodes.AnalyticQuotient: { instr.value.Assign(code[c].value); for (int j = 1; j < n; ++j) { var t = instr.value.One; t.Add(code[c + j].value.Clone().IntPower(2)); instr.value.Div(t.IntRoot(2)); } break; } case OpCodes.Tanh: { instr.value.AssignTanh(code[c].value); break; } default: throw new ArgumentException($"Unknown opcode {instr.opcode}"); } } return code[0].value; } protected Instruction[] Compile(ISymbolicExpressionTree tree) { var root = tree.Root.GetSubtree(0).GetSubtree(0); var code = new Instruction[root.GetLength()]; if (root.SubtreeCount > ushort.MaxValue) throw new ArgumentException("Number of subtrees is too big (>65.535)"); int c = 1, i = 0; foreach (var node in root.IterateNodesBreadth()) { if (node.SubtreeCount > ushort.MaxValue) throw new ArgumentException("Number of subtrees is too big (>65.535)"); code[i] = new Instruction { opcode = OpCodes.MapSymbolToOpCode(node), narg = (ushort)node.SubtreeCount, childIndex = c }; if (node is VariableTreeNode variable) { InitializeTerminalInstruction(ref code[i], variable); } else if (node is ConstantTreeNode constant) { InitializeTerminalInstruction(ref code[i], constant); } else { InitializeInternalInstruction(ref code[i], node); } c += node.SubtreeCount; ++i; } return code; } protected abstract void InitializeTerminalInstruction(ref Instruction instruction, ConstantTreeNode constant); protected abstract void InitializeTerminalInstruction(ref Instruction instruction, VariableTreeNode variable); protected abstract void InitializeInternalInstruction(ref Instruction instruction, ISymbolicExpressionTreeNode node); protected abstract void LoadVariable(Instruction a); } public sealed class VectorEvaluator : Interpreter { private const int BATCHSIZE = 128; [ThreadStatic] private Dictionary cachedData; [ThreadStatic] private IDataset dataset; [ThreadStatic] private int rowIndex; [ThreadStatic] private int[] rows; private void InitCache(IDataset dataset) { this.dataset = dataset; cachedData = new Dictionary(); foreach (var v in dataset.DoubleVariables) { cachedData[v] = dataset.GetReadOnlyDoubleValues(v).ToArray(); } } public double[] Evaluate(ISymbolicExpressionTree tree, IDataset dataset, int[] rows) { if (cachedData == null || this.dataset != dataset) { InitCache(dataset); } this.rows = rows; var code = Compile(tree); var remainingRows = rows.Length % BATCHSIZE; var roundedTotal = rows.Length - remainingRows; var result = new double[rows.Length]; for (rowIndex = 0; rowIndex < roundedTotal; rowIndex += BATCHSIZE) { Evaluate(code); code[0].value.CopyTo(result, rowIndex, BATCHSIZE); } if (remainingRows > 0) { Evaluate(code); code[0].value.CopyTo(result, roundedTotal, remainingRows); } return result; } protected override void InitializeTerminalInstruction(ref Instruction instruction, ConstantTreeNode constant) { instruction.dblVal = constant.Value; instruction.value = new AlgebraicDoubleVector(BATCHSIZE); instruction.value.AssignConstant(instruction.dblVal); } protected override void InitializeTerminalInstruction(ref Instruction instruction, VariableTreeNode variable) { instruction.dblVal = variable.Weight; instruction.value = new AlgebraicDoubleVector(BATCHSIZE); if (cachedData.ContainsKey(variable.VariableName)) { instruction.data = cachedData[variable.VariableName]; } else { instruction.data = dataset.GetDoubleValues(variable.VariableName).ToArray(); cachedData[variable.VariableName] = (double[])instruction.data; } } protected override void InitializeInternalInstruction(ref Instruction instruction, ISymbolicExpressionTreeNode node) { instruction.value = new AlgebraicDoubleVector(BATCHSIZE); } protected override void LoadVariable(Instruction a) { var data = (double[])a.data; for (int i = rowIndex; i < rows.Length && i - rowIndex < BATCHSIZE; i++) a.value[i - rowIndex] = data[rows[i]]; a.value.Scale(a.dblVal); } } public sealed class VectorAutoDiffEvaluator : Interpreter> { private const int BATCHSIZE = 128; [ThreadStatic] private Dictionary cachedData; [ThreadStatic] private IDataset dataset; [ThreadStatic] private int rowIndex; [ThreadStatic] private int[] rows; [ThreadStatic] private Dictionary node2paramIdx; private void InitCache(IDataset dataset) { this.dataset = dataset; cachedData = new Dictionary(); foreach (var v in dataset.DoubleVariables) { cachedData[v] = dataset.GetDoubleValues(v).ToArray(); } } /// /// /// /// /// /// /// /// Function output. Must be allocated by the caller. /// Jacobian matrix. Must be allocated by the caller. public void Evaluate(ISymbolicExpressionTree tree, IDataset dataset, int[] rows, ISymbolicExpressionTreeNode[] parameterNodes, double[] fi, double[,] jac) { if (cachedData == null || this.dataset != dataset) { InitCache(dataset); } int nParams = parameterNodes.Length; node2paramIdx = new Dictionary(); for (int i = 0; i < parameterNodes.Length; i++) node2paramIdx.Add(parameterNodes[i], i); var code = Compile(tree); var remainingRows = rows.Length % BATCHSIZE; var roundedTotal = rows.Length - remainingRows; this.rows = rows; for (rowIndex = 0; rowIndex < roundedTotal; rowIndex += BATCHSIZE) { Evaluate(code); code[0].value.Value.CopyTo(fi, rowIndex, BATCHSIZE); // TRANSPOSE into JAC var g = code[0].value.Gradient; for (int j = 0; j < nParams; ++j) { if (g.Elements.TryGetValue(j, out AlgebraicDoubleVector v)) { v.CopyColumnTo(jac, j, rowIndex, BATCHSIZE); } else { for (int r = 0; r < BATCHSIZE; r++) jac[rowIndex + r, j] = 0.0; } } } if (remainingRows > 0) { Evaluate(code); code[0].value.Value.CopyTo(fi, roundedTotal, remainingRows); var g = code[0].value.Gradient; for (int j = 0; j < nParams; ++j) if (g.Elements.TryGetValue(j, out AlgebraicDoubleVector v)) { v.CopyColumnTo(jac, j, roundedTotal, remainingRows); } else { for (int r = 0; r < remainingRows; r++) jac[roundedTotal + r, j] = 0.0; } } } protected override void InitializeInternalInstruction(ref Instruction instruction, ISymbolicExpressionTreeNode node) { var zero = new AlgebraicDoubleVector(BATCHSIZE); instruction.value = new MultivariateDual(zero); } protected override void InitializeTerminalInstruction(ref Instruction instruction, ConstantTreeNode constant) { var g_arr = new double[BATCHSIZE]; if (node2paramIdx.TryGetValue(constant, out var paramIdx)) { for (int i = 0; i < BATCHSIZE; i++) g_arr[i] = 1.0; var g = new AlgebraicDoubleVector(g_arr); instruction.value = new MultivariateDual(new AlgebraicDoubleVector(BATCHSIZE), paramIdx, g); // only a single column for the gradient } else { instruction.value = new MultivariateDual(new AlgebraicDoubleVector(BATCHSIZE)); } instruction.dblVal = constant.Value; instruction.value.Value.AssignConstant(instruction.dblVal); } protected override void InitializeTerminalInstruction(ref Instruction instruction, VariableTreeNode variable) { double[] data; if (cachedData.ContainsKey(variable.VariableName)) { data = cachedData[variable.VariableName]; } else { data = dataset.GetReadOnlyDoubleValues(variable.VariableName).ToArray(); cachedData[variable.VariableName] = (double[])instruction.data; } var paramIdx = -1; if (node2paramIdx.ContainsKey(variable)) { paramIdx = node2paramIdx[variable]; var f = new AlgebraicDoubleVector(BATCHSIZE); var g = new AlgebraicDoubleVector(BATCHSIZE); instruction.value = new MultivariateDual(f, paramIdx, g); } else { var f = new AlgebraicDoubleVector(BATCHSIZE); instruction.value = new MultivariateDual(f); } instruction.dblVal = variable.Weight; instruction.data = new object[] { data, paramIdx }; } protected override void LoadVariable(Instruction a) { var paramIdx = (int)((object[])a.data)[1]; var data = (double[])((object[])a.data)[0]; for (int i = rowIndex; i < rows.Length && i - rowIndex < BATCHSIZE; i++) a.value.Value[i - rowIndex] = data[rows[i]]; a.value.Scale(a.dblVal); if (paramIdx >= 0) { // update gradient with variable values var g = a.value.Gradient.Elements[paramIdx]; for (int i = rowIndex; i < rows.Length && i - rowIndex < BATCHSIZE; i++) { g[i - rowIndex] = data[rows[i]]; } } } } public sealed class IntervalEvaluator : Interpreter { [ThreadStatic] private IDictionary intervals; public Interval Evaluate(ISymbolicExpressionTree tree, IDictionary intervals) { this.intervals = intervals; var code = Compile(tree); Evaluate(code); if (code[0].value.LowerBound.Value.Value > code[0].value.UpperBound.Value.Value) throw new InvalidProgramException($"lower: {code[0].value.LowerBound.Value.Value} > upper: {code[0].value.UpperBound.Value.Value}"); return new Interval(code[0].value.LowerBound.Value.Value, code[0].value.UpperBound.Value.Value); } public Interval Evaluate(ISymbolicExpressionTree tree, IDictionary intervals, ISymbolicExpressionTreeNode[] paramNodes, out double[] lowerGradient, out double[] upperGradient) { this.intervals = intervals; var code = Compile(tree); Evaluate(code); lowerGradient = new double[paramNodes.Length]; upperGradient = new double[paramNodes.Length]; var l = code[0].value.LowerBound; var u = code[0].value.UpperBound; for (int i = 0; i < paramNodes.Length; ++i) { if (paramNodes[i] == null) continue; if (l.Gradient.Elements.TryGetValue(paramNodes[i], out AlgebraicDouble value)) lowerGradient[i] = value; if (u.Gradient.Elements.TryGetValue(paramNodes[i], out value)) upperGradient[i] = value; } return new Interval(code[0].value.LowerBound.Value.Value, code[0].value.UpperBound.Value.Value); } protected override void InitializeInternalInstruction(ref Instruction instruction, ISymbolicExpressionTreeNode node) { instruction.value = new AlgebraicInterval(0, 0); } protected override void InitializeTerminalInstruction(ref Instruction instruction, ConstantTreeNode constant) { instruction.dblVal = constant.Value; instruction.value = new AlgebraicInterval( new MultivariateDual(instruction.dblVal, constant, 1.0), new MultivariateDual(instruction.dblVal, constant, 1.0) // use node as key ); } protected override void InitializeTerminalInstruction(ref Instruction instruction, VariableTreeNode variable) { instruction.dblVal = variable.Weight; var v1 = instruction.dblVal * intervals[variable.VariableName].LowerBound; var v2 = instruction.dblVal * intervals[variable.VariableName].UpperBound; var lower = Math.Min(v1, v2); var upper = Math.Max(v1, v2); // we assume that the for variable nodes ( v(x,w) = w * x ) the gradient is returned for parameter w instruction.value = new AlgebraicInterval( low: new MultivariateDual(v: lower, key: variable, dv: intervals[variable.VariableName].LowerBound), high: new MultivariateDual(v: upper, key: variable, dv: intervals[variable.VariableName].UpperBound) ); } protected override void LoadVariable(Instruction a) { // nothing to do } } public interface IAlgebraicType { T Zero { get; } // Zero and One must create new objects T One { get; } T AssignAbs(T a); // set this to assign abs(a) T Assign(T a); // assign this to same value as a (copy!) T AssignNeg(T a); // set this to negative(a) T AssignInv(T a); // set this to inv(a); T Scale(double s); // scale this with s T Add(T a); // add a to this T Sub(T a); // subtract a from this T Mul(T a); // multiply this with a T Div(T a); // divide this by a T AssignLog(T a); // set this to log a T AssignExp(T a); // set this to exp(a) T AssignSin(T a); // set this to sin(a) T AssignCos(T a); // set this to cos(a) T AssignTanh(T a); // set this to tanh(a) T AssignIntPower(T a, int p); T AssignIntRoot(T a, int r); T AssignSgn(T a); // set this to sign(a) T AssignMin(T other); // set this min(this, other) T AssignMax(T other); // set this max(this, other) T Clone(); } public static class Algebraic { public static T Abs(this T a) where T : IAlgebraicType { a.AssignAbs(a.Clone()); return a; } public static T Neg(this T a) where T : IAlgebraicType { a.AssignNeg(a.Clone()); return a; } public static T Inv(this T a) where T : IAlgebraicType { a.AssignInv(a.Clone()); return a; } public static T Log(this T a) where T : IAlgebraicType { a.AssignLog(a.Clone()); return a; } public static T Exp(this T a) where T : IAlgebraicType { a.AssignExp(a.Clone()); return a; } public static T Sin(this T a) where T : IAlgebraicType { a.AssignSin(a.Clone()); return a; } public static T Cos(this T a) where T : IAlgebraicType { a.AssignCos(a.Clone()); return a; } public static T Sgn(this T a) where T : IAlgebraicType { a.AssignSgn(a.Clone()); return a; } public static T IntPower(this T a, int p) where T : IAlgebraicType { a.AssignIntPower(a.Clone(), p); return a; } public static T IntRoot(this T a, int r) where T : IAlgebraicType { a.AssignIntRoot(a.Clone(), r); return a; } internal static T Min(T a, T b) where T : IAlgebraicType { return a.Clone().AssignMin(b); } internal static T Max(T a, T b) where T : IAlgebraicType { return a.Clone().AssignMax(b); } // public static T Max(T a, T b) where T : IAlgebraicType { // // ((a + b) + abs(b - a)) / 2 // return a.Clone().Add(b).Add(b.Clone().Sub(a).Abs()).Scale(1.0 / 2.0); // } // public static T Min(T a, T b) where T : IAlgebraicType { // // ((a + b) - abs(a - b)) / 2 // return a.Clone().Add(b).Sub(a.Clone().Sub(b).Abs()).Scale(1.0 / 2.0); // } } // algebraic type wrapper for a double value [DebuggerDisplay("{Value}")] public sealed class AlgebraicDouble : IAlgebraicType { public static implicit operator AlgebraicDouble(double value) { return new AlgebraicDouble(value); } public static implicit operator double(AlgebraicDouble value) { return value.Value; } public double Value; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public AlgebraicDouble Zero => new AlgebraicDouble(0.0); [DebuggerBrowsable(DebuggerBrowsableState.Never)] public AlgebraicDouble One => new AlgebraicDouble(1.0); public bool IsInfinity => IsNegativeInfinity || IsPositiveInfinity; public bool IsNegativeInfinity => double.IsNegativeInfinity(Value); public bool IsPositiveInfinity => double.IsPositiveInfinity(Value); public AlgebraicDouble() { } public AlgebraicDouble(double value) { this.Value = value; } public AlgebraicDouble Assign(AlgebraicDouble a) { Value = a.Value; return this; } public AlgebraicDouble Add(AlgebraicDouble a) { Value += a.Value; return this; } public AlgebraicDouble Sub(AlgebraicDouble a) { Value -= a.Value; return this; } public AlgebraicDouble Mul(AlgebraicDouble a) { Value *= a.Value; return this; } public AlgebraicDouble Div(AlgebraicDouble a) { Value /= a.Value; return this; } public AlgebraicDouble Scale(double s) { Value *= s; return this; } public AlgebraicDouble AssignInv(AlgebraicDouble a) { Value = 1.0 / a.Value; return this; } public AlgebraicDouble AssignNeg(AlgebraicDouble a) { Value = -a.Value; return this; } public AlgebraicDouble AssignSin(AlgebraicDouble a) { Value = Math.Sin(a.Value); return this; } public AlgebraicDouble AssignCos(AlgebraicDouble a) { Value = Math.Cos(a.Value); return this; } public AlgebraicDouble AssignTanh(AlgebraicDouble a) { Value = Math.Tanh(a.Value); return this; } public AlgebraicDouble AssignLog(AlgebraicDouble a) { Value = Math.Log(a.Value); return this; } public AlgebraicDouble AssignExp(AlgebraicDouble a) { Value = Math.Exp(a.Value); return this; } public AlgebraicDouble AssignIntPower(AlgebraicDouble a, int p) { Value = Math.Pow(a.Value, p); return this; } public AlgebraicDouble AssignIntRoot(AlgebraicDouble a, int r) { Value = IntRoot(a.Value, r); return this; } public AlgebraicDouble AssignMin(AlgebraicDouble other) { Value = Math.Min(Value, other.Value); return this; } public AlgebraicDouble AssignMax(AlgebraicDouble other) { Value = Math.Max(Value, other.Value); return this; } // helper private static double IntRoot(double value, int r) { if (r % 2 == 0) return Math.Pow(value, 1.0 / r); else return value < 0 ? -Math.Pow(-value, 1.0 / r) : Math.Pow(value, 1.0 / r); } public AlgebraicDouble AssignAbs(AlgebraicDouble a) { Value = Math.Abs(a.Value); return this; } public AlgebraicDouble AssignSgn(AlgebraicDouble a) { Value = double.IsNaN(a.Value) ? double.NaN : Math.Sign(a.Value); return this; } public AlgebraicDouble Clone() { return new AlgebraicDouble(Value); } public override string ToString() { return Value.ToString(); } } // a simple vector as an algebraic type [DebuggerDisplay("DoubleVector(len={Length}): {string.}")] public class AlgebraicDoubleVector : IAlgebraicType { private double[] arr; public double this[int idx] { get { return arr[idx]; } set { arr[idx] = value; } } public int Length => arr.Length; public AlgebraicDoubleVector(int length) { arr = new double[length]; } public AlgebraicDoubleVector() { } /// /// /// /// array is not copied public AlgebraicDoubleVector(double[] arr) { this.arr = arr; } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public AlgebraicDoubleVector Zero => new AlgebraicDoubleVector(new double[this.Length]); // must return vector of same length as this (therefore Zero is not static) [DebuggerBrowsable(DebuggerBrowsableState.Never)] public AlgebraicDoubleVector One => new AlgebraicDoubleVector(new double[this.Length]).AssignConstant(1.0); // must return vector of same length as this (therefore Zero is not static) public AlgebraicDoubleVector Assign(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = a.arr[i]; } return this; } public AlgebraicDoubleVector Add(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] += a.arr[i]; } return this; } public AlgebraicDoubleVector Sub(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] -= a.arr[i]; } return this; } public AlgebraicDoubleVector Mul(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] *= a.arr[i]; } return this; } public AlgebraicDoubleVector Div(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] /= a.arr[i]; } return this; } public AlgebraicDoubleVector AssignNeg(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = -a.arr[i]; } return this; } public AlgebraicDoubleVector AssignInv(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = 1.0 / a.arr[i]; } return this; } public AlgebraicDoubleVector Scale(double s) { for (int i = 0; i < arr.Length; ++i) { arr[i] *= s; } return this; } public AlgebraicDoubleVector AssignLog(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Log(a.arr[i]); } return this; } public AlgebraicDoubleVector AssignSin(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Sin(a.arr[i]); } return this; } public AlgebraicDoubleVector AssignExp(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Exp(a.arr[i]); } return this; } public AlgebraicDoubleVector AssignCos(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Cos(a.arr[i]); } return this; } public AlgebraicDoubleVector AssignTanh(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Tanh(a.arr[i]); } return this; } public AlgebraicDoubleVector AssignIntPower(AlgebraicDoubleVector a, int p) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Pow(a.arr[i], p); } return this; } public AlgebraicDoubleVector AssignIntRoot(AlgebraicDoubleVector a, int r) { for (int i = 0; i < arr.Length; ++i) { arr[i] = IntRoot(a.arr[i], r); } return this; } public AlgebraicDoubleVector AssignMin(AlgebraicDoubleVector other) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Min(arr[i], other.arr[i]); } return this; } public AlgebraicDoubleVector AssignMax(AlgebraicDoubleVector other) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Max(arr[i], other.arr[i]); } return this; } // helper private double IntRoot(double v, int r) { if (r % 2 == 0) return Math.Pow(v, 1.0 / r); else return v < 0 ? -Math.Pow(-v, 1.0 / r) : Math.Pow(v, 1.0 / r); } public AlgebraicDoubleVector AssignAbs(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Abs(a.arr[i]); } return this; } public AlgebraicDoubleVector AssignSgn(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Sign(a.arr[i]); } return this; } public AlgebraicDoubleVector Clone() { var v = new AlgebraicDoubleVector(this.arr.Length); Array.Copy(arr, v.arr, v.arr.Length); return v; } public AlgebraicDoubleVector AssignConstant(double constantValue) { for (int i = 0; i < arr.Length; ++i) { arr[i] = constantValue; } return this; } public void CopyTo(double[] dest, int idx, int length) { Array.Copy(arr, 0, dest, idx, length); } public void CopyFrom(double[] data, int rowIndex) { Array.Copy(data, rowIndex, arr, 0, Math.Min(arr.Length, data.Length - rowIndex)); } public void CopyRowTo(double[,] dest, int row) { for (int j = 0; j < arr.Length; ++j) dest[row, j] = arr[j]; } internal void CopyColumnTo(double[,] dest, int column, int row, int len) { for (int j = 0; j < len; ++j) dest[row + j, column] = arr[j]; } public override string ToString() { return "{" + string.Join(", ", arr.Take(Math.Max(5, arr.Length))) + (arr.Length > 5 ? "..." : string.Empty) + "}"; } } /* // vectors of algebraic types public sealed class AlgebraicVector : IAlgebraicType> where T : IAlgebraicType, new() { private T[] elems; public T this[int idx] { get { return elems[idx]; } set { elems[idx] = value; } } public int Length => elems.Length; private AlgebraicVector() { } public AlgebraicVector(int len) { elems = new T[len]; } /// /// /// /// The array is copied (element-wise clone) public AlgebraicVector(T[] elems) { this.elems = new T[elems.Length]; for (int i = 0; i < elems.Length; ++i) { this.elems[i] = elems[i].Clone(); } } /// /// /// /// Array is not copied! /// public AlgebraicVector FromArray(T[] elems) { var v = new AlgebraicVector(); v.elems = elems; return v; } public void CopyTo(T[] dest) { if (dest.Length != elems.Length) throw new InvalidOperationException("arr lengths do not match in Vector.Copy"); Array.Copy(elems, dest, dest.Length); } public AlgebraicVector Clone() { return new AlgebraicVector(elems); } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public AlgebraicVector Zero => new AlgebraicVector(Length); [DebuggerBrowsable(DebuggerBrowsableState.Never)] public AlgebraicVector One { get { var v = new AlgebraicVector(Length); for (int i = 0; i < elems.Length; ++i) elems[i] = new T().One; return v; } } public AlgebraicVector Assign(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].Assign(a.elems[i]); } return this; } public AlgebraicVector Add(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].Add(a.elems[i]); } return this; } public AlgebraicVector Sub(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].Sub(a.elems[i]); } return this; } public AlgebraicVector Mul(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].Mul(a.elems[i]); } return this; } public AlgebraicVector Div(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].Div(a.elems[i]); } return this; } public AlgebraicVector AssignNeg(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].AssignNeg(a.elems[i]); } return this; } public AlgebraicVector Scale(double s) { for (int i = 0; i < elems.Length; ++i) { elems[i].Scale(s); } return this; } public AlgebraicVector Scale(T s) { for (int i = 0; i < elems.Length; ++i) { elems[i].Mul(s); } return this; } public AlgebraicVector AssignInv(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].AssignInv(a.elems[i]); } return this; } public AlgebraicVector AssignLog(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].AssignLog(a.elems[i]); } return this; } public AlgebraicVector AssignExp(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].AssignExp(a.elems[i]); } return this; } public AlgebraicVector AssignSin(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].AssignSin(a.elems[i]); } return this; } public AlgebraicVector AssignCos(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].AssignCos(a.elems[i]); } return this; } public AlgebraicVector AssignIntPower(AlgebraicVector a, int p) { for (int i = 0; i < elems.Length; ++i) { elems[i].AssignIntPower(a.elems[i], p); } return this; } public AlgebraicVector AssignIntRoot(AlgebraicVector a, int r) { for (int i = 0; i < elems.Length; ++i) { elems[i].AssignIntRoot(a.elems[i], r); } return this; } public AlgebraicVector AssignAbs(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].AssignAbs(a.elems[i]); } return this; } public AlgebraicVector AssignSgn(AlgebraicVector a) { for (int i = 0; i < elems.Length; ++i) { elems[i].AssignSgn(a.elems[i]); } return this; } } */ /// /// A sparse vector of algebraic types. Elements are accessed via a key of type K /// /// Key type /// Element type [DebuggerDisplay("SparseVector: {ToString()}")] public sealed class AlgebraicSparseVector : IAlgebraicType> where T : IAlgebraicType { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private Dictionary elems; public IReadOnlyDictionary Elements => elems; public AlgebraicSparseVector(AlgebraicSparseVector original) { elems = original.elems.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Clone()); } /// /// /// /// /// values are cloned public AlgebraicSparseVector(K[] keys, T[] values) { if (keys.Length != values.Length) throw new ArgumentException("lengths of keys and values doesn't match in SparseVector"); elems = new Dictionary(keys.Length); for (int i = 0; i < keys.Length; ++i) { elems.Add(keys[i], values[i].Clone()); } } public AlgebraicSparseVector() { this.elems = new Dictionary(); } // keep only elements from a private void AssignFromSource(AlgebraicSparseVector a, Func mapAssign) { // remove elems from this which don't occur in a List keysToRemove = new List(); foreach (var kvp in elems) { if (!a.elems.ContainsKey(kvp.Key)) keysToRemove.Add(kvp.Key); } foreach (var o in keysToRemove) elems.Remove(o); // -> zero foreach (var kvp in a.elems) { if (elems.TryGetValue(kvp.Key, out T value)) mapAssign(kvp.Value, value); else elems.Add(kvp.Key, mapAssign(kvp.Value, kvp.Value.Zero)); } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public AlgebraicSparseVector Zero => new AlgebraicSparseVector(); [DebuggerBrowsable(DebuggerBrowsableState.Never)] public AlgebraicSparseVector One => throw new NotSupportedException(); public AlgebraicSparseVector Scale(T s) { foreach (var kvp in elems) { kvp.Value.Mul(s); } return this; } public AlgebraicSparseVector Scale(double s) { foreach (var kvp in elems) { kvp.Value.Scale(s); } return this; } public AlgebraicSparseVector Assign(AlgebraicSparseVector a) { elems.Clear(); AssignFromSource(a, (src, dest) => dest.Assign(src)); return this; } public AlgebraicSparseVector AssignInv(AlgebraicSparseVector a) { AssignFromSource(a, (src, dest) => dest.AssignInv(src)); return this; } public AlgebraicSparseVector AssignNeg(AlgebraicSparseVector a) { AssignFromSource(a, (src, dest) => dest.AssignNeg(src)); return this; } public AlgebraicSparseVector AssignLog(AlgebraicSparseVector a) { AssignFromSource(a, (src, dest) => dest.AssignLog(src)); return this; } public AlgebraicSparseVector AssignExp(AlgebraicSparseVector a) { AssignFromSource(a, (src, dest) => dest.AssignExp(src)); return this; } public AlgebraicSparseVector AssignIntPower(AlgebraicSparseVector a, int p) { AssignFromSource(a, (src, dest) => dest.AssignIntPower(src, p)); return this; } public AlgebraicSparseVector AssignIntRoot(AlgebraicSparseVector a, int r) { AssignFromSource(a, (src, dest) => dest.AssignIntRoot(src, r)); return this; } public AlgebraicSparseVector AssignSin(AlgebraicSparseVector a) { AssignFromSource(a, (src, dest) => dest.AssignSin(src)); return this; } public AlgebraicSparseVector AssignCos(AlgebraicSparseVector a) { AssignFromSource(a, (src, dest) => dest.AssignCos(src)); return this; } public AlgebraicSparseVector AssignTanh(AlgebraicSparseVector a) { AssignFromSource(a, (src, dest) => dest.AssignTanh(src)); return this; } public AlgebraicSparseVector AssignAbs(AlgebraicSparseVector a) { AssignFromSource(a, (src, dest) => dest.AssignAbs(src)); return this; } public AlgebraicSparseVector AssignSgn(AlgebraicSparseVector a) { AssignFromSource(a, (src, dest) => dest.AssignSgn(src)); return this; } public AlgebraicSparseVector Add(AlgebraicSparseVector a) { foreach (var kvp in a.elems) { if (elems.TryGetValue(kvp.Key, out T value)) value.Add(kvp.Value); else elems.Add(kvp.Key, kvp.Value.Clone()); // 0 + a } return this; } public AlgebraicSparseVector Sub(AlgebraicSparseVector a) { foreach (var kvp in a.elems) { if (elems.TryGetValue(kvp.Key, out T value)) value.Sub(kvp.Value); else elems.Add(kvp.Key, kvp.Value.Zero.Sub(kvp.Value)); // 0 - a } return this; } public AlgebraicSparseVector Mul(AlgebraicSparseVector a) { var keys = elems.Keys.ToArray(); foreach (var k in keys) if (!a.elems.ContainsKey(k)) elems.Remove(k); // 0 * a => 0 foreach (var kvp in a.elems) { if (elems.TryGetValue(kvp.Key, out T value)) value.Mul(kvp.Value); // this * a } return this; } public AlgebraicSparseVector Div(AlgebraicSparseVector a) { return Mul(a.Clone().Inv()); } public AlgebraicSparseVector AssignMin(AlgebraicSparseVector other) { // assumes that keys without a matching key in other are zero and vice versa foreach (var kvp in elems) if (!other.elems.ContainsKey(kvp.Key)) kvp.Value.AssignMin(kvp.Value.Zero); // min(v, 0) foreach (var kvp in other.elems) { if (elems.TryGetValue(kvp.Key, out T value)) value.AssignMin(kvp.Value); else elems.Add(kvp.Key, kvp.Value.Zero.AssignMin(kvp.Value)); } return this; } public AlgebraicSparseVector AssignMax(AlgebraicSparseVector other) { // assumes that keys without a matching key in other are zero and vice versa foreach (var kvp in elems) if (!other.elems.ContainsKey(kvp.Key)) kvp.Value.AssignMax(kvp.Value.Zero); // max(v, 0) foreach (var kvp in other.elems) { if (elems.TryGetValue(kvp.Key, out T value)) value.AssignMax(kvp.Value); else elems.Add(kvp.Key, kvp.Value.Zero.AssignMax(kvp.Value)); } return this; } public AlgebraicSparseVector Clone() { return new AlgebraicSparseVector(this); } public override string ToString() { return "[" + string.Join(" ", elems.Select(kvp => kvp.Key + ": " + kvp.Value)) + "]"; } } // this is our own implementation of interval arithmetic // for a well worked out definition of interval operations for IEEE reals see: // Stahl: Interval Methods for Bounding the Range of Polynomials and Solving Systems of Nonlinear Equations, Dissertation, JKU, 1995 [DebuggerDisplay("[{low.Value}..{high.Value}]")] public class AlgebraicInterval : IAlgebraicType { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private MultivariateDual low; public MultivariateDual LowerBound => low.Clone(); [DebuggerBrowsable(DebuggerBrowsableState.Never)] private MultivariateDual high; public MultivariateDual UpperBound => high.Clone(); public AlgebraicInterval() : this(double.NegativeInfinity, double.PositiveInfinity) { } public AlgebraicInterval(MultivariateDual low, MultivariateDual high) { this.low = low.Clone(); this.high = high.Clone(); } public AlgebraicInterval(double low, double high) { this.low = new MultivariateDual(new AlgebraicDouble(low)); this.high = new MultivariateDual(new AlgebraicDouble(high)); } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public AlgebraicInterval Zero => new AlgebraicInterval(0.0, 0.0); [DebuggerBrowsable(DebuggerBrowsableState.Never)] public AlgebraicInterval One => new AlgebraicInterval(1.0, 1.0); public AlgebraicInterval Add(AlgebraicInterval a) { low.Add(a.low); high.Add(a.high); return this; } public AlgebraicInterval Mul(AlgebraicInterval a) { var v1 = low.Clone().Mul(a.low); var v2 = low.Clone().Mul(a.high); var v3 = high.Clone().Mul(a.low); var v4 = high.Clone().Mul(a.high); low = Min(Min(v1, v2), Min(v3, v4)); high = Max(Max(v1, v2), Max(v3, v4)); return this; } private static MultivariateDual Min(MultivariateDual a, MultivariateDual b) { return a.Value < b.Value ? a : b; } private static MultivariateDual Max(MultivariateDual a, MultivariateDual b) { return a.Value > b.Value ? a : b; } public AlgebraicInterval Assign(AlgebraicInterval a) { low = a.low; high = a.high; return this; } public AlgebraicInterval AssignCos(AlgebraicInterval a) { return AssignSin(a.Clone().Add(new AlgebraicInterval(Math.PI / 2, Math.PI / 2))); } public AlgebraicInterval Div(AlgebraicInterval a) { if (a.Contains(0.0)) { if (a.low.Value.Value == 0 && a.high.Value.Value == 0) { if (this.low.Value >= 0) { // pos / 0 } else if (this.high.Value <= 0) { // neg / 0 } else { low = new MultivariateDual(double.NegativeInfinity); high = new MultivariateDual(double.PositiveInfinity); } } else if (a.low.Value.Value >= 0) { // a is positive Mul(new AlgebraicInterval(a.Clone().high.Inv(), new MultivariateDual(double.PositiveInfinity))); } else if (a.high.Value <= 0) { // a is negative Mul(new AlgebraicInterval(new MultivariateDual(double.NegativeInfinity), a.low.Clone().Inv())); } else { // a is interval over zero low = new MultivariateDual(double.NegativeInfinity); high = new MultivariateDual(double.PositiveInfinity); } } else { Mul(new AlgebraicInterval(a.high.Clone().Inv(), a.low.Clone().Inv())); // inverting leads to inverse roles of high and low } return this; } public AlgebraicInterval AssignExp(AlgebraicInterval a) { low.AssignExp(a.low); high.AssignExp(a.high); return this; } // tanh is a bijective function public AlgebraicInterval AssignTanh(AlgebraicInterval a) { low.AssignTanh(a.low); high.AssignTanh(a.high); return this; } public AlgebraicInterval AssignIntPower(AlgebraicInterval a, int p) { if (p < 0) { // x^-3 == 1/(x^3) AssignIntPower(a, -p); return AssignInv(this); } else if (p == 0) { if (a.Contains(0.0)) { // => 0^0 = 0 ; might not be relevant low = new MultivariateDual(0.0); high = new MultivariateDual(1.0); return this; } else { // => 1 low = new MultivariateDual(1.0); high = new MultivariateDual(1.0); return this; } } else if (p == 1) return this; else if (p % 2 == 0) { // p is even => interval must be positive if (a.Contains(0.0)) { low = new MultivariateDual(0.0); high = a.low.IntPower(p).AssignMax(a.high.IntPower(p)); } else { var lowPower = a.low.IntPower(p); var highPower = a.high.IntPower(p); low = lowPower.AssignMin(highPower); high = lowPower.AssignMax(highPower); } } else { // p is uneven if (a.Contains(0.0)) { low.AssignIntPower(a.low, p); high.AssignIntPower(a.high, p); } else { var lowPower = a.low.IntPower(p); var highPower = a.high.IntPower(p); low = lowPower.AssignMin(highPower); high = lowPower.AssignMax(highPower); } } return this; } public AlgebraicInterval AssignIntRoot(AlgebraicInterval a, int r) { if (r == 0) { low = new MultivariateDual(double.NaN); high = new MultivariateDual(double.NaN); return this; } if (r == 1) return this; if (r < 0) { // x^ (-1/2) = 1 / (x^(1/2)) AssignIntRoot(a, -r); return AssignInv(this); } else { // root only defined for positive arguments for even roots if (r % 2 == 0 && a.LowerBound.Value.Value < 0) { low = new MultivariateDual(double.NaN); high = new MultivariateDual(double.NaN); return this; } else { low.AssignIntRoot(a.low, r); high.AssignIntRoot(a.high, r); return this; } } } public AlgebraicInterval AssignInv(AlgebraicInterval a) { low = new MultivariateDual(1.0); high = new MultivariateDual(1.0); return Div(a); } public AlgebraicInterval AssignLog(AlgebraicInterval a) { low.AssignLog(a.low); high.AssignLog(a.high); return this; } public AlgebraicInterval AssignNeg(AlgebraicInterval a) { low.AssignNeg(a.high); high.AssignNeg(a.low); return this; } public AlgebraicInterval Scale(double s) { low.Scale(s); high.Scale(s); if (s < 0) { var t = low; low = high; high = t; } return this; } public AlgebraicInterval AssignSin(AlgebraicInterval a) { var lower = a.LowerBound.Value.Value; var size = a.UpperBound.Value.Value - lower; if (size < 0) throw new InvalidProgramException(); // ASSERT interval >= 0; if (size >= Math.PI * 2) { low = new MultivariateDual(-1.0); // zero gradient high = new MultivariateDual(1.0); return this; } // assume low and high are in the same quadrant low = Algebraic.Min(a.LowerBound.Clone().Sin(), a.UpperBound.Clone().Sin()); high = Algebraic.Max(a.LowerBound.Clone().Sin(), a.UpperBound.Clone().Sin()); // override min and max if necessary // shift interval 'a' into the range [-2pi .. 2pi] without changing the size of the interval to simplify the checks lower = lower % (2 * Math.PI); // lower in [-2pi .. 2pi] // handle min = -1 and max = 1 cases explicitly var pi_2 = Math.PI / 2.0; var maxima = new double[] { -3 * pi_2, pi_2 }; var minima = new double[] { -pi_2, 3 * pi_2 }; // override min and max if necessary if (maxima.Any(m => lower < m && lower + size > m)) { // max = 1 high = new MultivariateDual(1.0); // zero gradient } if (minima.Any(m => lower < m && lower + size > m)) { // min = -1; low = new MultivariateDual(-1.0); // zero gradient } return this; } public AlgebraicInterval Sub(AlgebraicInterval a) { // [x1,x2] − [y1,y2] = [x1 − y2,x2 − y1] low.Sub(a.high); high.Sub(a.low); return this; } public AlgebraicInterval Clone() { return new AlgebraicInterval(low, high); } public bool Contains(double val) { return LowerBound.Value.Value <= val && val <= UpperBound.Value.Value; } public AlgebraicInterval AssignAbs(AlgebraicInterval a) { if (a.Contains(0.0)) { var abslow = a.low.Clone().Abs(); var abshigh = a.high.Clone().Abs(); a.high.Assign(Algebraic.Max(abslow, abshigh)); a.low.Assign(new MultivariateDual(0.0)); // lost gradient for lower bound } else { var abslow = a.low.Clone().Abs(); var abshigh = a.high.Clone().Abs(); a.low.Assign(Algebraic.Min(abslow, abshigh)); a.high.Assign(Algebraic.Max(abslow, abshigh)); } return this; } public AlgebraicInterval AssignSgn(AlgebraicInterval a) { low.AssignSgn(a.low); high.AssignSgn(a.high); return this; } public AlgebraicInterval AssignMin(AlgebraicInterval other) { low.AssignMin(other.low); high.AssignMin(other.high); return this; } public AlgebraicInterval AssignMax(AlgebraicInterval other) { low.AssignMax(other.low); high.AssignMax(other.high); return this; } } public class Dual : IAlgebraicType> where V : IAlgebraicType { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private V v; public V Value => v; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private V dv; public V Derivative => dv; public Dual(V v, V dv) { this.v = v; this.dv = dv; } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public Dual Zero => new Dual(Value.Zero, Derivative.Zero); [DebuggerBrowsable(DebuggerBrowsableState.Never)] public Dual One => new Dual(Value.One, Derivative.Zero); public Dual Assign(Dual a) { v.Assign(a.v); dv.Assign(a.dv); return this; } public Dual Scale(double s) { v.Scale(s); dv.Scale(s); return this; } public Dual Add(Dual a) { v.Add(a.v); dv.Add(a.dv); return this; } public Dual Sub(Dual a) { v.Sub(a.v); dv.Sub(a.dv); return this; } public Dual AssignNeg(Dual a) { v.AssignNeg(a.v); dv.AssignNeg(a.dv); return this; } public Dual AssignInv(Dual a) { v.AssignInv(a.v); dv.AssignNeg(a.dv).Mul(v).Mul(v); return this; } // (1/f(x))' = - f(x)' / f(x)^2 // (a(x) * b(x))' = b(x)*a(x)' + b(x)'*a(x); public Dual Mul(Dual a) { var t1 = a.dv.Clone().Mul(v); var t2 = dv.Clone().Mul(a.v); dv.Assign(t1).Add(t2); v.Mul(a.v); return this; } public Dual Div(Dual a) { Mul(a.Inv()); return this; } public Dual AssignExp(Dual a) { v.AssignExp(a.v); dv.Assign(a.dv).Mul(v); return this; } // exp(f(x)) = exp(f(x))*f(x)' public Dual AssignLog(Dual a) { v.AssignLog(a.v); dv.Assign(a.dv).Div(a.v); return this; } // log(x)' = 1/f(x) * f(x)' public Dual AssignIntPower(Dual a, int p) { v.AssignIntPower(a.v, p); dv.Assign(a.dv).Scale(p).Mul(a.v.Clone().IntPower(p - 1)); return this; } public Dual AssignIntRoot(Dual a, int r) { v.AssignIntRoot(a.v, r); dv.Assign(a.dv).Scale(1.0 / r).Mul(a.v.IntRoot(r - 1)); return this; } public Dual AssignSin(Dual a) { v.AssignSin(a.v); dv.Assign(a.dv).Mul(a.v.Clone().Cos()); return this; } public Dual AssignCos(Dual a) { v.AssignCos(a.v); dv.AssignNeg(a.dv).Mul(a.v.Clone().Sin()); return this; } public Dual AssignTanh(Dual a) { v.AssignTanh(a.v); dv.Assign(a.dv.Mul(v.Clone().IntPower(2).Neg().Add(Value.One))); return this; } public Dual AssignAbs(Dual a) { v.AssignAbs(a.v); dv.Assign(a.dv).Mul(a.v.Clone().Sgn()); return this; } // abs(f(x))' = f(x)*f'(x) / |f(x)| public Dual AssignSgn(Dual a) { v.AssignSgn(a.v); dv.Assign(a.dv.Zero); return this; } public Dual Clone() { return new Dual(v.Clone(), dv.Clone()); } public Dual AssignMin(Dual other) { throw new NotImplementedException(); } public Dual AssignMax(Dual other) { throw new NotImplementedException(); } } /// /// An algebraic type which has a value as well as the partial derivatives of the value over multiple variables. /// /// [DebuggerDisplay("v={Value}; dv={dv}")] public class MultivariateDual : IAlgebraicType> where V : IAlgebraicType, new() { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private V v; public V Value => v; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private AlgebraicSparseVector dv; public AlgebraicSparseVector Gradient => dv; // partial derivative identified via the key private MultivariateDual(MultivariateDual orig) { this.v = orig.v.Clone(); this.dv = orig.dv.Clone(); } /// /// Constructor without partial derivative /// /// public MultivariateDual(V v) { this.v = v.Clone(); this.dv = new AlgebraicSparseVector(); } /// /// Constructor for multiple partial derivatives /// /// /// /// public MultivariateDual(V v, object[] keys, V[] dv) { this.v = v.Clone(); this.dv = new AlgebraicSparseVector(keys, dv); } /// /// Constructor for a single partial derivative /// /// /// /// public MultivariateDual(V v, object key, V dv) { this.v = v.Clone(); this.dv = new AlgebraicSparseVector(new[] { key }, new[] { dv }); } /// /// Constructor with a given value and gradient. For internal use. /// /// The value (not cloned). /// The gradient (not cloned). internal MultivariateDual(V v, AlgebraicSparseVector gradient) { this.v = v; this.dv = gradient; } public MultivariateDual Clone() { return new MultivariateDual(this); } public MultivariateDual Zero => new MultivariateDual(Value.Zero, Gradient.Zero); public MultivariateDual One => new MultivariateDual(Value.One, Gradient.Zero); public MultivariateDual Scale(double s) { v.Scale(s); dv.Scale(s); return this; } public MultivariateDual Add(MultivariateDual a) { v.Add(a.v); dv.Add(a.dv); return this; } public MultivariateDual Sub(MultivariateDual a) { v.Sub(a.v); dv.Sub(a.dv); return this; } public MultivariateDual Assign(MultivariateDual a) { v.Assign(a.v); dv.Assign(a.dv); return this; } public MultivariateDual Mul(MultivariateDual a) { // (a(x) * b(x))' = b(x)*a(x)' + b(x)'*a(x); var t1 = a.dv.Clone().Scale(v); var t2 = dv.Clone().Scale(a.v); dv.Assign(t1).Add(t2); v.Mul(a.v); return this; } public MultivariateDual Div(MultivariateDual a) { v.Div(a.v); dv.Mul(a.dv.Inv()); return this; } public MultivariateDual AssignNeg(MultivariateDual a) { v.AssignNeg(a.v); dv.AssignNeg(a.dv); return this; } public MultivariateDual AssignInv(MultivariateDual a) { v.AssignInv(a.v); dv.AssignNeg(a.dv).Scale(v).Scale(v); return this; } // (1/f(x))' = - f(x)' / f(x)^2 public MultivariateDual AssignSin(MultivariateDual a) { v.AssignSin(a.v); dv.Assign(a.dv).Scale(a.v.Clone().Cos()); return this; } public MultivariateDual AssignCos(MultivariateDual a) { v.AssignCos(a.v); dv.AssignNeg(a.dv).Scale(a.v.Clone().Sin()); return this; } public MultivariateDual AssignTanh(MultivariateDual a) { v.AssignTanh(a.v); dv.Assign(a.dv.Scale(v.Clone().IntPower(2).Neg().Add(Value.One))); return this; } // tanh(f(x))' = f(x)'sech²(f(x)) = f(x)'(1 - tanh²(f(x))) public MultivariateDual AssignIntPower(MultivariateDual a, int p) { v.AssignIntPower(a.v, p); dv.Assign(a.dv).Scale(p).Scale(a.v.Clone().IntPower(p - 1)); return this; } public MultivariateDual AssignIntRoot(MultivariateDual a, int r) { v.AssignIntRoot(a.v, r); dv.Assign(a.dv).Scale(1.0 / r).Scale(a.v.IntRoot(r - 1)); return this; } public MultivariateDual AssignExp(MultivariateDual a) { v.AssignExp(a.v); dv.Assign(a.dv).Scale(v); return this; } // exp(f(x)) = exp(f(x))*f(x)' public MultivariateDual AssignLog(MultivariateDual a) { v.AssignLog(a.v); dv.Assign(a.dv).Scale(a.v.Clone().Inv()); return this; } // log(x)' = 1/f(x) * f(x)' public MultivariateDual AssignAbs(MultivariateDual a) { v.AssignAbs(a.v); dv.Assign(a.dv).Scale(a.v.Clone().Sgn()); return this; } // abs(f(x))' = f(x)*f'(x) / |f(x)| doesn't work for intervals public MultivariateDual AssignSgn(MultivariateDual a) { v.AssignSgn(a.v); dv = a.dv.Zero; return this; } // sign(f(x))' = 0; public MultivariateDual AssignMin(MultivariateDual other) { XXX } public MultivariateDual AssignMax(MultivariateDual other) { XXX } } }