using System;
using System.Diagnostics;
namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
///
/// 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 which sets value and derivative to zero
///
public MultivariateDual() {
v = new V(); // assumed to be zero
dv = new AlgebraicSparseVector();
}
///
/// 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) { Mul(a.Inv()); return this; } // f(x) / g(x)
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(a.v.IntPower(2).Inv()); 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).IntPower(r - 1).Inv()); 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;
}
}