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