/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.cirqwizard.generation.optimizer; import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import org.cirqwizard.generation.toolpath.Toolpath; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Vector; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class Optimizer { private final static int POPULATION_SIZE = 350; private final static int TOURNAMENT_SIZE = 7; private final static double MUTATION_PROBABILITY = 0.025; private final static int MAX_GENERATIONS_COUNT = 10_000; private final static int REEVALUATION_FREQUENCY = 200; private final static double MIN_IMPROVEMENT = 0.2; private Environment environment; private Generation currentGeneration; private double feed; private double arcFeed; private double zFeed; private double clearance; private double safetyHeight; private int mergeTolerance; private DoubleProperty progressProperty = new SimpleDoubleProperty(); private DoubleProperty bestSolutionDuration = new SimpleDoubleProperty(); private BooleanProperty cancelledProperty; public Optimizer(List<Chain> chains, double feed, double zFeed, double arcFeed, double clearance, double safetyHeight, int mergeTolerance, BooleanProperty cancelledProperty) { this.environment = new Environment(chains); this.feed = feed; this.arcFeed = arcFeed; this.zFeed = zFeed; this.clearance = clearance; this.safetyHeight = safetyHeight; this.mergeTolerance = mergeTolerance; this.cancelledProperty = cancelledProperty; } public List<Chain> optimize() { init(); double lastEvaluation = Double.MAX_VALUE; for (int i = 0; i < MAX_GENERATIONS_COUNT; i++) { if (cancelledProperty.get()) break; progressProperty.setValue((double) i / MAX_GENERATIONS_COUNT); breed(); if (i % REEVALUATION_FREQUENCY == 0) { Phenotype mostFit = currentGeneration.getBestFitness(environment); final List<Toolpath> l = new ArrayList<>(); for (int j : mostFit.getGenes()) l.addAll(environment.getChains().get(j).getSegments()); final double bestResult = TimeEstimator.calculateTotalDuration(l, feed, zFeed, arcFeed, clearance, safetyHeight, true, mergeTolerance); Platform.runLater(() -> bestSolutionDuration.setValue(bestResult)); if (Math.abs(lastEvaluation - bestResult) < MIN_IMPROVEMENT) break; lastEvaluation = bestResult; } } Phenotype mostFit = currentGeneration.getBestFitness(environment); ArrayList<Chain> result = new ArrayList<>(); for (int i : mostFit.getGenes()) result.add(environment.getChains().get(i)); return result; } public DoubleProperty progressProperty() { return progressProperty; } public double getBestSolutionDuration() { return bestSolutionDuration.get(); } public DoubleProperty bestSolutionDurationProperty() { return bestSolutionDuration; } private void init() { int[] originalGenes = new int[environment.getChains().size()]; for (int i = 0; i < originalGenes.length; i++) originalGenes[i] = i; currentGeneration = new Generation(); currentGeneration.populate(environment.getChains().size(), POPULATION_SIZE); } public void breed() { final Vector<Phenotype> newGeneration = new Vector<>(); ExecutorService pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); for (int i = 0; i < POPULATION_SIZE; i++) { pool.submit(() -> { Phenotype parent1 = currentGeneration.tournamentWinner(environment, TOURNAMENT_SIZE); Phenotype parent2 = currentGeneration.tournamentWinner(environment, TOURNAMENT_SIZE); Phenotype child = parent1.crossOver(parent2); if (Math.random() < MUTATION_PROBABILITY) child.mutate(); child.calculateFitness(environment); newGeneration.add(child); }); } try { pool.shutdown(); pool.awaitTermination(10, TimeUnit.DAYS); } catch (InterruptedException e) {} currentGeneration = new Generation(new ArrayList<>(newGeneration)); } }