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 */
020package io.jenetics.engine;
021
022import static java.util.Objects.requireNonNull;
023
024import java.util.function.Predicate;
025
026import io.jenetics.Gene;
027import io.jenetics.Genotype;
028import io.jenetics.Phenotype;
029import 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 */
064public final class RetryConstraint<
065        G extends Gene<?, G>,
066        C 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}