View Javadoc
1   package net.bmahe.genetics4j.gp.math;
2   
3   import java.util.Arrays;
4   import java.util.List;
5   
6   import org.apache.commons.lang3.Validate;
7   import org.apache.logging.log4j.LogManager;
8   import org.apache.logging.log4j.Logger;
9   
10  import net.bmahe.genetics4j.core.chromosomes.TreeNode;
11  import net.bmahe.genetics4j.gp.InputSpec;
12  import net.bmahe.genetics4j.gp.Operation;
13  import net.bmahe.genetics4j.gp.OperationFactories;
14  import net.bmahe.genetics4j.gp.OperationFactory;
15  import net.bmahe.genetics4j.gp.spec.mutation.ImmutableRule;
16  import net.bmahe.genetics4j.gp.spec.mutation.Rule;
17  import net.bmahe.genetics4j.gp.utils.TreeNodeUtils;
18  
19  public class SimplificationRules {
20  	final static public Logger logger = LogManager.getLogger(SimplificationRules.class);
21  
22  	public final static double DEFAULT_EPSILON = 0.0001;
23  
24  	protected static boolean isOperation(final TreeNode<Operation<?>> node, final String name) {
25  		Validate.notNull(node);
26  		Validate.notBlank(name);
27  		return name.equals(node.getData().getName());
28  	}
29  
30  	protected static boolean hasChildOperation(final TreeNode<Operation<?>> node, final int childIndex,
31  			final String name) {
32  		Validate.notNull(node);
33  		Validate.isTrue(childIndex >= 0);
34  		Validate.notBlank(name);
35  
36  		if (node.getChildren().size() <= childIndex) {
37  			return false;
38  		}
39  
40  		final TreeNode<Operation<?>> child = node.getChild(childIndex);
41  		return name.equals(child.getData().getName());
42  	}
43  
44  	protected static <T> T getChildAs(final TreeNode<Operation<?>> node, final int childIndex, final Class<T> clazz) {
45  		final TreeNode<Operation<?>> child = node.getChild(childIndex);
46  		final Operation<?> operation = child.getData();
47  		return (T) operation;
48  	}
49  
50  	protected static boolean isEqual(final double v1, final double v2, final double epsilon) {
51  		Validate.isTrue(epsilon >= 0);
52  
53  		return Math.abs(v2 - v1) < epsilon;
54  	}
55  
56  	protected static boolean isEqual(final double v1, final double v2) {
57  		return isEqual(v1, v2, DEFAULT_EPSILON);
58  	}
59  
60  	@SuppressWarnings("unchecked")
61  	final public static Rule ADD_TWO_COEFFCIENTS = ImmutableRule
62  			.of((t) -> isOperation(t, Functions.NAME_ADD) && hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT)
63  					&& hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT), (program, t) -> {
64  
65  						final InputSpec inputSpec = program.inputSpec();
66  
67  						final CoefficientOperation<Double> firstCoefficient = getChildAs(t,
68  								0,
69  								CoefficientOperation.class);
70  						final Double firstValue = firstCoefficient.value();
71  
72  						final CoefficientOperation<Double> secondCoefficient = getChildAs(t,
73  								1,
74  								CoefficientOperation.class);
75  						final Double secondValue = secondCoefficient.value();
76  
77  						final OperationFactory coefficientFactory = OperationFactories
78  								.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, firstValue + secondValue);
79  
80  						final Operation<?> newOperation = coefficientFactory.build(inputSpec);
81  
82  						return new TreeNode<>(newOperation);
83  					});
84  
85  	@SuppressWarnings("unchecked")
86  	final public static Rule MUL_TWO_COEFFICIENTS = ImmutableRule
87  			.of((t) -> isOperation(t, Functions.NAME_MUL) && hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT)
88  					&& hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT), (program, t) -> {
89  
90  						final InputSpec inputSpec = program.inputSpec();
91  
92  						final CoefficientOperation<Double> firstCoefficient = getChildAs(t,
93  								0,
94  								CoefficientOperation.class);
95  						final Double firstValue = firstCoefficient.value();
96  
97  						final CoefficientOperation<Double> secondCoefficient = getChildAs(t,
98  								1,
99  								CoefficientOperation.class);
100 						final Double secondValue = secondCoefficient.value();
101 
102 						final OperationFactory coefficientFactory = OperationFactories
103 								.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, firstValue * secondValue);
104 
105 						final Operation<?> newOperation = coefficientFactory.build(inputSpec);
106 
107 						return new TreeNode<>(newOperation);
108 					});
109 
110 	@SuppressWarnings("unchecked")
111 	final public static Rule SUB_TWO_COEFFICIENTS = ImmutableRule
112 			.of((t) -> isOperation(t, Functions.NAME_SUB) && hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT)
113 					&& hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT), (program, t) -> {
114 
115 						final InputSpec inputSpec = program.inputSpec();
116 
117 						final CoefficientOperation<Double> firstCoefficient = getChildAs(t,
118 								0,
119 								CoefficientOperation.class);
120 						final Double firstValue = firstCoefficient.value();
121 
122 						final CoefficientOperation<Double> secondCoefficient = getChildAs(t,
123 								1,
124 								CoefficientOperation.class);
125 						final Double secondValue = secondCoefficient.value();
126 
127 						final OperationFactory coefficientFactory = OperationFactories
128 								.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, firstValue - secondValue);
129 
130 						final Operation<?> newOperation = coefficientFactory.build(inputSpec);
131 
132 						return new TreeNode<>(newOperation);
133 					});
134 
135 	@SuppressWarnings("unchecked")
136 	final public static Rule SUB_INPUT_FROM_SAME_INPUT = ImmutableRule.of((t) -> {
137 		if (isOperation(t, Functions.NAME_SUB) == false) {
138 			return false;
139 		}
140 
141 		if (hasChildOperation(t, 0, Terminals.TYPE_INPUT) == false) {
142 			return false;
143 		}
144 
145 		if (hasChildOperation(t, 1, Terminals.TYPE_INPUT) == false) {
146 			return false;
147 		}
148 
149 		final InputOperation<?> firstInput = (InputOperation<Double>) t.getChild(0).getData();
150 
151 		final InputOperation<?> secondInput = (InputOperation<Double>) t.getChild(1).getData();
152 
153 		return firstInput.index() == secondInput.index();
154 	}, (program, t) -> {
155 
156 		final InputSpec inputSpec = program.inputSpec();
157 
158 		final OperationFactory coefficientFactory = OperationFactories
159 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 0.0d);
160 
161 		final Operation<?> newOperation = coefficientFactory.build(inputSpec);
162 
163 		return new TreeNode<>(newOperation);
164 	});
165 
166 	@SuppressWarnings("unchecked")
167 	final public static Rule SUB_ZERO_FROM_INPUT = ImmutableRule.of((t) -> {
168 		if (isOperation(t, Functions.NAME_SUB) == false) {
169 			return false;
170 		}
171 
172 		if (hasChildOperation(t, 0, Terminals.TYPE_INPUT) == false) {
173 			return false;
174 		}
175 
176 		if (hasChildOperation(t, 1, Terminals.TYPE_INPUT) == false) {
177 			return false;
178 		}
179 
180 		if (hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT) == false) {
181 			return false;
182 		}
183 
184 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
185 			return false;
186 		}
187 
188 		final CoefficientOperation<Double> firstCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
189 
190 		return isEqual(firstCoefficient.value(), 0.0d);
191 	}, (program, t) -> {
192 
193 		final InputSpec inputSpec = program.inputSpec();
194 
195 		final OperationFactory coefficientFactory = OperationFactories
196 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 0.0d);
197 
198 		final Operation<?> newOperation = coefficientFactory.build(inputSpec);
199 
200 		return new TreeNode<>(newOperation);
201 	});
202 
203 	@SuppressWarnings("unchecked")
204 	final public static Rule DIV_TWO_COEFFICIENT_FINITE = ImmutableRule.of((t) -> {
205 		if (isOperation(t, Functions.NAME_DIV) == false) {
206 			return false;
207 		}
208 
209 		if (hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT) == false) {
210 			return false;
211 		}
212 
213 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
214 			return false;
215 		}
216 
217 		final CoefficientOperation<Double> firstCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
218 
219 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
220 
221 		return Double.isFinite(firstCoefficient.value() / secondCoefficient.value());
222 	}, (program, t) -> {
223 
224 		final InputSpec inputSpec = program.inputSpec();
225 
226 		final CoefficientOperation<Double> firstCoefficient = getChildAs(t, 0, CoefficientOperation.class);
227 		final Double firstValue = firstCoefficient.value();
228 
229 		final CoefficientOperation<Double> secondCoefficient = getChildAs(t, 1, CoefficientOperation.class);
230 		final Double secondValue = secondCoefficient.value();
231 
232 		final OperationFactory coefficientFactory = OperationFactories
233 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, firstValue / secondValue);
234 
235 		final Operation<?> newOperation = coefficientFactory.build(inputSpec);
236 
237 		return new TreeNode<>(newOperation);
238 	});
239 
240 	@SuppressWarnings("unchecked")
241 	final public static Rule ADD_INPUT_TO_SAME_INPUT = ImmutableRule.of((t) -> {
242 		boolean result = isOperation(t, Functions.NAME_ADD) && hasChildOperation(t, 0, Terminals.TYPE_INPUT)
243 				&& hasChildOperation(t, 1, Terminals.TYPE_INPUT);
244 
245 		if (result == false) {
246 			return false;
247 		}
248 
249 		final InputOperation<?> firstInput = getChildAs(t, 0, InputOperation.class);
250 		final InputOperation<?> secondInput = getChildAs(t, 1, InputOperation.class);
251 
252 		return firstInput.index() == secondInput.index();
253 	}, (program, t) -> {
254 
255 		final InputSpec inputSpec = program.inputSpec();
256 
257 		final TreeNode<Operation<?>> multBaseTreeNode = new TreeNode<Operation<?>>(Functions.MUL.build(inputSpec));
258 
259 		final OperationFactory coefficientFactory = OperationFactories
260 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 2.0d);
261 		final TreeNode<Operation<?>> timesTwoTreeNode = new TreeNode<Operation<?>>(coefficientFactory.build(inputSpec));
262 		multBaseTreeNode.addChild(timesTwoTreeNode);
263 
264 		final InputOperation<?> firstInput = (InputOperation<Double>) t.getChild(0).getData();
265 		final TreeNode<Operation<?>> firstInputTreeNode = new TreeNode<Operation<?>>(firstInput);
266 		multBaseTreeNode.addChild(firstInputTreeNode);
267 
268 		return multBaseTreeNode;
269 	});
270 
271 	@SuppressWarnings("unchecked")
272 	final public static Rule MULTIPLY_INPUT_WITH_SAME_INPUT = ImmutableRule.of((t) -> {
273 
274 		if (isOperation(t, Functions.NAME_MUL) == false) {
275 			return false;
276 		}
277 
278 		if (hasChildOperation(t, 0, Terminals.TYPE_INPUT) == false) {
279 			return false;
280 		}
281 		if (hasChildOperation(t, 1, Terminals.TYPE_INPUT) == false) {
282 			return false;
283 		}
284 
285 		final InputOperation<?> firstInput = (InputOperation<Double>) t.getChild(0).getData();
286 
287 		final InputOperation<?> secondInput = (InputOperation<Double>) t.getChild(1).getData();
288 
289 		return firstInput.index() == secondInput.index();
290 	}, (program, t) -> {
291 
292 		final InputSpec inputSpec = program.inputSpec();
293 
294 		final TreeNode<Operation<?>> expBaseTreeNode = new TreeNode<Operation<?>>(Functions.EXP.build(inputSpec));
295 
296 		final InputOperation<?> firstInput = (InputOperation<Double>) t.getChild(0).getData();
297 		final TreeNode<Operation<?>> firstInputTreeNode = new TreeNode<Operation<?>>(firstInput);
298 		expBaseTreeNode.addChild(firstInputTreeNode);
299 
300 		final OperationFactory coefficientFactory = OperationFactories
301 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 2.0d);
302 		final TreeNode<Operation<?>> twoTreeNode = new TreeNode<Operation<?>>(coefficientFactory.build(inputSpec));
303 		expBaseTreeNode.addChild(twoTreeNode);
304 
305 		return expBaseTreeNode;
306 	});
307 
308 	@SuppressWarnings("unchecked")
309 	final public static Rule MULTIPLY_INPUT_WITH_EXP_SAME_INPUT_COEFF = ImmutableRule.of((t) -> {
310 		// ex: MULT( EXP( INPUT[0], 3), INPUT[0])
311 		// ==> EXP( INPUT[0], 4)
312 
313 		if (isOperation(t, Functions.NAME_MUL) == false) {
314 			return false;
315 		}
316 		if (hasChildOperation(t, 0, Functions.NAME_EXP) == false) {
317 			return false;
318 		}
319 		if (hasChildOperation(t, 1, Terminals.TYPE_INPUT) == false) {
320 			return false;
321 		}
322 
323 		final TreeNode<Operation<?>> expTreeNode = t.getChild(0);
324 		if (hasChildOperation(expTreeNode, 0, Terminals.TYPE_INPUT) == false) {
325 			return false;
326 		}
327 		if (hasChildOperation(expTreeNode, 1, Terminals.TYPE_COEFFICIENT) == false) {
328 			return false;
329 		}
330 
331 		final InputOperation<?> expInput = getChildAs(expTreeNode, 0, InputOperation.class);
332 		final InputOperation<?> secondInput = getChildAs(t, 1, InputOperation.class);
333 
334 		return expInput.index() == secondInput.index();
335 	}, (program, t) -> {
336 
337 		final InputSpec inputSpec = program.inputSpec();
338 		final TreeNode<Operation<?>> originalExpTreeNode = t.getChild(0);
339 		final CoefficientOperation<Double> originalCoefficientExp = getChildAs(originalExpTreeNode,
340 				1,
341 				CoefficientOperation.class);
342 
343 		final TreeNode<Operation<?>> expBaseTreeNode = new TreeNode<Operation<?>>(Functions.EXP.build(inputSpec));
344 
345 		final InputOperation<?> firstInput = (InputOperation<Double>) t.getChild(0).getData();
346 		final TreeNode<Operation<?>> firstInputTreeNode = new TreeNode<Operation<?>>(firstInput);
347 		expBaseTreeNode.addChild(firstInputTreeNode);
348 
349 		final OperationFactory coefficientFactory = OperationFactories
350 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, originalCoefficientExp.value() + 1.0d);
351 		final TreeNode<Operation<?>> newCoeffTreeNode = new TreeNode<Operation<?>>(coefficientFactory.build(inputSpec));
352 		expBaseTreeNode.addChild(newCoeffTreeNode);
353 
354 		return expBaseTreeNode;
355 	});
356 
357 	@SuppressWarnings("unchecked")
358 	final public static Rule MUL_1_WITH_ANYTHING = ImmutableRule.of((t) -> {
359 		if (isOperation(t, Functions.NAME_MUL) == false) {
360 			return false;
361 		}
362 
363 		if (hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT) == false) {
364 			return false;
365 		}
366 
367 		final CoefficientOperation<Double> firstCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
368 
369 		return firstCoefficient.value() < 1 + 0.0001 && firstCoefficient.value() > 1 - .0001;
370 	}, (program, t) -> {
371 
372 		return t.getChild(1);
373 	});
374 
375 	@SuppressWarnings("unchecked")
376 	final public static Rule MUL_ANYTHING_WITH_1 = ImmutableRule.of((t) -> {
377 		if (isOperation(t, Functions.NAME_MUL) == false) {
378 			return false;
379 		}
380 
381 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
382 			return false;
383 		}
384 
385 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
386 
387 		return isEqual(secondCoefficient.value(), 1);
388 	}, (program, t) -> {
389 
390 		return t.getChild(0);
391 	});
392 
393 	@SuppressWarnings("unchecked")
394 	final public static Rule ADD_0_WITH_ANYTHING = ImmutableRule.of((t) -> {
395 		if (isOperation(t, Functions.NAME_ADD) == false) {
396 			return false;
397 		}
398 
399 		if (hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT) == false) {
400 			return false;
401 		}
402 
403 		final CoefficientOperation<Double> firstCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
404 
405 		return isEqual(firstCoefficient.value(), 0.0d);
406 	}, (program, t) -> {
407 
408 		return t.getChild(1);
409 	});
410 
411 	@SuppressWarnings("unchecked")
412 	final public static Rule ADD_ANYTHING_WITH_0 = ImmutableRule.of((t) -> {
413 		if (isOperation(t, Functions.NAME_ADD) == false) {
414 			return false;
415 		}
416 
417 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
418 			return false;
419 		}
420 
421 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
422 
423 		return isEqual(secondCoefficient.value(), 0.0d);
424 	}, (program, t) -> {
425 
426 		return t.getChild(0);
427 	});
428 
429 	@SuppressWarnings("unchecked")
430 	final public static Rule MUL_0_WITH_ANYTHING = ImmutableRule.of((t) -> {
431 		if (isOperation(t, Functions.NAME_MUL) == false) {
432 			return false;
433 		}
434 
435 		if (hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT) == false) {
436 			return false;
437 		}
438 
439 		final CoefficientOperation<Double> firstCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
440 
441 		return isEqual(firstCoefficient.value(), 0.0d);
442 	}, (program, t) -> {
443 
444 		final InputSpec inputSpec = program.inputSpec();
445 
446 		final OperationFactory zeroFactory = OperationFactories
447 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 0.0d);
448 
449 		return new TreeNode<>(zeroFactory.build(inputSpec));
450 	});
451 
452 	@SuppressWarnings("unchecked")
453 	final public static Rule MUL_ANYTHING_WITH_0 = ImmutableRule.of((t) -> {
454 		if (isOperation(t, Functions.NAME_MUL) == false) {
455 			return false;
456 		}
457 
458 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
459 			return false;
460 		}
461 
462 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
463 
464 		return secondCoefficient.value() < 1 + 0.0001 && secondCoefficient.value() > 1 - .0001;
465 	}, (program, t) -> {
466 		final InputSpec inputSpec = program.inputSpec();
467 
468 		final OperationFactory zeroFactory = OperationFactories
469 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 0.0d);
470 
471 		return new TreeNode<>(zeroFactory.build(inputSpec));
472 	});
473 
474 	@SuppressWarnings("unchecked")
475 	final public static Rule POW_0 = ImmutableRule.of((t) -> {
476 		if (isOperation(t, Functions.NAME_POW) == false) {
477 			return false;
478 		}
479 
480 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
481 			return false;
482 		}
483 
484 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
485 
486 		return isEqual(secondCoefficient.value(), 0);
487 	}, (program, t) -> {
488 		final InputSpec inputSpec = program.inputSpec();
489 
490 		final OperationFactory oneFactory = OperationFactories
491 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 1.0d);
492 
493 		return new TreeNode<>(oneFactory.build(inputSpec));
494 	});
495 
496 	@SuppressWarnings("unchecked")
497 	final public static Rule POW_1 = ImmutableRule.of((t) -> {
498 		if (isOperation(t, Functions.NAME_POW) == false) {
499 			return false;
500 		}
501 
502 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
503 			return false;
504 		}
505 
506 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
507 
508 		return isEqual(secondCoefficient.value(), 1);
509 	}, (program, t) -> {
510 
511 		return t.getChild(0);
512 	});
513 
514 	@SuppressWarnings("unchecked")
515 	final public static Rule COS_OF_COEFFICIENT = ImmutableRule.of((t) -> {
516 		if (isOperation(t, Functions.NAME_COS) == false) {
517 			return false;
518 		}
519 
520 		return hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT);
521 	}, (program, t) -> {
522 
523 		final InputSpec inputSpec = program.inputSpec();
524 
525 		final CoefficientOperation<Double> cosCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
526 
527 		final double cosValue = Math.cos(cosCoefficient.value());
528 
529 		final OperationFactory cosValueOperationFactory = OperationFactories
530 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, cosValue);
531 
532 		return new TreeNode<>(cosValueOperationFactory.build(inputSpec));
533 	});
534 
535 	@SuppressWarnings("unchecked")
536 	final public static Rule SIN_OF_COEFFICIENT = ImmutableRule.of((t) -> {
537 		if (isOperation(t, Functions.NAME_SIN) == false) {
538 			return false;
539 		}
540 
541 		return hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT);
542 	}, (program, t) -> {
543 
544 		final InputSpec inputSpec = program.inputSpec();
545 
546 		final CoefficientOperation<Double> sinCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
547 
548 		final double sinValue = Math.sin(sinCoefficient.value());
549 
550 		final OperationFactory sinValueOperationFactory = OperationFactories
551 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, sinValue);
552 
553 		return new TreeNode<>(sinValueOperationFactory.build(inputSpec));
554 	});
555 
556 	final public static Rule SUB_SAME_BRANCHES = ImmutableRule.of((t) -> {
557 		if (isOperation(t, Functions.NAME_SUB) == false) {
558 			return false;
559 		}
560 
561 		return TreeNodeUtils.areSame(t.getChild(0), t.getChild(1));
562 	}, (program, t) -> {
563 
564 		final InputSpec inputSpec = program.inputSpec();
565 
566 		final OperationFactory zeroFactory = OperationFactories
567 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 0.0d);
568 
569 		return new TreeNode<>(zeroFactory.build(inputSpec));
570 	});
571 
572 	final public static Rule ADD_SAME_BRANCHES = ImmutableRule.of((t) -> {
573 		if (isOperation(t, Functions.NAME_ADD) == false) {
574 			return false;
575 		}
576 
577 		return TreeNodeUtils.areSame(t.getChild(0), t.getChild(1));
578 	}, (program, t) -> {
579 
580 		final InputSpec inputSpec = program.inputSpec();
581 
582 		final TreeNode<Operation<?>> baseAdd = new TreeNode<>(Functions.ADD.build(inputSpec));
583 
584 		final OperationFactory twoFactory = OperationFactories
585 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 2.0d);
586 		baseAdd.addChild(new TreeNode<>(twoFactory.build(inputSpec)));
587 
588 		baseAdd.addChild(t.getChild(0)); // TODO copy it instead?
589 
590 		return baseAdd;
591 	});
592 
593 	final public static Rule DIV_SAME_BRANCHES = ImmutableRule.of((t) -> {
594 		if (isOperation(t, Functions.NAME_DIV) == false) {
595 			return false;
596 		}
597 
598 		return TreeNodeUtils.areSame(t.getChild(0), t.getChild(1));
599 	}, (program, t) -> {
600 
601 		final InputSpec inputSpec = program.inputSpec();
602 
603 		final OperationFactory oneFactory = OperationFactories
604 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 1.0d);
605 
606 		return new TreeNode<>(oneFactory.build(inputSpec));
607 	});
608 
609 	@SuppressWarnings("unchecked")
610 	final public static Rule EXP_OF_COEFFICIENT = ImmutableRule.of((t) -> {
611 		if (isOperation(t, Functions.NAME_EXP) == false) {
612 			return false;
613 		}
614 
615 		return hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT);
616 	}, (program, t) -> {
617 
618 		final InputSpec inputSpec = program.inputSpec();
619 
620 		final CoefficientOperation<Double> expCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
621 
622 		final double expValue = Math.exp(expCoefficient.value());
623 
624 		final OperationFactory expValueOperationFactory = OperationFactories
625 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, expValue);
626 
627 		return new TreeNode<>(expValueOperationFactory.build(inputSpec));
628 	});
629 
630 	@SuppressWarnings("unchecked")
631 	final public static Rule POW_TWO_COEFFICIENTS = ImmutableRule
632 			.of((t) -> isOperation(t, Functions.NAME_POW) && hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT)
633 					&& hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT), (program, t) -> {
634 
635 						final InputSpec inputSpec = program.inputSpec();
636 
637 						final CoefficientOperation<Double> firstCoefficient = getChildAs(t,
638 								0,
639 								CoefficientOperation.class);
640 						final Double firstValue = firstCoefficient.value();
641 
642 						final CoefficientOperation<Double> secondCoefficient = getChildAs(t,
643 								1,
644 								CoefficientOperation.class);
645 						final Double secondValue = secondCoefficient.value();
646 
647 						final OperationFactory coefficientFactory = OperationFactories.ofCoefficient(
648 								Terminals.TYPE_COEFFICIENT,
649 								Double.class,
650 								Math.pow(firstValue, secondValue));
651 
652 						final Operation<?> newOperation = coefficientFactory.build(inputSpec);
653 
654 						return new TreeNode<>(newOperation);
655 					});
656 
657 	/**
658 	 * multiplication of the same branch -> square of the first branch
659 	 */
660 	final public static Rule MUL_SAME_BRANCHES = ImmutableRule.of((t) -> {
661 		if (isOperation(t, Functions.NAME_MUL) == false) {
662 			return false;
663 		}
664 
665 		return TreeNodeUtils.areSame(t.getChild(0), t.getChild(1));
666 	}, (program, t) -> {
667 
668 		final InputSpec inputSpec = program.inputSpec();
669 
670 		final TreeNode<Operation<?>> powNode = new TreeNode<>(Functions.POW.build(inputSpec));
671 
672 		powNode.addChild(t.getChild(0));
673 		powNode.addChild(new TreeNode<>(
674 				OperationFactories.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 2.0d).build(inputSpec)));
675 
676 		return powNode;
677 	});
678 
679 	final public static Rule COS_PI = ImmutableRule.of((t) -> {
680 		if (isOperation(t, Functions.NAME_COS) == false) {
681 			return false;
682 		}
683 
684 		return hasChildOperation(t, 0, Terminals.NAME_PI);
685 	}, (program, t) -> {
686 
687 		final InputSpec inputSpec = program.inputSpec();
688 
689 		final OperationFactory minusOneFactory = OperationFactories
690 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, -1.0d);
691 
692 		return new TreeNode<>(minusOneFactory.build(inputSpec));
693 	});
694 
695 	final public static Rule SIN_PI = ImmutableRule.of((t) -> {
696 		if (isOperation(t, Functions.NAME_SIN) == false) {
697 			return false;
698 		}
699 
700 		return hasChildOperation(t, 0, Terminals.NAME_PI);
701 	}, (program, t) -> {
702 
703 		final InputSpec inputSpec = program.inputSpec();
704 
705 		final OperationFactory zeroFactory = OperationFactories
706 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, -0.0d);
707 
708 		return new TreeNode<>(zeroFactory.build(inputSpec));
709 	});
710 
711 	final public static List<Rule> SIMPLIFY_RULES = Arrays.asList(MUL_SAME_BRANCHES,
712 			ADD_TWO_COEFFCIENTS,
713 			MUL_TWO_COEFFICIENTS,
714 			SUB_TWO_COEFFICIENTS,
715 			SUB_INPUT_FROM_SAME_INPUT,
716 			SUB_ZERO_FROM_INPUT,
717 			DIV_TWO_COEFFICIENT_FINITE,
718 			ADD_INPUT_TO_SAME_INPUT,
719 			MULTIPLY_INPUT_WITH_SAME_INPUT,
720 			MUL_1_WITH_ANYTHING,
721 			MUL_ANYTHING_WITH_1,
722 			MUL_0_WITH_ANYTHING,
723 			MUL_ANYTHING_WITH_0,
724 			POW_0,
725 			POW_1,
726 			COS_OF_COEFFICIENT,
727 			SIN_OF_COEFFICIENT,
728 			SUB_SAME_BRANCHES,
729 			ADD_SAME_BRANCHES,
730 			DIV_SAME_BRANCHES,
731 			EXP_OF_COEFFICIENT,
732 			POW_TWO_COEFFICIENTS,
733 			ADD_0_WITH_ANYTHING,
734 			ADD_ANYTHING_WITH_0,
735 			COS_PI,
736 			SIN_PI);
737 
738 }