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;
021
022import static java.util.Objects.requireNonNull;
023import static io.jenetics.internal.util.SerialIO.readInt;
024import static io.jenetics.internal.util.SerialIO.writeInt;
025
026import java.io.DataInput;
027import java.io.DataOutput;
028import java.io.IOException;
029import java.io.InvalidObjectException;
030import java.io.ObjectInputStream;
031import java.io.Serial;
032import java.io.Serializable;
033import java.util.function.Function;
034import java.util.stream.DoubleStream;
035import java.util.stream.IntStream;
036import java.util.stream.Stream;
037
038import io.jenetics.util.DoubleRange;
039import io.jenetics.util.ISeq;
040import io.jenetics.util.IntRange;
041import io.jenetics.util.MSeq;
042
043/**
044 * Numeric chromosome implementation which holds 64-bit floating point numbers.
045 *
046 * @see DoubleGene
047 *
048 * @implNote
049 * This class is immutable and thread-safe.
050 *
051 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
052 * @since 1.6
053 * @version 6.1
054 */
055public class DoubleChromosome
056        extends AbstractBoundedChromosome<Double, DoubleGene>
057        implements
058                NumericChromosome<Double, DoubleGene>,
059                Serializable
060{
061        @Serial
062        private static final long serialVersionUID = 3L;
063
064        /**
065         * Create a new chromosome from the given {@code genes} and the allowed
066         * length range of the chromosome.
067         *
068         * @since 4.0
069         *
070         * @param genes the genes that form the chromosome.
071         * @param lengthRange the allowed length range of the chromosome
072         * @throws NullPointerException if one of the arguments is {@code null}.
073         * @throws IllegalArgumentException if the length of the gene sequence is
074         *         empty, doesn't match with the allowed length range, the minimum
075         *         or maximum of the range is smaller or equal zero, or the given
076         *         range size is zero.
077         */
078        protected DoubleChromosome(
079                final ISeq<DoubleGene> genes,
080                final IntRange lengthRange
081        ) {
082                super(genes, lengthRange);
083        }
084
085        @Override
086        public DoubleChromosome newInstance(final ISeq<DoubleGene> genes) {
087                return new DoubleChromosome(genes, lengthRange());
088        }
089
090        @Override
091        public DoubleChromosome newInstance() {
092                return of(_min, _max, lengthRange());
093        }
094
095        /**
096         * Maps the gene alleles of this chromosome, given as {@code double[]} array,
097         * by applying the given mapper function {@code f}. The mapped gene values
098         * are then wrapped into a newly created chromosome.
099         * {@snippet lang="java":
100         * final DoubleChromosome chromosome = null; // @replace substring='null' replacement="..."
101         * final DoubleChromosome normalized = chromosome.map(Main::normalize);
102         *
103         * static double[] normalize(final double[] values) {
104         *     final double sum = sum(values);
105         *     for (int i = 0; i < values.length; ++i) {
106         *         values[i] /= sum;
107         *     }
108         *     return values;
109         * }
110         * }
111         *
112         * @since 6.1
113         *
114         * @param f the mapper function
115         * @return a newly created chromosome with the mapped gene values
116         * @throws NullPointerException if the mapper function is {@code null}.
117         * @throws IllegalArgumentException if the length of the mapped
118         *         {@code double[]} array is empty or doesn't match with the allowed
119         *         length range
120         */
121        public DoubleChromosome map(final Function<? super double[], double[]> f) {
122                requireNonNull(f);
123
124                final var range = DoubleRange.of(_min, _max);
125                final var genes = DoubleStream.of(f.apply(toArray()))
126                        .mapToObj(v -> DoubleGene.of(v, range))
127                        .collect(ISeq.toISeq());
128
129                return newInstance(genes);
130        }
131
132        /**
133         * Returns a sequential stream of the alleles with this chromosome as its
134         * source.
135         *
136         * @since 4.3
137         *
138         * @return a sequential stream of alleles
139         */
140        public DoubleStream doubleStream() {
141                return IntStream.range(0, length()).mapToDouble(this::doubleValue);
142        }
143
144        /**
145         * Returns a double array containing all the elements in this chromosome
146         * in a proper sequence.  If the chromosome fits in the specified array, it is
147         * returned therein. Otherwise, a new array is allocated with the length of
148         * this chromosome.
149         *
150         * @since 3.0
151         *
152         * @param array the array into which the elements of these chromosomes are to
153         *        be stored, if it is big enough; otherwise, a new array is
154         *        allocated for this purpose.
155         * @return an array containing the elements of this chromosome
156         * @throws NullPointerException if the given {@code array} is {@code null}
157         */
158        public double[] toArray(final double[] array) {
159                final double[] a = array.length >= length()
160                        ? array
161                        : new double[length()];
162
163                for (int i = length(); --i >= 0;) {
164                        a[i] = doubleValue(i);
165                }
166
167                return a;
168        }
169
170        /**
171         * Returns a double array containing all the elements in this chromosome
172         * in a proper sequence.
173         *
174         * @since 3.0
175         *
176         * @return an array containing the elements of this chromosome
177         */
178        public double[] toArray() {
179                return toArray(new double[length()]);
180        }
181
182
183        /* *************************************************************************
184         * Static factory methods.
185         * ************************************************************************/
186
187        /**
188         * Create a new {@code DoubleChromosome} with the given genes.
189         *
190         * @param genes the genes of the chromosome.
191         * @return a new chromosome with the given genes.
192         * @throws IllegalArgumentException if the length of the genes array is
193         *         empty or the given {@code genes} doesn't have the same range.
194         * @throws NullPointerException if the given {@code genes} array is
195         *         {@code null}
196         */
197        public static DoubleChromosome of(final DoubleGene... genes) {
198                checkGeneRange(Stream.of(genes).map(DoubleGene::range));
199                return new DoubleChromosome(ISeq.of(genes), IntRange.of(genes.length));
200        }
201
202        /**
203         * Create a new {@code DoubleChromosome} with the given genes.
204         *
205         * @since 4.3
206         *
207         * @param genes the genes of the chromosome.
208         * @return a new chromosome with the given genes.
209         * @throws NullPointerException if the given {@code genes} are {@code null}
210         * @throws IllegalArgumentException if the of the genes iterable is empty or
211         *         the given {@code genes} doesn't have the same range.
212         */
213        public static DoubleChromosome of(final Iterable<DoubleGene> genes) {
214                final ISeq<DoubleGene> values = ISeq.of(genes);
215                checkGeneRange(values.stream().map(DoubleGene::range));
216                return new DoubleChromosome(values, IntRange.of(values.length()));
217        }
218
219        /**
220         * Create a new random chromosome.
221         *
222         * @since 4.0
223         *
224         * @param min the min value of the {@link DoubleGene}s (inclusively).
225         * @param max the max value of the {@link DoubleGene}s (exclusively).
226         * @param lengthRange the allowed length range of the chromosome.
227         * @return a new {@code DoubleChromosome} with the given parameter
228         * @throws IllegalArgumentException if the length of the gene sequence is
229         *         empty, doesn't match with the allowed length range, the minimum
230         *         or maximum of the range is smaller or equal zero, or the given
231         *         range size is zero.
232         * @throws NullPointerException if the given {@code lengthRange} is
233         *         {@code null}
234         */
235        public static DoubleChromosome of(
236                final double min,
237                final double max,
238                final IntRange lengthRange
239        ) {
240                final ISeq<DoubleGene> genes = DoubleGene.seq(min, max, lengthRange);
241                return new DoubleChromosome(genes, lengthRange);
242        }
243
244        /**
245         * Create a new random {@code DoubleChromosome}.
246         *
247         * @param min the min value of the {@link DoubleGene}s (inclusively).
248         * @param max the max value of the {@link DoubleGene}s (exclusively).
249         * @param length the length of the chromosome.
250         * @return a new {@code DoubleChromosome} with the given parameter
251         * @throws IllegalArgumentException if the {@code length} is smaller than
252         *         one.
253         */
254        public static DoubleChromosome of(
255                final double min,
256                final double max,
257                final int length
258        ) {
259                return of(min, max, IntRange.of(length));
260        }
261
262        /**
263         * Create a new random chromosome.
264         *
265         * @since 4.0
266         *
267         * @param range the integer range of the chromosome.
268         * @param lengthRange the allowed length range of the chromosome.
269         * @return a new {@code DoubleChromosome} with the given parameter
270         * @throws IllegalArgumentException if the length of the gene sequence is
271         *         empty, doesn't match with the allowed length range, the minimum
272         *         or maximum of the range is smaller or equal zero, or the given
273         *         range size is zero.
274         * @throws NullPointerException if the given {@code lengthRange} is
275         *         {@code null}
276         */
277        public static DoubleChromosome of(
278                final DoubleRange range,
279                final IntRange lengthRange
280        ) {
281                return of(range.min(), range.max(), lengthRange);
282        }
283
284        /**
285         * Create a new random {@code DoubleChromosome}.
286         *
287         * @since 3.2
288         *
289         * @param range the integer range of the chromosome.
290         * @param length the length of the chromosome.
291         * @return a new random {@code DoubleChromosome}
292         * @throws NullPointerException if the given {@code range} is {@code null}
293         * @throws IllegalArgumentException if the {@code length} is smaller than
294         *         one.
295         */
296        public static DoubleChromosome of(final DoubleRange range, final int length) {
297                return of(range.min(), range.max(), length);
298        }
299
300        /**
301         * Create a new random {@code DoubleChromosome} of length one.
302         *
303         * @param min the minimal value of this chromosome (inclusively).
304         * @param max the maximal value of this chromosome (exclusively).
305         * @return a new {@code DoubleChromosome} with the given parameter
306         */
307        public static DoubleChromosome of(final double min, final double max) {
308                return of(min, max, 1);
309        }
310
311        /**
312         * Create a new random {@code DoubleChromosome} of length one.
313         *
314         * @since 3.2
315         *
316         * @param range the double range of the chromosome.
317         * @return a new random {@code DoubleChromosome} of length one
318         * @throws NullPointerException if the given {@code range} is {@code null}
319         */
320        public static DoubleChromosome of(final DoubleRange range) {
321                return of(range.min(), range.max());
322        }
323
324
325        /* *************************************************************************
326         *  Java object serialization
327         * ************************************************************************/
328
329        @Serial
330        private Object writeReplace() {
331                return new SerialProxy(SerialProxy.DOUBLE_CHROMOSOME, this);
332        }
333
334        @Serial
335        private void readObject(final ObjectInputStream stream)
336                throws InvalidObjectException
337        {
338                throw new InvalidObjectException("Serialization proxy required.");
339        }
340
341        void write(final DataOutput out) throws IOException {
342                writeInt(length(), out);
343                writeInt(lengthRange().min(), out);
344                writeInt(lengthRange().max(), out);
345                out.writeDouble(_min);
346                out.writeDouble(_max);
347
348                for (int i = 0, n = length(); i < n; ++i) {
349                        out.writeDouble(doubleValue(i));
350                }
351        }
352
353        static DoubleChromosome read(final DataInput in) throws IOException {
354                final var length = readInt(in);
355                final var lengthRange = IntRange.of(readInt(in), readInt(in));
356                final var min = in.readDouble();
357                final var max = in.readDouble();
358
359                final MSeq<DoubleGene> values = MSeq.ofLength(length);
360                for (int i = 0; i < length; ++i) {
361                        values.set(i, DoubleGene.of(in.readDouble(), min, max));
362                }
363
364                return new DoubleChromosome(values.toISeq(), lengthRange);
365        }
366
367}