package exampleAlgorithms; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.image.BufferedImage; import java.math.RoundingMode; import java.text.NumberFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Locale; import java.util.stream.Collectors; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.text.NumberFormatter; import api.Algorithm; import classes.AbstractCpsObject; import classes.CpsUpperNode; import classes.HolonElement; import classes.HolonObject; import classes.HolonSwitch; import ui.controller.Control; import ui.model.DecoratedGroupNode; import ui.model.DecoratedState; import ui.model.Model; import ui.view.Console; import ui.model.DecoratedHolonObject.HolonObjectState; public class GaAlgorithm implements Algorithm { //Parameter for Algo with default Values: /** * Should be even. */ private int popsize = 20; private int maxGenerations = 100; private double tournamentSize = 2.1; private double fixedSwapProbability = 0.02; private boolean useFixedSpawProbability = false; private double fixedMutateProbability = 0.02; private boolean useFixedMutateProbability = false; private int rounds = 2; //Settings For GroupNode using and cancel private boolean useGroupNode = false; private DecoratedGroupNode dGroupNode = null; private boolean cancel = false; //Parameter defined by Algo private HashMap access; LinkedList> resetChain = new LinkedList>(); private List initialState; private List switchList; private List objectList; //Gui Part: private Control control; private Console console = new Console(); private JPanel content = new JPanel(); //ProgressBar private JProgressBar progressBar = new JProgressBar(); private int progressBarCount = 0; private long startTime; private Thread runThread; public static void main(String[] args) { JFrame newFrame = new JFrame("exampleWindow"); GaAlgorithm instance = new GaAlgorithm(); newFrame.setContentPane(instance.getAlgorithmPanel()); newFrame.pack(); newFrame.setVisible(true); newFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public GaAlgorithm() { content.setLayout(new BorderLayout()); JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, createOptionPanel() , console); splitPane.setResizeWeight(0.0); content.add(splitPane, BorderLayout.CENTER); content.setPreferredSize(new Dimension(800,800)); } public JPanel createOptionPanel() { JPanel optionPanel = new JPanel(new BorderLayout()); JScrollPane scrollPane = new JScrollPane(createParameterPanel()); scrollPane.setBorder(BorderFactory.createTitledBorder("Parameter")); optionPanel.add(scrollPane, BorderLayout.CENTER); optionPanel.add(createButtonPanel(), BorderLayout.PAGE_END); return optionPanel; } private Component createParameterPanel() { JPanel parameterPanel = new JPanel(null); parameterPanel.setPreferredSize(new Dimension(510,300)); JLabel maxGenerationsLabel = new JLabel("Max. Generations:"); maxGenerationsLabel.setBounds(20, 60, 150, 20); parameterPanel.add(maxGenerationsLabel); JLabel populationLabel = new JLabel("Population Size:"); populationLabel.setBounds(20, 85, 150, 20); parameterPanel.add(populationLabel); JLabel roundLabel = new JLabel("Rounds:"); roundLabel.setBounds(20, 110, 150, 20); parameterPanel.add(roundLabel); JLabel mutationLabel = new JLabel("FixedMutation:"); mutationLabel.setBounds(50, 135, 150, 20); mutationLabel.setEnabled(useFixedMutateProbability); parameterPanel.add(mutationLabel); JLabel swapLabel = new JLabel("FixedSwap:"); swapLabel.setBounds(50, 160, 150, 20); swapLabel.setEnabled(this.useFixedSpawProbability); parameterPanel.add(swapLabel); JLabel tournamentLabel = new JLabel("TournamentSize:"); tournamentLabel.setBounds(20, 185, 150, 20); parameterPanel.add(tournamentLabel); JLabel progressLabel = new JLabel("Progress:"); progressLabel.setBounds(350, 135, 170, 20); parameterPanel.add(progressLabel); progressBar.setBounds(350, 155, 185, 20); progressBar.setStringPainted(true); parameterPanel.add(progressBar); JPanel borderPanel = new JPanel(null); borderPanel.setBounds(350, 85, 185, 50); borderPanel.setBorder(BorderFactory.createTitledBorder("")); parameterPanel.add(borderPanel); JLabel showGroupNodeLabel = new JLabel("Use Group Node:"); showGroupNodeLabel.setBounds(10, 1, 170, 20); borderPanel.add(showGroupNodeLabel); JButton selectGroupNodeButton = new JButton("Select GroupNode"); selectGroupNodeButton.setEnabled(false); selectGroupNodeButton.setBounds(10, 25, 165, 20); selectGroupNodeButton.addActionListener(actionEvent -> selectGroupNode()); borderPanel.add(selectGroupNodeButton); JCheckBox useGroupNodeCheckBox = new JCheckBox(); useGroupNodeCheckBox.setSelected(false); useGroupNodeCheckBox.setBounds(155, 1, 25, 20); useGroupNodeCheckBox.addActionListener(actionEvent -> { useGroupNode = useGroupNodeCheckBox.isSelected(); selectGroupNodeButton.setEnabled(useGroupNode); }); borderPanel.add(useGroupNodeCheckBox); //Integer formatter NumberFormat format = NumberFormat.getIntegerInstance(); format.setGroupingUsed(false); format.setParseIntegerOnly(true); NumberFormatter integerFormatter = new NumberFormatter(format); integerFormatter.setMinimum(1); integerFormatter.setCommitsOnValidEdit(true); JFormattedTextField maxGenerationsTextField = new JFormattedTextField(integerFormatter); maxGenerationsTextField.setValue(this.maxGenerations); maxGenerationsTextField.setToolTipText("Only positive Integer."); maxGenerationsTextField.addPropertyChangeListener(actionEvent -> this.maxGenerations = Integer.parseInt(maxGenerationsTextField.getValue().toString())); maxGenerationsTextField.setBounds(160, 60, 50, 20); parameterPanel.add(maxGenerationsTextField); JFormattedTextField popSizeTextField = new JFormattedTextField(integerFormatter); popSizeTextField.setValue(this.popsize); popSizeTextField.setToolTipText("Only positive Integer."); popSizeTextField.addPropertyChangeListener(propertyChange -> this.popsize = Integer.parseInt(popSizeTextField.getValue().toString())); popSizeTextField.setBounds(160, 85, 50, 20); parameterPanel.add(popSizeTextField); JFormattedTextField roundsTextField = new JFormattedTextField(integerFormatter); roundsTextField.setValue(this.rounds); roundsTextField.setToolTipText("Only positive Integer."); roundsTextField.addPropertyChangeListener(propertyChange -> this.rounds = Integer.parseInt(roundsTextField.getValue().toString())); roundsTextField.setBounds(160, 110, 50, 20); parameterPanel.add(roundsTextField); //Double Format: NumberFormat doubleFormat = NumberFormat.getNumberInstance(Locale.US); doubleFormat.setMinimumFractionDigits(1); doubleFormat.setMaximumFractionDigits(3); doubleFormat.setRoundingMode(RoundingMode.HALF_UP); //Limit Formatter: NumberFormatter limitFormatter = new NumberFormatter(doubleFormat); limitFormatter.setMinimum(0.0); limitFormatter.setMaximum(1.0); JFormattedTextField mutateTextField = new JFormattedTextField(limitFormatter); mutateTextField.setValue(this.fixedMutateProbability); mutateTextField.setEnabled(this.useFixedMutateProbability); mutateTextField.setToolTipText("Only Double in range [0.0, 1.0] with DecimalSeperator Point('.')."); mutateTextField.addPropertyChangeListener(propertyChange -> fixedMutateProbability = Double.parseDouble(mutateTextField.getValue().toString())); mutateTextField.setBounds(160, 135, 50, 20); parameterPanel.add(mutateTextField); JCheckBox useFixMutateCheckBox = new JCheckBox(); useFixMutateCheckBox.setSelected(this.useFixedMutateProbability); useFixMutateCheckBox.setToolTipText("If not checked its 1/ProblemSize"); useFixMutateCheckBox.setBounds(20, 135, 25, 20); useFixMutateCheckBox.addActionListener(actionEvent -> { boolean state = useFixMutateCheckBox.isSelected(); this.useFixedMutateProbability = state; mutateTextField.setEnabled(state); mutationLabel.setEnabled(state); }); parameterPanel.add(useFixMutateCheckBox); JFormattedTextField swapTextField = new JFormattedTextField(limitFormatter); swapTextField.setValue(this.fixedSwapProbability); swapTextField.setEnabled(this.useFixedMutateProbability); swapTextField.setToolTipText("Only Double in range [0.0, 1.0] with DecimalSeperator Point('.')."); swapTextField.addPropertyChangeListener(propertyChange -> this.fixedSwapProbability = Double.parseDouble(swapTextField.getValue().toString())); swapTextField.setBounds(160, 160, 50, 20); parameterPanel.add(swapTextField); JCheckBox useFixSwapCheckBox = new JCheckBox(); useFixSwapCheckBox.setSelected(this.useFixedSpawProbability); useFixSwapCheckBox.setToolTipText("If not checked its 1/ProblemSize"); useFixSwapCheckBox.setBounds(20, 160, 25, 20); useFixSwapCheckBox.addActionListener(actionEvent -> { boolean state = useFixSwapCheckBox.isSelected(); this.useFixedSpawProbability = state; swapTextField.setEnabled(state); swapLabel.setEnabled(state); }); parameterPanel.add(useFixSwapCheckBox); NumberFormatter tournamentFormatter = new NumberFormatter(doubleFormat); tournamentFormatter.setMinimum(1.0); JFormattedTextField tournamentSizeTextField = new JFormattedTextField(tournamentFormatter); tournamentSizeTextField.setValue(this.tournamentSize); tournamentSizeTextField.setToolTipText("Only Double bigger or equal then 1."); tournamentSizeTextField.addPropertyChangeListener(propertyChange -> this.tournamentSize = Double.parseDouble(tournamentSizeTextField.getValue().toString())); tournamentSizeTextField.setBounds(160, 185, 50, 20); parameterPanel.add(tournamentSizeTextField); return parameterPanel; } public JPanel createButtonPanel() { JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); JButton resetButton = new JButton("ResetAll"); resetButton.setToolTipText("Resets the State to before the Algorithm has runed."); resetButton.addActionListener(actionEvent -> resetAll()); buttonPanel.add(resetButton); JButton cancelButton = new JButton("Cancel Run"); cancelButton.addActionListener(actionEvent -> cancel()); buttonPanel.add(cancelButton); JButton undoButton = new JButton("Undo"); undoButton.setToolTipText("One Algo Step Back."); undoButton.addActionListener(actionEvent -> resetLast()); buttonPanel.add(undoButton); JButton runButton = new JButton("Run"); runButton.addActionListener(actionEvent -> { Runnable task = () -> run(); runThread = new Thread(task); runThread.start(); }); buttonPanel.add(runButton); return buttonPanel; } private void cancel() { if(runThread.isAlive()) { println(""); println("Cancel run."); cancel = true; progressBar.setValue(0); } else { println("Nothing to cancel."); } } private void run() { cancel = false; disableGuiInput(true); executeAlgoWithParameter(); updateVisual(); disableGuiInput(false); } private void resetLast() { if(runThread.isAlive()) { println("Run have to be cancelled First."); return; } if(!resetChain.isEmpty()) { println("Resetting.."); resetState(); resetChain.removeLast(); control.resetSimulation(); updateVisual(); }else { println("No run inistialized."); } } private void resetAll() { if(runThread.isAlive()) { println("Run have to be cancelled First."); return; } if(!resetChain.isEmpty()) { println("Resetting.."); setState(resetChain.getFirst()); resetChain.clear(); control.resetSimulation(); control.setCurIteration(0); updateVisual(); }else { println("No run inistialized."); } } private void disableGuiInput(boolean bool) { control.guiDiable(bool); } @Override public JPanel getAlgorithmPanel() { return content; } @Override public void setController(Control control) { this.control = control; } private void clear() { console.clear(); } private void println(String message) { console.println(message); } private void selectGroupNode() { Object[] possibilities = control.getSimManager().getActualVisualRepresentationalState().getCreatedGroupNodes().values().stream().map(aCps -> new Handle(aCps)).toArray(); @SuppressWarnings("unchecked") Handle selected = (Handle) JOptionPane.showInputDialog(content, "Select GroupNode:", "GroupNode?", JOptionPane.OK_OPTION,new ImageIcon(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)) , possibilities, ""); if(selected != null) { println("Selected: " + selected); dGroupNode = selected.object; } } private void progressBarStep(){ progressBar.setValue(++progressBarCount); } private void calculateProgressBarParameter() { int max = this.maxGenerations * this.popsize * this.rounds; progressBarCount = 0; progressBar.setValue(0); progressBar.setMaximum(max); } private void startTimer(){ startTime = System.currentTimeMillis(); } private void printElapsedTime(){ long elapsedMilliSeconds = System.currentTimeMillis() - startTime; println("Execution Time of Algo in Milliseconds:" + elapsedMilliSeconds); } private void executeAlgoWithParameter(){ int actualIteration = control.getModel().getCurIteration(); println("TimeStep:" + actualIteration); calculateProgressBarParameter(); startTimer(); Individual runBest = new Individual(); runBest.fitness = Double.MAX_VALUE; for(int r = 0; r < rounds; r++) { Individual roundBest = executeGaAlgo(); if(cancel)return; resetState(); if(roundBest.fitness < runBest.fitness) runBest = roundBest; } printElapsedTime(); setState(runBest.position); updateVisual(); println("AlgoResult:" + runBest.fitness); } //Algo Part: /** * Algorithm 20 !! Fitness is better when smaller.: * PseudoCode: * Best <- actual; * population = initPopulationRandom(); * for(maxGeneration times){ * for(each Individual i from population){ * fitness <- evaluatePosition(i); * if(fitness < best.fitnessValue) Best <- i; * } * childList <- {}; * for(popsize/2 times){ * parentA <- selectAParent(population); * parentB <- selectAParent(population); * childA,childB <- crossover(Copy(parentA ), Copy(parentB)); * mutate(childA); * mutate(childB); * childList.add(childA); * childList.add(childB); * } * population = childList; * * } * @return */ private Individual executeGaAlgo() { Individual best = new Individual(); best.position = extractPositionAndAccess(); println("Bit-Array_length: " + best.position.size()); best.fitness = evaluatePosition(best.position, false); println("Start with Fitness: " + best.fitness); int problemSize = best.position.size(); List population = initPopuluationRandom(problemSize, best); println("Size To Test:" + population.size()); for(int generation = 0; generation< maxGenerations; generation++) { println("Generation" + generation + " start with Fitness: " + best.fitness); for(Individual i : population) { i.fitness = evaluatePosition(i.position, true); println("Fitness" + i.fitness); if(i.fitness < best.fitness) best = i; } List childList = new ArrayList(); for(int k = 0; k iter = child.position.listIterator(); while(iter.hasNext()) { boolean boolValue = iter.next(); if(Random.nextDouble() <= probability) { iter.set(!boolValue); } } } /** * Algorithm 25 Uniform Crossover. * Probability is set to 1/Problemsize when not changed. */ private void crossover(Individual childA, Individual childB, int problemSize) { double probability = (this.useFixedSpawProbability) ? this.fixedSwapProbability : 1.0/(double)problemSize; ListIterator iterA = childA.position.listIterator(); ListIterator iterB = childB.position.listIterator(); for(int i= 0; i < problemSize; i++) { boolean boolA = iterA.next(); boolean boolB = iterB.next(); if(Random.nextDouble() <= probability ) { //Swap iterA.set(boolB); iterB.set(boolA); } } } /** * Algorithm 32 Tournament Selection. * The fitnessValues are calculated for the Population List. * PseudoCode */ private Individual selectAParent(List population,int popsize) { Individual tournamentBest = population.get(Random.nextIntegerInRange(0, popsize)); double participants; for(participants = tournamentSize ; participants >= 2; participants -= 1.0) { Individual next = population.get(Random.nextIntegerInRange(0, popsize)); if(next.fitness < tournamentBest.fitness) tournamentBest = next; } //if tournament size is not a whole number like 2.5 or 3.6 //the remaining part is the chance to fight another time; 2.7 -> 70% chance to fight a second time if( participants > 1) { if(Random.nextDouble() < participants - 1.0) { println("Chance to find a match"); Individual next = population.get(Random.nextIntegerInRange(0, popsize)); if(next.fitness < tournamentBest.fitness) tournamentBest = next; } } return tournamentBest; } /** * Initialize the Population with Individuals that have a random Position. */ private List initPopuluationRandom(int problemSize, Individual startIndidual){ List population = new ArrayList(); for(int i = 0; i < popsize -1; i++) { population.add(createRandomIndividualWithoutFitness(problemSize)); } //Add Start Position population.add(new Individual(startIndidual)); return population; } /** * Algorithm 21 The BooleanVeator initialization. * @param problemSize * @return */ private Individual createRandomIndividualWithoutFitness(int problemSize) { //create Random Individual Without Fitness Individual result = new Individual(); result.position = new ArrayList(); for (int index = 0; index < problemSize; index++){ result.position.add(Random.nextBoolean()); } return result; } /** * Method to get the current Position alias a ListOf Booleans for aktive settings on the Objects on the Canvas. * Also initialize the Access Hashmap to swap faster positions. * @param model * @return */ private List extractPositionAndAccess() { Model model = control.getModel(); switchList = new ArrayList(); objectList = new ArrayList(); initialState = new ArrayList(); access= new HashMap(); rollOutNodes((useGroupNode && (dGroupNode != null))? dGroupNode.getModel().getNodes() :model.getObjectsOnCanvas(), initialState, model.getCurIteration()); resetChain.add(initialState); return initialState; } /** * Method to extract the Informations recursively out of the Model. * @param nodes * @param positionToInit * @param timeStep */ private void rollOutNodes(List nodes, List positionToInit, int timeStep) { for(AbstractCpsObject aCps : nodes) { if (aCps instanceof HolonObject) { for (HolonElement hE : ((HolonObject) aCps).getElements()) { positionToInit.add(hE.isActive()); access.put(positionToInit.size() - 1 , new AccessWrapper(hE)); } objectList.add((HolonObject) aCps); } else if (aCps instanceof HolonSwitch) { HolonSwitch sw = (HolonSwitch) aCps; positionToInit.add(sw.getState(timeStep)); switchList.add(sw); access.put(positionToInit.size() - 1 , new AccessWrapper(sw)); } else if(aCps instanceof CpsUpperNode) { rollOutNodes(((CpsUpperNode)aCps).getNodes(), positionToInit ,timeStep ); } } } private double evaluatePosition(List position, boolean doIncreaseCounter) { setState(position); if(doIncreaseCounter)progressBarStep(); control.calculateStateOnlyForCurrentTimeStep(); DecoratedState actualstate = control.getSimManager().getActualDecorState(); return PSOAlgorithm.getFitnessValueForState(actualstate); } /** * To let the User See the current state without touching the Canvas. */ private void updateVisual() { control.calculateStateAndVisualForCurrentTimeStep(); //control.updateCanvas(); //control.getGui().triggerUpdateController(null); } /** * Sets the Model back to its original State before the LAST run. */ private void resetState() { setState(resetChain.getLast()); } /** * Sets the State out of the given position for calculation or to show the user. * @param position */ private void setState(List position) { for(int i = 0;i position; Individual(){}; /** * Copy Constructor */ Individual(Individual c){ position = c.position.stream().collect(Collectors.toList()); fitness = c.fitness; } } /** * A Wrapper Class for Access HolonElement and HolonSwitch in one Element and not have to split the List. */ private class AccessWrapper { public static final int HOLONELEMENT = 0; public static final int SWITCH = 1; private int type; private HolonSwitch hSwitch; private HolonElement hElement; public AccessWrapper(HolonSwitch hSwitch){ type = SWITCH; this.hSwitch = hSwitch; } public AccessWrapper(HolonElement hElement){ type = HOLONELEMENT; this.hElement = hElement; } public void setState(boolean state) { if(type == HOLONELEMENT) { hElement.setActive(state); }else{//is switch hSwitch.setManualMode(true); hSwitch.setManualState(state); } } public boolean getState(int timeStep) { return (type == HOLONELEMENT)?hElement.isActive():hSwitch.getState(timeStep); } public int getType() { return type; } } private class RunResult { public int activatedFlex = 0; public int deactivatedElements = 0; public float totalCost = 0; public LinkedList timeStepList = new LinkedList(); public TimeStepStateResult addTimeStepStateResult(){ TimeStepStateResult aResult = new TimeStepStateResult(); timeStepList.add(aResult); return aResult; } public class TimeStepStateResult{ public int amountOfProducer = 0; public int amountOfConsumer = 0; public int amountOfPassiv = 0; public int amountOfConsumerOverSupplied = 0; public int amountOfConsumerSupplied = 0; public int amountOfConsumerPartiallySupplied = 0; public int amountOfConsumerUnSupplied= 0; public float getProportionWithState(HolonObjectState state) { float amountOfObjects = amountOfProducer + amountOfConsumer + amountOfPassiv; switch(state) { case NOT_SUPPLIED: return (float) amountOfConsumerUnSupplied / amountOfObjects; case NO_ENERGY: return (float) amountOfPassiv / amountOfObjects; case OVER_SUPPLIED: return (float) amountOfConsumerOverSupplied / amountOfObjects; case PARTIALLY_SUPPLIED: return (float) amountOfConsumerPartiallySupplied / amountOfObjects; case PRODUCER: return (float) amountOfProducer / amountOfObjects; case SUPPLIED: return (float) amountOfConsumerSupplied / amountOfObjects; default: return 0.f; } } } public float getAvergaeProportionWithState(HolonObjectState state) { return timeStepList.stream().map(step -> step.getProportionWithState(state)).reduce((a,b) -> (a + b)).orElse(0.f) / (float) 100; } } /** * To create Random and maybe switch the random generation in the future. */ private static class Random{ private static java.util.Random random = new java.util.Random(); /** * True or false * @return the random boolean. */ public static boolean nextBoolean(){ return random.nextBoolean(); } /** * Between 0.0(inclusive) and 1.0 (exclusive) * @return the random double. */ public static double nextDouble() { return random.nextDouble(); } /** * Random Int in Range [min;max[ with UniformDistirbution * @param min * @param max * @return */ public static int nextIntegerInRange(int min, int max) { return min + random.nextInt(max - min); } } private class Handle{ public T object; Handle(T object){ this.object = object; } public String toString() { return object.toString(); } } }