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