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