001/*
002 * Java Genetic Algorithm Library (jenetics-8.3.0).
003 * Copyright (c) 2007-2025 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;
021
022import static io.jenetics.internal.util.Hashes.hash;
023import static io.jenetics.internal.util.SerialIO.readInt;
024import static io.jenetics.internal.util.SerialIO.writeInt;
025
026import java.io.IOException;
027import java.io.InvalidObjectException;
028import java.io.ObjectInput;
029import java.io.ObjectInputStream;
030import java.io.ObjectOutput;
031import java.io.Serial;
032import java.io.Serializable;
033import java.util.Objects;
034
035import io.jenetics.util.BaseSeq;
036import io.jenetics.util.Factory;
037import io.jenetics.util.ISeq;
038import io.jenetics.util.MSeq;
039import io.jenetics.util.Verifiable;
040
041/**
042 * The central class the GA is working with, is the {@code Genotype}. It is the
043 * structural representative of an individual. This class is the encoded problem
044 * solution with one to many {@link Chromosome}.
045 * <p>
046 * <img alt="Genotype" src="doc-files/Genotype.svg" width="400" height="252" >
047 * </p>
048 * The chromosomes of a genotype don't have to have necessarily the same size.
049 * It is only required that all genes are from the same type and the genes within
050 * a chromosome have the same constraints; e.g., the same min- and max values
051 * for the genes value.
052 * {@snippet lang="java":
053 * final Genotype<DoubleGene> genotype = Genotype.of(
054 *     DoubleChromosome.of(0.0, 1.0, 8),
055 *     DoubleChromosome.of(1.0, 2.0, 10),
056 *     DoubleChromosome.of(0.0, 10.0, 9),
057 *     DoubleChromosome.of(0.1, 0.9, 5)
058 * );
059 * }
060 * The code snippet above creates a genotype with the same structure as shown in
061 * the figure above. In this example the {@link DoubleGene} has been chosen as
062 * a gene type.
063 *
064 * @see Chromosome
065 * @see Phenotype
066 *
067 * @implNote
068 * This class is immutable and thread-safe.
069 *
070 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
071 * @since 1.0
072 * @version 7.2
073 */
074public final class Genotype<G extends Gene<?, G>>
075        implements
076                BaseSeq<Chromosome<G>>,
077                Factory<Genotype<G>>,
078                Verifiable,
079                Serializable
080{
081        @Serial
082        private static final long serialVersionUID = 3L;
083
084        private final ISeq<Chromosome<G>> _chromosomes;
085
086        //Caching isValid value.
087        private byte _valid = -1;
088
089        /**
090         * Create a new Genotype from a given sequence of {@code Chromosomes}.
091         *
092         * @param chromosomes The {@code Chromosome} array the {@code Genotype}
093         *         consists of
094         * @throws NullPointerException if {@code chromosomes} is null or one of its
095         *         elements
096         * @throws IllegalArgumentException if {@code chromosome.length == 0}
097         */
098        Genotype(final ISeq<? extends Chromosome<G>> chromosomes) {
099                if (chromosomes.isEmpty()) {
100                        throw new IllegalArgumentException("No chromosomes given.");
101                }
102
103                _chromosomes = ISeq.upcast(chromosomes);
104        }
105
106        /**
107         * Return the chromosome at the given index. It is guaranteed that the
108         * returned chromosome is not null.
109         *
110         * @since 4.0
111         *
112         * @param index the chromosome index
113         * @return the chromosome with the given index
114         * @throws IndexOutOfBoundsException if
115         *         {@code (index < 0 || index >= _length)}.
116         */
117        @Override
118        public Chromosome<G> get(final int index) {
119                return _chromosomes.get(index);
120        }
121
122        /**
123         * Getting the number of chromosomes of this genotype.
124         *
125         * @return number of chromosomes.
126         */
127        @Override
128        public int length() {
129                return _chromosomes.length();
130        }
131
132        /**
133         * Return the first chromosome. This is an alias for
134         * {@snippet lang="java":
135         * final Genotype<DoubleGene> gt = null; // @replace substring='null' replacement="..."
136         * final Chromosome<DoubleGene> chromosome = gt.get(0);
137         * }
138         *
139         * @since 5.2
140         *
141         * @return The first chromosome.
142         */
143        public Chromosome<G> chromosome() {
144                return get(0);
145        }
146
147        /**
148         * Return the first {@link Gene} of the first {@link Chromosome} of this
149         * {@code Genotype}. This is an alias for
150         * {@snippet lang="java":
151         * final Genotype<DoubleGene> gt = null; // @replace substring='null' replacement="..."
152         * final DoubleGene gene = gt.get(0).get(0);
153         * }
154         *
155         * @since 5.2
156         *
157         * @return the first {@link Gene} of the first {@link Chromosome} of this
158         *         {@code Genotype}.
159         */
160        public G gene() {
161                return get(0).get(0);
162        }
163
164        /**
165         * Return the number of genes this genotype consists of. This is the sum of
166         * the number of genes of the genotype chromosomes.
167         *
168         * @return Return the number of genes this genotype consists of
169         */
170        public int geneCount() {
171                int count = 0;
172                for (var chromosome : this) {
173                        count += chromosome.length();
174                }
175                return count;
176        }
177
178        /**
179         * Test if this genotype is valid. A genotype is valid if all its
180         * {@link Chromosome}s are valid.
181         *
182         * @return true if this genotype is valid, false otherwise.
183         */
184        @Override
185        public boolean isValid() {
186                byte valid = _valid;
187                if (valid == -1) {
188                        valid = (byte)(_chromosomes.forAll(Verifiable::isValid) ? 1 : 0);
189                        _valid = valid;
190                }
191
192                return _valid == 1;
193        }
194
195        /**
196         * Create a new Genotype which consists of the chromosomes from the given
197         * {@code fromIndex} (inclusively) to the given {@code toIndex} (exclusively).
198         * This method creates a <em>view</em> of the underlying chromosomes.
199         *
200         * @since 7.2
201         *
202         * @param fromIndex the start chromosome index, inclusively
203         * @param toIndex the end chromosome index, exclusively
204         * @return a new genotype consisting of the chromosomes within the given
205         *         indexes
206         * @throws IndexOutOfBoundsException for an illegal end point index value
207         *          ({@code fromIndex < 0 || toIndex > length() || fromIndex > toIndex}).
208         */
209        public Genotype<G> slice(int fromIndex, int toIndex) {
210                return new Genotype<>(_chromosomes.subSeq(fromIndex, toIndex));
211        }
212
213        /**
214         * Return a new, random genotype by creating new, random chromosomes (calling
215         * the {@link Chromosome#newInstance()} method) from the chromosomes of this
216         * genotype.
217         */
218        @Override
219        public Genotype<G> newInstance() {
220                return new Genotype<>(_chromosomes.map(Factory::newInstance));
221        }
222
223        @Override
224        public int hashCode() {
225                return hash(_chromosomes);
226        }
227
228        @Override
229        public boolean equals(final Object obj) {
230                return obj instanceof Genotype<?> other &&
231                        Objects.equals(_chromosomes, other._chromosomes);
232        }
233
234        @Override
235        public String toString() {
236                return _chromosomes.toString();
237        }
238
239        /**
240         * Create a new {@code Genotype} from a given array of {@code Chromosomes}.
241         *
242         * @since 3.0
243         *
244         * @param <G> the gene type
245         * @param first the first {@code Chromosome} of the {@code Genotype}
246         * @param rest the rest of the genotype chromosomes.
247         * @return a new {@code Genotype} from the given chromosomes
248         * @throws NullPointerException if {@code chromosomes} is {@code null} or
249         *         one of its elements.
250         */
251        @SafeVarargs
252        public static <G extends Gene<?, G>> Genotype<G> of(
253                final Chromosome<G> first,
254                final Chromosome<G>... rest
255        ) {
256                final MSeq<Chromosome<G>> seq = MSeq.ofLength(1 + rest.length);
257                seq.set(0, first);
258                for (int i = 0; i < rest.length; ++i) {
259                        seq.set(i + 1, rest[i]);
260                }
261                return new Genotype<>(seq.toISeq());
262        }
263
264        /**
265         * Create a new {@code Genotype} which consists of {@code n} chromosomes,
266         * which are created by the given {@code factory}. This method can be used
267         * for easily creating a <i>gene matrix</i>. The following example will
268         * create a 10x5 {@code DoubleGene} <i>matrix</i>.
269         * {@snippet lang="java":
270         * final Genotype<DoubleGene> gt = Genotype
271         *     .of(DoubleChromosome.of(0.0, 1.0, 10), 5);
272         * }
273         *
274         * @since 3.0
275         *
276         * @param <G> the gene type
277         * @param factory the factory which creates the chromosomes this genotype
278         *        consists of
279         * @param n the number of chromosomes this genotype consists of
280         * @return new {@code Genotype} containing {@code n} chromosomes
281         * @throws IllegalArgumentException if {@code n < 1}
282         * @throws NullPointerException if the {@code factory} is {@code null}
283         */
284        public static <G extends Gene<?, G>> Genotype<G>
285        of(final Factory<? extends Chromosome<G>> factory, final int n) {
286                final ISeq<Chromosome<G>> ch = ISeq.of(factory::newInstance, n);
287                return new Genotype<>(ch);
288        }
289
290        /**
291         * Create a new {@code Genotype} from a given array of {@code chromosomes}.
292         *
293         * @since 3.0
294         *
295         * @param <G> the gene type
296         * @param chromosomes the {@code Chromosome}s the returned genotype consists
297         *        of
298         * @return a new {@code Genotype} from the given chromosomes
299         * @throws NullPointerException if {@code chromosomes} is {@code null} or
300         *         one of its elements.
301         * @throws IllegalArgumentException if {@code chromosome.length() < 1}
302         */
303        public static <G extends Gene<?, G>> Genotype<G>
304        of(final Iterable<? extends Chromosome<G>> chromosomes) {
305                return new Genotype<>(ISeq.of(chromosomes));
306        }
307
308
309        /* *************************************************************************
310         *  Java object serialization
311         * ************************************************************************/
312
313        @Serial
314        private Object writeReplace() {
315                return new SerialProxy(SerialProxy.GENOTYPE, this);
316        }
317
318        @Serial
319        private void readObject(final ObjectInputStream stream)
320                throws InvalidObjectException
321        {
322                throw new InvalidObjectException("Serialization proxy required.");
323        }
324
325        void write(final ObjectOutput out) throws IOException {
326                writeInt(_chromosomes.length(), out);
327                for (var ch : _chromosomes) {
328                        out.writeObject(ch);
329                }
330        }
331
332        @SuppressWarnings({"unchecked", "rawtypes"})
333        static Object read(final ObjectInput in)
334                throws IOException, ClassNotFoundException
335        {
336                final int length = readInt(in);
337                final MSeq chromosomes = MSeq.ofLength(length);
338                for (int i = 0; i < length; ++i) {
339                        chromosomes.set(i, in.readObject());
340                }
341
342                return new Genotype(chromosomes.asISeq());
343        }
344
345}