BitChromosome.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-5.0.0).
003  * Copyright (c) 2007-2019 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.lang.Math.min;
023 import static java.lang.String.format;
024 import static java.util.Objects.requireNonNull;
025 import static java.util.stream.Collectors.joining;
026 import static io.jenetics.internal.util.Hashes.hash;
027 import static io.jenetics.internal.util.SerialIO.readInt;
028 import static io.jenetics.internal.util.SerialIO.writeInt;
029 
030 import java.io.DataInput;
031 import java.io.DataOutput;
032 import java.io.IOException;
033 import java.io.InvalidObjectException;
034 import java.io.ObjectInputStream;
035 import java.io.Serializable;
036 import java.math.BigInteger;
037 import java.util.Arrays;
038 import java.util.BitSet;
039 import java.util.Iterator;
040 import java.util.ListIterator;
041 import java.util.stream.IntStream;
042 
043 import io.jenetics.internal.util.bit;
044 import io.jenetics.internal.util.require;
045 import io.jenetics.util.ISeq;
046 
047 /**
048  * Implementation of the <i>classical</i> BitChromosome.
049  *
050  @see BitGene
051  *
052  * @implSpec
053  * This class is immutable and thread-safe.
054  *
055  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
056  @since 1.0
057  @version 5.0
058  */
059 public class BitChromosome extends Number
060     implements
061         Chromosome<BitGene>,
062         Comparable<BitChromosome>,
063         Serializable
064 {
065     private static final long serialVersionUID = 2L;
066 
067 
068     /**
069      * The ones probability of the randomly generated Chromosome.
070      */
071     protected double _p;
072 
073     /**
074      * The length of the chromosomes (number of bits).
075      */
076     protected int _length;
077 
078     /**
079      * The boolean array which holds the {@link BitGene}s.
080      */
081     protected byte[] _genes;
082 
083     // Wraps the genes byte array into a Seq<BitGene>.
084     private transient BitGeneISeq _seq;
085 
086     // Private primary constructor.
087     private BitChromosome(final byte[] bits, final int length, final double p) {
088         _genes = bits;
089         _length = length;
090         _p = p;
091         _seq = BitGeneMSeq.of(_genes, length).toISeq();
092     }
093 
094     /**
095      * Create a new bit chromosome from the given bit (byte) array.
096      *
097      @param bits the bit values of the new chromosome gene.
098      @param start the initial (bit) index of the range to be copied, inclusive
099      @param end the final (bit) index of the range to be copied, exclusive.
100      *        (This index may lie outside the array.)
101      @throws java.lang.ArrayIndexOutOfBoundsException if {@code start < 0} or
102      *         {@code start > bits.length*8}
103      @throws java.lang.IllegalArgumentException if {@code start > end}
104      @throws java.lang.NullPointerException if the {@code bits} array is
105      *         {@code null}.
106      */
107     public BitChromosome(final byte[] bits, final int start, final int end) {
108         this(
109             bit.copy(bits, start, end),
110             min(bits.length << 3, end- start,
111             0.0
112         );
113         _p = (double)bit.count(_genes)/(double)_length;
114     }
115 
116     /**
117      * Create a new {@code BitChromosome} from the given {@code byte} array.
118      * This is a shortcut for {@code new BitChromosome(bits, 0, bits.length*8)}.
119      *
120      @param bits the {@code byte} array.
121      */
122     public BitChromosome(final byte[] bits) {
123         this(bits, 0, bits.length << 3);
124     }
125 
126     private BitChromosome(final byte[] bits, final int length) {
127         this(
128             bits,
129             length == -? bits.length*: length,
130             (double)bit.count(bits)/
131             (double)(length == -? bits.length*: length)
132         );
133     }
134 
135     private static byte[] toByteArray(final CharSequence value) {
136         final byte[] bytes = bit.newArray(value.length());
137         for (int i = value.length(); --i >= 0;) {
138             final char c = value.charAt(i);
139             if (c == '1') {
140                 bit.set(bytes, i);
141             else if (c != '0') {
142                 throw new IllegalArgumentException(format(
143                     "Illegal character '%s' at position %d", c, i
144                 ));
145             }
146         }
147 
148         return bytes;
149     }
150 
151     private void rangeCheck(final int index) {
152         if (index < || index >= _length) {
153             throw new IndexOutOfBoundsException(
154                 "Index: " + index + ", Length: " + _length
155             );
156         }
157     }
158 
159     /**
160      * Return the one probability of this chromosome.
161      *
162      @since 3.9
163      *
164      @return the one probability of this chromosome.
165      */
166     public double getOneProbability() {
167         return _p;
168     }
169 
170     @Override
171     public BitGene getGene() {
172         assert _genes != null;
173         assert _genes.length > 0;
174         return BitGene.of(bit.get(_genes, 0));
175     }
176 
177     /**
178      * Return the value of the first gene of this chromosome.
179      *
180      @since 4.2
181      *
182      @return the first value of this chromosome.
183      */
184     public boolean booleanValue() {
185         return bit.get(_genes, 0);
186     }
187 
188     @Override
189     public BitGene getGene(final int index) {
190         rangeCheck(index);
191         assert _genes != null;
192         return BitGene.of(bit.get(_genes, index));
193     }
194 
195     /**
196      * Return the value on the specified index.
197      *
198      @since 4.2
199      *
200      @param index the gene index
201      @return the wanted gene value
202      @throws IndexOutOfBoundsException if the index is out of range
203      *          (index &lt; 1 || index &gt;= length()).
204      */
205     public boolean booleanValue(final int index) {
206         rangeCheck(index);
207         return bit.get(_genes, index);
208     }
209 
210     @Override
211     public ISeq<BitGene> toSeq() {
212         return _seq;
213     }
214 
215     @Override
216     public int length() {
217         return _length;
218     }
219 
220     /**
221      * Returns the number of bits set to true in this {@code BitChromosome}.
222      *
223      @return the number of bits set to true in this {@code BitChromosome}
224      */
225     public int bitCount() {
226         return bit.count(_genes);
227     }
228 
229     @Override
230     public Iterator<BitGene> iterator() {
231         return _seq.iterator();
232     }
233 
234     public ListIterator<BitGene> listIterator() {
235         return _seq.listIterator();
236     }
237 
238     /**
239      * Return the long value this BitChromosome represents.
240      *
241      @return long value this BitChromosome represents.
242      */
243     @Override
244     public int intValue() {
245         return (int)longValue();
246     }
247 
248     /**
249      * Return the long value this BitChromosome represents.
250      *
251      @return long value this BitChromosome represents.
252      */
253     @Override
254     public long longValue() {
255         return toBigInteger().longValue();
256     }
257 
258     /**
259      * Return the float value this BitChromosome represents.
260      *
261      @return float value this BitChromosome represents.
262      */
263     @Override
264     public float floatValue() {
265         return (float)longValue();
266     }
267 
268     /**
269      * Return the double value this BitChromosome represents.
270      *
271      @return double value this BitChromosome represents.
272      */
273     @Override
274     public double doubleValue() {
275         return longValue();
276     }
277 
278     @Override
279     public boolean isValid() {
280         return true;
281     }
282 
283     /**
284      * Return the {@code BigInteger} value this {@code BitChromosome} represents.
285      *
286      @return {@code BigInteger} value this {@code BitChromosome} represents.
287      */
288     public BigInteger toBigInteger() {
289         return new BigInteger(_genes);
290     }
291 
292     /**
293      * Returns the two's-complement binary representation of this
294      * large integer. The output array is in <i>big-endian</i>
295      * byte-order: the most significant byte is at the offset position.
296      *
297      <p>Note: This representation is consistent with {@code java.lang.BigInteger
298      *          } byte array representation and can be used for conversion
299      *          between the two classes.</p>
300      *
301      @param bytes the bytes to hold the binary representation
302      *           (two's-complement) of this large integer.
303      @return the number of bytes written.
304      @throws IndexOutOfBoundsException
305      *         if {@code bytes.length < (int)Math.ceil(length()/8.0)}
306      @throws NullPointerException it the give array is {@code null}.
307      */
308     public int toByteArray(final byte[] bytes) {
309         if (bytes.length < _genes.length) {
310             throw new IndexOutOfBoundsException();
311         }
312 
313         System.arraycopy(_genes, 0, bytes, 0, _genes.length);
314         return _genes.length;
315     }
316 
317     /**
318      @return a byte array which represents this {@code BitChromosome}. The
319      *         length of the array is {@code (int)Math.ceil(length()/8.0)}.
320      *
321      @see #toByteArray(byte[])
322      */
323     public byte[] toByteArray() {
324         final byte[] data = new byte[_genes.length];
325         toByteArray(data);
326         return data;
327     }
328 
329     /**
330      * Return the corresponding BitSet of this BitChromosome.
331      *
332      @return The corresponding BitSet of this BitChromosome.
333      */
334     public BitSet toBitSet() {
335         final BitSet set = new BitSet(length());
336         for (int i = 0, n = length(); i < n; ++i) {
337             set.set(i, getGene(i).getBit());
338         }
339         return set;
340     }
341 
342     /**
343      * Return the indexes of the <i>ones</i> of this bit-chromosome as stream.
344      *
345      @since 3.0
346      *
347      @return the indexes of the <i>ones</i> of this bit-chromosome
348      */
349     public IntStream ones() {
350         return IntStream.range(0, length())
351             .filter(index -> bit.get(_genes, index));
352     }
353 
354     /**
355      * Return the indexes of the <i>zeros</i> of this bit-chromosome as stream.
356      *
357      @since 3.0
358      *
359      @return the indexes of the <i>zeros</i> of this bit-chromosome
360      */
361     public IntStream zeros() {
362         return IntStream.range(0, length())
363             .filter(index -> !bit.get(_genes, index));
364     }
365 
366     @Override
367     public BitChromosome newInstance(final ISeq<BitGene> genes) {
368         requireNonNull(genes, "Genes");
369         if (genes.isEmpty()) {
370             throw new IllegalArgumentException(
371                 "The genes sequence must contain at least one gene."
372             );
373         }
374 
375         final BitChromosome chromosome = new BitChromosome(
376             bit.newArray(genes.length()), genes.length()
377         );
378         int ones = 0;
379 
380         if (genes instanceof BitGeneISeq) {
381             final BitGeneISeq iseq = (BitGeneISeq)genes;
382             iseq.copyTo(chromosome._genes);
383             ones = bit.count(chromosome._genes);
384         else {
385             for (int i = genes.length(); --i >= 0;) {
386                 if (genes.get(i).booleanValue()) {
387                     bit.set(chromosome._genes, i);
388                     ++ones;
389                 }
390             }
391         }
392 
393         chromosome._p = (double)ones/(double)genes.length();
394         return chromosome;
395     }
396 
397     @Override
398     public BitChromosome newInstance() {
399         return of(_length, _p);
400     }
401 
402     /**
403      * Return the BitChromosome as String. A TRUE is represented by a 1 and
404      * a FALSE by a 0. The returned string can be used to create a new
405      * chromosome with the {@link #of(CharSequence)} constructor.
406      *
407      @return String representation (containing only '1' and '0') of the
408      *         BitChromosome.
409      */
410     public String toCanonicalString() {
411         return stream()
412             .map(g -> g.booleanValue() "1" "0")
413             .collect(joining());
414     }
415 
416     @Override
417     public int compareTo(final BitChromosome that) {
418         return toBigInteger().compareTo(that.toBigInteger());
419     }
420 
421     /**
422      * Invert the ones and zeros of this bit chromosome.
423      *
424      @return a new BitChromosome with inverted ones and zeros.
425      */
426     public BitChromosome invert() {
427         final byte[] data = _genes.clone();
428         bit.invert(data);
429         return new BitChromosome(data, _length, 1.0 - _p);
430     }
431 
432     /**
433      * Construct a new BitChromosome with the given _length.
434      *
435      @param length Length of the BitChromosome, number of bits.
436      @param p Probability of the TRUEs in the BitChromosome.
437      @return a new {@code BitChromosome} with the given parameter
438      @throws NegativeArraySizeException if the {@code length} is smaller
439      *         than one.
440      @throws IllegalArgumentException if {@code p} is not a valid probability.
441      */
442     public static BitChromosome of(final int length, final double p) {
443         return new BitChromosome(bit.newArray(length, p), length, p);
444     }
445 
446     /**
447      * Constructing a new BitChromosome with the given _length. The TRUEs and
448      * FALSE in the {@code Chromosome} are equally distributed.
449      *
450      @param length Length of the BitChromosome.
451      @return a new {@code BitChromosome} with the given parameter
452      @throws NegativeArraySizeException if the {@code _length} is smaller
453      *         than one.
454      */
455     public static BitChromosome of(final int length) {
456         return new BitChromosome(bit.newArray(length, 0.5), length, 0.5);
457     }
458 
459     /**
460      * Create a new {@code BitChromosome} with the given parameters.
461      *
462      @param length length of the BitChromosome.
463      @param bits the bit-set which initializes the chromosome
464      @return a new {@code BitChromosome} with the given parameter
465      @throws NegativeArraySizeException if the {@code length} is smaller
466      *         than one.
467      @throws NullPointerException if the {@code bitSet} is
468      *         {@code null}.
469      */
470     public static BitChromosome of(final BitSet bits, final int length) {
471         final byte[] bytes = bit.newArray(length);
472         for (int i = 0; i < length; ++i) {
473             if (bits.get(i)) {
474                 bit.set(bytes, i);
475             }
476         }
477         final double p = (double)bit.count(bytes)/(double)length;
478 
479         return new BitChromosome(bytes, length, p);
480     }
481 
482     /**
483      * Create a new {@code BitChromosome} with the given parameters.
484      *
485      @param length length of the BitChromosome.
486      @param bits the bit-set which initializes the chromosome
487      @param p Probability of the TRUEs in the BitChromosome.
488      @return a new {@code BitChromosome} with the given parameter
489      @throws NegativeArraySizeException if the {@code length} is smaller than
490      *         one.
491      @throws NullPointerException if the {@code bitSet} is {@code null}.
492      @throws IllegalArgumentException if {@code p} is not a valid probability.
493      */
494     public static BitChromosome of(
495         final BitSet bits,
496         final int length,
497         final double p
498     ) {
499         final byte[] bytes = bit.newArray(length);
500         for (int i = 0; i < length; ++i) {
501             if (bits.get(i)) {
502                 bit.set(bytes, i);
503             }
504         }
505 
506         return new BitChromosome(bytes, length, require.probability(p));
507     }
508 
509     /**
510      * Constructing a new BitChromosome from a given BitSet.
511      * The BitSet is copied while construction. The length of the constructed
512      * BitChromosome will be {@code bitSet.length()} ({@link BitSet#length}).
513      *
514      @param bits the bit-set which initializes the chromosome
515      @return a new {@code BitChromosome} with the given parameter
516      @throws NullPointerException if the {@code bitSet} is
517      *        {@code null}.
518      */
519     public static BitChromosome of(final BitSet bits) {
520         return new BitChromosome(bits.toByteArray(), -1);
521     }
522 
523     /**
524      * Create a new {@code BitChromosome} from the given big integer value.
525      *
526      @param value the value of the created {@code BitChromosome}
527      @return a new {@code BitChromosome} with the given parameter
528      @throws NullPointerException if the given {@code value} is {@code null}.
529      */
530     public static BitChromosome of(final BigInteger value) {
531         return new BitChromosome(value.toByteArray(), -1);
532     }
533 
534     /**
535      * Create a new {@code BitChromosome} from the given big integer value and
536      * ones probability.
537      *
538      @param value the value of the created {@code BitChromosome}
539      @param p Probability of the TRUEs in the BitChromosome.
540      @return a new {@code BitChromosome} with the given parameter
541      @throws NullPointerException if the given {@code value} is {@code null}.
542      @throws IllegalArgumentException if {@code p} is not a valid probability.
543      */
544     public static BitChromosome of(final BigInteger value, final double p) {
545         final byte[] bits = value.toByteArray();
546         return new BitChromosome(bits, bits.length*8, require.probability(p));
547     }
548 
549     /**
550      * Create a new {@code BitChromosome} from the given character sequence
551      * containing '0' and '1'; as created with the {@link #toCanonicalString()}
552      * method.
553      *
554      @param value the input string.
555      @return a new {@code BitChromosome} with the given parameter
556      @throws NullPointerException if the {@code value} is {@code null}.
557      @throws IllegalArgumentException if the length of the character sequence
558      *         is zero or contains other characters than '0' or '1'.
559      */
560     public static BitChromosome of(final CharSequence value) {
561         return new BitChromosome(toByteArray(requireNonNull(value, "Input")), -1);
562     }
563 
564     /**
565      * Create a new {@code BitChromosome} from the given character sequence
566      * containing '0' and '1'; as created with the {@link #toCanonicalString()}
567      * method.
568      *
569      @param value the input string.
570      @param p Probability of the TRUEs in the BitChromosome.
571      @return a new {@code BitChromosome} with the given parameter
572      @throws NullPointerException if the {@code value} is {@code null}.
573      @throws IllegalArgumentException if the length of the character sequence
574      *         is zero or contains other characters than '0' or '1'.
575      @throws IllegalArgumentException if {@code p} is not a valid probability.
576      */
577     public static BitChromosome of(final CharSequence value, final double p) {
578         final byte[] bits = toByteArray(requireNonNull(value, "Input"));
579         return new BitChromosome(bits, bits.length*8, require.probability(p));
580     }
581 
582     /**
583      * Create a new {@code BitChromosome} from the given character sequence
584      * containing '0' and '1'; as created with the {@link #toCanonicalString()}
585      * method.
586      *
587      @param value the input string.
588      @param length length of the BitChromosome
589      @param p Probability of the TRUEs in the BitChromosome.
590      @return a new {@code BitChromosome} with the given parameter
591      @throws NullPointerException if the {@code value} is {@code null}.
592      @throws IllegalArgumentException if the length of the character sequence
593      *         is zero or contains other characters than '0' or '1'.
594      @throws IllegalArgumentException if {@code p} is not a valid probability.
595      */
596     public static BitChromosome of(
597         final CharSequence value,
598         final int length,
599         final double p
600     ) {
601         final byte[] bits = toByteArray(requireNonNull(value, "Input"));
602         return new BitChromosome(bits, length, require.probability(p));
603     }
604 
605     @Override
606     public int hashCode() {
607         return hash(_genes, hash(getClass()));
608     }
609 
610     @Override
611     public boolean equals(final Object obj) {
612         return obj == this ||
613             obj != null &&
614             getClass() == obj.getClass() &&
615             length() == ((BitChromosome)obj).length() &&
616             Arrays.equals(_genes, ((BitChromosome)obj)._genes);
617     }
618 
619     @Override
620     public String toString() {
621         return bit.toByteString(_genes);
622     }
623 
624 
625     /* *************************************************************************
626      *  Java object serialization
627      * ************************************************************************/
628 
629     private Object writeReplace() {
630         return new Serial(Serial.BIT_CHROMOSOME, this);
631     }
632 
633     private void readObject(final ObjectInputStream stream)
634         throws InvalidObjectException
635     {
636         throw new InvalidObjectException("Serialization proxy required.");
637     }
638 
639     void write(final DataOutput outthrows IOException {
640         writeInt(_length, out);
641         out.writeDouble(_p);
642         writeInt(_genes.length, out);
643         out.write(_genes);
644     }
645 
646     static BitChromosome read(final DataInput inthrows IOException {
647         final int length = readInt(in);
648         final double p = in.readDouble();
649         final byte[] genes = new byte[readInt(in)];
650         in.readFully(genes);
651 
652         return new BitChromosome(genes, length, p);
653     }
654 
655 }