I'm using Jenetics to nest a list of polygons using NoFitPolygon
I have a function that gived a list of polygon nests them following the order in the list.
I adapted the TravellingSalesman problem in order to have a genotype that represent the list of polygons, and during evolution the order change.
Now I want to add the rotation to the genotype, in order to get better results, but I don't know how to add it to my code.
Currently the order of the nestpaths determines the fitness, what i want to do is add, for every nestpath, a double value (doubleChromosome?) to the genotype that represent the rotation of the nestpath.
Nestpath is the class that represent the polygon
public class NFP_Nesting implements Problem<ISeq<NestPath>, EnumGene<NestPath>, Double>{
static Phenotype<EnumGene<NestPath>,Double> tmpBest = null;
static Result tmpBestResult =null;
private NestPath _binPolygon;
Map<String,List<NestPath>> nfpCache=new HashMap<>();
private final ISeq<NestPath> _list;
public NFP_Nesting(ISeq<NestPath> lista,NestPath binpolygon ,double binw, double binh)
{
_binPolygon = binpolygon;
_list=Objects.requireNonNull(lista);
}
@Override
public Codec<ISeq<NestPath>, EnumGene<NestPath>> codec() {
return Codecs.ofPermutation(_list);
}
@Override
public Function<ISeq<NestPath>, Double> fitness() {
return this::scalar_fitness;
}
/**
* @param seq_nestpath
* @return Fitness of the model
*/
Double scalar_fitness(final ISeq<NestPath> seq_nestpath) {
List<NestPath> paths = seq_nestpath.asList();
final Random random = RandomRegistry.random();
//TODO NOT RANDOM ROTATION
List<Integer> ids = new ArrayList<>();
for(int i = 0 ; i < paths.size(); i ++){
ids.add(paths.get(i).getId());
NestPath n = paths.get(i);
if(n.getPossibleRotations()!= null)
{
n.setRotation(n.getPossibleRotations()[random.nextInt(n.getPossibleRotations().length)]);
}
}
///complicated function here
return fitness;
}
private static NFP_Nesting of (List<NestPath> l, NestPath binpol, double binw, double binh)
{
final MSeq<NestPath> paths = MSeq.ofLength(l.size());
final Random random = RandomRegistry.random();
for ( int i = 0 ; i < l.size(); ++i ) {
paths.set(i, l.get(i));
}
//initial shuffle list of polygons
for(int j=paths.length()-1; j>0;--j)
{
final int i = random.nextInt(j+1);
final NestPath tmp=paths.get(i);
paths.set(i, paths.get(j));
paths.set(j, tmp);
}
return new NFP_Nesting(paths.toISeq(),binpol,binw,binh);
}
Main:
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(1);
NFP_Nesting nst = NFP_Nesting.of(tree,binPolygon,binWidth,binHeight);
Engine<EnumGene<NestPath>,Double> engine = Engine
.builder(nst)
.optimize(Optimize.MINIMUM)
.populationSize(config.POPULATION_SIZE)
.executor(executor)
.alterers(
new SwapMutator<>(0.25),
new PartiallyMatchedCrossover<>(0.35)
)
.build();
final EvolutionStatistics<Double, ?> statistics = EvolutionStatistics.ofNumber();
Phenotype<EnumGene<NestPath>,Double> best=
engine.stream()
.limit(Limits.bySteadyFitness(5))
.limit(Limits.byExecutionTime(Duration.ofSeconds(MAX_SEC_DURATION)))
//.limit(10)
.peek(NFP_Nesting::update)
.peek(statistics)
.collect(toBestPhenotype());
System.out.println(statistics);
//System.out.println(best);
}
I think I understand your problem now. What you want to do is to have an additional rotation encoded in the genotype. But Jenetics only allows one gene type per genotype. Using the permutation-codec, you fixed the gene-type to a
EnumGene
and for your rotation you will needDoubleGene
. With a simple trick we are able to express the path permutation also asDoubleGene
, by using the order of the genes within the chromosome.The following code will show you how to do this.