RetryConstraint.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-8.0.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  */
020 package io.jenetics.engine;
021 
022 import static java.util.Objects.requireNonNull;
023 
024 import java.util.function.Predicate;
025 
026 import io.jenetics.Gene;
027 import io.jenetics.Genotype;
028 import io.jenetics.Phenotype;
029 import io.jenetics.util.Factory;
030 
031 /**
032  * This simple {@code Constraint} implementation <em>repairs</em> an invalid
033  * phenotype by creating new individuals until a valid one has been created.
034  * If the probability of creating invalid individuals isn't to high, this is the
035  * preferred constraint implementation. E.g., if the probability of creating an
036  * invalid individual is 0.1, then the probability of creating an invalid
037  * phenotype after <em>n</em> retries, is 0.1<sup>n</sup>.
038  <p>
039  * The following example constraint checks a 2-dimensional point for validity.
040  * In this example, a point is considered as valid if it lies within the unit
041  * circle.
042  * {@snippet lang="java":
043  * InvertibleCodec<double[], DoubleGene> codec = Codecs.ofVector(DoubleRange.of(-1, 1), 2);
044  * Constraint<DoubleGene, Double> constraint = RetryConstraint.of(
045  *     codec,
046  *     p -> p[0]*p[0] + p[1]*p[1] <= 1
047  * );
048  * }
049  * The probability that a randomly created point lies outside the unit circle is
050  <em>1 - π/4 &asymp; 0.2146</em>. This leads to a failure probability after 10
051  * tries of <em>0.2146<sup>10</sup> &asymp; 0.000000207173567</em>. Since we are
052  * using an {@link InvertibleCodec}, it is much easier to implement our
053  * constraint. Otherwise, we would need to check the validity on the
054  {@link Phenotype} directly
055  *
056  * @apiNote
057  * This class is part of the more advanced API and is not needed for default use
058  * cases.
059  *
060  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
061  @version 5.2
062  @since 5.0
063  */
064 public final class RetryConstraint<
065     extends Gene<?, G>,
066     extends Comparable<? super C>
067 >
068     implements Constraint<G, C>
069 {
070 
071     /**
072      * The default retry-count for creating new, valid phenotypes.
073      */
074     public static final int DEFAULT_RETRY_COUNT = 10;
075 
076     private final Predicate<? super Phenotype<G, C>> _validator;
077     private final Factory<Genotype<G>> _genotypeFactory;
078     private final int _retryLimit;
079 
080     /**
081      * Create a new retry-constraint with the given parameters.
082      *
083      @param validator the phenotype validator
084      @param genotypeFactory the genotype factory used for creating new
085      *        phenotypes. The genotype factory may be {@code null}. In this case,
086      *        the phenotype to be repaired is used as a template.
087      @param retryLimit the limit of the phenotype creation retries. If more
088      *        re-creation tries are necessary, an invalid phenotype is returned.
089      *        This limit guarantees the termination of the
090      *        {@link #repair(Phenotype,long)} method.
091      @throws NullPointerException if the {@code validator} is {@code null}
092      */
093     public RetryConstraint(
094         final Predicate<? super Phenotype<G, C>> validator,
095         final Factory<Genotype<G>> genotypeFactory,
096         final int retryLimit
097     ) {
098         _validator = requireNonNull(validator);
099         _genotypeFactory = genotypeFactory;
100         _retryLimit = retryLimit;
101     }
102 
103     @Override
104     public boolean test(final Phenotype<G, C> individual) {
105         return _validator.test(individual);
106     }
107 
108     @Override
109     public Phenotype<G, C> repair(
110         final Phenotype<G, C> individual,
111         final long generation
112     ) {
113         final Factory<Genotype<G>> gtf = _genotypeFactory != null
114             ? _genotypeFactory
115             : individual.genotype();
116 
117         int count = 0;
118         Phenotype<G, C> phenotype;
119         do {
120             phenotype = Phenotype.of(gtf.newInstance(), generation);
121         while (++count < _retryLimit && !test(phenotype));
122 
123         return phenotype;
124     }
125 
126     /**
127      * Return a new constraint with the given genotype factory. The phenotype
128      * validator is set to {@link Phenotype#isValid()} and the retry counts to
129      {@link #DEFAULT_RETRY_COUNT}.
130      *
131      @param genotypeFactory the genotype factory used for creating new
132      *        phenotypes
133      @param <G> the gene type
134      @param <C> the fitness value type
135      @return a new constraint strategy
136      */
137     public static <G extends Gene<?, G>, C extends Comparable<? super C>>
138     RetryConstraint<G, C> of(final Factory<Genotype<G>> genotypeFactory) {
139         return new RetryConstraint<>(
140             Phenotype::isValid,
141             genotypeFactory,
142             DEFAULT_RETRY_COUNT
143         );
144     }
145 
146     /**
147      * Return a new constraint with the given {@code validator} and the
148      {@link #DEFAULT_RETRY_COUNT}.
149      *
150      @param validator the phenotype validator
151      @param <G> the gene type
152      @param <C> the fitness value type
153      @return a new constraint strategy
154      @throws NullPointerException if the {@code validator} is {@code null}
155      */
156     public static <G extends Gene<?, G>, C extends Comparable<? super C>>
157     RetryConstraint<G, C> of(final Predicate<? super Phenotype<G, C>> validator) {
158         return new RetryConstraint<>(
159             validator,
160             null,
161             DEFAULT_RETRY_COUNT
162         );
163     }
164 
165     /**
166      * Return a new constraint with the given {@code validator} and the
167      {@link #DEFAULT_RETRY_COUNT}.
168      *
169      @since 5.2
170      *
171      @param codec the invertible codec used for simplify the needed
172      *        validator
173      @param validator the phenotype validator
174      @param <T> the type of the <em>native</em> problem domain
175      @param <G> the gene type
176      @param <C> the fitness value type
177      @return a new constraint strategy
178      @throws NullPointerException if the {@code codec} or {@code validator} is
179      *         {@code null}
180      */
181     public static <T, G extends Gene<?, G>, C extends Comparable<? super C>>
182     Constraint<G, C> of(
183         final InvertibleCodec<T, G> codec,
184         final Predicate<? super T> validator
185     ) {
186         return of(pt -> validator.test(codec.decode(pt.genotype())));
187     }
188 
189     /**
190      * Return a new constraint with the given {@code validator} and
191      * {@code retryLimit}.
192      *
193      @param validator the phenotype validator
194      @param retryLimit the limit of the phenotype creation retries. If more
195      *        re-creation tries are necessary, an invalid phenotype is returned.
196      *        This limit guarantees the termination of the
197      *        {@link #repair(Phenotype, long)} method.
198      @param <G> the gene type
199      @param <C> the fitness value type
200      @return a new constraint strategy
201      */
202     public static <G extends Gene<?, G>, C extends Comparable<? super C>>
203     RetryConstraint<G, C> of(
204         final Predicate<? super Phenotype<G, C>> validator,
205         final int retryLimit
206     ) {
207         return new RetryConstraint<>(
208             validator,
209             null,
210             retryLimit
211         );
212     }
213 
214     /**
215      * Return a new constraint with the given {@code validator} and
216      * {@code retryLimit}.
217      *
218      @since 5.2
219      *
220      @param codec the invertible codec used for simplify the needed
221      *        validator
222      @param validator the phenotype validator
223      @param retryLimit the limit of the phenotype creation retries. If more
224      *        re-creation tries are necessary, an invalid phenotype is returned.
225      *        This limit guarantees the termination of the
226      *        {@link #repair(Phenotype, long)} method.
227      @param <T> the type of the <em>native</em> problem domain
228      @param <G> the gene type
229      @param <C> the fitness value type
230      @return a new constraint strategy
231      @throws NullPointerException if the {@code codec} or {@code validator} is
232      *         {@code null}
233      */
234     public static <T, G extends Gene<?, G>, C extends Comparable<? super C>>
235     Constraint<G, C> of(
236         final InvertibleCodec<T, G> codec,
237         final Predicate<? super T> validator,
238         final int retryLimit
239     ) {
240         return of(
241             pt -> validator.test(codec.decode(pt.genotype())),
242             retryLimit
243         );
244     }
245 
246 }