1 // Copyright (C) 2016-2018 T. Zachary Laine
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 #include "autodiff.h"
7 
8 #include <iostream>
9 
10 #include <boost/yap/algorithm.hpp>
11 #include <boost/polymorphic_cast.hpp>
12 #include <boost/hana/for_each.hpp>
13 
14 #define BOOST_TEST_MODULE autodiff_test
15 #include <boost/test/included/unit_test.hpp>
16 
17 
18 double const Epsilon = 10.0e-6;
19 #define CHECK_CLOSE(A,B) do { BOOST_CHECK_CLOSE(A,B,Epsilon); } while(0)
20 
21 using namespace AutoDiff;
22 
23 //[ autodiff_expr_template_decl
24 template <boost::yap::expr_kind Kind, typename Tuple>
25 struct autodiff_expr
26 {
27     static boost::yap::expr_kind const kind = Kind;
28 
29     Tuple elements;
30 };
31 
32 BOOST_YAP_USER_UNARY_OPERATOR(negate, autodiff_expr, autodiff_expr)
33 
34 BOOST_YAP_USER_BINARY_OPERATOR(plus, autodiff_expr, autodiff_expr)
35 BOOST_YAP_USER_BINARY_OPERATOR(minus, autodiff_expr, autodiff_expr)
36 BOOST_YAP_USER_BINARY_OPERATOR(multiplies, autodiff_expr, autodiff_expr)
37 BOOST_YAP_USER_BINARY_OPERATOR(divides, autodiff_expr, autodiff_expr)
38 //]
39 
40 //[ autodiff_expr_literals_decl
41 namespace autodiff_placeholders {
42 
43     // This defines a placeholder literal operator that creates autodiff_expr
44     // placeholders.
45     BOOST_YAP_USER_LITERAL_PLACEHOLDER_OPERATOR(autodiff_expr)
46 
47 }
48 //]
49 
50 //[ autodiff_function_terminals
51 template <OPCODE Opcode>
52 struct autodiff_fn_expr :
53     autodiff_expr<boost::yap::expr_kind::terminal, boost::hana::tuple<OPCODE>>
54 {
autodiff_fn_exprautodiff_fn_expr55     autodiff_fn_expr () :
56         autodiff_expr {boost::hana::tuple<OPCODE>{Opcode}}
57     {}
58 
59     BOOST_YAP_USER_CALL_OPERATOR_N(::autodiff_expr, 1);
60 };
61 
62 // Someone included <math.h>, so we have to add trailing underscores.
63 autodiff_fn_expr<OP_SIN> const sin_;
64 autodiff_fn_expr<OP_COS> const cos_;
65 autodiff_fn_expr<OP_SQRT> const sqrt_;
66 //]
67 
68 //[ autodiff_xform
69 struct xform
70 {
71     // Create a var-node for each placeholder when we see it for the first
72     // time.
73     template <long long I>
operator ()xform74     Node * operator() (boost::yap::expr_tag<boost::yap::expr_kind::terminal>,
75                        boost::yap::placeholder<I>)
76     {
77         if (list_.size() < I)
78             list_.resize(I);
79         auto & retval = list_[I - 1];
80         if (retval == nullptr)
81             retval = create_var_node();
82         return retval;
83     }
84 
85     // Create a param-node for every numeric terminal in the expression.
operator ()xform86     Node * operator() (boost::yap::expr_tag<boost::yap::expr_kind::terminal>, double x)
87     { return create_param_node(x); }
88 
89     // Create a "uary" node for each call expression, using its OPCODE.
90     template <typename Expr>
operator ()xform91     Node * operator() (boost::yap::expr_tag<boost::yap::expr_kind::call>,
92                        OPCODE opcode, Expr const & expr)
93     {
94         return create_uary_op_node(
95             opcode,
96             boost::yap::transform(boost::yap::as_expr<autodiff_expr>(expr), *this)
97         );
98     }
99 
100     template <typename Expr>
operator ()xform101     Node * operator() (boost::yap::expr_tag<boost::yap::expr_kind::negate>,
102                        Expr const & expr)
103     {
104         return create_uary_op_node(
105             OP_NEG,
106             boost::yap::transform(boost::yap::as_expr<autodiff_expr>(expr), *this)
107         );
108     }
109 
110     // Define a mapping from binary arithmetic expr_kind to OPCODE...
op_for_kindxform111     static OPCODE op_for_kind (boost::yap::expr_kind kind)
112     {
113         switch (kind) {
114         case boost::yap::expr_kind::plus: return OP_PLUS;
115         case boost::yap::expr_kind::minus: return OP_MINUS;
116         case boost::yap::expr_kind::multiplies: return OP_TIMES;
117         case boost::yap::expr_kind::divides: return OP_DIVID;
118         default: assert(!"This should never execute"); return OPCODE{};
119         }
120         assert(!"This should never execute");
121         return OPCODE{};
122     }
123 
124     // ... and use it to handle all the binary arithmetic operators.
125     template <boost::yap::expr_kind Kind, typename Expr1, typename Expr2>
operator ()xform126     Node * operator() (boost::yap::expr_tag<Kind>, Expr1 const & expr1, Expr2 const & expr2)
127     {
128         return create_binary_op_node(
129             op_for_kind(Kind),
130             boost::yap::transform(boost::yap::as_expr<autodiff_expr>(expr1), *this),
131             boost::yap::transform(boost::yap::as_expr<autodiff_expr>(expr2), *this)
132         );
133     }
134 
135     vector<Node *> & list_;
136 };
137 //]
138 
139 //[ autodiff_to_node
140 template <typename Expr, typename ...T>
to_auto_diff_node(Expr const & expr,vector<Node * > & list,T...args)141 Node * to_auto_diff_node (Expr const & expr, vector<Node *> & list, T ... args)
142 {
143     Node * retval = nullptr;
144 
145     // This fills in list as a side effect.
146     retval = boost::yap::transform(expr, xform{list});
147 
148     assert(list.size() == sizeof...(args));
149 
150     // Fill in the values of the value-nodes in list with the "args"
151     // parameter pack.
152     auto it = list.begin();
153     boost::hana::for_each(
154         boost::hana::make_tuple(args ...),
155         [&it](auto x) {
156             Node * n = *it;
157             VNode * v = boost::polymorphic_downcast<VNode *>(n);
158             v->val = x;
159             ++it;
160         }
161     );
162 
163     return retval;
164 }
165 //]
166 
167 struct F{
FF168 	F() {	AutoDiff::autodiff_setup();	}
~FF169 	~F(){	AutoDiff::autodiff_cleanup();	}
170 };
171 
172 
BOOST_FIXTURE_TEST_SUITE(all,F)173 BOOST_FIXTURE_TEST_SUITE(all, F)
174 
175 //[ autodiff_original_node_builder
176 Node* build_linear_fun1_manually(vector<Node*>& list)
177 {
178 	//f(x1,x2,x3) = -5*x1+sin(10)*x1+10*x2-x3/6
179 	PNode* v5 = create_param_node(-5);
180 	PNode* v10 = create_param_node(10);
181 	PNode* v6 = create_param_node(6);
182 	VNode*	x1 = create_var_node();
183 	VNode*	x2 = create_var_node();
184 	VNode*	x3 = create_var_node();
185 
186 	OPNode* op1 = create_binary_op_node(OP_TIMES,v5,x1);   //op1 = v5*x1
187 	OPNode* op2 = create_uary_op_node(OP_SIN,v10);         //op2 = sin(v10)
188 	OPNode* op3 = create_binary_op_node(OP_TIMES,op2,x1);  //op3 = op2*x1
189 	OPNode* op4 = create_binary_op_node(OP_PLUS,op1,op3);  //op4 = op1 + op3
190 	OPNode*	op5 = create_binary_op_node(OP_TIMES,v10,x2);  //op5 = v10*x2
191 	OPNode* op6 = create_binary_op_node(OP_PLUS,op4,op5);  //op6 = op4+op5
192 	OPNode* op7 = create_binary_op_node(OP_DIVID,x3,v6);   //op7 = x3/v6
193 	OPNode* op8 = create_binary_op_node(OP_MINUS,op6,op7); //op8 = op6 - op7
194 	x1->val = -1.9;
195 	x2->val = 2;
196 	x3->val = 5./6.;
197 	list.push_back(x1);
198 	list.push_back(x2);
199 	list.push_back(x3);
200 	return op8;
201 }
202 //]
203 
204 //[ autodiff_yap_node_builder
build_linear_fun1(vector<Node * > & list)205 Node* build_linear_fun1(vector<Node*>& list)
206 {
207     //f(x1,x2,x3) = -5*x1+sin(10)*x1+10*x2-x3/6
208     using namespace autodiff_placeholders;
209     return to_auto_diff_node(
210         -5 * 1_p + sin_(10) * 1_p + 10 * 2_p - 3_p / 6,
211         list,
212         -1.9,
213         2,
214         5./6.
215     );
216 }
217 //]
218 
build_linear_function2_manually(vector<Node * > & list)219 Node* build_linear_function2_manually(vector<Node*>& list)
220 {
221 	//f(x1,x2,x3) = -5*x1+-10*x1+10*x2-x3/6
222 	PNode* v5 = create_param_node(-5);
223 	PNode* v10 = create_param_node(10);
224 	PNode* v6 = create_param_node(6);
225 	VNode*	x1 = create_var_node();
226 	VNode*	x2 = create_var_node();
227 	VNode*	x3 = create_var_node();
228 	list.push_back(x1);
229 	list.push_back(x2);
230 	list.push_back(x3);
231 	OPNode* op1 = create_binary_op_node(OP_TIMES,v5,x1); //op1 = v5*x1
232 	OPNode* op2 = create_uary_op_node(OP_NEG,v10);       //op2 = -v10
233 	OPNode* op3 = create_binary_op_node(OP_TIMES,op2,x1);//op3 = op2*x1
234 	OPNode* op4 = create_binary_op_node(OP_PLUS,op1,op3);//op4 = op1 + op3
235 	OPNode*	op5 = create_binary_op_node(OP_TIMES,v10,x2);//op5 = v10*x2
236 	OPNode* op6 = create_binary_op_node(OP_PLUS,op4,op5);//op6 = op4+op5
237 	OPNode* op7 = create_binary_op_node(OP_DIVID,x3,v6); //op7 = x3/v6
238 	OPNode* op8 = create_binary_op_node(OP_MINUS,op6,op7);//op8 = op6 - op7
239 	x1->val = -1.9;
240 	x2->val = 2;
241 	x3->val = 5./6.;
242 	return op8;
243 }
244 
build_linear_function2(vector<Node * > & list)245 Node* build_linear_function2(vector<Node*>& list)
246 {
247     //f(x1,x2,x3) = -5*x1+-10*x1+10*x2-x3/6
248     using namespace autodiff_placeholders;
249     auto ten = boost::yap::make_terminal<autodiff_expr>(10);
250     return to_auto_diff_node(
251         -5 * 1_p + -ten * 1_p + 10 * 2_p - 3_p / 6,
252         list,
253         -1.9,
254         2,
255         5./6.
256     );
257 }
258 
build_nl_function1_manually(vector<Node * > & list)259 Node* build_nl_function1_manually(vector<Node*>& list)
260 {
261 //	(x1*x2 * sin(x1))/x3 + x2*x4 - x1/x2
262 	VNode* x1 = create_var_node();
263 	VNode* x2 = create_var_node();
264 	VNode* x3 = create_var_node();
265 	VNode* x4 = create_var_node();
266 	x1->val = -1.23;
267 	x2->val = 7.1231;
268 	x3->val = 2;
269 	x4->val = -10;
270 	list.push_back(x1);
271 	list.push_back(x2);
272 	list.push_back(x3);
273 	list.push_back(x4);
274 
275 	OPNode* op1 = create_binary_op_node(OP_TIMES,x2,x1);
276 	OPNode* op2 = create_uary_op_node(OP_SIN,x1);
277 	OPNode* op3 = create_binary_op_node(OP_TIMES,op1,op2);
278 	OPNode* op4 = create_binary_op_node(OP_DIVID,op3,x3);
279 	OPNode* op5 = create_binary_op_node(OP_TIMES,x2,x4);
280 	OPNode* op6 = create_binary_op_node(OP_PLUS,op4,op5);
281 	OPNode* op7 = create_binary_op_node(OP_DIVID,x1,x2);
282 	OPNode* op8 = create_binary_op_node(OP_MINUS,op6,op7);
283 	return op8;
284 }
285 
build_nl_function1(vector<Node * > & list)286 Node* build_nl_function1(vector<Node*>& list)
287 {
288     // (x1*x2 * sin(x1))/x3 + x2*x4 - x1/x2
289     using namespace autodiff_placeholders;
290     return to_auto_diff_node(
291         (1_p * 2_p * sin_(1_p)) / 3_p + 2_p * 4_p - 1_p / 2_p,
292         list,
293 	-1.23,
294 	7.1231,
295 	2,
296 	-10
297     );
298 }
299 
BOOST_AUTO_TEST_CASE(test_linear_fun1)300 BOOST_AUTO_TEST_CASE( test_linear_fun1 )
301 {
302 	BOOST_TEST_MESSAGE("test_linear_fun1");
303 	vector<Node*> list;
304 	Node* root = build_linear_fun1(list);
305 	vector<double> grad;
306 	double val1 = grad_reverse(root,list,grad);
307 	double val2 = eval_function(root);
308 	double x1g[] = {-5.5440211108893697744548489936278,10.0,-0.16666666666666666666666666666667};
309 
310 	for(unsigned int i=0;i<3;i++){
311 		CHECK_CLOSE(grad[i],x1g[i]);
312 	}
313 
314 	double eval = 30.394751221800913;
315 
316 	CHECK_CLOSE(val1,eval);
317 	CHECK_CLOSE(val2,eval);
318 
319 
320 	EdgeSet s;
321 	nonlinearEdges(root,s);
322 	unsigned int n = nzHess(s);
323 	BOOST_CHECK_EQUAL(n,0);
324 }
325 
BOOST_AUTO_TEST_CASE(test_grad_sin)326 BOOST_AUTO_TEST_CASE( test_grad_sin )
327 {
328 	BOOST_TEST_MESSAGE("test_grad_sin");
329 	VNode* x1 = create_var_node();
330 	x1->val = 10;
331 	OPNode* root = create_uary_op_node(OP_SIN,x1);
332 	vector<Node*> nodes;
333 	nodes.push_back(x1);
334 	vector<double> grad;
335 	grad_reverse(root,nodes,grad);
336 	double x1g =  -0.83907152907645244;
337 	//the matlab give cos(10) = -0.839071529076452
338 
339 	CHECK_CLOSE(grad[0],x1g);
340 	BOOST_CHECK_EQUAL(nodes.size(),1);
341 
342 	EdgeSet s;
343 	nonlinearEdges(root,s);
344 	unsigned int n = nzHess(s);
345 	BOOST_CHECK_EQUAL(n,1);
346 }
347 
BOOST_AUTO_TEST_CASE(test_grad_single_node)348 BOOST_AUTO_TEST_CASE(test_grad_single_node)
349 {
350 	VNode* x1 = create_var_node();
351 	x1->val = -2;
352 	vector<Node*> nodes;
353 	nodes.push_back(x1);
354 	vector<double> grad;
355 	double val = grad_reverse(x1,nodes,grad);
356 	CHECK_CLOSE(grad[0],1);
357 	CHECK_CLOSE(val,-2);
358 
359 	EdgeSet s;
360 	unsigned int n = 0;
361 	nonlinearEdges(x1,s);
362 	n = nzHess(s);
363 	BOOST_CHECK_EQUAL(n,0);
364 
365 	grad.clear();
366 	nodes.clear();
367 	PNode* p = create_param_node(-10);
368 	//OPNode*	op = create_binary_op_node(TIMES,p,create_param_node(2));
369 	val = grad_reverse(p,nodes,grad);
370 	BOOST_CHECK_EQUAL(grad.size(),0);
371 	CHECK_CLOSE(val,-10);
372 
373 	s.clear();
374 	nonlinearEdges(p,s);
375 	n = nzHess(s);
376 	BOOST_CHECK_EQUAL(n,0);
377 }
378 
BOOST_AUTO_TEST_CASE(test_grad_neg)379 BOOST_AUTO_TEST_CASE(test_grad_neg)
380 {
381 	VNode* x1 = create_var_node();
382 	x1->val = 10;
383 	PNode* p2 = create_param_node(-1);
384 	vector<Node*> nodes;
385 	vector<double> grad;
386 	nodes.push_back(x1);
387 	Node* root = create_binary_op_node(OP_TIMES,x1,p2);
388 	grad_reverse(root,nodes,grad);
389 	CHECK_CLOSE(grad[0],-1);
390 	BOOST_CHECK_EQUAL(nodes.size(),1);
391 	nodes.clear();
392 	grad.clear();
393 	nodes.push_back(x1);
394 	root = create_uary_op_node(OP_NEG,x1);
395 	grad_reverse(root,nodes,grad);
396 	CHECK_CLOSE(grad[0],-1);
397 
398 	EdgeSet s;
399 	unsigned int n = 0;
400 	nonlinearEdges(root,s);
401 	n = nzHess(s);
402 	BOOST_CHECK_EQUAL(n,0);
403 }
404 
BOOST_AUTO_TEST_CASE(test_nl_function)405 BOOST_AUTO_TEST_CASE( test_nl_function)
406 {
407 	vector<Node*> list;
408 	Node* root = build_nl_function1(list);
409 	double val = eval_function(root);
410 	vector<double> grad;
411 	grad_reverse(root,list,grad);
412 	double eval =-66.929555552886214;
413 	double gx[] = {-4.961306690356109,-9.444611307649055,-2.064383410399700,7.123100000000000};
414 	CHECK_CLOSE(val,eval);
415 
416 	for(unsigned int i=0;i<4;i++)
417 	{
418 		CHECK_CLOSE(grad[i],gx[i]);
419 	}
420 	unsigned int nzgrad = nzGrad(root);
421 	unsigned int tol = numTotalNodes(root);
422 	BOOST_CHECK_EQUAL(nzgrad,4);
423 	BOOST_CHECK_EQUAL(tol,16);
424 
425 	EdgeSet s;
426 	nonlinearEdges(root,s);
427 	unsigned int n = nzHess(s);
428 	BOOST_CHECK_EQUAL(n,11);
429 }
430 
BOOST_AUTO_TEST_CASE(test_hess_reverse_1)431 BOOST_AUTO_TEST_CASE( test_hess_reverse_1)
432 {
433 	vector<Node*> nodes;
434 	Node* root = build_linear_fun1(nodes);
435 	vector<double> grad;
436 	double val = grad_reverse(root,nodes,grad);
437 	double eval = eval_function(root);
438 //	cout<<eval<<"\t"<<grad[0]<<"\t"<<grad[1]<<"\t"<<grad[2]<<"\t"<<endl;
439 
440 	CHECK_CLOSE(val,eval);
441 
442 	for(unsigned int i=0;i<nodes.size();i++)
443 	{
444 		static_cast<VNode*>(nodes[i])->u = 0;
445 	}
446 
447 	static_cast<VNode*>(nodes[0])->u = 1;
448 	double hval = 0;
449 	vector<double> dhess;
450 	hval = hess_reverse(root,nodes,dhess);
451 	CHECK_CLOSE(hval,eval);
452 	for(unsigned int i=0;i<dhess.size();i++)
453 	{
454 		CHECK_CLOSE(dhess[i],0);
455 	}
456 }
457 
BOOST_AUTO_TEST_CASE(test_hess_reverse_2)458 BOOST_AUTO_TEST_CASE( test_hess_reverse_2)
459 {
460 	vector<Node*> nodes;
461 	Node* root = build_linear_function2(nodes);
462 	vector<double> grad;
463 	double val = grad_reverse(root,nodes,grad);
464 	double eval = eval_function(root);
465 
466 	CHECK_CLOSE(val,eval);
467 	for(unsigned int i=0;i<nodes.size();i++)
468 	{
469 		static_cast<VNode*>(nodes[i])->u = 0;
470 	}
471 
472 	static_cast<VNode*>(nodes[0])->u = 1;
473 	double hval = 0;
474 	vector<double> dhess;
475 	hval = hess_reverse(root,nodes,dhess);
476 	CHECK_CLOSE(hval,eval);
477 
478 	for(unsigned int i=0;i<dhess.size();i++)
479 	{
480 		CHECK_CLOSE(dhess[i],0);
481 	}
482 
483 	EdgeSet s;
484 	nonlinearEdges(root,s);
485 	unsigned int n = nzHess(s);
486 	BOOST_CHECK_EQUAL(n,0);
487 }
488 
BOOST_AUTO_TEST_CASE(test_hess_reverse_4)489 BOOST_AUTO_TEST_CASE( test_hess_reverse_4)
490 {
491 	vector<Node*> nodes;
492 //	Node* root = build_nl_function1(nodes);
493 
494 	VNode* x1 = create_var_node();
495 	nodes.push_back(x1);
496 	x1->val = 1;
497 	x1->u =1;
498 	Node* op = create_uary_op_node(OP_SIN,x1);
499 	Node* root = create_uary_op_node(OP_SIN,op);
500 	vector<double> grad;
501 	double eval = eval_function(root);
502 	vector<double> dhess;
503 	double hval = hess_reverse(root,nodes,dhess);
504 	CHECK_CLOSE(hval,eval);
505 	BOOST_CHECK_EQUAL(dhess.size(),1);
506 	CHECK_CLOSE(dhess[0], -0.778395788418109);
507 
508 	EdgeSet s;
509 	nonlinearEdges(root,s);
510 	unsigned int n = nzHess(s);
511 	BOOST_CHECK_EQUAL(n,1);
512 }
513 
BOOST_AUTO_TEST_CASE(test_hess_reverse_3)514 BOOST_AUTO_TEST_CASE( test_hess_reverse_3)
515 {
516 	vector<Node*> nodes;
517 	VNode* x1 = create_var_node();
518 	VNode* x2 = create_var_node();
519 	nodes.push_back(x1);
520 	nodes.push_back(x2);
521 	x1->val = 2.5;
522 	x2->val = -9;
523 	Node* op1 = create_binary_op_node(OP_TIMES,x1,x2);
524 	Node* root = create_binary_op_node(OP_TIMES,x1,op1);
525 	double eval = eval_function(root);
526 	for(unsigned int i=0;i<nodes.size();i++)
527 	{
528 		static_cast<VNode*>(nodes[i])->u = 0;
529 	}
530 	static_cast<VNode*>(nodes[0])->u = 1;
531 
532 	vector<double> dhess;
533 	double hval = hess_reverse(root,nodes,dhess);
534 	BOOST_CHECK_EQUAL(dhess.size(),2);
535 	CHECK_CLOSE(hval,eval);
536 	double hx[]={-18,5};
537 	for(unsigned int i=0;i<dhess.size();i++)
538 	{
539 		//Print("\t["<<i<<"]="<<dhess[i]);
540 		CHECK_CLOSE(dhess[i],hx[i]);
541 	}
542 
543 	EdgeSet s;
544 	nonlinearEdges(root,s);
545 	unsigned int n = nzHess(s);
546 	BOOST_CHECK_EQUAL(n,3);
547 }
548 
BOOST_AUTO_TEST_CASE(test_hess_reverse_5)549 BOOST_AUTO_TEST_CASE( test_hess_reverse_5)
550 {
551 	vector<Node*> nodes;
552 	VNode* x1 = create_var_node();
553 	VNode* x2 = create_var_node();
554 	nodes.push_back(x1);
555 	nodes.push_back(x2);
556 	x1->val = 2.5;
557 	x2->val = -9;
558 	Node* op1 = create_binary_op_node(OP_TIMES,x1,x1);
559 	Node* op2 = create_binary_op_node(OP_TIMES,x2,x2);
560 	Node* op3 = create_binary_op_node(OP_MINUS,op1,op2);
561 	Node* op4 = create_binary_op_node(OP_PLUS,op1,op2);
562 	Node* root = create_binary_op_node(OP_TIMES,op3,op4);
563 
564 	double eval = eval_function(root);
565 
566 	for(unsigned int i=0;i<nodes.size();i++)
567 	{
568 		static_cast<VNode*>(nodes[i])->u = 0;
569 	}
570 	static_cast<VNode*>(nodes[0])->u = 1;
571 
572 	vector<double> dhess;
573 	double hval = hess_reverse(root,nodes,dhess);
574 	CHECK_CLOSE(hval,eval);
575 	double hx[] ={75,0};
576 	for(unsigned int i=0;i<dhess.size();i++)
577 	{
578 		CHECK_CLOSE(dhess[i],hx[i]);
579 	}
580 
581 	for(unsigned int i=0;i<nodes.size();i++)
582 	{
583 		static_cast<VNode*>(nodes[i])->u = 0;
584 	}
585 	static_cast<VNode*>(nodes[1])->u = 1;
586 
587 	double hx2[] = {0, -972};
588 	hval = hess_reverse(root,nodes,dhess);
589 	for(unsigned int i=0;i<dhess.size();i++)
590 	{
591 		CHECK_CLOSE(dhess[i],hx2[i]);
592 	}
593 
594 	EdgeSet s;
595 	nonlinearEdges(root,s);
596 	unsigned int n = nzHess(s);
597 	BOOST_CHECK_EQUAL(n,4);
598 }
BOOST_AUTO_TEST_CASE(test_hess_reverse_6)599 BOOST_AUTO_TEST_CASE( test_hess_reverse_6)
600 {
601 	vector<Node*> nodes;
602 //	Node* root = build_nl_function1(nodes);
603 
604 	VNode* x1 = create_var_node();
605 	VNode* x2 = create_var_node();
606 	nodes.push_back(x1);
607 	nodes.push_back(x2);
608 	x1->val = 2.5;
609 	x2->val = -9;
610 	Node* root = create_binary_op_node(OP_POW,x1,x2);
611 
612 	double eval = eval_function(root);
613 
614 	static_cast<VNode*>(nodes[0])->u=1;static_cast<VNode*>(nodes[1])->u=0;
615 	vector<double> dhess;
616 	double hval = hess_reverse(root,nodes,dhess);
617 	CHECK_CLOSE(hval,eval);
618 	double hx1[] ={0.003774873600000 , -0.000759862823419};
619 	double hx2[] ={-0.000759862823419, 0.000220093141567};
620 	for(unsigned int i=0;i<dhess.size();i++)
621 	{
622 		CHECK_CLOSE(dhess[i],hx1[i]);
623 	}
624 	static_cast<VNode*>(nodes[0])->u=0;static_cast<VNode*>(nodes[1])->u=1;
625 	hess_reverse(root,nodes,dhess);
626 	for(unsigned int i=0;i<dhess.size();i++)
627 	{
628 		CHECK_CLOSE(dhess[i],hx2[i]);
629 	}
630 
631 	EdgeSet s;
632 	nonlinearEdges(root,s);
633 	unsigned int n = nzHess(s);
634 	BOOST_CHECK_EQUAL(n,4);
635 }
636 
BOOST_AUTO_TEST_CASE(test_hess_reverse_7)637 BOOST_AUTO_TEST_CASE( test_hess_reverse_7)
638 {
639 	vector<Node*> nodes;
640 	Node* root = build_nl_function1(nodes);
641 
642 	double eval = eval_function(root);
643 
644 
645 	vector<double> dhess;
646 	double hx0[] ={-1.747958066718855,
647 			  -0.657091724418110,
648 			   2.410459188139686,
649 			                   0};
650 	double hx1[] ={ -0.657091724418110,
651 			   0.006806564792590,
652 			  -0.289815306593997,
653 			   1.000000000000000};
654 	double hx2[] ={  2.410459188139686,
655 			  -0.289815306593997,
656 			   2.064383410399700,
657 			                   0};
658 	double hx3[] ={0,1,0,0};
659 	for(unsigned int i=0;i<nodes.size();i++)
660 	{
661 		static_cast<VNode*>(nodes[i])->u = 0;
662 	}
663 	static_cast<VNode*>(nodes[0])->u = 1;
664 	double hval = hess_reverse(root,nodes,dhess);
665 	CHECK_CLOSE(hval,eval);
666 	for(unsigned int i=0;i<dhess.size();i++)
667 	{
668 		CHECK_CLOSE(dhess[i],hx0[i]);
669 	}
670 
671 	for (unsigned int i = 0; i < nodes.size(); i++) {
672 		static_cast<VNode*>(nodes[i])->u = 0;
673 	}
674 	static_cast<VNode*>(nodes[1])->u = 1;
675 	hess_reverse(root, nodes, dhess);
676 	for (unsigned int i = 0; i < dhess.size(); i++) {
677 		CHECK_CLOSE(dhess[i], hx1[i]);
678 	}
679 
680 	for (unsigned int i = 0; i < nodes.size(); i++) {
681 		static_cast<VNode*>(nodes[i])->u = 0;
682 	}
683 	static_cast<VNode*>(nodes[2])->u = 1;
684 	hess_reverse(root, nodes, dhess);
685 	for (unsigned int i = 0; i < dhess.size(); i++) {
686 		CHECK_CLOSE(dhess[i], hx2[i]);
687 	}
688 
689 	for (unsigned int i = 0; i < nodes.size(); i++) {
690 		static_cast<VNode*>(nodes[i])->u = 0;
691 	}
692 	static_cast<VNode*>(nodes[3])->u = 1;
693 	hess_reverse(root, nodes, dhess);
694 	for (unsigned i = 0; i < dhess.size(); i++) {
695 		CHECK_CLOSE(dhess[i], hx3[i]);
696 	}
697 }
698 
699 #if FORWARD_ENABLED
test_hess_forward(Node * root,unsigned int & nvar)700 void test_hess_forward(Node* root, unsigned int& nvar)
701 {
702 	AutoDiff::num_var = nvar;
703 	unsigned int len = (nvar+3)*nvar/2;
704 	double* hess = new double[len];
705 	hess_forward(root,nvar,&hess);
706 	for(unsigned int i=0;i<len;i++){
707 		cout<<"hess["<<i<<"]="<<hess[i]<<endl;
708 	}
709 	delete[] hess;
710 }
711 #endif
712 
BOOST_AUTO_TEST_CASE(test_hess_reverse_8)713 BOOST_AUTO_TEST_CASE( test_hess_reverse_8)
714 {
715 	vector<Node*> list;
716 	vector<double> dhess;
717 
718 	VNode* x1 = create_var_node();
719 	list.push_back(x1);
720 	static_cast<VNode*>(list[0])->val = -10.5;
721 	static_cast<VNode*>(list[0])->u = 1;
722 	double deval = hess_reverse(x1,list,dhess);
723 	CHECK_CLOSE(deval,-10.5);
724 	BOOST_CHECK_EQUAL(dhess.size(),1);
725 	BOOST_CHECK(isnan(dhess[0]));
726 
727 	EdgeSet s;
728 	nonlinearEdges(x1,s);
729 	unsigned int n = nzHess(s);
730 	BOOST_CHECK_EQUAL(n,0);
731 
732 	PNode* p1 = create_param_node(-1.5);
733 	list.clear();
734 	deval = hess_reverse(p1,list,dhess);
735 	CHECK_CLOSE(deval,-1.5);
736 	BOOST_CHECK_EQUAL(dhess.size(),0);
737 
738 	s.clear();
739 	nonlinearEdges(p1,s);
740 	n = nzHess(s);
741 	BOOST_CHECK_EQUAL(n,0);
742 }
743 
BOOST_AUTO_TEST_CASE(test_hess_revers9)744 BOOST_AUTO_TEST_CASE( test_hess_revers9)
745 {
746 	vector<Node*> list;
747 	vector<double> dhess;
748 	VNode* x1 = create_var_node();
749 	list.push_back(x1);
750 	static_cast<VNode*>(list[0])->val = 2.5;
751 	static_cast<VNode*>(list[0])->u =1;
752 	Node* op1 = create_binary_op_node(OP_TIMES,x1,x1);
753 	Node* root = create_binary_op_node(OP_TIMES,op1,op1);
754 	double deval = hess_reverse(root,list,dhess);
755 	double eval = eval_function(root);
756 	CHECK_CLOSE(eval,deval);
757 	BOOST_CHECK_EQUAL(dhess.size(),1);
758 	CHECK_CLOSE(dhess[0],75);
759 
760 	EdgeSet s;
761 	nonlinearEdges(root,s);
762 	unsigned int n = nzHess(s);
763 	BOOST_CHECK_EQUAL(n,1);
764 
765 }
766 
BOOST_AUTO_TEST_CASE(test_hess_revers10)767 BOOST_AUTO_TEST_CASE( test_hess_revers10)
768 {
769 	vector<Node*> list;
770 	vector<double> dhess;
771 	VNode* x1 = create_var_node();
772 	VNode* x2 = create_var_node();
773 	list.push_back(x1);
774 	list.push_back(x2);
775 	Node* op1 = create_binary_op_node(OP_TIMES, x1,x2);
776 	Node* op2 = create_uary_op_node(OP_SIN,op1);
777 	Node* op3 = create_uary_op_node(OP_COS,op1);
778 	Node* root = create_binary_op_node(OP_TIMES, op2, op3);
779 	static_cast<VNode*>(list[0])->val = 2.1;
780 	static_cast<VNode*>(list[1])->val = 1.8;
781 	double eval = eval_function(root);
782 
783 	//second column
784 	static_cast<VNode*>(list[0])->u = 0;
785 	static_cast<VNode*>(list[1])->u = 1;
786 	double deval = hess_reverse(root,list,dhess);
787 	CHECK_CLOSE(eval,deval);
788 	BOOST_CHECK_EQUAL(dhess.size(),2);
789 	CHECK_CLOSE(dhess[0], -6.945893481707861);
790 	CHECK_CLOSE(dhess[1],  -8.441601940854081);
791 
792 	//first column
793 	static_cast<VNode*>(list[0])->u = 1;
794 	static_cast<VNode*>(list[1])->u = 0;
795 	deval = hess_reverse(root,list,dhess);
796 	CHECK_CLOSE(eval,deval);
797 	BOOST_CHECK_EQUAL(dhess.size(),2);
798 	CHECK_CLOSE(dhess[0], -6.201993262668304);
799 	CHECK_CLOSE(dhess[1], -6.945893481707861);
800 }
801 
BOOST_AUTO_TEST_CASE(test_grad_reverse11)802 BOOST_AUTO_TEST_CASE( test_grad_reverse11)
803 {
804 	vector<Node*> list;
805 	VNode* x1 = create_var_node();
806 	Node* p2 = create_param_node(2);
807 	list.push_back(x1);
808 	Node* op1 = create_binary_op_node(OP_POW,x1,p2);
809 	static_cast<VNode*>(x1)->val = 0;
810 	vector<double> grad;
811 	grad_reverse(op1,list,grad);
812 	BOOST_CHECK_EQUAL(grad.size(),1);
813 	CHECK_CLOSE(grad[0],0);
814 }
815 
BOOST_AUTO_TEST_CASE(test_hess_reverse12)816 BOOST_AUTO_TEST_CASE( test_hess_reverse12)
817 {
818 	vector<Node*> list;
819 	VNode* x1 = create_var_node();
820 	Node* p2 = create_param_node(2);
821 	list.push_back(x1);
822 	Node* op1 = create_binary_op_node(OP_POW,x1,p2);
823 	x1->val = 0;
824 	x1->u = 1;
825 	vector<double> hess;
826 	hess_reverse(op1,list,hess);
827 	BOOST_CHECK_EQUAL(hess.size(),1);
828 	CHECK_CLOSE(hess[0],2);
829 }
830 
BOOST_AUTO_TEST_CASE(test_grad_reverse13)831 BOOST_AUTO_TEST_CASE( test_grad_reverse13)
832 {
833 	vector<Node*> list;
834 	VNode* x1 = create_var_node();
835 	PNode* p1 = create_param_node(0.090901);
836 	VNode* x2 = create_var_node();
837 	PNode* p2 = create_param_node(0.090901);
838 	list.push_back(x1);
839 	list.push_back(x2);
840 	Node* op1 = create_binary_op_node(OP_TIMES,x1,p1);
841 	Node* op2 = create_binary_op_node(OP_TIMES,x2,p2);
842 	Node* root = create_binary_op_node(OP_PLUS,op1,op2);
843 	x1->val = 1;
844 	x2->val = 1;
845 	vector<double> grad;
846 	grad_reverse(root,list,grad);
847 	BOOST_CHECK_EQUAL(grad.size(),2);
848 	CHECK_CLOSE(grad[0],0.090901);
849 	CHECK_CLOSE(grad[1],0.090901);
850 }
851 
852 BOOST_AUTO_TEST_SUITE_END()
853