ProgramGene.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-7.1.0).
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.prog;
021 
022 import static java.lang.String.format;
023 import static java.util.Objects.requireNonNull;
024 
025 import java.io.Serial;
026 import java.util.function.Function;
027 
028 import io.jenetics.Gene;
029 import io.jenetics.util.ISeq;
030 import io.jenetics.util.RandomRegistry;
031 
032 import io.jenetics.ext.AbstractTreeGene;
033 import io.jenetics.ext.util.TreeNode;
034 
035 import io.jenetics.prog.op.Op;
036 import io.jenetics.prog.op.Program;
037 
038 /**
039  * This gene represents a program, build upon an AST of {@link Op} functions.
040  * Because of the tight coupling with the {@link ProgramChromosome}, a
041  * {@code ProgramGene} can't be created directly. This reduces the the possible
042  <em>error space</em>. Since the {@code ProgramGene} also is a {@code Tree},
043  * it can be easily used as result.
044  *
045  <pre>{@code
046  * final ProgramGene<Double> program = engine.stream()
047  *     .limit(300)
048  *     .collect(EvolutionResult.toBestGenotype())
049  *     .getGene();
050  *
051  * final double result = program.eval(3.4);
052  * }</pre>
053  *
054  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
055  @version 5.2
056  @since 3.9
057  */
058 public final class ProgramGene<A>
059     extends AbstractTreeGene<Op<A>, ProgramGene<A>>
060     implements Gene<Op<A>, ProgramGene<A>>, Function<A[], A>
061 {
062 
063     @Serial
064     private static final long serialVersionUID = 1L;
065 
066     private final ISeq<? extends Op<A>> _operations;
067     private final ISeq<? extends Op<A>> _terminals;
068 
069     ProgramGene(
070         final Op<A> op,
071         final int childOffset,
072         final ISeq<? extends Op<A>> operations,
073         final ISeq<? extends Op<A>> terminals
074     ) {
075         super(requireNonNull(get(op)), childOffset, op.arity());
076         _operations = requireNonNull(operations);
077         _terminals = requireNonNull(terminals);
078     }
079 
080     private static <A> Op<A> get(final Op<A> op) {
081         final Op<A> instance = op.get();
082         if (instance != op && instance.arity() != op.arity()) {
083             throw new IllegalArgumentException(format(
084                 "Original op and created op have different arity: %d != %d,",
085                 instance.arity(), op.arity()
086             ));
087         }
088         return instance;
089     }
090 
091     /**
092      * Evaluates this program gene (recursively) with the given variable values.
093      *
094      @see ProgramGene#eval(Object[])
095      @see ProgramChromosome#eval(Object[])
096      *
097      @param args the input variables
098      @return the evaluated value
099      @throws NullPointerException if the given variable array is {@code null}
100      */
101     @Override
102     public A apply(final A[] args) {
103         checkTreeState();
104         return Program.eval(this, args);
105     }
106 
107     /**
108      * Convenient method, which lets you apply the program function without
109      * explicitly create a wrapper array.
110      *
111      @see ProgramGene#apply(Object[])
112      @see ProgramChromosome#eval(Object[])
113      *
114      @param args the function arguments
115      @return the evaluated value
116      @throws NullPointerException if the given variable array is {@code null}
117      */
118     @SafeVarargs
119     public final A eval(final A... args) {
120         return apply(args);
121     }
122 
123     /**
124      * Return the allowed operations.
125      *
126      @return the allowed operations
127      */
128     public ISeq<Op<A>> operations() {
129         return ISeq.upcast(_operations);
130     }
131 
132     /**
133      * Return the allowed terminal operations.
134      *
135      @return the allowed terminal operations
136      */
137     public ISeq<Op<A>> terminals() {
138         return ISeq.upcast(_terminals);
139     }
140 
141     /**
142      * Creates a new {@link TreeNode} from this program gene.
143      *
144      @since 5.0
145      *
146      @return a new tree node value build from this program gene
147      */
148     public TreeNode<Op<A>> toTreeNode() {
149         return TreeNode.ofTree(this);
150     }
151 
152     @Override
153     public ProgramGene<A> newInstance() {
154         final var random = RandomRegistry.random();
155 
156         Op<A> operation = value();
157         if (isLeaf()) {
158             operation = _terminals.get(random.nextInt(_terminals.length()));
159         else {
160             final ISeq<Op<A>> operations = _operations.stream()
161                 .filter(op -> op.arity() == value().arity())
162                 .collect(ISeq.toISeq());
163 
164             if (operations.length() 1) {
165                 operation = operations.get(random.nextInt(operations.length()));
166             }
167         }
168 
169         return newInstance(operation);
170     }
171 
172     /**
173      * Create a new program gene with the given operation.
174      *
175      @param op the operation of the new program gene
176      @return a new program gene with the given operation
177      @throws NullPointerException if the given {@code op} is {@code null}
178      @throws IllegalArgumentException if the arity of the given operation is
179      *         different from the arity of current operation. This restriction
180      *         ensures that only valid program genes are created by this method.
181      */
182     @Override
183     public ProgramGene<A> newInstance(final Op<A> op) {
184         if (value().arity() != op.arity()) {
185             throw new IllegalArgumentException(format(
186                 "New operation must have same arity: %s[%d] != %s[%d]",
187                 value().name(), value().arity(), op.name(), op.arity()
188             ));
189         }
190         return new ProgramGene<>(op, childOffset(), _operations, _terminals);
191     }
192 
193     /**
194      * Return a new program gene with the given operation and the <em>local</em>
195      * tree structure.
196      *
197      @param op the new operation
198      @param childOffset the offset of the first node child within the
199      *        chromosome
200      @param childCount the number of children of the new tree gene
201      @return a new tree gene with the given parameters
202      @throws IllegalArgumentException  if the {@code childCount} is smaller
203      *         than zero
204      @throws IllegalArgumentException if the operation arity is different from
205      *         the {@code childCount}.
206      @throws NullPointerException if the given {@code op} is {@code null}
207      */
208     @Override
209     public ProgramGene<A> newInstance(
210         final Op<A> op,
211         final int childOffset,
212         final int childCount
213     ) {
214         if (op.arity() != childCount) {
215             throw new IllegalArgumentException(format(
216                 "Operation arity and child count are different: %d, != %d",
217                 op.arity(), childCount
218             ));
219         }
220 
221         return new ProgramGene<>(op, childOffset, _operations, _terminals);
222     }
223 
224 }