Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PartiallyMappedCrossover for GA #2401

Open
wants to merge 4 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions ravenframework/Optimizers/GeneticAlgorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ def getInputSpecification(cls):
\item onePointCrossover.
\item twoPointsCrossover.
\item uniformCrossover
\item partiallyMappedCrossover
\end{itemize}
\item mutators:
\begin{itemize}
Expand Down Expand Up @@ -191,11 +192,15 @@ def getInputSpecification(cls):
contentType=InputTypes.StringType,
printPriority=108,
descr=r"""a subnode containing the implemented crossover mechanisms.
This includes: a. onePointCrossover,
b. twoPointsCrossover,
c. uniformCrossover.""")
crossover.addParam("type", InputTypes.StringType, True,
descr="type of crossover operation to be used (e.g., OnePoint, MultiPoint, or Uniform)")
\begin{itemize}
\item \textit{onePointCrossover} - It selects a random crossover point along the chromosome of parent individuals and swapping the genetic material beyond that point to create offspring.
\item \textit{twoPointsCrossover} - It selects two random crossover points along the chromosome of parent individuals and swapping the genetic material beyond that point to create offspring.
\item \textit{uniformCrossover} - It randomly selects genes from two parent chromosomes with equal probability, creating offspring by exchanging genes at corresponding positions.
\item \textit{partiallyMappedCrossover} - Method designed to perform a two point partially mapped crossover (MPX) on two parents to create offspring.
\end{itemize}""")
crossoverType = InputTypes.makeEnumType("crossover", "crossoverType", ["onePointCrossover","twoPointsCrossover", "uniformCrossover", "partiallyMappedCrossover"])
crossover.addParam("type", crossoverType, required=True,
descr="type of crossover operation to be used (e.g., onePointCrossover, twoPointsCrossover, uniformCrossover, or partiallyMappedCrossover)")
crossoverPoint = InputData.parameterInputFactory('points', strictMode=True,
contentType=InputTypes.IntegerListType,
printPriority=108,
Expand Down
101 changes: 99 additions & 2 deletions ravenframework/Optimizers/crossOverOperators/crossovers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
Implementation of crossovers for crossover process of Genetic Algorithm
currently the implemented crossover algorithms are:
1. OnePoint Crossover
2. TwoPoints Crossover
3. Uniform Crossover
4. TwoPoints Partially Mapped Crossover (PMX)

Created June,16,2020
@authors: Mohammad Abdo, Diego Mandelli, Andrea Alfonsi
@authors: Mohammad Abdo, Diego Mandelli, Andrea Alfonsi, Juan Luque-Gutierrez
"""

import numpy as np
Expand Down Expand Up @@ -151,11 +154,57 @@ def twoPointsCrossover(parents, **kwargs):

return children

def partiallyMappedCrossover(parents, **kwargs):
"""
Method designed to perform a two point partially mapped crossover (MPX) on 2 parents:
Partition each parents in three sequences (A,B,C):
parent1 = A1 B1 C1
parent2 = A2 B2 C2
Then:
children1 = A1* B2 C1*
children2 = A2* B1 C2*
Children should have the same elements as their parents, but in different order.
This crossover preserves the genes in a chromosome.
@ In, parents, xr.DataArray, parents involved in the mating process
@ In, kwargs, dict, dictionary of parameters for this mutation method:
parents, 2D array, parents in the current mating process.
Shape is nParents x len(chromosome) i.e, number of Genes/Vars
crossoverProb, float, crossoverProb determines when child takes genes from a specific parent, default is random
points, integer, point at which the cross over happens, default is random
@ Out, children, xr.DataArray, children resulting from the crossover. Shape is nParents x len(chromosome) i.e, number of Genes/Vars
"""
nParents, nGenes = np.shape(parents)
children = xr.DataArray(np.zeros((int(2*comb(nParents,2)), np.shape(parents)[1])),
dims = ['chromosome', 'Gene'],
coords = {'chromosome': np.arange(int(2*comb(nParents, 2))),
'Gene':parents.coords['Gene'].values})
parentPairs = list(combinations(parents, 2))
index = 0
if nGenes <= 2:
ValueError('The number of genes should be >= 3')
for couples in parentPairs:
[loc1, loc2] = randomUtils.randomChoice(list(range(1, nGenes)), size = 2, replace=False, engine=None)
if loc1 > loc2:
locL = loc2
locU = loc1
else:
locL = loc1
locU = loc2
parent1 = couples[0]
parent2 = couples[1]
children1, children2 = twoPointsPMXMethod(parent1, parent2, locL, locU)

children[index] = children1
children[index + 1] = children2
index = index + 2

return children

__crossovers = {}
__crossovers['onePointCrossover'] = onePointCrossover
__crossovers['twoPointsCrossover'] = twoPointsCrossover
__crossovers['uniformCrossover'] = uniformCrossover

__crossovers['partiallyMappedCrossover'] = partiallyMappedCrossover

def returnInstance(cls, name):
"""
Expand Down Expand Up @@ -215,3 +264,51 @@ def uniformCrossoverMethod(parent1,parent2,crossoverProb):
children2[pos] = parent2[pos]

return children1,children2

def twoPointsPMXMethod(parent1, parent2, locL, locU):
"""
Method designed to perform a two point Partially Mapped Crossover (PMX) on 2 arrays:
Partition each array into three sequences (A, B, C):
parent1 = A1 B1 C1
parent2 = A2 B2 C2
We map the values contained in B1 to B2.
Then:
children1 = X B2 X
children2 = X B1 X
We verify if the values in A and C are found in B for each children. If so, we
replace such values for the ones in the map.
children1 = A1* B2 C1*
children2 = A2* B1 C2*
Children should have the same elements as their parents, but in different order.
@ In, parent1, xr.DataArray, first array
@ In, parent2, xr.DataArray, second array
@ In, locL, integer, first location
@ In, LocU, integer, second location
@ Out, children1, xr.DataArray, first generated array
@ Out, children2, xr.DataArray, second generated array
"""

size = len(parent1)

children1 = parent1.copy(deep=True)
children2 = parent2.copy(deep=True)

seqB1 = parent1.values[locL:locU]
seqB2 = parent2.values[locL:locU]

children1[locL:locU] = seqB2
children2[locL:locU] = seqB1

# Determine mapping relationship
mapping1 = {parent2.values[i]: parent1.values[i] for i in range(locL, locU)}
mapping2 = {parent1.values[i]: parent2.values[i] for i in range(locL, locU)}

for i in list(range(locL)) + list(range(locU, size)):
if children1.values[i] in mapping1:
while children1.values[i] in mapping1:
children1.values[i] = mapping1[children1.values[i]]
if children2.values[i] in mapping2:
while children2.values[i] in mapping2:
children2.values[i] = mapping2[children2.values[i]]

return children1, children2
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?xml version="1.0" ?>
<Simulation verbosity="debug">
<TestInfo>
<name>framework/Optimizers/GeneticAlgorithms/discrete/unconstrained.MaxwoRepPartiallyMappedCrossover</name>
<author>wangc</author>
<created>2024-11-26</created>
<classesTested>GeneticAlgorithm</classesTested>
<description>
This test assesses the Genetic algorithm using the weighted sum found in myLocalSum.py function.
The nominal dimensionality of the test problem is 3.
The objective variable is ans. The problem in unconstrained, it is a maximization problem, and the sampling is from discrete variables without replacement.
The cross over mechanism used is the partiallyMappedCrossover algorithm
</description>
<analytic>
This test uses myLocalSum's analytic objective function.
</analytic>
</TestInfo>

<RunInfo>
<WorkingDir>MaxwoRepPartiallyMappedCrossover</WorkingDir>
<Sequence>optimize, print</Sequence>
</RunInfo>

<Steps>
<MultiRun name="optimize" re-seeding="2286">
<Input class="DataObjects" type="PointSet">placeholder</Input>
<Model class="Models" type="ExternalModel">myLocalSum</Model>
<Optimizer class="Optimizers" type="GeneticAlgorithm">GAopt</Optimizer>
<SolutionExport class="DataObjects" type="PointSet">opt_export</SolutionExport>
<Output class="DataObjects" type="PointSet">optOut</Output>
</MultiRun>
<IOStep name="print">
<Input class="DataObjects" type="PointSet">opt_export</Input>
<Input class="DataObjects" type="PointSet">optOut</Input>
<Output class="OutStreams" type="Print">opt_export</Output>
<Output class="OutStreams" type="Print">optOut</Output>
</IOStep>
</Steps>

<Distributions>
<UniformDiscrete name='uniform_dist_Repl_1'>
<lowerBound>1</lowerBound>
<upperBound>6</upperBound>
<strategy>withReplacement</strategy>
</UniformDiscrete>

<UniformDiscrete name='uniform_dist_woRepl_1'>
<lowerBound>1</lowerBound>
<upperBound>6</upperBound>
<strategy>withoutReplacement</strategy>
</UniformDiscrete>
</Distributions>

<Optimizers>
<GeneticAlgorithm name="GAopt">
<samplerInit>
<limit>20</limit>
<initialSeed>42</initialSeed>
<writeSteps>every</writeSteps>
<type>max</type>
</samplerInit>

<GAparams>
<populationSize>20</populationSize>
<parentSelection>rouletteWheel</parentSelection>
<reproduction>
<crossover type="partiallyMappedCrossover">
<crossoverProb>0.8</crossoverProb>
</crossover>
<mutation type="swapMutator">
<mutationProb>0.9</mutationProb>
</mutation>
</reproduction>
<fitness type="invLinear">
<a>2.0</a>
<b>1.0</b>
</fitness>
<survivorSelection>fitnessBased</survivorSelection>
</GAparams>

<convergence>
<objective>-1</objective>
</convergence>

<variable name="x1">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>

<variable name="x2">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>

<variable name="x3">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>

<variable name="x4">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>

<variable name="x5">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>

<variable name="x6">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>

<objective>ans</objective>
<TargetEvaluation class="DataObjects" type="PointSet">optOut</TargetEvaluation>
<Sampler class="Samplers" type="MonteCarlo">MC_samp</Sampler>
</GeneticAlgorithm>
</Optimizers>

<Samplers>
<MonteCarlo name="MC_samp">
<samplerInit>
<limit>20</limit>
<initialSeed>20021986</initialSeed>
</samplerInit>
<variable name="x1">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>
<variable name="x2">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>
<variable name="x3">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>
<variable name="x4">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>
<variable name="x5">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>
<variable name="x6">
<distribution>uniform_dist_woRepl_1</distribution>
</variable>
</MonteCarlo>
</Samplers>

<Models>
<ExternalModel ModuleToLoad="../../../../../AnalyticModels/optimizing/myLocalSum.py" name="myLocalSum" subType="">
<inputs>x1,x2,x3,x4,x5,x6,ans</inputs>
<outputs>x1,x2,x3,x4,x5,x6,ans</outputs>
</ExternalModel>
</Models>

<DataObjects>
<PointSet name="placeholder"/>
<PointSet name="optOut">
<Input>x1,x2,x3,x4,x5,x6</Input>
<Output>ans</Output>
</PointSet>
<PointSet name="opt_export">
<Input>trajID</Input>
<Output>x1,x2,x3,x4,x5,x6,ans,age,batchId,fitness,iteration,accepted,conv_objective</Output>
</PointSet>
</DataObjects>

<OutStreams>
<Print name="optOut">
<type>csv</type>
<source>optOut</source>
</Print>
<Print name="opt_export">
<type>csv</type>
<source>opt_export</source>
<clusterLabel>trajID</clusterLabel>
</Print>
</OutStreams>
</Simulation>
Loading