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().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  	final public static 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  	final public static 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 	final public static 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 	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).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 	final public static 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 	final public static 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 	final public static 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<Operation<?>>(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<Operation<?>>(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<Operation<?>>(firstInput);
259 		multBaseTreeNode.addChild(firstInputTreeNode);
260 
261 		return multBaseTreeNode;
262 	});
263 
264 	@SuppressWarnings("unchecked")
265 	final public static 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<Operation<?>>(Functions.EXP.build(inputSpec));
288 
289 		final InputOperation<?> firstInput = (InputOperation<Double>) t.getChild(0).getData();
290 		final TreeNode<Operation<?>> firstInputTreeNode = new TreeNode<Operation<?>>(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<Operation<?>>(coefficientFactory.build(inputSpec));
296 		expBaseTreeNode.addChild(twoTreeNode);
297 
298 		return expBaseTreeNode;
299 	});
300 
301 	@SuppressWarnings("unchecked")
302 	final public static 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<Operation<?>>(Functions.EXP.build(inputSpec));
337 
338 		final InputOperation<?> firstInput = (InputOperation<Double>) t.getChild(0).getData();
339 		final TreeNode<Operation<?>> firstInputTreeNode = new TreeNode<Operation<?>>(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<Operation<?>>(coefficientFactory.build(inputSpec));
345 		expBaseTreeNode.addChild(newCoeffTreeNode);
346 
347 		return expBaseTreeNode;
348 	});
349 
350 	@SuppressWarnings("unchecked")
351 	final public static 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) -> {
364 
365 		return t.getChild(1);
366 	});
367 
368 	@SuppressWarnings("unchecked")
369 	final public static Rule MUL_ANYTHING_WITH_1 = ImmutableRule.of((t) -> {
370 		if (isOperation(t, Functions.NAME_MUL) == false) {
371 			return false;
372 		}
373 
374 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
375 			return false;
376 		}
377 
378 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
379 
380 		return isEqual(secondCoefficient.value(), 1);
381 	}, (program, t) -> {
382 
383 		return t.getChild(0);
384 	});
385 
386 	@SuppressWarnings("unchecked")
387 	final public static Rule ADD_0_WITH_ANYTHING = ImmutableRule.of((t) -> {
388 		if (isOperation(t, Functions.NAME_ADD) == false) {
389 			return false;
390 		}
391 
392 		if (hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT) == false) {
393 			return false;
394 		}
395 
396 		final CoefficientOperation<Double> firstCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
397 
398 		return isEqual(firstCoefficient.value(), 0.0d);
399 	}, (program, t) -> {
400 
401 		return t.getChild(1);
402 	});
403 
404 	@SuppressWarnings("unchecked")
405 	final public static Rule ADD_ANYTHING_WITH_0 = ImmutableRule.of((t) -> {
406 		if (isOperation(t, Functions.NAME_ADD) == false) {
407 			return false;
408 		}
409 
410 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
411 			return false;
412 		}
413 
414 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
415 
416 		return isEqual(secondCoefficient.value(), 0.0d);
417 	}, (program, t) -> {
418 
419 		return t.getChild(0);
420 	});
421 
422 	@SuppressWarnings("unchecked")
423 	final public static Rule MUL_0_WITH_ANYTHING = ImmutableRule.of((t) -> {
424 		if (isOperation(t, Functions.NAME_MUL) == false) {
425 			return false;
426 		}
427 
428 		if (hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT) == false) {
429 			return false;
430 		}
431 
432 		final CoefficientOperation<Double> firstCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
433 
434 		return isEqual(firstCoefficient.value(), 0.0d);
435 	}, (program, t) -> {
436 
437 		final InputSpec inputSpec = program.inputSpec();
438 
439 		final OperationFactory zeroFactory = OperationFactories
440 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 0.0d);
441 
442 		return new TreeNode<>(zeroFactory.build(inputSpec));
443 	});
444 
445 	@SuppressWarnings("unchecked")
446 	final public static Rule MUL_ANYTHING_WITH_0 = ImmutableRule.of((t) -> {
447 		if (isOperation(t, Functions.NAME_MUL) == false) {
448 			return false;
449 		}
450 
451 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
452 			return false;
453 		}
454 
455 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
456 
457 		return secondCoefficient.value() < 1 + 0.0001 && secondCoefficient.value() > 1 - .0001;
458 	}, (program, t) -> {
459 		final InputSpec inputSpec = program.inputSpec();
460 
461 		final OperationFactory zeroFactory = OperationFactories
462 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 0.0d);
463 
464 		return new TreeNode<>(zeroFactory.build(inputSpec));
465 	});
466 
467 	@SuppressWarnings("unchecked")
468 	final public static Rule POW_0 = ImmutableRule.of((t) -> {
469 		if (isOperation(t, Functions.NAME_POW) == false) {
470 			return false;
471 		}
472 
473 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
474 			return false;
475 		}
476 
477 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
478 
479 		return isEqual(secondCoefficient.value(), 0);
480 	}, (program, t) -> {
481 		final InputSpec inputSpec = program.inputSpec();
482 
483 		final OperationFactory oneFactory = OperationFactories
484 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 1.0d);
485 
486 		return new TreeNode<>(oneFactory.build(inputSpec));
487 	});
488 
489 	@SuppressWarnings("unchecked")
490 	final public static Rule POW_1 = ImmutableRule.of((t) -> {
491 		if (isOperation(t, Functions.NAME_POW) == false) {
492 			return false;
493 		}
494 
495 		if (hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT) == false) {
496 			return false;
497 		}
498 
499 		final CoefficientOperation<Double> secondCoefficient = (CoefficientOperation<Double>) t.getChild(1).getData();
500 
501 		return isEqual(secondCoefficient.value(), 1);
502 	}, (program, t) -> {
503 
504 		return t.getChild(0);
505 	});
506 
507 	@SuppressWarnings("unchecked")
508 	final public static Rule COS_OF_COEFFICIENT = ImmutableRule.of((t) -> {
509 		if (isOperation(t, Functions.NAME_COS) == false) {
510 			return false;
511 		}
512 
513 		return hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT);
514 	}, (program, t) -> {
515 
516 		final InputSpec inputSpec = program.inputSpec();
517 
518 		final CoefficientOperation<Double> cosCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
519 
520 		final double cosValue = Math.cos(cosCoefficient.value());
521 
522 		final OperationFactory cosValueOperationFactory = OperationFactories
523 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, cosValue);
524 
525 		return new TreeNode<>(cosValueOperationFactory.build(inputSpec));
526 	});
527 
528 	@SuppressWarnings("unchecked")
529 	final public static Rule SIN_OF_COEFFICIENT = ImmutableRule.of((t) -> {
530 		if (isOperation(t, Functions.NAME_SIN) == false) {
531 			return false;
532 		}
533 
534 		return hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT);
535 	}, (program, t) -> {
536 
537 		final InputSpec inputSpec = program.inputSpec();
538 
539 		final CoefficientOperation<Double> sinCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
540 
541 		final double sinValue = Math.sin(sinCoefficient.value());
542 
543 		final OperationFactory sinValueOperationFactory = OperationFactories
544 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, sinValue);
545 
546 		return new TreeNode<>(sinValueOperationFactory.build(inputSpec));
547 	});
548 
549 	final public static Rule SUB_SAME_BRANCHES = ImmutableRule.of((t) -> {
550 		if (isOperation(t, Functions.NAME_SUB) == false) {
551 			return false;
552 		}
553 
554 		return TreeNodeUtils.areSame(t.getChild(0), t.getChild(1));
555 	}, (program, t) -> {
556 
557 		final InputSpec inputSpec = program.inputSpec();
558 
559 		final OperationFactory zeroFactory = OperationFactories
560 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 0.0d);
561 
562 		return new TreeNode<>(zeroFactory.build(inputSpec));
563 	});
564 
565 	final public static Rule ADD_SAME_BRANCHES = ImmutableRule.of((t) -> {
566 		if (isOperation(t, Functions.NAME_ADD) == false) {
567 			return false;
568 		}
569 
570 		return TreeNodeUtils.areSame(t.getChild(0), t.getChild(1));
571 	}, (program, t) -> {
572 
573 		final InputSpec inputSpec = program.inputSpec();
574 
575 		final TreeNode<Operation<?>> baseAdd = new TreeNode<>(Functions.ADD.build(inputSpec));
576 
577 		final OperationFactory twoFactory = OperationFactories
578 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 2.0d);
579 		baseAdd.addChild(new TreeNode<>(twoFactory.build(inputSpec)));
580 
581 		baseAdd.addChild(t.getChild(0)); // TODO copy it instead?
582 
583 		return baseAdd;
584 	});
585 
586 	final public static Rule DIV_SAME_BRANCHES = ImmutableRule.of((t) -> {
587 		if (isOperation(t, Functions.NAME_DIV) == false) {
588 			return false;
589 		}
590 
591 		return TreeNodeUtils.areSame(t.getChild(0), t.getChild(1));
592 	}, (program, t) -> {
593 
594 		final InputSpec inputSpec = program.inputSpec();
595 
596 		final OperationFactory oneFactory = OperationFactories
597 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 1.0d);
598 
599 		return new TreeNode<>(oneFactory.build(inputSpec));
600 	});
601 
602 	@SuppressWarnings("unchecked")
603 	final public static Rule EXP_OF_COEFFICIENT = ImmutableRule.of((t) -> {
604 		if (isOperation(t, Functions.NAME_EXP) == false) {
605 			return false;
606 		}
607 
608 		return hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT);
609 	}, (program, t) -> {
610 
611 		final InputSpec inputSpec = program.inputSpec();
612 
613 		final CoefficientOperation<Double> expCoefficient = (CoefficientOperation<Double>) t.getChild(0).getData();
614 
615 		final double expValue = Math.exp(expCoefficient.value());
616 
617 		final OperationFactory expValueOperationFactory = OperationFactories
618 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, expValue);
619 
620 		return new TreeNode<>(expValueOperationFactory.build(inputSpec));
621 	});
622 
623 	@SuppressWarnings("unchecked")
624 	final public static Rule POW_TWO_COEFFICIENTS = ImmutableRule.of(
625 			(t) -> isOperation(t, Functions.NAME_POW) && hasChildOperation(t, 0, Terminals.TYPE_COEFFICIENT)
626 					&& hasChildOperation(t, 1, Terminals.TYPE_COEFFICIENT),
627 				(program, t) -> {
628 
629 					final InputSpec inputSpec = program.inputSpec();
630 
631 					final CoefficientOperation<Double> firstCoefficient = getChildAs(t, 0, CoefficientOperation.class);
632 					final Double firstValue = firstCoefficient.value();
633 
634 					final CoefficientOperation<Double> secondCoefficient = getChildAs(t, 1, CoefficientOperation.class);
635 					final Double secondValue = secondCoefficient.value();
636 
637 					final OperationFactory coefficientFactory = OperationFactories
638 							.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, Math.pow(firstValue, secondValue));
639 
640 					final Operation<?> newOperation = coefficientFactory.build(inputSpec);
641 
642 					return new TreeNode<>(newOperation);
643 				});
644 
645 	/**
646 	 * multiplication of the same branch -> square of the first branch
647 	 */
648 	final public static Rule MUL_SAME_BRANCHES = ImmutableRule.of((t) -> {
649 		if (isOperation(t, Functions.NAME_MUL) == false) {
650 			return false;
651 		}
652 
653 		return TreeNodeUtils.areSame(t.getChild(0), t.getChild(1));
654 	}, (program, t) -> {
655 
656 		final InputSpec inputSpec = program.inputSpec();
657 
658 		final TreeNode<Operation<?>> powNode = new TreeNode<>(Functions.POW.build(inputSpec));
659 
660 		powNode.addChild(t.getChild(0));
661 		powNode.addChild(
662 				new TreeNode<>(
663 						OperationFactories.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, 2.0d).build(inputSpec)));
664 
665 		return powNode;
666 	});
667 
668 	final public static Rule COS_PI = ImmutableRule.of((t) -> {
669 		if (isOperation(t, Functions.NAME_COS) == false) {
670 			return false;
671 		}
672 
673 		return hasChildOperation(t, 0, Terminals.NAME_PI);
674 	}, (program, t) -> {
675 
676 		final InputSpec inputSpec = program.inputSpec();
677 
678 		final OperationFactory minusOneFactory = OperationFactories
679 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, -1.0d);
680 
681 		return new TreeNode<>(minusOneFactory.build(inputSpec));
682 	});
683 
684 	final public static Rule SIN_PI = ImmutableRule.of((t) -> {
685 		if (isOperation(t, Functions.NAME_SIN) == false) {
686 			return false;
687 		}
688 
689 		return hasChildOperation(t, 0, Terminals.NAME_PI);
690 	}, (program, t) -> {
691 
692 		final InputSpec inputSpec = program.inputSpec();
693 
694 		final OperationFactory zeroFactory = OperationFactories
695 				.ofCoefficient(Terminals.TYPE_COEFFICIENT, Double.class, -0.0d);
696 
697 		return new TreeNode<>(zeroFactory.build(inputSpec));
698 	});
699 
700 	final public static List<Rule> SIMPLIFY_RULES = Arrays.asList(
701 			MUL_SAME_BRANCHES,
702 				ADD_TWO_COEFFCIENTS,
703 				MUL_TWO_COEFFICIENTS,
704 				SUB_TWO_COEFFICIENTS,
705 				SUB_INPUT_FROM_SAME_INPUT,
706 				SUB_ZERO_FROM_INPUT,
707 				DIV_TWO_COEFFICIENT_FINITE,
708 				ADD_INPUT_TO_SAME_INPUT,
709 				MULTIPLY_INPUT_WITH_SAME_INPUT,
710 				MUL_1_WITH_ANYTHING,
711 				MUL_ANYTHING_WITH_1,
712 				MUL_0_WITH_ANYTHING,
713 				MUL_ANYTHING_WITH_0,
714 				POW_0,
715 				POW_1,
716 				COS_OF_COEFFICIENT,
717 				SIN_OF_COEFFICIENT,
718 				SUB_SAME_BRANCHES,
719 				ADD_SAME_BRANCHES,
720 				DIV_SAME_BRANCHES,
721 				EXP_OF_COEFFICIENT,
722 				POW_TWO_COEFFICIENTS,
723 				ADD_0_WITH_ANYTHING,
724 				ADD_ANYTHING_WITH_0,
725 				COS_PI,
726 				SIN_PI);
727 
728 }