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 }