From 59ab1fa49923fc22a9de8de291f8a315ee22b204 Mon Sep 17 00:00:00 2001 From: tiagoperes Date: Mon, 29 May 2017 22:24:15 -0300 Subject: [PATCH] AEMMD --- aemmd/aemmd.main.js | 163 ++++++++---- aemmd/aemmdOLD.main.js | 139 ++++++++++ aemmt/aemmt.main.js | 450 ++++++++++++++++----------------- moead/moead.main.js | 344 ++++++++++++------------- nsga/nsga3.main.js | 488 ++++++++++++++++++------------------ problem/problem.knapsack.js | 5 +- spea/spea.fitness.js | 120 ++++----- spea/spea.main.js | 124 ++++----- spea/spea.selection.js | 142 +++++------ 9 files changed, 1084 insertions(+), 891 deletions(-) create mode 100644 aemmd/aemmdOLD.main.js diff --git a/aemmd/aemmd.main.js b/aemmd/aemmd.main.js index 2774e09..701b92f 100644 --- a/aemmd/aemmd.main.js +++ b/aemmd/aemmd.main.js @@ -3,22 +3,19 @@ var dominates = moea.help.pareto.dominates; - function generateRandomPopulation(populationSize, randomizationFunction) { + function generateRandomPopulation(populationSize, randomizationFunction, objectives) { var population = []; for (let i = 0; i < populationSize; i++) { - population.push(randomizationFunction()); + let randomSolution = randomizationFunction(); + population.push({ + solution: randomSolution, + fitness: [], + objectiveValues: _.map(objectives, function (objective) {return objective(randomSolution)}) + }); } return population; } - function getSubsetFromIndexes(list, indexes) { - let subset = []; - for (let i = 0; i < indexes.length; i++) { - subset.push(list[indexes[i]]); - } - return subset; - } - function updateCombinationIndexes(indexes, maxValue) { var i = indexes.length - 1; indexes[i]++; @@ -36,31 +33,43 @@ var combinations = []; var indexes = Array.from(Array(size).keys()); while (_.last(indexes) < list.length) { - combinations.push(getSubsetFromIndexes(list, indexes)); + combinations.push(_.clone(indexes)); updateCombinationIndexes(indexes, list.length); } return combinations; } - function createDominationTable(objectives) { + function createTableObjectiveFunctions(objectiveIndexes) { + return _.map(objectiveIndexes, function (index) { + return function(individual) { + return individual.objectiveValues[index]; + } + }); + } + + function createTable(label, objectiveIndexes, maxLength) { return { - objectives: objectives, - solutions: [], + label: label, + objectives: createTableObjectiveFunctions(objectiveIndexes), + population: [], + maxLength: maxLength, score: 0 }; } - function createDominationTables(objectives) { + function createTables(objectives) { var tables = []; for (let i = 2; i <= objectives.length; i++) { - tables = _.concat(tables, _.map(getCombinations(objectives, i), createDominationTable)); + tables = _.concat(tables, _.map(getCombinations(objectives, i), function (objectiveIndexes, count) { + return createTable(tables.length + count, objectiveIndexes); + })); } return tables; } function selectParents(tables) { - var p1 = _.sample(_.maxBy(_.sampleSize(tables, 3), 'score').solutions); - var p2 = _.sample(_.maxBy(_.sampleSize(tables, 3), 'score').solutions); + var p1 = _.maxBy(_.sampleSize(tables, 3), 'score'); + var p2 = _.maxBy(_.sampleSize(tables, 3), 'score'); return [p1, p2]; } @@ -71,65 +80,109 @@ return solution; } - function crossover(parents, crossoverSettings, mutationSettings) { - var children = crossoverSettings.method(parents[0], parents[1]); - return _.map(children, _.partial(mutate, _, mutationSettings)); - } - - function getSolutionInObjectiveSpace(solution, objectives) { - return _.map(objectives, function(objective) { - return objective(solution); + function crossover(parentTables, crossoverSettings, mutationSettings, objectives) { + var p1 = _.sample(parentTables[0].population).solution, + p2 = _.sample(parentTables[1].population).solution, + children = crossoverSettings.method(p1, p2); + + children = _.map(children, function (c) { + return { + solution: mutate(c, mutationSettings), + fitness: [], + objectiveValues: _.map(objectives, function (objective) {return objective(c)}) + }; }); - } - function isEqualInObjectiveSpace(a, b, objectives) { - a = getSolutionInObjectiveSpace(a, objectives); - b = getSolutionInObjectiveSpace(b, objectives); - for (let i = 0; i < a.length; i++) { - if (Math.abs(a[i] - b[i]) > 0.000000001) return false; - } - return true; + return children; } - function updateTableWithSolution(table, solution, shouldUpdateScore) { - var newSolutions = [solution]; + function updateTable(table, individual) { + var newSolutions = []; - for (let i = 0; i < table.solutions.length; i++) { - let isEqual = isEqualInObjectiveSpace(table.solutions[i], solution, table.objectives); - if (isEqual || dominates(table.solutions[i], solution, table.objectives)) { - return; + for (let i = 0; i < table.population.length; i++) { + let isEqual = _.isEqual(table.population[i].objectiveValues, individual.objectiveValues); + if (isEqual || dominates(table.population[i], individual, table.objectives)) { + return false; } - if (!dominates(solution, table.solutions[i], table.objectives)) { - newSolutions.push(table.solutions[i]); + if (!dominates(individual, table.population[i], table.objectives)) { + newSolutions.push(table.population[i]); } } - table.solutions = newSolutions; - table.score += shouldUpdateScore ? 1 : 0; + individual.fitness[table.label] = getWeightingFitness(individual, table.objectives); + var allFitness = _.map(newSolutions, function (individual) {return individual.fitness[table.label]}); + var orderedIndex = getOrderedIndex(allFitness, individual.fitness[table.label], 0, allFitness.length); + newSolutions.splice(orderedIndex, 0, individual); + if (newSolutions.length > table.maxLength) { + newSolutions.pop(); + } + + table.population = newSolutions; + table.score++; } - function updateTablesWithSolutions(tables, solutions, shouldUpdateScore) { - _.forEach(solutions, function (s, index) { - if (!shouldUpdateScore) console.log('evaluating population member ' + index); - _.forEach(tables, _.partial(updateTableWithSolution, _, s, shouldUpdateScore)); + function getWeightingFitness(solution, objectives) { + var sum = _.sumBy(objectives, function (objective) { + return objective(solution); + }); + return sum / objectives.length; + } + + function getOrderedIndex(list, element, begin, end) { + var half = begin + Math.floor((end - begin) / 2); + if (begin === end) return begin; + if (element > list[half]) return getOrderedIndex(list, element, half+1, end); + if (element === list[half]) return -1; + return getOrderedIndex(list, element, begin, half); + } + + function updateTablesWithPopulation(tables, population, elementsPerTable, dominationTableLimit) { + _.forEach(population, function (individual) { + _.forEach(tables, function (table) { + updateTable(table, individual); + }); + }); + } + + function getNonDominatedSetFromTables(tables) { + var solutions = [], + numberOfObjectives = _.last(tables).population[0].objectiveValues.length, + objectives = []; + + for (let i = 0; i < numberOfObjectives; i++) { + objectives.push(function (ind) { + return ind.objectiveValues[i]; + }); + } + + _.forEach(tables, function (table) { + solutions = _.concat(solutions, table.population); }); + + console.log(solutions.length); + var res = _.uniqWith(moea.help.pareto.getNonDominatedSet(solutions, objectives), function (a,b) {return _.isEqual(a.objectiveValues,b.objectiveValues)}); + console.log(res.length); + return res; } function aemmd(settings) { - var tables = createDominationTables(settings.objectives), - population = generateRandomPopulation(settings.populationSize, settings.randomize), + var tables = createTables(settings.objectives, settings.elementsPerTable), + population = generateRandomPopulation(settings.elementsPerTable * tables.length, settings.randomize, settings.objectives), tableInvolvingAllObjectives = _.last(tables); - updateTablesWithSolutions(tables, population); + tableInvolvingAllObjectives.maxLength = settings.dominationTableLimit; + updateTablesWithPopulation(tables, population, settings.elementsPerTable, settings.dominationTableLimit); for (let i = 0; i < settings.numberOfGenerations; i++) { - if (i % 100 === 0) console.log('it ' + i +'; biggest table: ' + _.maxBy(tables, 'solutions.length').solutions.length); + if (i % 100 === 0) { + console.log('it ' + i +'; domination table size: ' + tableInvolvingAllObjectives.population.length); + } let parents = selectParents(tables); - let children = crossover(parents, settings.crossover, settings.mutation); - updateTablesWithSolutions(tables, children, true); + let children = crossover(parents, settings.crossover, settings.mutation, settings.objectives); + updateTablesWithPopulation(tables, children, settings.elementsPerTable, settings.dominationTableLimit); } - return tableInvolvingAllObjectives.solutions; + return _.map(getNonDominatedSetFromTables(tables), 'solution'); } window.moea = window.moea || {}; diff --git a/aemmd/aemmdOLD.main.js b/aemmd/aemmdOLD.main.js new file mode 100644 index 0000000..e91d4bd --- /dev/null +++ b/aemmd/aemmdOLD.main.js @@ -0,0 +1,139 @@ +(function () { + 'use strict'; + + var dominates = moea.help.pareto.dominates; + + function generateRandomPopulation(populationSize, randomizationFunction) { + var population = []; + for (let i = 0; i < populationSize; i++) { + population.push(randomizationFunction()); + } + return population; + } + + function getSubsetFromIndexes(list, indexes) { + let subset = []; + for (let i = 0; i < indexes.length; i++) { + subset.push(list[indexes[i]]); + } + return subset; + } + + function updateCombinationIndexes(indexes, maxValue) { + var i = indexes.length - 1; + indexes[i]++; + + while (i > 0 && _.last(indexes) >= maxValue) { + i--; + indexes[i]++; + for (let j = i + 1; j < indexes.length; j++) { + indexes[j] = indexes[j-1] + 1; + } + } + } + + function getCombinations(list, size) { + var combinations = []; + var indexes = Array.from(Array(size).keys()); + while (_.last(indexes) < list.length) { + combinations.push(getSubsetFromIndexes(list, indexes)); + updateCombinationIndexes(indexes, list.length); + } + return combinations; + } + + function createDominationTable(objectives, maxLength) { + return { + objectives: objectives, + solutions: [], + score: 0, + maxLength: maxLength + }; + } + + function createDominationTables(objectives, maxLength) { + var tables = []; + for (let i = 2; i <= objectives.length; i++) { + tables = _.concat(tables, _.map(getCombinations(objectives, i), _.partial(createDominationTable, _, maxLength))); + } + return tables; + } + + function selectParents(tables) { + var p1 = _.sample(_.maxBy(_.sampleSize(tables, 3), 'score').solutions); + var p2 = _.sample(_.maxBy(_.sampleSize(tables, 3), 'score').solutions); + return [p1, p2]; + } + + function mutate(solution, mutation) { + if (Math.random() < mutation.rate) { + return mutation.method(solution); + } + return solution; + } + + function crossover(parents, crossoverSettings, mutationSettings) { + var children = crossoverSettings.method(parents[0], parents[1]); + return _.map(children, _.partial(mutate, _, mutationSettings)); + } + + function getSolutionInObjectiveSpace(solution, objectives) { + return _.map(objectives, function(objective) { + return objective(solution); + }); + } + + function isEqualInObjectiveSpace(a, b, objectives) { + a = getSolutionInObjectiveSpace(a, objectives); + b = getSolutionInObjectiveSpace(b, objectives); + for (let i = 0; i < a.length; i++) { + if (Math.abs(a[i] - b[i]) > 0.000000001) return false; + } + return true; + } + + function updateTableWithSolution(table, solution, shouldUpdateScore) { + var newSolutions = [solution]; + + for (let i = 0; i < table.solutions.length; i++) { + let isEqual = isEqualInObjectiveSpace(table.solutions[i], solution, table.objectives); + if (isEqual || dominates(table.solutions[i], solution, table.objectives)) { + return; + } + if (!dominates(solution, table.solutions[i], table.objectives)) { + newSolutions.push(table.solutions[i]); + } + } + + table.solutions = newSolutions; + table.score += shouldUpdateScore ? 1 : 0; + } + + function updateTablesWithSolutions(tables, solutions, shouldUpdateScore) { + _.forEach(solutions, function (s, index) { + if (!shouldUpdateScore) console.log('evaluating population member ' + index); + _.forEach(tables, _.partial(updateTableWithSolution, _, s, shouldUpdateScore)); + }); + } + + function aemmd(settings) { + var tables = createDominationTables(settings.objectives, settings.elementsPerTable), + population = generateRandomPopulation(settings.elementsPerTable * tables.length, settings.randomize), + tableInvolvingAllObjectives = _.last(tables); + + tableInvolvingAllObjectives.maxLength = settings.dominationTableLimit; + updateTablesWithSolutions(tables, population); + + for (let i = 0; i < settings.numberOfGenerations; i++) { + if (i % 100 === 0) console.log('it ' + i +'; biggest table: ' + _.maxBy(tables, 'solutions.length').solutions.length); + let parents = selectParents(tables); + let children = crossover(parents, settings.crossover, settings.mutation); + updateTablesWithSolutions(tables, children, true); + } + + return tableInvolvingAllObjectives.solutions; + } + + window.moea = window.moea || {}; + _.set(moea, 'aemmd.main.execute', aemmd); +}()); diff --git a/aemmt/aemmt.main.js b/aemmt/aemmt.main.js index 062e290..ee08b91 100644 --- a/aemmt/aemmt.main.js +++ b/aemmt/aemmt.main.js @@ -1,225 +1,225 @@ -(function () { - 'use strict'; - - var dominates = moea.help.pareto.dominates; - - function generateRandomPopulation(populationSize, randomizationFunction, objectives) { - var population = []; - for (let i = 0; i < populationSize; i++) { - let randomSolution = randomizationFunction(); - population.push({ - solution: randomSolution, - fitness: [], - objectiveValues: _.map(objectives, function (objective) {return objective(randomSolution)}) - }); - } - return population; - } - - function updateCombinationIndexes(indexes, maxValue) { - var i = indexes.length - 1; - indexes[i]++; - - while (i > 0 && _.last(indexes) >= maxValue) { - i--; - indexes[i]++; - for (let j = i + 1; j < indexes.length; j++) { - indexes[j] = indexes[j-1] + 1; - } - } - } - - function getCombinations(list, size) { - var combinations = []; - var indexes = Array.from(Array(size).keys()); - while (_.last(indexes) < list.length) { - combinations.push(_.clone(indexes)); - updateCombinationIndexes(indexes, list.length); - } - return combinations; - } - - function createTableObjectiveFunctions(objectiveIndexes) { - return _.map(objectiveIndexes, function (index) { - return function(individual) { - return individual.objectiveValues[index]; - } - }); - } - - function createTable(label, objectiveIndexes, isDomination) { - return { - label: label, - objectives: createTableObjectiveFunctions(objectiveIndexes), - isDomination: isDomination, - population: [], - score: 0 - }; - } - - function createTables(objectives) { - var tables = []; - for (let i = 1; i <= objectives.length - 1; i++) { - tables = _.concat(tables, _.map(getCombinations(objectives, i), function (objectiveIndexes, count) { - return createTable(tables.length + count, objectiveIndexes, false); - })); - } - tables.push(createTable(tables.length, Array.from(Array(objectives.length).keys()), true)); - return tables; - } - - function selectParents(tables) { - var p1 = _.maxBy(_.sampleSize(tables, 3), 'score'); - var p2 = _.maxBy(_.sampleSize(tables, 3), 'score'); - return [p1, p2]; - } - - function mutate(solution, mutation) { - if (Math.random() < mutation.rate) { - return mutation.method(solution); - } - return solution; - } - - function crossover(parentTables, crossoverSettings, mutationSettings, objectives) { - var p1 = _.sample(parentTables[0].population).solution, - p2 = _.sample(parentTables[1].population).solution, - children = crossoverSettings.method(p1, p2); - - children = _.map(children, function (c) { - return { - solution: mutate(c, mutationSettings), - fitness: [], - parentTables: parentTables, - objectiveValues: _.map(objectives, function (objective) {return objective(c)}) - }; - }); - - return children; - } - - function updateDominationTable(table, individual, maxTableSize) { - var newSolutions = []; - - for (let i = 0; i < table.population.length; i++) { - let isEqual = _.isEqual(table.population[i].objectiveValues, individual.objectiveValues); - if (isEqual || dominates(table.population[i], individual, table.objectives)) { - return false; - } - if (!dominates(individual, table.population[i], table.objectives)) { - newSolutions.push(table.population[i]); - } - } - - individual.fitness[table.label] = getWeightingFitness(individual, table.objectives); - var allFitness = _.map(newSolutions, function (individual) {return individual.fitness[table.label]}); - var orderedIndex = getOrderedIndex(allFitness, individual.fitness[table.label], 0, allFitness.length); - newSolutions.splice(orderedIndex, 0, individual); - if (newSolutions.length > maxTableSize) { - newSolutions.pop(); - } - - table.population = newSolutions; - return true; - } - - function getWeightingFitness(solution, objectives) { - var sum = _.sumBy(objectives, function (objective) { - return objective(solution); - }); - return sum / objectives.length; - } - - function getOrderedIndex(list, element, begin, end) { - var half = begin + Math.floor((end - begin) / 2); - if (begin === end) return begin; - if (element > list[half]) return getOrderedIndex(list, element, half+1, end); - if (element === list[half]) return -1; - return getOrderedIndex(list, element, begin, half); - } - - function updateWeightingTable(table, individual, maxTableSize) { - individual.fitness[table.label] = getWeightingFitness(individual, table.objectives); - var allFitness = _.map(table.population, function (individual) {return individual.fitness[table.label]}); - var orderedIndex = getOrderedIndex(allFitness, individual.fitness[table.label], 0, allFitness.length); - if (orderedIndex !== -1) { - table.population.splice(orderedIndex, 0, individual); - if (table.population.length > maxTableSize) table.population.pop(); - return true; - } - } - - function updateTableWithNewIndividual(table, individual, elementsPerTable, dominationTableLimit) { - if (table.isDomination) { - return updateDominationTable(table, individual, dominationTableLimit); - } - return updateWeightingTable(table, individual, elementsPerTable); - } - - function updateTablesWithPopulation(tables, population, elementsPerTable, dominationTableLimit) { - _.forEach(population, function (individual) { - var updated = false; - - _.forEach(tables, function (table) { - let isTableUpdated = updateTableWithNewIndividual(table, individual, elementsPerTable, dominationTableLimit); - updated = updated || isTableUpdated; - }); - - if (updated) { - _.forEach(individual.parentTables, function (t) { - t.score++; - }); - } - }); - } - - function resetTablesScores(tables) { - _.forEach(tables, function (table) { - table.score = 0; - }); - } - - function getNonDominatedSetFromTables(tables) { - var solutions = [], - numberOfObjectives = _.last(tables).population[0].objectiveValues.length, - objectives = []; - - for (let i = 0; i < numberOfObjectives; i++) { - objectives.push(function (ind) { - return ind.objectiveValues[i]; - }); - } - - _.forEach(tables, function (table) { - solutions = _.concat(solutions, table.population); - }); - - console.log(solutions.length); - var res = _.uniqWith(moea.help.pareto.getNonDominatedSet(solutions, objectives), function (a,b) {return _.isEqual(a.objectiveValues,b.objectiveValues)}); - console.log(res.length); - return res; - } - - function aemmt(settings) { - var tables = createTables(settings.objectives), - population = generateRandomPopulation(settings.elementsPerTable * tables.length, settings.randomize, settings.objectives), - tableInvolvingAllObjectives = _.last(tables); - - updateTablesWithPopulation(tables, population, settings.elementsPerTable, settings.dominationTableLimit); - - for (let i = 0; i < settings.numberOfGenerations; i++) { - if (i % 100 === 0) { - resetTablesScores(tables); - console.log('it ' + i +'; domination table size: ' + tableInvolvingAllObjectives.population.length); - } - let parents = selectParents(tables); - let children = crossover(parents, settings.crossover, settings.mutation, settings.objectives); - updateTablesWithPopulation(tables, children, settings.elementsPerTable, settings.dominationTableLimit); - } - - return _.map(getNonDominatedSetFromTables(tables), 'solution'); - } - - window.moea = window.moea || {}; - _.set(moea, 'aemmt.main.execute', aemmt); -}()); +(function () { + 'use strict'; + + var dominates = moea.help.pareto.dominates; + + function generateRandomPopulation(populationSize, randomizationFunction, objectives) { + var population = []; + for (let i = 0; i < populationSize; i++) { + let randomSolution = randomizationFunction(); + population.push({ + solution: randomSolution, + fitness: [], + objectiveValues: _.map(objectives, function (objective) {return objective(randomSolution)}) + }); + } + return population; + } + + function updateCombinationIndexes(indexes, maxValue) { + var i = indexes.length - 1; + indexes[i]++; + + while (i > 0 && _.last(indexes) >= maxValue) { + i--; + indexes[i]++; + for (let j = i + 1; j < indexes.length; j++) { + indexes[j] = indexes[j-1] + 1; + } + } + } + + function getCombinations(list, size) { + var combinations = []; + var indexes = Array.from(Array(size).keys()); + while (_.last(indexes) < list.length) { + combinations.push(_.clone(indexes)); + updateCombinationIndexes(indexes, list.length); + } + return combinations; + } + + function createTableObjectiveFunctions(objectiveIndexes) { + return _.map(objectiveIndexes, function (index) { + return function(individual) { + return individual.objectiveValues[index]; + } + }); + } + + function createTable(label, objectiveIndexes, isDomination) { + return { + label: label, + objectives: createTableObjectiveFunctions(objectiveIndexes), + isDomination: isDomination, + population: [], + score: 0 + }; + } + + function createTables(objectives) { + var tables = []; + for (let i = 1; i <= objectives.length - 1; i++) { + tables = _.concat(tables, _.map(getCombinations(objectives, i), function (objectiveIndexes, count) { + return createTable(tables.length + count, objectiveIndexes, false); + })); + } + tables.push(createTable(tables.length, Array.from(Array(objectives.length).keys()), true)); + return tables; + } + + function selectParents(tables) { + var p1 = _.maxBy(_.sampleSize(tables, 3), 'score'); + var p2 = _.maxBy(_.sampleSize(tables, 3), 'score'); + return [p1, p2]; + } + + function mutate(solution, mutation) { + if (Math.random() < mutation.rate) { + return mutation.method(solution); + } + return solution; + } + + function crossover(parentTables, crossoverSettings, mutationSettings, objectives) { + var p1 = _.sample(parentTables[0].population).solution, + p2 = _.sample(parentTables[1].population).solution, + children = crossoverSettings.method(p1, p2); + + children = _.map(children, function (c) { + return { + solution: mutate(c, mutationSettings), + fitness: [], + parentTables: parentTables, + objectiveValues: _.map(objectives, function (objective) {return objective(c)}) + }; + }); + + return children; + } + + function updateDominationTable(table, individual, maxTableSize) { + var newSolutions = []; + + for (let i = 0; i < table.population.length; i++) { + let isEqual = _.isEqual(table.population[i].objectiveValues, individual.objectiveValues); + if (isEqual || dominates(table.population[i], individual, table.objectives)) { + return false; + } + if (!dominates(individual, table.population[i], table.objectives)) { + newSolutions.push(table.population[i]); + } + } + + individual.fitness[table.label] = getWeightingFitness(individual, table.objectives); + var allFitness = _.map(newSolutions, function (individual) {return individual.fitness[table.label]}); + var orderedIndex = getOrderedIndex(allFitness, individual.fitness[table.label], 0, allFitness.length); + newSolutions.splice(orderedIndex, 0, individual); + if (newSolutions.length > maxTableSize) { + newSolutions.pop(); + } + + table.population = newSolutions; + return true; + } + + function getWeightingFitness(solution, objectives) { + var sum = _.sumBy(objectives, function (objective) { + return objective(solution); + }); + return sum / objectives.length; + } + + function getOrderedIndex(list, element, begin, end) { + var half = begin + Math.floor((end - begin) / 2); + if (begin === end) return begin; + if (element > list[half]) return getOrderedIndex(list, element, half+1, end); + if (element === list[half]) return -1; + return getOrderedIndex(list, element, begin, half); + } + + function updateWeightingTable(table, individual, maxTableSize) { + individual.fitness[table.label] = getWeightingFitness(individual, table.objectives); + var allFitness = _.map(table.population, function (individual) {return individual.fitness[table.label]}); + var orderedIndex = getOrderedIndex(allFitness, individual.fitness[table.label], 0, allFitness.length); + if (orderedIndex !== -1) { + table.population.splice(orderedIndex, 0, individual); + if (table.population.length > maxTableSize) table.population.pop(); + return true; + } + } + + function updateTableWithNewIndividual(table, individual, elementsPerTable, dominationTableLimit) { + if (table.isDomination) { + return updateDominationTable(table, individual, dominationTableLimit); + } + return updateWeightingTable(table, individual, elementsPerTable); + } + + function updateTablesWithPopulation(tables, population, elementsPerTable, dominationTableLimit) { + _.forEach(population, function (individual) { + var updated = false; + + _.forEach(tables, function (table) { + let isTableUpdated = updateTableWithNewIndividual(table, individual, elementsPerTable, dominationTableLimit); + updated = updated || isTableUpdated; + }); + + if (updated) { + _.forEach(individual.parentTables, function (t) { + t.score++; + }); + } + }); + } + + function resetTablesScores(tables) { + _.forEach(tables, function (table) { + table.score = 0; + }); + } + + function getNonDominatedSetFromTables(tables) { + var solutions = [], + numberOfObjectives = _.last(tables).population[0].objectiveValues.length, + objectives = []; + + for (let i = 0; i < numberOfObjectives; i++) { + objectives.push(function (ind) { + return ind.objectiveValues[i]; + }); + } + + _.forEach(tables, function (table) { + solutions = _.concat(solutions, table.population); + }); + + console.log(solutions.length); + var res = _.uniqWith(moea.help.pareto.getNonDominatedSet(solutions, objectives), function (a,b) {return _.isEqual(a.objectiveValues,b.objectiveValues)}); + console.log(res.length); + return res; + } + + function aemmt(settings) { + var tables = createTables(settings.objectives), + population = generateRandomPopulation(settings.elementsPerTable * tables.length, settings.randomize, settings.objectives), + tableInvolvingAllObjectives = _.last(tables); + + updateTablesWithPopulation(tables, population, settings.elementsPerTable, settings.dominationTableLimit); + + for (let i = 0; i < settings.numberOfGenerations; i++) { + if (i % 100 === 0) { + resetTablesScores(tables); + console.log('it ' + i +'; domination table size: ' + tableInvolvingAllObjectives.population.length); + } + let parents = selectParents(tables); + let children = crossover(parents, settings.crossover, settings.mutation, settings.objectives); + updateTablesWithPopulation(tables, children, settings.elementsPerTable, settings.dominationTableLimit); + } + + return _.map(getNonDominatedSetFromTables(tables), 'solution'); + } + + window.moea = window.moea || {}; + _.set(moea, 'aemmt.main.execute', aemmt); +}()); diff --git a/moead/moead.main.js b/moead/moead.main.js index 90589c1..d8ee8f7 100644 --- a/moead/moead.main.js +++ b/moead/moead.main.js @@ -1,172 +1,172 @@ -(function () { - 'use strict'; - - var dominates = moea.help.pareto.dominates, - scalarize, best; - - function getRandomWeightVector(numberOfObjectives) { - var rand = _.map(new Array(numberOfObjectives - 1), Math.random); - var weights = []; - rand.push(0); - rand.push(1); - rand = _.orderBy(rand); - for (let i = 1; i < rand.length; i++) { - weights.push(rand[i] - rand[i - 1]); - } - return weights; - } - - function generateCells(populationSize, numberOfObjectives, randomize) { - var cells = []; - for (let i = 0; i < populationSize; i++) { - let weights = getRandomWeightVector(numberOfObjectives); - let solution = randomize(); - cells.push({weights: weights, solution: solution}); - } - return cells; - } - - function getEuclideanDistance(a, b) { - return Math.sqrt(_.reduce(a, function (sum, value, index) { - return Math.pow(value - b[index], 2); - }, 0)); - } - - function createEmptyMatrix(size) { - var matrix = []; - for (let i = 0; i < size; i++) { - matrix[i] = []; - } - return matrix; - } - - function getDistanceMatrix(cells) { - var distanceMatrix = createEmptyMatrix(cells.length); - - for (let i = 0; i < cells.length; i++) { - distanceMatrix[i][i] = 0; - for (let j = i + 1; j < cells.length; j++) { - let dist = getEuclideanDistance(cells[i].weights, cells[j].weights); - distanceMatrix[i][j] = {distance: dist, cell: cells[j]}; - distanceMatrix[j][i] = {distance: dist, cell: cells[i]}; - } - } - return distanceMatrix; - } - - function createNeighborhoodMap(cells, neighborhoodSize) { - var distanceMatrix = getDistanceMatrix(cells); - return _.map(cells, function (cell, index) { - return _.map(_.slice(_.orderBy(distanceMatrix[index], 'distance'), 0, neighborhoodSize), 'cell'); - }); - } - - function scalarizeWS(solution, weights, objectives) { - return _.reduce(weights, function (sum, weight, index) { - return sum + weight * objectives[index](solution); - }, 0); - } - - function scalarizeTE(solution, weights, objectives) { - return _.reduce(weights, function (max, weight, index) { - var solValue = objectives[index](solution), - dif; - - if (best[index] === undefined || solValue < best[index]) { - best[index] = solValue; - } - - dif = weight * Math.abs(solValue - best[index]); - return dif > max ? dif : max; - }, 0); - } - - function evaluate(cells, objectives) { - _.forEach(cells, function (cell) { - cell.fitness = scalarize(cell.solution, cell.weights, objectives); - }); - } - - function updateNeighborhood(neighborhood, child, objectives) { - _.forEach(neighborhood, function (cell) { - var childFitness = scalarize(child, cell.weights, objectives); - if (childFitness <= cell.fitness) { - cell.solution = child; - cell.fitness = childFitness; - return false; - } - }); - } - - function getSolutionInObjectiveSpace(solution, objectives) { - return _.map(objectives, function(objective) { - return objective(solution); - }); - } - - function isContainedInArchive(element, archive, objectives) { - var fElement = getSolutionInObjectiveSpace(element, objectives); - var contained = false; - var i = 0; - while (!contained && i < archive.length) { - let fArchiveElement = getSolutionInObjectiveSpace(archive[i], objectives); - contained = _.isEqual(fElement, fArchiveElement); - i++; - } - return contained; - } - - function updateArchive(archive, child, objectives) { - var newArchive = []; - - if (isContainedInArchive(child, archive, objectives)) { - return archive; - } - - for (let i = 0; i < archive.length; i++) { - if (dominates(archive[i], child, objectives)) { - return archive; - } - if (!dominates(child, archive[i], objectives)) { - newArchive.push(archive[i]); - } - } - - newArchive.push(child); - return newArchive; - } - - function mutate(child, mutation) { - if (Math.random() < mutation.rate) { - return mutation.method(child); - } - return child; - } - - function moead(settings) { - var cells = generateCells(settings.populationSize, settings.objectives.length, settings.randomize), - neighborhoodMap = createNeighborhoodMap(cells, settings.neighborhoodSize), - archive = []; - - best = []; - scalarize = settings.useTchebycheff ? scalarizeTE : scalarizeWS; - evaluate(cells, settings.objectives); - - for (let i = 0; i < settings.numberOfGenerations; i++) { - console.log(i); - _.forEach(cells, function (cell, index) { - let neighborhood = neighborhoodMap[index]; - let parents = _.map(_.sampleSize(neighborhood, 2), 'solution'); - let child = mutate(_.sample(settings.crossover.method(parents[0], parents[1])), settings.mutation); - updateNeighborhood(neighborhood, child, settings.objectives); - archive = updateArchive(archive, child, settings.objectives); - }); - } - - console.log(best); - return archive; - } - - window.moea = window.moea || {}; - _.set(moea, 'moead.main.execute', moead); -}()); +(function () { + 'use strict'; + + var dominates = moea.help.pareto.dominates, + scalarize, best; + + function getRandomWeightVector(numberOfObjectives) { + var rand = _.map(new Array(numberOfObjectives - 1), Math.random); + var weights = []; + rand.push(0); + rand.push(1); + rand = _.orderBy(rand); + for (let i = 1; i < rand.length; i++) { + weights.push(rand[i] - rand[i - 1]); + } + return weights; + } + + function generateCells(populationSize, numberOfObjectives, randomize) { + var cells = []; + for (let i = 0; i < populationSize; i++) { + let weights = getRandomWeightVector(numberOfObjectives); + let solution = randomize(); + cells.push({weights: weights, solution: solution}); + } + return cells; + } + + function getEuclideanDistance(a, b) { + return Math.sqrt(_.reduce(a, function (sum, value, index) { + return Math.pow(value - b[index], 2); + }, 0)); + } + + function createEmptyMatrix(size) { + var matrix = []; + for (let i = 0; i < size; i++) { + matrix[i] = []; + } + return matrix; + } + + function getDistanceMatrix(cells) { + var distanceMatrix = createEmptyMatrix(cells.length); + + for (let i = 0; i < cells.length; i++) { + distanceMatrix[i][i] = 0; + for (let j = i + 1; j < cells.length; j++) { + let dist = getEuclideanDistance(cells[i].weights, cells[j].weights); + distanceMatrix[i][j] = {distance: dist, cell: cells[j]}; + distanceMatrix[j][i] = {distance: dist, cell: cells[i]}; + } + } + return distanceMatrix; + } + + function createNeighborhoodMap(cells, neighborhoodSize) { + var distanceMatrix = getDistanceMatrix(cells); + return _.map(cells, function (cell, index) { + return _.map(_.slice(_.orderBy(distanceMatrix[index], 'distance'), 0, neighborhoodSize), 'cell'); + }); + } + + function scalarizeWS(solution, weights, objectives) { + return _.reduce(weights, function (sum, weight, index) { + return sum + weight * objectives[index](solution); + }, 0); + } + + function scalarizeTE(solution, weights, objectives) { + return _.reduce(weights, function (max, weight, index) { + var solValue = objectives[index](solution), + dif; + + if (best[index] === undefined || solValue < best[index]) { + best[index] = solValue; + } + + dif = weight * Math.abs(solValue - best[index]); + return dif > max ? dif : max; + }, 0); + } + + function evaluate(cells, objectives) { + _.forEach(cells, function (cell) { + cell.fitness = scalarize(cell.solution, cell.weights, objectives); + }); + } + + function updateNeighborhood(neighborhood, child, objectives) { + _.forEach(neighborhood, function (cell) { + var childFitness = scalarize(child, cell.weights, objectives); + if (childFitness <= cell.fitness) { + cell.solution = child; + cell.fitness = childFitness; + return false; + } + }); + } + + function getSolutionInObjectiveSpace(solution, objectives) { + return _.map(objectives, function(objective) { + return objective(solution); + }); + } + + function isContainedInArchive(element, archive, objectives) { + var fElement = getSolutionInObjectiveSpace(element, objectives); + var contained = false; + var i = 0; + while (!contained && i < archive.length) { + let fArchiveElement = getSolutionInObjectiveSpace(archive[i], objectives); + contained = _.isEqual(fElement, fArchiveElement); + i++; + } + return contained; + } + + function updateArchive(archive, child, objectives) { + var newArchive = []; + + if (isContainedInArchive(child, archive, objectives)) { + return archive; + } + + for (let i = 0; i < archive.length; i++) { + if (dominates(archive[i], child, objectives)) { + return archive; + } + if (!dominates(child, archive[i], objectives)) { + newArchive.push(archive[i]); + } + } + + newArchive.push(child); + return newArchive; + } + + function mutate(child, mutation) { + if (Math.random() < mutation.rate) { + return mutation.method(child); + } + return child; + } + + function moead(settings) { + var cells = generateCells(settings.populationSize, settings.objectives.length, settings.randomize), + neighborhoodMap = createNeighborhoodMap(cells, settings.neighborhoodSize), + archive = []; + + best = []; + scalarize = settings.useTchebycheff ? scalarizeTE : scalarizeWS; + evaluate(cells, settings.objectives); + + for (let i = 0; i < settings.numberOfGenerations; i++) { + console.log(i); + _.forEach(cells, function (cell, index) { + let neighborhood = neighborhoodMap[index]; + let parents = _.map(_.sampleSize(neighborhood, 2), 'solution'); + let child = mutate(_.sample(settings.crossover.method(parents[0], parents[1])), settings.mutation); + updateNeighborhood(neighborhood, child, settings.objectives); + archive = updateArchive(archive, child, settings.objectives); + }); + } + + console.log(best); + return archive; + } + + window.moea = window.moea || {}; + _.set(moea, 'moead.main.execute', moead); +}()); diff --git a/nsga/nsga3.main.js b/nsga/nsga3.main.js index be2d1b1..00e9da7 100644 --- a/nsga/nsga3.main.js +++ b/nsga/nsga3.main.js @@ -1,244 +1,244 @@ -(function () { - 'use strict'; - - function nsga(settings) { - var population = generateRandomPopulation(settings.populationSize, settings.randomize); - var fronts = moea.nsga.ranking.rank(population, settings.objectives); - for (let i = 0; i < settings.numberOfGenerations; i++) { - console.log(i); - - if (settings.filter) { - population = settings.filter(population); - fronts = moea.nsga.ranking.rank(population, settings.objectives); - } - - let parents = selectParents(population, settings.crossover.rate); - let children = generateOffspring(parents, settings.crossover.method, settings.mutation); - population = _.concat(population, children); - fronts = moea.nsga.ranking.rank(population, settings.objectives); - - population = naturalSelection(fronts, settings.populationSize, settings.objectives, settings.numberOfMeanPoints); - } - return fronts[0]; - } - - function findBestFrontWithEnoughPoints(fronts, minPoints) { - return _.find(fronts, function (f) { - return f.length >= minPoints; - }); - } - - function calculateNormalizedObjectivesInFronts(fronts, objectives) { - var max = _.fill(new Array(objectives.length), -Infinity), - min = _.fill(new Array(objectives.length), Infinity); - - _.forEach(fronts, function (f) { - _.forEach(f, function (solution) { - var values = objectives.map(function (objective, index) { - var value = objective(solution); - if (value > max[index]) max[index] = value; - if (value < min[index]) min[index] = value; - return value; - }); - solution.nsga3 = {values: values}; - }); - }); - - _.forEach(fronts, function (f) { - _.forEach(f, function (solution) { - solution.nsga3.values = _.map(solution.nsga3.values, function (v, index) { - return (v - min[index]) / ((max[index] + 0.01) - min[index]); - }); - }); - }); - } - - function getExtremePoints(front) { - var numberOfObjectives = front[0].nsga3.values.length; - var extremes = []; - for (let i = 0; i < numberOfObjectives; i++) { - extremes.push(_.minBy(front, function (s) { - var sqSum = _.reduce(s.nsga3.values, function (sum, value, index) { - return (index === i) ? sum : (sum + value * value); - }, 0); - return Math.sqrt(sqSum); - }).nsga3.values); - } - return extremes; - } - - function getMeanPoint(x, y) { - return x.map(function (value, index) { - return (value + y[index]) / 2; - }); - } - - function getMeanPointsInLine(line, depth) { - var mean = getMeanPoint(line.x, line.y); - if (depth === 0) return []; - return _.concat( - getMeanPointsInLine({x: line.x, y: mean, equation: line.equation}, depth - 1), - [{point: mean, lineEquation: line.equation, solutions: [], score: 0}], - getMeanPointsInLine({x: mean, y: line.y, equation: line.equation}, depth - 1) - ); - } - - function getLineEquation(x, y) { - var v = _.map(x, function (value, index) { - return value - y[index]; - }); - - var parameters = _.map(_.dropRight(v), function (value) { - return 1 / (value + 0.01); - }); - - parameters.push(-(v.length - 1) / (_.last(v) + 0.01)); - - var constant = _.reduce(_.dropRight(v), function (sum, value, index) { - return sum - x[index] / (value + 0.01); - }, 0); - - constant += (v.length - 1) * _.last(x) / (_.last(v) + 0.01); - - return {parameters: parameters, constant: constant}; - } - - function createLines(extremes) { - var lines = []; - for (let i = 0; i < extremes.length; i++) { - for (let j = i + 1; j < extremes.length; j++) { - lines.push({ - x: extremes[i], - y: extremes[j], - equation: getLineEquation(extremes[i], extremes[j]) - }); - } - } - return lines; - } - - function createNiches(extremes, numberOfMeanPoints) { - var lines = createLines(extremes); - var depth = Math.log2(numberOfMeanPoints + 1); - var meanPoints = []; - _.forEach(lines, function (line) { - meanPoints = _.concat(meanPoints, getMeanPointsInLine(line, depth)); - }); - return meanPoints; - } - - function getEuclideanDistance(a, b) { - return Math.sqrt(_.reduce(a, function (sum, value, index) { - return sum + Math.pow(value - b[index], 2); - }, 0)); - } - - function distributeFrontToNiches(front, niches) { - _.forEach(front, function (solution) { - var niche = _.minBy(niches, function (n) { - return getEuclideanDistance(solution.nsga3.values, n.point); - }); - niche.solutions.push(solution); - niche.score++; - }); - } - - function getDistanceToLine(point, lineEquation) { - var sum = _.reduce(point, function (sum, value, index) { - return sum + value * lineEquation.parameters[index]; - }, 0); - var divisor = Math.sqrt(_.sumBy(lineEquation.parameters, _.partial(Math.pow, _, 2))); - return (sum + lineEquation.constant) / divisor; - } - - function orderSolutionsAccordingToDistanceToLine(niche) { - niche.solutions = _.orderBy(niche.solutions, function (s) { - return getDistanceToLine(s.nsga3.values, niche.lineEquation); - }, 'desc'); - } - - function extractSolutionFromNiches(niches) { - var niche = _.minBy(niches, 'score'); - niche.score++; - return niche.solutions.pop(); - } - - function isNicheNotEmpty(niche) { - return niche.solutions.length; - } - - function truncate(frontToTruncate, numberOfElementsToKeep, fronts, objectives, numberOfMeanPoints) { - var bestFront = findBestFrontWithEnoughPoints(fronts, objectives.length - 1); - calculateNormalizedObjectivesInFronts([bestFront, frontToTruncate], objectives); - var extremes = getExtremePoints(bestFront, frontToTruncate); - var niches = createNiches(extremes, numberOfMeanPoints); - var result = []; - distributeFrontToNiches(frontToTruncate, niches); - _.forEach(niches, orderSolutionsAccordingToDistanceToLine); - for (let i = 0; i < numberOfElementsToKeep; i++) { - niches = _.filter(niches, isNicheNotEmpty); - result.push(extractSolutionFromNiches(niches)); - } - return result; - } - - function generateRandomPopulation(populationSize, randomizationFunction) { - var population = []; - for (let i = 0; i < populationSize; i++) { - population.push(randomizationFunction()); - } - return population; - } - - function calculateDistances(fronts, objectives) { - var calculate = moea.nsga.crowding.calculateDistances; - _.forEach(fronts, _.partial(calculate, _, objectives)); - } - - function selectParents(population, crossoverRate) { - var pairs = [], - maxPairs = _.floor(crossoverRate * population.length); - while (pairs.length < maxPairs) { - pairs.push([tournament(population), tournament(population)]); - } - return pairs; - } - - function tournament(population) { - return _.minBy(_.sampleSize(population, 2), 'fitness.rank'); - } - - function generateOffspring(parents, crossoverMethod, mutation) { - return _.flatten(_.map(parents, function (pair) { - var children = crossoverMethod(pair[0], pair[1]); - return _.map(children, _.partial(mutate, _, mutation)); - })); - } - - function mutate(solution, mutation) { - var rand = _.random(1, true); - if (rand <= mutation.rate) { - return mutation.method(solution); - } - return solution; - } - - function naturalSelection(fronts, maxPopulationSize, objectives, numberOfMeanPoints) { - var population = []; - var frontsToCheck = fronts; - while (population.length < maxPopulationSize) { - let front = _.head(frontsToCheck); - if (population.length + front.length <= maxPopulationSize) { - population = _.concat(population, front); - frontsToCheck = _.tail(frontsToCheck); - } else { - let truncatedFront = truncate(front, maxPopulationSize - population.length, fronts, objectives, numberOfMeanPoints); - population = _.concat(population, truncatedFront); - } - } - return population; - } - - window.moea = window.moea || {}; - _.set(moea, 'nsga3.main.execute', nsga); -}()); +(function () { + 'use strict'; + + function nsga(settings) { + var population = generateRandomPopulation(settings.populationSize, settings.randomize); + var fronts = moea.nsga.ranking.rank(population, settings.objectives); + for (let i = 0; i < settings.numberOfGenerations; i++) { + console.log(i); + + if (settings.filter) { + population = settings.filter(population); + fronts = moea.nsga.ranking.rank(population, settings.objectives); + } + + let parents = selectParents(population, settings.crossover.rate); + let children = generateOffspring(parents, settings.crossover.method, settings.mutation); + population = _.concat(population, children); + fronts = moea.nsga.ranking.rank(population, settings.objectives); + + population = naturalSelection(fronts, settings.populationSize, settings.objectives, settings.numberOfMeanPoints); + } + return fronts[0]; + } + + function findBestFrontWithEnoughPoints(fronts, minPoints) { + return _.find(fronts, function (f) { + return f.length >= minPoints; + }); + } + + function calculateNormalizedObjectivesInFronts(fronts, objectives) { + var max = _.fill(new Array(objectives.length), -Infinity), + min = _.fill(new Array(objectives.length), Infinity); + + _.forEach(fronts, function (f) { + _.forEach(f, function (solution) { + var values = objectives.map(function (objective, index) { + var value = objective(solution); + if (value > max[index]) max[index] = value; + if (value < min[index]) min[index] = value; + return value; + }); + solution.nsga3 = {values: values}; + }); + }); + + _.forEach(fronts, function (f) { + _.forEach(f, function (solution) { + solution.nsga3.values = _.map(solution.nsga3.values, function (v, index) { + return (v - min[index]) / ((max[index] + 0.01) - min[index]); + }); + }); + }); + } + + function getExtremePoints(front) { + var numberOfObjectives = front[0].nsga3.values.length; + var extremes = []; + for (let i = 0; i < numberOfObjectives; i++) { + extremes.push(_.minBy(front, function (s) { + var sqSum = _.reduce(s.nsga3.values, function (sum, value, index) { + return (index === i) ? sum : (sum + value * value); + }, 0); + return Math.sqrt(sqSum); + }).nsga3.values); + } + return extremes; + } + + function getMeanPoint(x, y) { + return x.map(function (value, index) { + return (value + y[index]) / 2; + }); + } + + function getMeanPointsInLine(line, depth) { + var mean = getMeanPoint(line.x, line.y); + if (depth === 0) return []; + return _.concat( + getMeanPointsInLine({x: line.x, y: mean, equation: line.equation}, depth - 1), + [{point: mean, lineEquation: line.equation, solutions: [], score: 0}], + getMeanPointsInLine({x: mean, y: line.y, equation: line.equation}, depth - 1) + ); + } + + function getLineEquation(x, y) { + var v = _.map(x, function (value, index) { + return value - y[index]; + }); + + var parameters = _.map(_.dropRight(v), function (value) { + return 1 / (value + 0.01); + }); + + parameters.push(-(v.length - 1) / (_.last(v) + 0.01)); + + var constant = _.reduce(_.dropRight(v), function (sum, value, index) { + return sum - x[index] / (value + 0.01); + }, 0); + + constant += (v.length - 1) * _.last(x) / (_.last(v) + 0.01); + + return {parameters: parameters, constant: constant}; + } + + function createLines(extremes) { + var lines = []; + for (let i = 0; i < extremes.length; i++) { + for (let j = i + 1; j < extremes.length; j++) { + lines.push({ + x: extremes[i], + y: extremes[j], + equation: getLineEquation(extremes[i], extremes[j]) + }); + } + } + return lines; + } + + function createNiches(extremes, numberOfMeanPoints) { + var lines = createLines(extremes); + var depth = Math.log2(numberOfMeanPoints + 1); + var meanPoints = []; + _.forEach(lines, function (line) { + meanPoints = _.concat(meanPoints, getMeanPointsInLine(line, depth)); + }); + return meanPoints; + } + + function getEuclideanDistance(a, b) { + return Math.sqrt(_.reduce(a, function (sum, value, index) { + return sum + Math.pow(value - b[index], 2); + }, 0)); + } + + function distributeFrontToNiches(front, niches) { + _.forEach(front, function (solution) { + var niche = _.minBy(niches, function (n) { + return getEuclideanDistance(solution.nsga3.values, n.point); + }); + niche.solutions.push(solution); + niche.score++; + }); + } + + function getDistanceToLine(point, lineEquation) { + var sum = _.reduce(point, function (sum, value, index) { + return sum + value * lineEquation.parameters[index]; + }, 0); + var divisor = Math.sqrt(_.sumBy(lineEquation.parameters, _.partial(Math.pow, _, 2))); + return (sum + lineEquation.constant) / divisor; + } + + function orderSolutionsAccordingToDistanceToLine(niche) { + niche.solutions = _.orderBy(niche.solutions, function (s) { + return getDistanceToLine(s.nsga3.values, niche.lineEquation); + }, 'desc'); + } + + function extractSolutionFromNiches(niches) { + var niche = _.minBy(niches, 'score'); + niche.score++; + return niche.solutions.pop(); + } + + function isNicheNotEmpty(niche) { + return niche.solutions.length; + } + + function truncate(frontToTruncate, numberOfElementsToKeep, fronts, objectives, numberOfMeanPoints) { + var bestFront = findBestFrontWithEnoughPoints(fronts, objectives.length - 1); + calculateNormalizedObjectivesInFronts([bestFront, frontToTruncate], objectives); + var extremes = getExtremePoints(bestFront, frontToTruncate); + var niches = createNiches(extremes, numberOfMeanPoints); + var result = []; + distributeFrontToNiches(frontToTruncate, niches); + _.forEach(niches, orderSolutionsAccordingToDistanceToLine); + for (let i = 0; i < numberOfElementsToKeep; i++) { + niches = _.filter(niches, isNicheNotEmpty); + result.push(extractSolutionFromNiches(niches)); + } + return result; + } + + function generateRandomPopulation(populationSize, randomizationFunction) { + var population = []; + for (let i = 0; i < populationSize; i++) { + population.push(randomizationFunction()); + } + return population; + } + + function calculateDistances(fronts, objectives) { + var calculate = moea.nsga.crowding.calculateDistances; + _.forEach(fronts, _.partial(calculate, _, objectives)); + } + + function selectParents(population, crossoverRate) { + var pairs = [], + maxPairs = _.floor(crossoverRate * population.length); + while (pairs.length < maxPairs) { + pairs.push([tournament(population), tournament(population)]); + } + return pairs; + } + + function tournament(population) { + return _.minBy(_.sampleSize(population, 2), 'fitness.rank'); + } + + function generateOffspring(parents, crossoverMethod, mutation) { + return _.flatten(_.map(parents, function (pair) { + var children = crossoverMethod(pair[0], pair[1]); + return _.map(children, _.partial(mutate, _, mutation)); + })); + } + + function mutate(solution, mutation) { + var rand = _.random(1, true); + if (rand <= mutation.rate) { + return mutation.method(solution); + } + return solution; + } + + function naturalSelection(fronts, maxPopulationSize, objectives, numberOfMeanPoints) { + var population = []; + var frontsToCheck = fronts; + while (population.length < maxPopulationSize) { + let front = _.head(frontsToCheck); + if (population.length + front.length <= maxPopulationSize) { + population = _.concat(population, front); + frontsToCheck = _.tail(frontsToCheck); + } else { + let truncatedFront = truncate(front, maxPopulationSize - population.length, fronts, objectives, numberOfMeanPoints); + population = _.concat(population, truncatedFront); + } + } + return population; + } + + window.moea = window.moea || {}; + _.set(moea, 'nsga3.main.execute', nsga); +}()); diff --git a/problem/problem.knapsack.js b/problem/problem.knapsack.js index 0e0090f..c05f02f 100644 --- a/problem/problem.knapsack.js +++ b/problem/problem.knapsack.js @@ -189,10 +189,11 @@ setInstance(numberOfObjectives, numberOfItems); return aemmd({ - populationSize: 100, + elementsPerTable: 50, + dominationTableLimit: 150, randomize: generateRandom, objectives: getObjectiveArray(), - numberOfGenerations: numberOfItems * 10, + numberOfGenerations: 15000, crossover: {method: crossover}, mutation: {rate: 2 / instance.items, method: moea.help.binary.mutate} }); diff --git a/spea/spea.fitness.js b/spea/spea.fitness.js index e28a22f..c47baba 100644 --- a/spea/spea.fitness.js +++ b/spea/spea.fitness.js @@ -1,60 +1,60 @@ -(function () { - 'use strict'; - - var dominates = moea.help.pareto.dominates; - - function initializeFitness(population) { - _.forEach(population, function (individual) { - individual.fitness = { - strength: 0, - raw: 0, - dominatedByArray: [] - }; - }); - } - - function calculateDomination(population, objectives) { - for (let i = 0; i < population.length; i++) { - for (let j = i + 1; j < population.length; j++) { - if (dominates(population[i], population[j], objectives)) { - population[i].fitness.strength++; - population[j].fitness.dominatedByArray.push(i); - } else if (dominates(population[j], population[i], objectives)) { - population[i].fitness.dominatedByArray.push(j); - population[j].fitness.strength++; - } - } - } - } - - function calculateRawFitness(population) { - _.forEach(population, function (individual) { - _.forEach(individual.fitness.dominatedByArray, function (index) { - individual.fitness.raw += population[index].fitness.strength; - }); - }); - } - - function calculateDensity(distancesToNeighbors) { - var k = Math.round(Math.sqrt(distancesToNeighbors.length)); - return 1 / (distancesToNeighbors[k] + 2); - } - - function calculateOverallFitnessValues(population, distanceMatrix) { - _.forEach(population, function (individual) { - var distanceToNeighbors = distanceMatrix[individual.distanceMatrixKey], - density = calculateDensity(distanceToNeighbors); - individual.fitness.value = individual.fitness.raw + density; - }); - } - - function calculateFitness(population, objectives, distanceMatrix) { - initializeFitness(population); - calculateDomination(population, objectives); - calculateRawFitness(population); - calculateOverallFitnessValues(population, distanceMatrix); - } - - window.moea = window.moea || {}; - _.set(moea, 'spea.fitness.calculate', calculateFitness); -}()); +(function () { + 'use strict'; + + var dominates = moea.help.pareto.dominates; + + function initializeFitness(population) { + _.forEach(population, function (individual) { + individual.fitness = { + strength: 0, + raw: 0, + dominatedByArray: [] + }; + }); + } + + function calculateDomination(population, objectives) { + for (let i = 0; i < population.length; i++) { + for (let j = i + 1; j < population.length; j++) { + if (dominates(population[i], population[j], objectives)) { + population[i].fitness.strength++; + population[j].fitness.dominatedByArray.push(i); + } else if (dominates(population[j], population[i], objectives)) { + population[i].fitness.dominatedByArray.push(j); + population[j].fitness.strength++; + } + } + } + } + + function calculateRawFitness(population) { + _.forEach(population, function (individual) { + _.forEach(individual.fitness.dominatedByArray, function (index) { + individual.fitness.raw += population[index].fitness.strength; + }); + }); + } + + function calculateDensity(distancesToNeighbors) { + var k = Math.round(Math.sqrt(distancesToNeighbors.length)); + return 1 / (distancesToNeighbors[k] + 2); + } + + function calculateOverallFitnessValues(population, distanceMatrix) { + _.forEach(population, function (individual) { + var distanceToNeighbors = distanceMatrix[individual.distanceMatrixKey], + density = calculateDensity(distanceToNeighbors); + individual.fitness.value = individual.fitness.raw + density; + }); + } + + function calculateFitness(population, objectives, distanceMatrix) { + initializeFitness(population); + calculateDomination(population, objectives); + calculateRawFitness(population); + calculateOverallFitnessValues(population, distanceMatrix); + } + + window.moea = window.moea || {}; + _.set(moea, 'spea.fitness.calculate', calculateFitness); +}()); diff --git a/spea/spea.main.js b/spea/spea.main.js index 61f12d9..25c73d0 100644 --- a/spea/spea.main.js +++ b/spea/spea.main.js @@ -1,62 +1,62 @@ -(function () { - 'use strict'; - - function spea(settings) { - var population = generateRandomPopulation(settings.populationSize, settings.randomize), - distanceMatrix = moea.spea.distance.calculateDistanceMatrix(population, settings.objectives), - archive; - - moea.spea.fitness.calculate(population, settings.objectives, distanceMatrix); - archive = moea.spea.selection.selectArchive(population, settings.archiveSize); - for (let i = 0; i < settings.numberOfGenerations; i++) { - console.log(i); - let parents = selectParents(archive, settings.crossover.rate, settings.populationSize); - population = generateOffspring(parents, settings.crossover.method, settings.mutation); - let everybody = _.concat(population, archive); - distanceMatrix = moea.spea.distance.calculateDistanceMatrix(everybody, settings.objectives); - moea.spea.fitness.calculate(everybody, settings.objectives, distanceMatrix); - archive = moea.spea.selection.selectArchive(everybody, settings.archiveSize, distanceMatrix); - } - - return archive; - } - - function generateRandomPopulation(populationSize, randomizationFunction) { - var population = []; - for (let i = 0; i < populationSize; i++) { - population.push(randomizationFunction()); - } - return population; - } - - function selectParents(population, crossoverRate, maximumPopulationSize) { - var pairs = [], - maxPairs = _.floor(crossoverRate * maximumPopulationSize); - while (pairs.length < maxPairs) { - pairs.push([tournament(population), tournament(population)]); - } - return pairs; - } - - function tournament(population) { - return _.minBy(_.sampleSize(population, 2), 'fitness.value'); - } - - function generateOffspring(parents, crossoverMethod, mutation) { - return _.flatten(_.map(parents, function (pair) { - var children = crossoverMethod(pair[0], pair[1]); - return _.map(children, _.partial(mutate, _, mutation)); - })); - } - - function mutate(solution, mutation) { - var rand = _.random(1, true); - if (rand <= mutation.rate) { - return mutation.method(solution); - } - return solution; - } - - window.moea = window.moea || {}; - _.set(moea, 'spea.main.execute', spea); -}()); +(function () { + 'use strict'; + + function spea(settings) { + var population = generateRandomPopulation(settings.populationSize, settings.randomize), + distanceMatrix = moea.spea.distance.calculateDistanceMatrix(population, settings.objectives), + archive; + + moea.spea.fitness.calculate(population, settings.objectives, distanceMatrix); + archive = moea.spea.selection.selectArchive(population, settings.archiveSize); + for (let i = 0; i < settings.numberOfGenerations; i++) { + console.log(i); + let parents = selectParents(archive, settings.crossover.rate, settings.populationSize); + population = generateOffspring(parents, settings.crossover.method, settings.mutation); + let everybody = _.concat(population, archive); + distanceMatrix = moea.spea.distance.calculateDistanceMatrix(everybody, settings.objectives); + moea.spea.fitness.calculate(everybody, settings.objectives, distanceMatrix); + archive = moea.spea.selection.selectArchive(everybody, settings.archiveSize, distanceMatrix); + } + + return archive; + } + + function generateRandomPopulation(populationSize, randomizationFunction) { + var population = []; + for (let i = 0; i < populationSize; i++) { + population.push(randomizationFunction()); + } + return population; + } + + function selectParents(population, crossoverRate, maximumPopulationSize) { + var pairs = [], + maxPairs = _.floor(crossoverRate * maximumPopulationSize); + while (pairs.length < maxPairs) { + pairs.push([tournament(population), tournament(population)]); + } + return pairs; + } + + function tournament(population) { + return _.minBy(_.sampleSize(population, 2), 'fitness.value'); + } + + function generateOffspring(parents, crossoverMethod, mutation) { + return _.flatten(_.map(parents, function (pair) { + var children = crossoverMethod(pair[0], pair[1]); + return _.map(children, _.partial(mutate, _, mutation)); + })); + } + + function mutate(solution, mutation) { + var rand = _.random(1, true); + if (rand <= mutation.rate) { + return mutation.method(solution); + } + return solution; + } + + window.moea = window.moea || {}; + _.set(moea, 'spea.main.execute', spea); +}()); diff --git a/spea/spea.selection.js b/spea/spea.selection.js index 38af6ba..ddd6fbc 100644 --- a/spea/spea.selection.js +++ b/spea/spea.selection.js @@ -1,71 +1,71 @@ -(function () { - 'use strict'; - - function isNonDominated(individual) { - return individual.fitness.value < 1; - } - - function findDistancesToNeighbors(archive, distanceMatrix) { - _.forEach(archive, function (individual) { - individual.distances = moea.spea.distance.getNeighborsDistances(archive, individual, distanceMatrix); - }); - } - - function getMostSimilar(archive, distanceMatrix) { - var dist2Analyze = 0, distanceArraySize, min; - - findDistancesToNeighbors(archive, distanceMatrix); - distanceArraySize = archive[0].distances.length; - - while (archive.length > 1 && dist2Analyze < distanceArraySize) { - let distMin = archive[0].distances[dist2Analyze]; - min = [archive[0]]; - - for (let i = 1; i < archive.length; i++) { - let distI = archive[i].distances[dist2Analyze]; - if (distI < distMin) { - min = [archive[i]]; - distMin = distI; - } else if (distI === distMin) { - min.push(archive[i]); - } - } - - dist2Analyze++; - archive = min; - } - - return min[0]; - } - - function truncateArchive(archive, archiveSize, distanceMatrix) { - while (archive.length > archiveSize) { - var mostSimilar = getMostSimilar(archive, distanceMatrix); - var indexToRemove = _.findIndex(archive, _.partial(_.isEqual, mostSimilar)); - archive.splice(indexToRemove, 1); - } - return archive; - } - - function selectArchive(individuals, archiveSize, distanceMatrix) { - var nonDominatedSet = _.filter(individuals, isNonDominated); - - //let debug = _.uniq(_.map(nonDominatedSet, moea.help.binary.toInt)); - //let debug = _.uniq(_.map(nonDominatedSet, _.head)); - //if (debug.length) console.log('Non-dominated set: ' + debug.join(', ')); - - if (nonDominatedSet.length === archiveSize) { - return nonDominatedSet; - } - if (nonDominatedSet.length < archiveSize) { - return _.slice(_.orderBy(individuals, 'fitness.value'), 0, archiveSize); - } - return truncateArchive(nonDominatedSet, archiveSize, distanceMatrix); - } - - window.moea = window.moea || {}; - _.set(moea, 'spea.selection', { - selectArchive: selectArchive, - isNonDominated: isNonDominated - }); -}()); +(function () { + 'use strict'; + + function isNonDominated(individual) { + return individual.fitness.value < 1; + } + + function findDistancesToNeighbors(archive, distanceMatrix) { + _.forEach(archive, function (individual) { + individual.distances = moea.spea.distance.getNeighborsDistances(archive, individual, distanceMatrix); + }); + } + + function getMostSimilar(archive, distanceMatrix) { + var dist2Analyze = 0, distanceArraySize, min; + + findDistancesToNeighbors(archive, distanceMatrix); + distanceArraySize = archive[0].distances.length; + + while (archive.length > 1 && dist2Analyze < distanceArraySize) { + let distMin = archive[0].distances[dist2Analyze]; + min = [archive[0]]; + + for (let i = 1; i < archive.length; i++) { + let distI = archive[i].distances[dist2Analyze]; + if (distI < distMin) { + min = [archive[i]]; + distMin = distI; + } else if (distI === distMin) { + min.push(archive[i]); + } + } + + dist2Analyze++; + archive = min; + } + + return min[0]; + } + + function truncateArchive(archive, archiveSize, distanceMatrix) { + while (archive.length > archiveSize) { + var mostSimilar = getMostSimilar(archive, distanceMatrix); + var indexToRemove = _.findIndex(archive, _.partial(_.isEqual, mostSimilar)); + archive.splice(indexToRemove, 1); + } + return archive; + } + + function selectArchive(individuals, archiveSize, distanceMatrix) { + var nonDominatedSet = _.filter(individuals, isNonDominated); + + //let debug = _.uniq(_.map(nonDominatedSet, moea.help.binary.toInt)); + //let debug = _.uniq(_.map(nonDominatedSet, _.head)); + //if (debug.length) console.log('Non-dominated set: ' + debug.join(', ')); + + if (nonDominatedSet.length === archiveSize) { + return nonDominatedSet; + } + if (nonDominatedSet.length < archiveSize) { + return _.slice(_.orderBy(individuals, 'fitness.value'), 0, archiveSize); + } + return truncateArchive(nonDominatedSet, archiveSize, distanceMatrix); + } + + window.moea = window.moea || {}; + _.set(moea, 'spea.selection', { + selectArchive: selectArchive, + isNonDominated: isNonDominated + }); +}());