SingleNodeCrossover.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-7.1.1).
003  * Copyright (c) 2007-2022 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019  */
020 package io.jenetics.ext;
021 
022 import static java.lang.String.format;
023 
024 import io.jenetics.util.ISeq;
025 import io.jenetics.util.RandomRegistry;
026 
027 import io.jenetics.ext.util.TreeNode;
028 
029 /**
030  * Swaps two, randomly chosen, nodes (sub-trees) from two given trees.
031  <pre> {@code
032  *     Tree A                 Tree B
033  *   0                      a
034  *   ├── 1                  ├── b
035  *   │   ├── 4              │   ├── e
036  *   │   └── 5              │   └── f
037  *   ├── 2                  ├── c
038  *   │   └── 6              │   └── g
039  *   └── 3                  └── d
040  *       ├── 7                  ├── h
041  *       │   ├── 10             │   ├── k
042  *       │   └── 11             │   └── l
043  *       ├── 8                  ├── i
044  *       └── 9                  └── j
045  *
046  *     Swap node "3" of A with node "c" of B
047  *
048  *   0                      a
049  *   ├── 1                  ├── b
050  *   │   ├── 4              │   ├── e
051  *   │   └── 5              │   └── f
052  *   ├── 2                  ├── 3
053  *   │   └── 6              │   ├── 7
054  *   └── c                  │   │   ├── 10
055  *       └── g              │   │   └── 11
056  *                          │   ├── 8
057  *                          │   └── 9
058  *                          └── d
059  *                              ├── h
060  *                              │   ├── k
061  *                              │   └── l
062  *                              ├── i
063  *                              └── j
064  * }</pre>
065  *
066  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
067  @version 5.0
068  @since 3.9
069  */
070 public class SingleNodeCrossover<
071     extends TreeGene<?, G>,
072     extends Comparable<? super C>
073 >
074     extends TreeCrossover<G, C>
075 {
076 
077     public SingleNodeCrossover(double probability) {
078         super(probability);
079     }
080 
081     public SingleNodeCrossover() {
082         this(DEFAULT_ALTER_PROBABILITY);
083     }
084 
085     @Override
086     protected <A> int crossover(final TreeNode<A> that, final TreeNode<A> other) {
087         return swap(that, other);
088     }
089 
090     // The static method makes it easier to test.
091     static <A> int swap(final TreeNode<A> that, final TreeNode<A> other) {
092         assert that != null;
093         assert other != null;
094 
095         final var random = RandomRegistry.random();
096 
097         final ISeq<TreeNode<A>> seq1 = that.breadthFirstStream()
098             .collect(ISeq.toISeq());
099 
100         final ISeq<TreeNode<A>> seq2 = other.breadthFirstStream()
101             .collect(ISeq.toISeq());
102 
103         final int changed;
104         if (seq1.length() && seq2.length() 1) {
105             final TreeNode<A> n1 = seq1.get(random.nextInt(seq1.length() 11);
106             final TreeNode<A> p1 = n1.parent().orElseThrow(AssertionError::new);
107 
108             final TreeNode<A> n2 = seq2.get(random.nextInt(seq2.length() 11);
109             final TreeNode<A> p2 = n2.parent().orElseThrow(AssertionError::new);
110 
111             final int i1 = p1.indexOf(n1);
112             final int i2 = p2.indexOf(n2);
113 
114             p1.insert(i1, n2.detach());
115             p2.insert(i2, n1.detach());
116 
117             changed = 2;
118         else {
119             changed = 0;
120         }
121 
122         return changed;
123     }
124 
125     @Override
126     public String toString() {
127         return format("SingleNodeCrossover[%f]", _probability);
128     }
129 
130 }