diff --git a/src/lib/crossover/numeric/floating/BaseFloatingCrossover.ts b/src/lib/crossover/numeric/floating/BaseFloatingCrossover.ts new file mode 100644 index 0000000..d814c9e --- /dev/null +++ b/src/lib/crossover/numeric/floating/BaseFloatingCrossover.ts @@ -0,0 +1,73 @@ +/* + * @license + * Copyright (c) 2019 Cristian Abrante. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + */ + +import { Generator } from '../../../generator/utils'; +import { NumericRange } from '../../../individual/numeric/base'; +import { FloatingIndividual } from '../../../individual/numeric/floating'; +import BaseCrossover from '../../base/BaseCrossover'; +import { CrossoverParams, IndividualConstructor } from '../../base/Crossover'; + +export interface BaseFloatingCrossoverParams extends CrossoverParams { + alpha: number; +} + +abstract class BaseFloatingCrossover extends BaseCrossover { + private recombinationPoint: number = 0; + public cross( + firstParent: FloatingIndividual, + secondParent: FloatingIndividual, + alpha = 0.5, + engine = Generator.DEFAULT_ENGINE, + ): FloatingIndividual[] { + return this.crossWith(firstParent, secondParent, { engine, alpha, individualConstructor: FloatingIndividual }); + } + + public crossWith( + firstParent: FloatingIndividual, + secondParent: FloatingIndividual, + params: BaseFloatingCrossoverParams, + ): FloatingIndividual[] { + this.generateRecombinationPoint(firstParent.length(), params); + this.checkParams(params); + return super.crossWith(firstParent, secondParent, params); + } + + protected getGenotypeValues( + firstParent: FloatingIndividual, + secondParent: FloatingIndividual, + params: BaseFloatingCrossoverParams, + index: number, + ): { first: number; second: number } { + const recombinationCondition = this.getRecombinationCondition(index); + const firstValue = firstParent.get(index); + const secondValue = secondParent.get(index); + return { + first: recombinationCondition ? firstValue : this.getRecombinationValue(firstValue, secondValue, params), + second: recombinationCondition ? secondValue : this.getRecombinationValue(secondValue, firstValue, params), + }; + } + + protected abstract getRecombinationCondition(index: number): boolean; + + protected getRecombinationValue( + firstParentValue: number, + secondParentValue: number, + params: BaseFloatingCrossoverParams, + ): number { + const { alpha } = params; + return alpha * firstParentValue + (1.0 - alpha) * secondParentValue; + } + + private checkParams(params: BaseFloatingCrossoverParams) { + if (!Generator.probabilityIsValid(params.alpha)) { + throw new Error(`BaseFloatingCrossover: alpha of ${params.alpha} is not in range [0.0, 1.0]`); + } + } + + private generateRecombinationPoint(parentsLength: number, params: BaseFloatingCrossoverParams) { + this.recombinationPoint = Generator.generateInteger(new NumericRange(0, parentsLength - 1), params.engine); + } +} diff --git a/src/lib/crossover/numeric/floating/SimpleArithmeticRecombination.ts b/src/lib/crossover/numeric/floating/SimpleArithmeticRecombination.ts new file mode 100644 index 0000000..fd5b43d --- /dev/null +++ b/src/lib/crossover/numeric/floating/SimpleArithmeticRecombination.ts @@ -0,0 +1,69 @@ +/* + * @license + * Copyright (c) 2019 Cristian Abrante. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + */ + +import { Generator } from '../../../generator/utils'; +import { NumericRange } from '../../../individual/numeric/base'; +import { FloatingIndividual } from '../../../individual/numeric/floating'; +import BaseCrossover from '../../base/BaseCrossover'; +import { CrossoverParams, IndividualConstructor } from '../../base/Crossover'; + +export interface SimpleArithmeticRecombinationParams extends CrossoverParams { + alpha: number; +} + +class SimpleArithmeticRecombination extends BaseCrossover< + FloatingIndividual, + number, + SimpleArithmeticRecombinationParams +> { + private recombinationPoint: number = 0; + + public cross( + firstParent: FloatingIndividual, + secondParent: FloatingIndividual, + individualConstructor: IndividualConstructor, + alpha = 0.5, + engine = Generator.DEFAULT_ENGINE, + ): FloatingIndividual[] { + return this.crossWith(firstParent, secondParent, { + alpha, + engine, + individualConstructor, + }); + } + + public crossWith( + firstParent: FloatingIndividual, + secondParent: FloatingIndividual, + params: SimpleArithmeticRecombinationParams, + ): FloatingIndividual[] { + this.generateRecombinationPoint(firstParent.length(), params); + return super.crossWith(firstParent, secondParent, params); + } + + protected getGenotypeValues( + firstParent: FloatingIndividual, + secondParent: FloatingIndividual, + params: SimpleArithmeticRecombinationParams, + index: number, + ): { first: number; second: number } { + const parentSelectionCondition = index < this.recombinationPoint; + const firstValue = firstParent.get(index); + const secondValue = secondParent.get(index); + return { + first: parentSelectionCondition ? firstValue : this.getRecombinedValues(secondValue, firstValue, params), + second: parentSelectionCondition ? secondValue : this.getRecombinedValues(firstValue, secondValue, params), + }; + } + + private getRecombinedValues(firstValue: number, secondValue: number, params: SimpleArithmeticRecombinationParams) { + return params.alpha * firstValue + (1.0 - params.alpha) * secondValue; + } + + private generateRecombinationPoint(parentsLength: number, params: SimpleArithmeticRecombinationParams) { + this.recombinationPoint = Generator.generateInteger(new NumericRange(1, parentsLength - 1), params.engine); + } +} diff --git a/src/lib/crossover/numeric/floating/SingleArithmeticRecombination.ts b/src/lib/crossover/numeric/floating/SingleArithmeticRecombination.ts new file mode 100644 index 0000000..39057c5 --- /dev/null +++ b/src/lib/crossover/numeric/floating/SingleArithmeticRecombination.ts @@ -0,0 +1,64 @@ +/* + * @license + * Copyright (c) 2019 Cristian Abrante. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + */ + +import { Generator } from '../../../generator/utils'; +import { NumericRange } from '../../../individual/numeric/base'; +import { FloatingIndividual } from '../../../individual/numeric/floating'; +import BaseCrossover from '../../base/BaseCrossover'; +import { CrossoverParams } from '../../base/Crossover'; +import { SimpleArithmeticRecombinationParams } from './SimpleArithmeticRecombination'; + +export interface SingleArithmeticRecombinationParams extends CrossoverParams { + alpha: number; +} + +class SingleArithmeticRecombination extends BaseCrossover< + FloatingIndividual, + number, + SingleArithmeticRecombinationParams +> { + private recombinationPoint: number = 0; + + public cross( + firstParent: FloatingIndividual, + secondParent: FloatingIndividual, + ...args: any[] + ): FloatingIndividual[] { + return []; + } + + public crossWith( + firstParent: FloatingIndividual, + secondParent: FloatingIndividual, + params: SingleArithmeticRecombinationParams, + ): FloatingIndividual[] { + this.generateRecombinationPoint(firstParent.length(), params); + return super.crossWith(firstParent, secondParent, params); + } + + protected getGenotypeValues( + firstParent: FloatingIndividual, + secondParent: FloatingIndividual, + params: SingleArithmeticRecombinationParams, + index: number, + ): { first: number; second: number } { + const parentSelectionCondition = index === this.recombinationPoint; + const firstValue = firstParent.get(index); + const secondValue = secondParent.get(index); + return { + first: parentSelectionCondition ? firstValue : this.getRecombinedValues(secondValue, firstValue, params), + second: parentSelectionCondition ? secondValue : this.getRecombinedValues(firstValue, secondValue, params), + }; + } + + private getRecombinedValues(firstValue: number, secondValue: number, params: SimpleArithmeticRecombinationParams) { + return params.alpha * firstValue + (1.0 - params.alpha) * secondValue; + } + + private generateRecombinationPoint(parentsLength: number, params: SimpleArithmeticRecombinationParams) { + this.recombinationPoint = Generator.generateInteger(new NumericRange(1, parentsLength - 1), params.engine); + } +}