001/* 002 * Java Genetic Algorithm Library (jenetics-8.1.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 */ 020package io.jenetics; 021 022import static java.lang.String.format; 023import static java.util.Objects.requireNonNull; 024 025import java.util.stream.IntStream; 026 027import io.jenetics.internal.util.Requires; 028import io.jenetics.util.ISeq; 029import io.jenetics.util.IntRange; 030import io.jenetics.util.MSeq; 031import io.jenetics.util.Seq; 032 033/** 034 * This alterer wraps a given alterer which works on a given section of the 035 * genotype's chromosomes. 036 * {@snippet class="BaseSnippets" region="PartialAltererSnippets.usage"} 037 * 038 * If you are using chromosome indices which are greater or equal than the 039 * number of chromosomes defined in the genotype, a 040 * {@link java.util.concurrent.CompletionException} is thrown when the evolution 041 * stream is evaluated. 042 * 043 * @implNote 044 * This alterer is slower than the performance of the wrapped alterer, because 045 * of the needed <em>sectioning</em> of the genotype. 046 * 047 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 048 * @version 5.0 049 * @since 5.0 050 */ 051public final class PartialAlterer< 052 G extends Gene<?, G>, 053 C extends Comparable<? super C> 054> 055 implements Alterer<G, C> 056{ 057 058 private final Alterer<G, C> _alterer; 059 private final Projection _projection; 060 061 private PartialAlterer(final Alterer<G, C> alterer, final Projection projection) { 062 _alterer = requireNonNull(alterer); 063 _projection = requireNonNull(projection); 064 } 065 066 @Override 067 public AltererResult<G, C> 068 alter(final Seq<Phenotype<G, C>> population, final long generation) { 069 if (!population.isEmpty()) { 070 _projection.checkIndices(population.get(0).genotype().length()); 071 072 final var projectedPopulation = _projection.project(population); 073 final var result = _alterer.alter(projectedPopulation, generation); 074 075 return new AltererResult<>( 076 _projection.merge(result.population(), population), 077 result.alterations() 078 ); 079 } else { 080 return new AltererResult<>(population.asISeq()); 081 } 082 } 083 084 /** 085 * Wraps the given {@code alterer}, so that it will only work on chromosomes 086 * with the given chromosome indices. 087 * 088 * @see #of(Alterer, IntRange) 089 * 090 * @param alterer the alterer to user for altering the chromosomes with the 091 * given {@code indices} 092 * @param indices the chromosomes indices (section) 093 * @param <G> the gene type 094 * @param <C> the fitness value type 095 * @return a wrapped alterer which only works for the given chromosome 096 * section 097 * @throws NullPointerException if the given {@code indices} array is 098 * {@code null} 099 * @throws IllegalArgumentException if the given {@code indices} array is 100 * empty 101 * @throws NegativeArraySizeException if one of the given {@code indices} is 102 * negative 103 */ 104 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 105 Alterer<G, C> of(final Alterer<G, C> alterer, final int... indices) { 106 return new PartialAlterer<>(alterer, new Projection(indices)); 107 } 108 109 /** 110 * Wraps the given {@code alterer}, so that it will only work on chromosomes 111 * with the given chromosome indices. 112 * 113 * @see #of(Alterer, int...) 114 * 115 * @param alterer the alterer to user for altering the chromosomes with the 116 * given {@code indices} 117 * @param section the half-open chromosome index range {@code [min, max)} 118 * @param <G> the gene type 119 * @param <C> the fitness value type 120 * @return a wrapped alterer which only works for the given chromosome 121 * section 122 * @throws NullPointerException if the given {@code indices} array is 123 * {@code null} 124 * @throws IllegalArgumentException if the given {@code indices} array is 125 * empty 126 * @throws NegativeArraySizeException if one of the given {@code indices} is 127 * negative 128 */ 129 public static <G extends Gene<?, G>, C extends Comparable<? super C>> 130 Alterer<G, C> of(final Alterer<G, C> alterer, final IntRange section) { 131 return new PartialAlterer<>( 132 alterer, 133 new Projection(section.stream().toArray()) 134 ); 135 } 136 137 138 /** 139 * The section class, which defines the chromosomes used by the alterer. 140 */ 141 record Projection(int[] indices) { 142 Projection { 143 if (indices.length == 0) { 144 throw new IllegalArgumentException( 145 "Chromosome indices must not be empty." 146 ); 147 } 148 for (int index : indices) { 149 Requires.nonNegative(index); 150 } 151 } 152 153 void checkIndices(final int length) { 154 for (int index : indices) { 155 if (index >= length) { 156 throw new IndexOutOfBoundsException(format( 157 "Genotype contains %d Chromosome, but found " + 158 "SectionAlterer for Chromosome index %d.", 159 length, index 160 )); 161 } 162 } 163 } 164 165 <G extends Gene<?, G>, C extends Comparable<? super C>> 166 Seq<Phenotype<G, C>> project(final Seq<Phenotype<G, C>> population) { 167 return population.map(this::project); 168 } 169 170 <G extends Gene<?, G>, C extends Comparable<? super C>> 171 Phenotype<G, C> project(final Phenotype<G, C> pt) { 172 final MSeq<Chromosome<G>> chromosomes = MSeq.ofLength(indices.length); 173 for (int i = 0; i < indices.length; ++i) { 174 chromosomes.set(i, pt.genotype().get(indices[i])); 175 } 176 final var gt = Genotype.of(chromosomes); 177 178 return pt.isEvaluated() 179 ? Phenotype.of(gt, pt.generation(), pt.fitness()) 180 : Phenotype.of(gt, pt.generation()); 181 } 182 183 <G extends Gene<?, G>, C extends Comparable<? super C>> 184 ISeq<Phenotype<G, C>> merge( 185 final Seq<Phenotype<G, C>> projection, 186 final Seq<Phenotype<G, C>> population 187 ) { 188 assert projection.length() == population.length(); 189 190 return IntStream.range(0, projection.length()) 191 .mapToObj(i -> merge(projection.get(i), population.get(i))) 192 .collect(ISeq.toISeq()); 193 } 194 195 <G extends Gene<?, G>, C extends Comparable<? super C>> 196 Phenotype<G, C> merge( 197 final Phenotype<G, C> projection, 198 final Phenotype<G, C> pt 199 ) { 200 final var ch = MSeq.of(pt.genotype()); 201 for (int i = 0; i < indices.length; ++i) { 202 ch.set(indices[i], projection.genotype().get(i)); 203 } 204 final var gt = Genotype.of(ch); 205 206 return gt.equals(pt.genotype()) 207 ? pt 208 : Phenotype.of(gt, pt.generation()); 209 } 210 211 } 212 213 214}