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 java.lang.String.format; 023 024import java.io.Serial; 025import java.io.Serializable; 026import java.util.Objects; 027 028import io.jenetics.util.ISeq; 029import io.jenetics.util.RandomRegistry; 030 031/** 032 * <p> 033 * Gene which holds enumerable (countable) genes. Will be used for combinatorial 034 * problems in combination with the {@link PermutationChromosome}. 035 * </p> 036 * The following code shows how to create a combinatorial genotype factory which 037 * can be used when creating an {@link io.jenetics.engine.Engine} instance. 038 * {@snippet lang="java": 039 * final ISeq<Integer> alleles = ISeq.of(1, 2, 3, 4, 5, 6, 7, 8); 040 * final Factory<Genotype<EnumGene<Integer>>> gtf = Genotype.of( 041 * PermutationChromosome.of(alleles) 042 * ); 043 * } 044 * 045 * The following code shows the assurances of the {@code EnumGene}. 046 * {@snippet lang="java": 047 * final ISeq<Integer> alleles = ISeq.of(1, 2, 3, 4, 5, 6, 7, 8); 048 * final EnumGene<Integer> gene = new EnumGene<>(5, alleles); 049 * 050 * assert(gene.alleleIndex() == 5); 051 * assert(gene.allele() == gene.validAlleles().get(5)); 052 * assert(gene.validAlleles() == alleles); 053 * } 054 * 055 * @see PermutationChromosome 056 * @see PartiallyMatchedCrossover 057 * 058 * @implNote 059 * This class is immutable and thread-safe. 060 * 061 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 062 * @since 1.0 063 * @version 5.2 064 */ 065public final class EnumGene<A> 066 implements 067 Gene<A, EnumGene<A>>, 068 Comparable<EnumGene<A>>, 069 Serializable 070{ 071 072 @Serial 073 private static final long serialVersionUID = 2L; 074 075 private final ISeq<A> _validAlleles; 076 private final int _alleleIndex; 077 078 /** 079 * Create a new enum gene from the given valid genes and the chosen allele 080 * index. 081 * 082 * @param alleleIndex the index of the allele for this gene 083 * @param validAlleles the sequence of valid alleles 084 * @throws IllegalArgumentException if the give valid alleles sequence is 085 * empty 086 * @throws NullPointerException if the valid alleles seq is {@code null} 087 */ 088 EnumGene(final int alleleIndex, final ISeq<? extends A> validAlleles) { 089 if (validAlleles.isEmpty()) { 090 throw new IllegalArgumentException( 091 "Array of valid alleles must be greater than zero." 092 ); 093 } 094 095 if (alleleIndex < 0 || alleleIndex >= validAlleles.length()) { 096 throw new IndexOutOfBoundsException(format( 097 "Allele index is not in range [0, %d): %d.", 098 validAlleles.length(), 099 alleleIndex 100 )); 101 } 102 103 _validAlleles = ISeq.upcast(validAlleles); 104 _alleleIndex = alleleIndex; 105 } 106 107 /** 108 * Return sequence of the valid alleles where this gene is a part of. 109 * 110 * @return the sequence of the valid alleles. 111 */ 112 public ISeq<A> validAlleles() { 113 return _validAlleles; 114 } 115 116 /** 117 * Return the index of the allele this gene is representing. 118 * 119 * @return the index of the allele this gene is representing 120 */ 121 public int alleleIndex() { 122 return _alleleIndex; 123 } 124 125 @Override 126 public A allele() { 127 return _validAlleles.get(_alleleIndex); 128 } 129 130 @Override 131 public boolean isValid() { 132 return _alleleIndex >= 0 && _alleleIndex < _validAlleles.length(); 133 } 134 135 @Override 136 public EnumGene<A> newInstance() { 137 return new EnumGene<>( 138 RandomRegistry.random().nextInt(_validAlleles.length()), 139 _validAlleles 140 ); 141 } 142 143 /** 144 * Create a new gene from the given {@code value} and the gene context. 145 * 146 * @since 1.6 147 * 148 * @param value the value of the new gene 149 * @return a new gene with the given value 150 */ 151 public EnumGene<A> newInstance(final A value) { 152 return new EnumGene<>( 153 _validAlleles.indexOf(value), 154 _validAlleles 155 ); 156 } 157 158 @Override 159 public int compareTo(final EnumGene<A> gene) { 160 int result = 0; 161 if (_alleleIndex > gene._alleleIndex) { 162 result = 1; 163 } else if (_alleleIndex < gene._alleleIndex) { 164 result = -1; 165 } 166 167 return result; 168 } 169 170 @Override 171 public int hashCode() { 172 return Objects.hash(_alleleIndex, _validAlleles); 173 } 174 175 @Override 176 public boolean equals(final Object obj) { 177 return obj instanceof EnumGene<?> other && 178 _alleleIndex == other._alleleIndex && 179 Objects.equals(_validAlleles, other._validAlleles); 180 } 181 182 @Override 183 public String toString() { 184 return Objects.toString(allele()); 185 } 186 187 188 /* ************************************************************************* 189 * Static object creation methods 190 * ************************************************************************/ 191 192 /** 193 * Create a new enum gene from the given valid genes and the chosen allele 194 * index. 195 * 196 * @since 3.4 197 * 198 * @param <A> the allele type 199 * @param alleleIndex the index of the allele for this gene 200 * @param validAlleles the sequence of valid alleles 201 * @return a new {@code EnumGene} with the given with the allele 202 * {@code validAlleles.get(alleleIndex)} 203 * @throws IllegalArgumentException if the give valid alleles sequence is 204 * empty 205 * @throws NullPointerException if the valid alleles seq is {@code null}. 206 */ 207 public static <A> EnumGene<A> of( 208 final int alleleIndex, 209 final ISeq<? extends A> validAlleles 210 ) { 211 return new EnumGene<>(alleleIndex, validAlleles); 212 } 213 214 /** 215 * Return a new enum gene with an allele randomly chosen from the given 216 * valid alleles. 217 * 218 * @param <A> the allele type 219 * @param validAlleles the sequence of valid alleles. 220 * @return a new {@code EnumGene} with a randomly chosen allele from the 221 * sequence of valid alleles 222 * @throws java.lang.IllegalArgumentException if the give valid alleles 223 * sequence is empty 224 * @throws NullPointerException if the valid alleles seq is {@code null}. 225 */ 226 public static <A> EnumGene<A> of(final ISeq<? extends A> validAlleles) { 227 return new EnumGene<>( 228 RandomRegistry.random().nextInt(validAlleles.length()), 229 validAlleles 230 ); 231 } 232 233 /** 234 * Create a new enum gene from the given valid genes and the chosen allele 235 * index. 236 * 237 * @param <A> the allele type 238 * @param alleleIndex the index of the allele for this gene 239 * @param validAlleles the array of valid alleles. 240 * @return a new {@code EnumGene} with the given with the allele 241 * {@code validAlleles[alleleIndex]} 242 * @throws java.lang.IllegalArgumentException if the give valid alleles 243 * array is empty of the allele index is out of range. 244 */ 245 @SafeVarargs 246 public static <A> EnumGene<A> of( 247 final int alleleIndex, 248 final A... validAlleles 249 ) { 250 return new EnumGene<>(alleleIndex, ISeq.of(validAlleles)); 251 } 252 253 /** 254 * Return a new enum gene with an allele randomly chosen from the given 255 * valid alleles. 256 * 257 * @param <A> the allele type 258 * @param validAlleles the array of valid alleles 259 * @return a new {@code EnumGene} with a randomly chosen allele from the 260 * sequence of valid alleles 261 * @throws IllegalArgumentException if the give valid alleles array is empty 262 */ 263 @SafeVarargs 264 public static <A> EnumGene<A> of(final A... validAlleles) { 265 return EnumGene.of(ISeq.of(validAlleles)); 266 } 267 268}