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 AcoAlgorithm implements Algorithm { //Parameter for Algo with default Values: /** * Should be even. */ private int popsize = 20; private int maxGenerations = 200; /** * The vaporization factor; */ private double p = 0.3; private double convergenceFactorReset = 0.99; private int rounds = 3; private int resetCount = 0; //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"); AcoAlgorithm instance = new AcoAlgorithm(); newFrame.setContentPane(instance.getAlgorithmPanel()); newFrame.pack(); newFrame.setVisible(true); newFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public AcoAlgorithm() { 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 vaporizationLabel = new JLabel("Vaporization:"); vaporizationLabel.setBounds(20, 135, 150, 20); parameterPanel.add(vaporizationLabel); JLabel resetLabel = new JLabel("Reset Trigger:"); resetLabel.setBounds(20, 160, 150, 20); parameterPanel.add(resetLabel); 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 vaporizationField = new JFormattedTextField(limitFormatter); vaporizationField.setValue(this.p); vaporizationField.setToolTipText("Only Double in range [0.0, 1.0] with DecimalSeperator Point('.')."); vaporizationField.addPropertyChangeListener(propertyChange -> p = Double.parseDouble(vaporizationField.getValue().toString())); vaporizationField.setBounds(160, 135, 50, 20); parameterPanel.add(vaporizationField); JFormattedTextField resetFactorField = new JFormattedTextField(limitFormatter); resetFactorField.setValue(this.convergenceFactorReset); resetFactorField.setToolTipText("Only Double in range [0.0, 1.0] with DecimalSeperator Point('.')."); resetFactorField.addPropertyChangeListener(propertyChange -> this.convergenceFactorReset = Double.parseDouble(resetFactorField.getValue().toString())); resetFactorField.setBounds(160, 160, 50, 20); parameterPanel.add(resetFactorField); 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 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(); println("Algo Parameter: p="+ p + " cfreset=" + convergenceFactorReset); resetCount = 0; startTimer(); Individual runBest = new Individual(); runBest.fitness = Double.MAX_VALUE; for(int r = 0; r < rounds; r++) { Individual roundBest = executeAcoAlgo(); if(cancel)return; resetState(); if(roundBest.fitness < runBest.fitness) runBest = roundBest; } printElapsedTime(); setState(runBest.position); updateVisual(); println("ResetsCount:"+resetCount); println("AlgoResult:" + runBest.fitness); } //Algo Part: /** * Algorithm 20 !! Fitness is better when smaller.: * PseudoCode: * Best <- actual; * pheromones = initPheromons(); * for(maxGeneration times){ * population = createSolutionsBiasedBy(pheromones); * for(each Individual i from population){ * fitness <- evaluatePosition(i); * if(fitness < best.fitnessValue) Best <- i; * } * vaporizeIntensifiePheromons(pheromones); * } * @return */ private Individual executeAcoAlgo() { 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(); if(problemSize == 0) return best; List pheromones = initPheromones(problemSize); List population = new ArrayList(); println("Size To Test:" + population.size()); for(int generation = 0; generation< maxGenerations; generation++) { population.clear(); population = constructSolutionsBiasedBy(pheromones); 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; } println("________________"); vaporizeIntensifiePheromons(pheromones, best.position, problemSize); double cf = calculateConvergenceFactor(pheromones, problemSize); println("ConvergenceFactor = " + cf); if(cf > this.convergenceFactorReset) { pheromones = initPheromones(problemSize); resetCount++; } } println("RoundResult:" + best.fitness); return best; } /** * tj1 is the pheromon level in the j position * cf is the convergence factor cf e [0;1] * difference = | tj1 - tj0 | = | tj1 - (1 - tj1) | * * * * @param pheromones * @return cf */ private double calculateConvergenceFactor(List pheromones,int problemSize) { double sumOfDifference = pheromones.stream().map(tj1 -> Math.abs(tj1 - (1.0 - tj1))).reduce(0.0, Double::sum); double cf = sumOfDifference / (double)problemSize; return cf; } /** * pheromone <- (1-p) * pheromone; * if(best is true at this position) pheromone <- pheromone + p; * @param pheromones * @param position */ private void vaporizeIntensifiePheromons(List pheromones, List position, int problemSize) { ListIterator iterPheromone = pheromones.listIterator(); ListIterator iterBest = position.listIterator(); for(int i = 0; i < problemSize; i++) { double pheromone = iterPheromone.next(); boolean bestDecision = iterBest.next(); iterPheromone.set((1.0 - p) * pheromone + (bestDecision?p:0.0)); } } /** * * @param pheromones * @return */ private List constructSolutionsBiasedBy(List pheromones) { List population = new ArrayList(); for(int i = 0; i < popsize; i++) { population.add(constructASolutionBiasedBy(pheromones)); } return population; } /** * Walks the path with a ant and decide by pheromones if should take true or false; * A pheromone have a level of 0 < pheromone < 1. * A Pheromone is equal to the probability. * @param pheromones * @return */ private Individual constructASolutionBiasedBy(List pheromones) { Individual result = new Individual(); result.position = new ArrayList(); for(double pheromone : pheromones) { result.position.add((Random.nextDouble() < pheromone)); } return result; } /** * Initialize Pheromons with 0.5 */ private List initPheromones(int problemSize) { List result = new ArrayList(); for(int i = 0; i < problemSize;i++) { result.add(0.5); } 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(); } } }