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