1 module lbfgsd.cost; 2 3 private import lbfgsd.autodiff; 4 5 /** 6 *Basic Cost Function 7 */ 8 abstract class CostFunction(T, size_t NInput) 9 { 10 abstract T evaluate(const scope T[] x, scope T[] gradient) 11 in 12 { 13 assert(x.length == NInput); 14 assert(gradient.length == 0 || gradient.length == NInput); 15 } 16 do 17 { 18 return T.init; 19 } 20 } 21 22 /** 23 *Automatic Differentation 24 */ 25 final class AutoDiffCostFunction(TFunc, T, size_t NInput) : CostFunction!(T, NInput) 26 { 27 public: 28 this(TFunc func) 29 { 30 _func = func; 31 _x.length = NInput; 32 } 33 34 public: 35 override T evaluate(const scope T[] x, scope T[] gradient) 36 { 37 if (gradient == null || gradient.length == 0) 38 return _func(x); 39 40 assert(gradient.length == NInput); 41 foreach (i; 0 .. NInput) 42 { 43 _x[i].a = x[i]; 44 _x[i].d[] = T(0); 45 _x[i].d[i] = T(1); 46 } 47 48 auto y = _func(_x); 49 gradient[] = y.d[]; 50 return y.a; 51 } 52 53 private: 54 TFunc _func; 55 Variable!(T, NInput)[] _x; 56 } 57 58 unittest 59 { 60 import lbfgsd.functions; 61 62 RosenBrockFunction fn; 63 CostFunction!(double, 2) cost = new AutoDiffCostFunction!(RosenBrockFunction, double, 2)(fn); 64 65 auto x = new double[2]; 66 x[0] = 1; 67 x[1] = 1; 68 auto y = cost.evaluate(x, null); 69 assert(y == 0); 70 } 71 unittest 72 { 73 import lbfgsd.functions; 74 75 RosenBrockFunction fn; 76 CostFunction!(double, 2) cost = new AutoDiffCostFunction!(RosenBrockFunction, double, 2)(fn); 77 78 auto x = new double[2]; 79 x[0] = 1; 80 x[1] = 1; 81 auto g = new double[2]; 82 g[] = 0; 83 auto y = cost.evaluate(x, g); 84 assert(y == 0); 85 assert(g[0] == 0); 86 assert(g[1] == 0); 87 } 88 89 /** 90 *Numeric Differentation 91 */ 92 final class NumericDiffCostFunction(TFunc, T, size_t NInput) : CostFunction!(T, NInput) 93 { 94 public: 95 this(TFunc func) 96 { 97 _func = func; 98 _x.length = NInput; 99 } 100 101 public: 102 override T evaluate(const scope T[] x, scope T[] gradient) 103 { 104 if (gradient == null || gradient.length == 0) 105 return _func(x); 106 107 assert(gradient.length == NInput); 108 enum eps = 1e-8; 109 enum neps = 0.5 / eps; 110 _x[] = x[]; 111 foreach (i; 0 .. NInput) 112 { 113 auto org = _x[i]; 114 _x[i] = org + eps; 115 auto f1 = _func(_x); 116 _x[i] = org - eps; 117 auto f2 = _func(_x); 118 _x[i] = org; 119 gradient[i] = (f1 - f2) * neps; 120 } 121 return _func(x); 122 } 123 124 private: 125 TFunc _func; 126 T[] _x; 127 } 128 129 unittest 130 { 131 import lbfgsd.functions; 132 133 RosenBrockFunction fn; 134 CostFunction!(double, 2) cost = new NumericDiffCostFunction!(RosenBrockFunction, double, 2)(fn); 135 136 auto x = new double[2]; 137 x[0] = 1; 138 x[1] = 1; 139 auto y = cost.evaluate(x, null); 140 assert(y == 0); 141 } 142 143 unittest 144 { 145 import lbfgsd.functions; 146 import std.math: approxEqual; 147 148 RosenBrockFunction fn; 149 CostFunction!(double, 2) cost = new NumericDiffCostFunction!(RosenBrockFunction, double, 2)(fn); 150 151 auto x = new double[2]; 152 x[0] = 1; 153 x[1] = 1; 154 auto g = new double[2]; 155 g[] = 0; 156 auto y = cost.evaluate(x, g); 157 assert(y == 0); 158 assert(approxEqual(g[0], 0)); 159 assert(approxEqual(g[1], 0)); 160 }