001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.0). 003 * Copyright (c) 2007-2024 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 */ 020package io.jenetics.engine; 021 022import static java.util.Objects.requireNonNull; 023 024import java.util.function.Predicate; 025import java.util.function.Supplier; 026 027import io.jenetics.Gene; 028import io.jenetics.Genotype; 029import io.jenetics.Phenotype; 030import io.jenetics.util.ISeq; 031 032/** 033 * This interface defines the capability of creating {@link EvolutionStream}s 034 * from a given {@link EvolutionStart} object. It also decouples the engine's 035 * capability from the capability to create evolution streams. The purpose of 036 * this interface is similar to the {@link Iterable} interface. 037 * 038 * @see EvolutionStream 039 * 040 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 041 * @version 4.1 042 * @since 4.1 043 */ 044public interface EvolutionStreamable< 045 G extends Gene<?, G>, 046 C extends Comparable<? super C> 047> { 048 049 /** 050 * Create a new, possibly <em>infinite</em>, evolution stream with the given 051 * evolution start. If an empty {@code Population} is given, the engine's 052 * genotype factory is used for creating the population. The given 053 * population might be the result of another engine, and this method allows 054 * to start the evolution with the outcome of a different engine. 055 * The fitness function is replaced by the one defined for this engine. 056 * 057 * @param start the data the evolution stream starts with 058 * @return a new <b>infinite</b> evolution stream 059 * @throws java.lang.NullPointerException if the given evolution 060 * {@code start} is {@code null}. 061 */ 062 EvolutionStream<G, C> 063 stream(final Supplier<EvolutionStart<G, C>> start); 064 065 /** 066 * Create a new, possibly <em>infinite</em>, evolution stream with the given 067 * initial value. If an empty {@code Population} is given, the engines genotype 068 * factory is used for creating the population. The given population might 069 * be the result of another engine, and this method allows to start the 070 * evolution with the outcome of a different engine. The fitness function 071 * is replaced by the one defined for this engine. 072 * 073 * @param init the data the evolution stream is initialized with 074 * @return a new <b>infinite</b> evolution stream 075 * @throws java.lang.NullPointerException if the given evolution 076 * {@code start} is {@code null}. 077 */ 078 EvolutionStream<G, C> stream(final EvolutionInit<G> init); 079 080 081 /* ************************************************************************* 082 * Default interface methods. 083 * ************************************************************************/ 084 085 /** 086 * Create a new, possibly <em>infinite</em>, evolution stream with a newly 087 * created population. This method is a shortcut for 088 * {@snippet lang="java": 089 * final EvolutionStream<G, C> stream = streamable 090 * .stream(() -> EvolutionStart.of(ISeq.empty(), 1)); 091 * } 092 * 093 * @return a new evolution stream. 094 */ 095 default EvolutionStream<G, C> stream() { 096 return stream((Supplier<EvolutionStart<G, C>>)EvolutionStart::empty); 097 } 098 099 /** 100 * Create a new, possibly <em>infinite</em>, evolution stream with the given 101 * evolution start. If an empty {@code Population} is given, the engine's genotype 102 * factory is used for creating the population. The given population might 103 * be the result of another engine, and this method allows to start the 104 * evolution with the outcome of a different engine. The fitness function 105 * is replaced by the one defined for this engine. 106 * 107 * @param start the data the evolution stream starts with 108 * @return a new <b>infinite</b> evolution iterator 109 * @throws java.lang.NullPointerException if the given evolution 110 * {@code start} is {@code null}. 111 */ 112 default EvolutionStream<G, C> 113 stream(final EvolutionStart<G, C> start) { 114 return stream(() -> start); 115 } 116 117 /** 118 * Create a new {@code EvolutionStream} starting with a previously evolved 119 * {@link EvolutionResult}. The stream is initialized with the population 120 * of the given {@code result} and its total generation 121 * {@link EvolutionResult#totalGenerations()}. 122 * {@snippet lang="java": 123 * private static final Problem<Double, DoubleGene, Double> 124 * PROBLEM = Problem.of( 125 * x -> cos(0.5 + sin(x))*cos(x), 126 * Codecs.ofScalar(DoubleRange.of(0.0, 2.0*PI)) 127 * ); 128 * 129 * private static final Engine<DoubleGene, Double> 130 * ENGINE = Engine.builder(PROBLEM) 131 * .optimize(Optimize.MINIMUM) 132 * .offspringSelector(new RouletteWheelSelector<>()) 133 * .build(); 134 * 135 * public static void main(final String[] args) throws IOException { 136 * // Result of the first evolution run. 137 * final EvolutionResult<DoubleGene, Double> rescue = ENGINE.stream() 138 * .limit(Limits.bySteadyFitness(10)) 139 * .collect(EvolutionResult.toBestEvolutionResult()); 140 * 141 * // Save the result of the first run into a file. 142 * final Path path = Paths.get("result.bin"); 143 * IO.object.write(rescue, path); 144 * 145 * // Load the previous result and continue evolution. 146 * @SuppressWarnings("unchecked") 147 * final EvolutionResult<DoubleGene, Double> result = ENGINE 148 * .stream((EvolutionResult<DoubleGene, Double>)IO.object.read(path)) 149 * .limit(Limits.bySteadyFitness(20)) 150 * .collect(EvolutionResult.toBestEvolutionResult()); 151 * 152 * System.out.println(result.bestPhenotype()); 153 * } 154 * } 155 * 156 * The example above shows how to save an {@link EvolutionResult} from a 157 * first run, save it to disk and continue the evolution. 158 * 159 * @param result the previously evolved {@code EvolutionResult} 160 * @return a new evolution stream, which continues a previous one 161 * @throws NullPointerException if the given evolution {@code result} is 162 * {@code null} 163 */ 164 default EvolutionStream<G, C> 165 stream(final EvolutionResult<G, C> result) { 166 return stream(EvolutionStart.of( 167 result.population(), 168 result.generation() 169 )); 170 } 171 172 /** 173 * Create a new, possibly <em>infinite</em>, evolution stream with the given 174 * initial population. If an empty {@code Population} is given, the engine's 175 * genotype factory is used for creating the population. The given population 176 * might be the result of another engine, and this method allows to start the 177 * evolution with the outcome of a different engine. The fitness function 178 * is replaced by the one defined for this engine. 179 * 180 * @param population the initial individuals used for the evolution stream. 181 * Missing individuals are created and individuals not needed are 182 * skipped. 183 * @param generation the generation the stream starts from; must be greater 184 * than zero. 185 * @return a new evolution stream. 186 * @throws java.lang.NullPointerException if the given {@code population} is 187 * {@code null}. 188 * @throws IllegalArgumentException if the given {@code generation} is 189 * smaller then one 190 */ 191 default EvolutionStream<G, C> stream( 192 final ISeq<Phenotype<G, C>> population, 193 final long generation 194 ) { 195 return stream(EvolutionStart.of(population, generation)); 196 } 197 198 /** 199 * Create a new, possibly <em>infinite</em>, evolution stream with the given 200 * initial population. If an empty {@code Population} is given, the engine's 201 * genotype factory is used for creating the population. The given population 202 * might be the result of another engine, and this method allows to start the 203 * evolution with the outcome of a different engine. The fitness function 204 * is replaced by the one defined for this engine. 205 * 206 * @param population the initial individuals used for the evolution stream. 207 * Missing individuals are created and individuals not needed are 208 * skipped. 209 * @return a new evolution stream. 210 * @throws java.lang.NullPointerException if the given {@code population} is 211 * {@code null}. 212 */ 213 default EvolutionStream<G, C> stream(final ISeq<Phenotype<G, C>> population) { 214 return stream(EvolutionStart.of(population, 1)); 215 } 216 217 /** 218 * Create a new, possibly <em>infinite</em>, evolution stream with the given 219 * initial individuals. If an empty {@code Iterable} is given, the engine's 220 * genotype factory is used for creating the population. 221 * 222 * @param genotypes the initial individuals used for the evolution stream. 223 * Missing individuals are created and individuals not needed are 224 * skipped. 225 * @param generation the generation the stream starts from; must be greater 226 * than zero. 227 * @return a new evolution stream. 228 * @throws java.lang.NullPointerException if the given {@code genotypes} is 229 * {@code null}. 230 * @throws IllegalArgumentException if the given {@code generation} is 231 * smaller then one 232 */ 233 default EvolutionStream<G, C> stream( 234 final Iterable<Genotype<G>> genotypes, 235 final long generation 236 ) { 237 return stream(EvolutionInit.of(ISeq.of(genotypes), generation)); 238 } 239 240 /** 241 * Create a new, possibly <em>infinite</em>, evolution stream with the given 242 * initial individuals. If an empty {@code Iterable} is given, the engine's 243 * genotype factory is used for creating the population. 244 * 245 * @param genotypes the initial individuals used for the evolution stream. 246 * Missing individuals are created and individuals not needed are 247 * skipped. 248 * @return a new evolution stream. 249 * @throws java.lang.NullPointerException if the given {@code genotypes} is 250 * {@code null}. 251 */ 252 default EvolutionStream<G, C> 253 stream(final Iterable<Genotype<G>> genotypes) { 254 return stream(genotypes, 1); 255 } 256 257 /** 258 * Return a new {@code EvolutionStreamable} instance where all created 259 * {@code EvolutionStream}s are limited by the given predicate. Since some 260 * predicates have to maintain internal state, a predicate {@code Supplier} 261 * must be given instead a plain limiting predicate. 262 * 263 * @param proceed the limiting predicate supplier. 264 * @return a new evolution-streamable object 265 * @throws NullPointerException if the give {@code predicate} is {@code null} 266 */ 267 default EvolutionStreamable<G, C> 268 limit(final Supplier<? extends Predicate<? super EvolutionResult<G, C>>> proceed) { 269 requireNonNull(proceed); 270 271 return new EvolutionStreamable<>() { 272 @Override 273 public EvolutionStream<G, C> 274 stream(final Supplier<EvolutionStart<G, C>> start) { 275 return EvolutionStreamable.this.stream(start).limit(proceed.get()); 276 } 277 278 @Override 279 public EvolutionStream<G, C> stream(final EvolutionInit<G> init) { 280 return EvolutionStreamable.this.stream(init).limit(proceed.get()); 281 } 282 }; 283 } 284 285 /** 286 * Return a new {@code EvolutionStreamable} instance where all created 287 * {@code EvolutionStream}s are limited to the given number of generations. 288 * 289 * @param generations the number of generations after the created evolution 290 * streams are truncated 291 * @return a new evolution-streamable object 292 * @throws IllegalArgumentException if the given {@code generations} is 293 * smaller than zero. 294 */ 295 default EvolutionStreamable<G, C> limit(final long generations) { 296 return limit(() -> Limits.byFixedGeneration(generations)); 297 } 298 299}