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