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.Optional;
025import java.util.function.Function;
026
027import io.jenetics.Gene;
028import io.jenetics.Genotype;
029
030/**
031 * This interface describes a <i>problem</i> which can be solved by the GA
032 * evolution {@code Engine}. It connects the actual {@link #fitness()} function
033 * and the needed {@link #codec()}.
034 *
035 * {@snippet lang="java":
036 * final Problem<ISeq<BitGene>, BitGene, Integer> counting = Problem.of(
037 *     // Native fitness function
038 *     genes -> (int)genes.stream()
039 *         .filter(BitGene::bit)
040 *         .count(),
041 *     // Problem encoding
042 *     Codec.of(
043 *         Genotype.of(BitChromosome.of(100)),
044 *         gt -> ISeq.of(gt.chromosome())
045 *     )
046 * );
047 * }
048 *
049 * The example above shows the Ones-Counting problem definition.
050 *
051 * @see Codec
052 * @see Engine
053 *
054 * @param <T> the (<i>native</i>) argument type of the problem fitness function
055 * @param <G> the gene type the evolution engine is working with
056 * @param <C> the result type of the fitness function
057 *
058 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
059 * @version 6.1
060 * @since 3.4
061 */
062public interface Problem<
063        T,
064        G extends Gene<?, G>,
065        C extends Comparable<? super C>
066> {
067
068        /**
069         * Return the fitness function of the <i>problem</i> in the <i>native</i>
070         * problem domain.
071         *
072         * @return the fitness function
073         */
074        Function<T, C> fitness();
075
076        /**
077         * Return the codec, which translates the types of the problem domain into
078         * types, which can be understood by the evolution {@code Engine}.
079         *
080         * @return the engine codec
081         */
082        Codec<T, G> codec();
083
084        /**
085         * Return the constraint, associated with {@code this} problem, if available.
086         *
087         * @since 6.1
088         *
089         * @return the constraint, associated with {@code this} problem
090         */
091        default Optional<Constraint<G, C>> constraint() {
092                return Optional.empty();
093        }
094
095        /**
096         * Converts the given {@link Genotype} to the target type {@link T}. This is
097         * a shortcut for
098         * {@snippet lang="java":
099         * final Problem<SomeObject, DoubleGene, Double> problem = null; // @replace substring='null' replacement="..."
100         * final Genotype<DoubleGene> gt = problem.codec().encoding().newInstance();
101         *
102         * final SomeObject arg = problem.decode(gt);
103         * }
104         *
105         * @since 4.2
106         *
107         * @see Codec#decode(Genotype)
108         *
109         * @param genotype the genotype to be converted
110         * @return the converted genotype
111         * @throws NullPointerException if the given {@code genotype} is {@code null}
112         */
113        default T decode(final Genotype<G> genotype) {
114                return codec().decode(genotype);
115        }
116
117        /**
118         * Returns the fitness value for the given argument.
119         *
120         * @since 4.1
121         *
122         * @param arg the argument of the fitness function
123         * @return the fitness value
124         */
125        default C fitness(final T arg) {
126                return fitness().apply(arg);
127        }
128
129        /**
130         * Returns the fitness value for the given argument.
131         *
132         * @since 4.1
133         *
134         * @param genotype the argument of the fitness function
135         * @return the fitness value
136         */
137        default C fitness(final Genotype<G> genotype) {
138                return fitness(codec().decode(genotype));
139        }
140
141        /**
142         * Return a new optimization <i>problem</i> with the given parameters. The
143         * given {@code constraint} is applied to the {@link Engine}, via
144         * {@link Engine.Builder#constraint(Constraint)}, and the {@link Codec}, via
145         * {@link Constraint#constrain(Codec)}.
146         * <p>
147         * <b>Note</b><br>
148         *     When creating a new {@code Problem} instance with this factory method,
149         *     there is no need for additionally <em>constraining</em> the given
150         *     {@code codec} with {@link Constraint#constrain(Codec)}.
151         *
152         * @since 6.1
153         *
154         * @see Engine.Builder#constraint(Constraint)
155         * @see Constraint#constrain(Codec)
156         *
157         * @param fitness the problem fitness function
158         * @param codec the evolution engine codec
159         * @param constraint the problem constraint, may be {@code null}
160         * @param <T> the (<i>native</i>) argument type of the problem fitness function
161         * @param <G> the gene type the evolution engine is working with
162         * @param <C> the result type of the fitness function
163         * @return a new problem object from the given parameters
164         * @throws NullPointerException if the {@code fitness} or {@code codec} is
165         *         {@code null}
166         */
167        static <T, G extends Gene<?, G>, C extends Comparable<? super C>>
168        Problem<T, G, C> of(
169                final Function<T, C> fitness,
170                final Codec<T, G> codec,
171                final Constraint<G, C> constraint
172        ) {
173                requireNonNull(fitness);
174                requireNonNull(codec);
175
176                final var constrainedCodec = wrap(constraint, codec);
177
178                return new Problem<>() {
179                        @Override
180                        public Codec<T, G> codec() {
181                                return constrainedCodec;
182                        }
183                        @Override
184                        public Function<T, C> fitness() {
185                                return fitness;
186                        }
187                        @Override
188                        public Optional<Constraint<G, C>> constraint() {
189                                return Optional.ofNullable(constraint);
190                        }
191                };
192        }
193
194        private static  <T, G extends Gene<?, G>, C extends Comparable<? super C>>
195        Codec<T, G> wrap(final Constraint<G, C> constraint, final Codec<T, G> codec) {
196                Codec<T, G> result = codec;
197                if (constraint != null) {
198                        result = codec instanceof InvertibleCodec<T, G> ic
199                                ? constraint.constrain(ic)
200                                : constraint.constrain(codec);
201                }
202
203                return result;
204        }
205
206        /**
207         * Return a new optimization <i>problem</i> with the given parameters.
208         *
209         * @param fitness the problem fitness function
210         * @param codec the evolution engine codec
211         * @param <T> the (<i>native</i>) argument type of the problem fitness function
212         * @param <G> the gene type the evolution engine is working with
213         * @param <C> the result type of the fitness function
214         * @return a new problem object from the given parameters
215         * @throws NullPointerException if one of the arguments is {@code null}
216         */
217        static <T, G extends Gene<?, G>, C extends Comparable<? super C>>
218        Problem<T, G, C> of(
219                final Function<T, C> fitness,
220                final Codec<T, G> codec
221        ) {
222                return of(fitness, codec, null);
223        }
224
225}
226