Search code examples
javacollectionsconcurrentmodification

ConcurrentModificationException when iterating through connections


I have a program emulating a Neural Network, that when finished will evolve it via NEAT algorithm.

Neural networks work by having a load of neurons, connected by connections. Part of evolving the program is to cross over neural networks, basically taking random neurons and connections from each one and putting them together. If however it takes a connection connecting 2 non-existing or disabled neurons, it gets disabled. And if a neuron has less than 1 existing or enabled input/output connection, it gets disabled. However, when iterating through the neuron's connections, I get the ConcurrentModificationException.

I am slightly confused as to why, and I think it is a misunderstanding on my part. What's wrong with the code?

Error:

Exception in thread "AWT-EventQueue-0"    java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at GUIDisplay$Handler.actionPerformed(GUIDisplay.java:610)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$300(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

GUIDisplay class (organises and displays neural nets):

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class GUIDisplay extends JPanel {

JButton save = new JButton("Save neural network");
JButton cross = new JButton("Cross 2 neural networks");
JButton newNN = new JButton("Create new neural network");
JButton load = new JButton("Load neural network");
NeuralRegister nr = new NeuralRegister();
int lastX = 0;
boolean drag = false;

InputNeuron inputA = new InputNeuron(nr),
        inputB = new InputNeuron(nr);

OutputNeuron outputA = new OutputNeuron(nr),
        outputB = new OutputNeuron(nr);

//ArrayList<Neuron> hidden = new ArrayList<Neuron>();
//ArrayList<Connection> connections = new ArrayList<Connection>();
ArrayList<JTextField> inputs = new ArrayList<JTextField>();
Map<String,NeuralRegister> species = new HashMap<String,NeuralRegister>();

Neuron startConnection;
Neuron endConnection;

JFrame j;

public GUIDisplay (final JFrame j) {

    this.j = j;

    setLayout(null);
    save.setLocation(10, 30);
    save.setSize(newNN.getPreferredSize());
    cross.setLocation(10, 55);
    cross.setSize(newNN.getPreferredSize());
    newNN.setLocation(10, 80);
    newNN.setSize(newNN.getPreferredSize());
    load.setLocation(10, 105);
    load.setSize(newNN.getPreferredSize());
    add(save);
    add(cross);
    add(newNN);
    add(load);

    for (Neuron n : nr.getNeurons()) {

        if (n instanceof InputNeuron) {

            JTextField text = new JTextField(Double.toString(n.getOutput()));
            text.setSize(100, 25);
            inputs.add(text);

        }

    }

    /*hidden.add(new Neuron(nr));
    hidden.add(new Neuron(nr));

    Connection inA = new Connection(inputA, hidden.get(0), nr);
    Connection inB = new Connection(inputB, hidden.get(1), nr);

    Connection outA = new Connection(hidden.get(hidden.size() - 1), outputA, nr);
    Connection outB = new Connection(hidden.get(hidden.size() - 2), outputB, nr);

    for (int i = 0; i < hidden.size(); i ++) {

        if (i < hidden.size() - 1) {

            Connection newC = new Connection(hidden.get(i), hidden.get(i + 1), nr);

        }

    }*/

    calculateNeuronLocations();

    inputA.setOutput(20);
    inputB.setOutput(-20);

    //inputA.calculateOutput();
    //inputB.calculateOutput();

    Handler h = new Handler();
    addMouseListener(h);
    addMouseMotionListener(h);
    save.addActionListener(h);
    cross.addActionListener(h);
    newNN.addActionListener(h);
    load.addActionListener(h);

}

public void calculateNeuronLocations () {

    int x = 100;
    int y = 250;

    int xGap = 100;
    int yGap = 40;

    for (Neuron n : nr.getNeurons()) {

        if (n instanceof InputNeuron) {

            n.setLocation(new Point(x,y));
            y += yGap;

        }

    }

    y = 250;
    x += xGap;

    for (Neuron n : nr.getNeurons()) {

        if (!(n instanceof InputNeuron) && !(n instanceof OutputNeuron)) {

            n.setLocation(new Point(x,y));
            y += yGap;

            if (y >= 200) {

                y = 100;
                x += xGap;

            }

        }

    }

    y = 250;
    x = j.getWidth() - 100;

    for (Neuron n : nr.getNeurons()) {

        if (n instanceof OutputNeuron) {

            n.setLocation(new Point(x,y));
            y += yGap;

        }

    }

    lastX = x;

}

@Override
public void paintComponent (Graphics g) {

    super.paintComponent(g);

    g.setColor(Color.BLACK);
    g.drawString("Connections: " + nr.getConnections().size(), 10, 10);
    g.drawString("Neurons: " + nr.getNeurons().size(), 10, 20);

    g.setColor(Color.BLUE);
    g.drawString("Input", 100, 230);
    g.drawString("Output", lastX, 230);

    for (Connection c : nr.getConnections()) {

        if (c.isEnabled()) {
            g.setColor(Color.GREEN);
        } else {
            g.setColor(Color.MAGENTA);
        }

        g.drawLine((int)c.getA().getLocation().getX(),
                (int)c.getA().getLocation().getY(),
                (int)c.getB().getLocation().getX(),
                (int)c.getB().getLocation().getY());

    }

    for (Neuron n : nr.getNeurons()) {

        if (n instanceof InputNeuron || n instanceof OutputNeuron) {

            g.setColor(new Color(150,0,0));

            g.drawRect((int)n.getLocation().getX() - 2,
                    (int)n.getLocation().getY() - 2,
                    4,
                    4);

        } else {

            if (n.isEnabled()) {
                g.setColor(Color.RED);
            } else {
                g.setColor(Color.BLUE);
            }

        }

        g.drawRect((int)n.getLocation().getX() - 5,
                (int)n.getLocation().getY() - 5,
                10,
                10);
        g.setColor(Color.BLACK);
        g.drawString(n.getId() + " (" + n.getOutput() + ")",
                (int)n.getLocation().getX() - 5,
                (int)n.getLocation().getY() - 5);

    }

    int cx = 100;
    int nx = 100;
    int y = j.getHeight() - 350;
    int width = 100, height = 100;

    for (Gene ge : nr.getGenome()) {

        boolean neuron = true;
        ArrayList<String> text = new ArrayList<String>();
        int yAddon = 15;
        int yStart = 15;

        text.add("ID: " + ge.getId());
        text.add("Enabled: " + ge.isEnabled());

        if (ge instanceof Neuron) {

            g.drawRect(nx, y, width, height);
            Neuron ne = (Neuron) ge;
            String nType = "Node: ";

            if (ne instanceof InputNeuron) {

                nType += "input";

            } else if (ne instanceof OutputNeuron) {

                nType += "output";

            } else {

                nType += "hidden";

            }

            text.add(nType);
            nx += width;

        } else if (ge instanceof Connection) {

            g.drawRect(cx, y + 150, width, height);
            yStart += 150;
            Connection ce = (Connection) ge;
            text.add("Neurons: " + ce.getA().getId() + " -> " + ce.getB().getId());
            text.add("Weight: " + ce.getWeight());
            cx += width;
            neuron = false;

        }

        for (String s : text) {

            int x = (neuron) ? nx : cx;
            g.drawString(s, x + 5 - 100, y + yStart + yAddon);
            yStart += yAddon;

        }

    }

    if (drag) {

        g.setColor(new Color(0,150,0));;
        if (startConnection != null) g.drawLine((int)startConnection.getLocation().getX(),
                (int)startConnection.getLocation().getY(),
                (int)getMousePosition().getX(),
                (int)getMousePosition().getY());

    }

    Set<String> keys = species.keySet();
    int x = j.getWidth() - 100;
    int yT = 30;
    int yGap = 15;

    for (String key : keys) {

        g.drawString(key, x, yT);
        yT += yGap;

    }

}

private class Handler implements ActionListener, MouseListener, MouseMotionListener {

    @Override
    public void mouseClicked(MouseEvent arg0) {

        Neuron toAdd = new Neuron(nr);
        toAdd.setLocation(getMousePosition());
        //hidden.add(toAdd);
        repaint();
        j.repaint();

    }

    @Override
    public void mouseEntered(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseExited(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mousePressed(MouseEvent arg0) {

        for (Neuron n : nr.getNeurons()) {

            Rectangle m = new Rectangle((int)getMousePosition().getX(),
                    (int)getMousePosition().getY(),
                    1,1);
            Rectangle r = new Rectangle();
            r.setLocation((int)n.getLocation().getX() - 5, (int)n.getLocation().getY() - 5);
            r.setSize(10, 10);
            if (m.intersects(r)) {

                startConnection = n;

            }

        }

    }

    @Override
    public void mouseReleased(MouseEvent arg0) {

        if (startConnection != null) {

        if (drag) {

            boolean found = false;

            for (Neuron n : nr.getNeurons()) {

                if (n != startConnection) {

                Rectangle m = new Rectangle((int)getMousePosition().getX(),
                        (int)getMousePosition().getY(),
                        1,1);
                Rectangle r = new Rectangle();
                r.setLocation((int)n.getLocation().getX() - 5, (int)n.getLocation().getY() - 5);
                r.setSize(10, 10);
                if (m.intersects(r)) {

                    endConnection = n;
                    found = true;

                }

                }

            }

            if (found) {

                Connection c = new Connection(startConnection,endConnection,nr);
                c.transferOutput(c.getA().getOutput());

            }

            repaint();
            j.repaint();

        }

        }

        drag = false;
        startConnection = null;

    }

    @Override
    public void actionPerformed(ActionEvent e) {

        if (e.getSource() == save) {

            String name = JOptionPane.showInputDialog(null, "Enter desired name for network", "Save", JOptionPane.INFORMATION_MESSAGE);
            species.put(name, nr);
            repaint();

        } else if (e.getSource() == newNN) {

            nr = new NeuralRegister();
            InputNeuron inputA = new InputNeuron(nr),
                    inputB = new InputNeuron(nr);

            OutputNeuron outputA = new OutputNeuron(nr),
                    outputB = new OutputNeuron(nr);

            inputA.setOutput(20);
            inputB.setOutput(-20);

            calculateNeuronLocations();
            repaint();

        } else if (e.getSource() == load) {

            load();

        } else if (e.getSource() == cross) {

            String nr1Str = JOptionPane.showInputDialog(null,"Enter name of first neural network", "Cross", JOptionPane.INFORMATION_MESSAGE);
            String nr2Str = JOptionPane.showInputDialog(null,"Enter name of second neural network", "Cross", JOptionPane.INFORMATION_MESSAGE);
            NeuralRegister nr1 = species.get(nr1Str);
            NeuralRegister nr2 = species.get(nr2Str);
            NeuralRegister nrNew = new NeuralRegister();
            NeuralRegister nrAdded = new NeuralRegister();
            ArrayList<Neuron> nDone = new ArrayList<Neuron>();
            ArrayList<Connection> cDone = new ArrayList<Connection>();

            /*NeuralRegister biggestNeurons = (nr1.getNeurons().size() > nr2.getNeurons().size()) ? nr1 : nr2;
            NeuralRegister smallestNeurons = (biggestNeurons == nr1) ? nr2 : nr1;

            NeuralRegister biggestConnections = (nr1.getConnections().size() > nr2.getConnections().size()) ? nr1 : nr2;
            NeuralRegister smallestConnections = (biggestConnections == nr1) ? nr2 : nr1;
            */

            for (Neuron n : nr1.getNeurons()) {

                nrAdded.registerNeuron(n);

            }

            for (Neuron n : nr2.getNeurons()) {

                nrAdded.registerNeuron(n);

            }

            for (Connection c : nr1.getConnections()) {

                nrAdded.registerConnection(c);

            }

            for (Connection c : nr2.getConnections()) {

                nrAdded.registerConnection(c);

            }

            for (Neuron g1 : nrAdded.getNeurons()) {

                if (!nDone.contains(g1)) {

                Neuron match = null;
                ArrayList<Neuron> search = (nr1.getNeurons().contains(g1)) ? nr2.getNeurons() :  nr1.getNeurons();

                for (Neuron g2 : search) {

                    if (g1.getId() == g2.getId()) {

                        match = g2;
                        //System.out.println("Match Found " + g1.getId());
                        nDone.add(g2);

                    }

                }

                Random r = new Random();
                Neuron select = null;
                if (match != null) {

                    select = (r.nextInt(2) == 1) ? g1 : match;
                    //System.out.println("Selected " + ((select == g1) ? "NR1" : "NR2"));

                } else {

                    select = (r.nextInt(2) == 1) ? g1 : null;

                }

                nDone.add(g1);

                if (select != null) nrNew.registerNeuron(select);

                }

            }

            for (Connection c : nrAdded.getConnections()) {

                if (!cDone.contains(c)) {

                    Connection match = null;
                    ArrayList<Connection> search = (nr1.getConnections().contains(c)) ? nr2.getConnections() :  nr1.getConnections();

                    for (Connection g2 : search) {

                        if (c.getId() == g2.getId()) {

                            match = g2;
                            //System.out.println("Match Found " + g1.getId());
                            cDone.add(g2);

                        }

                    }

                    Random r = new Random();
                    Connection select = null;
                    if (match != null) {

                        select = (r.nextInt(2) == 1) ? c : match;
                        //System.out.println("Selected " + ((select == g1) ? "NR1" : "NR2"));

                    } else {

                        select = (r.nextInt(2) == 1) ? c : null;

                    }

                    cDone.add(c);

                    if (select != null) {

                    int complete = 0;

                    for (Neuron n : nrNew.getNeurons()) {

                        if (select.getA().getId() == n.getId() && n.isEnabled()) {

                            complete ++;
                            select.setA(n);

                        } else if (select.getB().getId() == n.getId() && n.isEnabled()) {

                            complete ++;
                            select.setB(n);

                        }

                    }

                    if (complete == 2) {

                        //select.setEnabled(true);
                        nrNew.registerConnection(select);

                    } else {

                        select.setEnabled(false);
                        nrNew.registerConnection(select);

                    }

                    }

                }

            }

            for (Neuron n : nrNew.getNeurons()) {

                for (Connection c : n.getInputs()) {

                    for (Connection c2 : nrNew.getConnections()) {

                        if (c.getId() != c2.getId() || !c2.isEnabled()) {

                            n.getInputs().remove(c);

                        }

                    }

                }

                //CONCURRENT MODIFICATION EXCEPTION
                for (Connection c : n.getOutputs()) {

                    for (Connection c2 : nrNew.getConnections()) {

                        if (c.getId() != c2.getId() || !c2.isEnabled()) {

                            n.getOutputs().remove(c);

                        }

                    }

                }

                if (n.getInputs().size() < 1 || n.getOutputs().size() < 1) {

                    if (!(n instanceof InputNeuron) &&
                            !(n instanceof OutputNeuron))
                        n.setEnabled(false);

                }

            }

            load(nrNew);

        }

    }

    @Override
    public void mouseDragged(MouseEvent arg0) {

        drag = true;
        repaint();

    }

    @Override
    public void mouseMoved(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

}

public void load () {

    String search = JOptionPane.showInputDialog(null,"Enter name of neural network to load", "Load", JOptionPane.INFORMATION_MESSAGE);
    load(species.get(search));
    repaint();

}

public void load (NeuralRegister nrLoad) {

    //String search = JOptionPane.showInputDialog(null,"Enter name of neural network to load", "Load", JOptionPane.INFORMATION_MESSAGE);
    nr = nrLoad;
    nr.refresh();
    repaint();

}

}

Neuron class:

import java.awt.Point;
import java.util.ArrayList;


public class Neuron extends Gene {

double threshold = 1;
ArrayList<Connection> inputs = new ArrayList<Connection>();
ArrayList<Connection> outputs = new ArrayList<Connection>();
double output;
Point location = new Point(0,0);

public Neuron (NeuralRegister nr) {

    id = nr.registerNeuron(this);

}

public void calculateOutput () {

    double sumOfInputs = 0;
    //System.out.println("Neuron " + id + " evaluation\n----------");

    for (Connection c : inputs) {

        sumOfInputs += c.getWeight() * c.getValue();

    }

    //System.out.println("Sum of inputs = " + sumOfInputs);
    sumOfInputs += -1 * threshold;
    //System.out.println("Sum of inputs - threshold = " + sumOfInputs);
    output = 1/(1 + Math.pow(Math.E, -sumOfInputs));
    //System.out.println("Raw output = " + output);
    output = (output == 0.5) ? 0.5 : Math.round(output);
    //System.out.println("Output = " + output + "\n----------------");

    for (Connection c : outputs) {

        if (c.isEnabled()) c.transferOutput(output);

    }

}

public void addInput (Connection c) {

    inputs.add(c);

}

public void addOutput (Connection c) {

    outputs.add(c);

}

public double getOutput() {
    return output;
}

public void setOutput(double output) {
    this.output = output;
    for (Connection c : outputs) {

        c.transferOutput(output);

    }
}

public double getThreshold() {
    return threshold;
}

public void setThreshold(double threshold) {
    this.threshold = threshold;
}

public Point getLocation() {
    return location;
}

public void setLocation(Point location) {
    this.location = location;
}

public ArrayList<Connection> getInputs() {
    return inputs;
}

public void setInputs(ArrayList<Connection> inputs) {
    this.inputs = inputs;
}

public ArrayList<Connection> getOutputs() {
    return outputs;
}

public void setOutputs(ArrayList<Connection> outputs) {
    this.outputs = outputs;
}

}

Connection class:

public class Connection extends Gene {

Neuron a;
Neuron b;
double weight = 1;
double value = 0;

public Connection (Neuron start, Neuron end, NeuralRegister nr) {

    id = nr.registerConnection(this);
    a = start;
    b = end;
    a.addOutput(this);
    b.addInput(this);

    System.out.println("New connection made between " + a.getId() + " and " + b.getId());

}

public void transferOutput (double output) {

    value = output;
    b.calculateOutput();

}

public void setWeight (double w) {

    weight = w;

}

public double getWeight () {

    return weight;

}

public double getValue() {
    return value;
}

public void setValue(double value) {
    this.value = value;
}

public Neuron getA() {
    return a;
}

public void setA(Neuron a) {
    this.a = a;
}

public Neuron getB() {
    return b;
}

public void setB(Neuron b) {
    this.b = b;
}

}

Solution

  • for (Connection c : n.getInputs()) {
    
        for (Connection c2 : nrNew.getConnections()) {
    
            if (c.getId() != c2.getId() || !c2.isEnabled()) {
    
                n.getInputs().remove(c);
    
            }
    
        }
    
    }
    
    //CONCURRENT MODIFICATION EXCEPTION
    for (Connection c : n.getOutputs()) {
    
        for (Connection c2 : nrNew.getConnections()) {
    
            if (c.getId() != c2.getId() || !c2.isEnabled()) {
    
                n.getOutputs().remove(c);
    
            }
    
        }
    
    }
    

    Your removing from the n.getInputs() and n.getOutputs()Connection collection while iterating through the collection and while not using an iterator. The solution is the same for all similar questions: get the iterator, loop through the collection with the iterator, and only remove with the iterator.