import java.util.*; import java.io.*; import java.net.*; public class LagrangeAgent { public static final String IDENT = "anti60"; public static final int RUNS = 100; public static final int TIME_LIMIT = 1000; public static final int TIME_TICK = 100; public static final int SET_SIZE = 60; public BrainStore brain; public int degree = 1; public final int numberOfAgents; public final int sensor; public final int desired; public LagrangeAgent [] world; public int [] messageQueue; public boolean [] neighbour; public int lastUseful = -1; public boolean [] allUseful = new boolean [SET_SIZE]; public int numUseful = 0; private int index; private boolean done = false; private Random random; /** Constructor -- creates LagrangeAgent. */ public LagrangeAgent (LagrangeAgent [] w, int i, int n, int limit, double irrat, Random r) throws GraphException { index = i; world = w; random = r; int inx = (23 * i) % n; if (n == 60) { sensor = inx; // 0..59, if n==60 desired = (inx / 2) + 33; // 33..62, if n==60 brain = new LagrangeStore (desired, limit, r, irrat); } else throw new GraphException ("LagrangeAgent not set up for " + n); numberOfAgents = n; messageQueue = new int [n]; for (int j = 0; j < numberOfAgents; j++) { messageQueue [j] = -1; } neighbour = new boolean [n]; tryANumber (sensor); } /** Send a message */ public boolean send (int num, int to) throws GraphException { world [to].messageQueue [index] = num; return true; } /** Try a number, return whether it was useful. */ public boolean tryANumber (int num) throws GraphException { boolean useful = brain.tryNumber (num); if (useful) { addANumber (num); } if (brain.getSuccess ()) done = true; return useful; } /** Add a useful number. */ public void addANumber (int num) throws GraphException { lastUseful = num; if (! allUseful [num]) { allUseful [num] = true; numUseful++; } } /** Get a useful number. */ public int getANumber () throws GraphException { if (numUseful == 0) return sensor; int j = (int) (numUseful * random.nextDouble ()); int k = -1; int inx = -1; while (k < j) { inx++; if (allUseful [inx]) k++; } return inx; } /** Do tick -- send */ public void sendPhase () throws GraphException { int num = getANumber (); for (int i = 0; i < numberOfAgents; i++) { if (neighbour [i]) send (num, i); } } /** Do tick -- receive */ public void receivePhase () throws GraphException { for (int i = 0; i < numberOfAgents; i++) { int j = i; int num = messageQueue [j]; messageQueue [j] = -1; if (num >= 0) { boolean suc = tryANumber (num); } } } /** Do tick */ public static boolean tick (int start, LagrangeAgent [] w, int n) throws GraphException { for (int i = 0; i < n; i++) { w [(start + i) % n].sendPhase (); } for (int i = 0; i < n; i++) { w [(start + i) % n].receivePhase (); } boolean alldone = true; for (int i = 0; i < n; i++) { if (! w [i].done) alldone = false; } return alldone; } /** Run: return time taken for success, or number of failures (coded negative) */ public static long run (LagrangeAgent [] w, int n, Random r) throws GraphException { long t = 0; boolean finished = false; while (t < TIME_LIMIT && ! finished) { int start = (int) (n * r.nextDouble ()); tick (start, w, n); finished = true; for (int i = 0; i < n; i++) { if (! w [i].done) finished = false; } t++; if (t % TIME_TICK == 0) { int tot = 0; for (int i = 0; i < n; i++) { if (w [i].done) tot++; } System.err.println ("Tick " + t + " " + (tot * 100.0 / n) + "%"); } } int fails = 0; for (int i = 0; i < n; i++) { if (! w [i].done) fails++; } if (fails > 0 && t < TIME_LIMIT) throw new GraphException ("Failure test bug"); return t < TIME_LIMIT ? t : -fails; } /** Setup */ public static LagrangeAgent [] setup (Graph g, int n, int limit, double irrat, Random r) throws GraphException { LagrangeAgent [] w = new LagrangeAgent [n]; for (int i = 0; i < n; i++) { w [i] = new LagrangeAgent (w, i, n, limit, irrat, r); } for (int i = 0; i < n; i++) { Node node = g.getNode (i); Vector edges = g.edgesFrom (node); w[i].degree = edges.size (); Enumeration en = edges.elements (); while (en.hasMoreElements ()) { Edge edge = (Edge) en.nextElement (); int j = g.getIndex (edge.getTo ()); w[i].neighbour [j] = true; w[j].neighbour [i] = true; } } return w; } /** Go */ public static long runOne (Graph g, int n, int limit, double irrat, Random r) throws GraphException { LagrangeAgent [] w = setup (g, n, limit, irrat, r); return run (w, n, r); } /** Go */ public static long runOne (Graph g, int limit, double irrat, Random r) throws GraphException { int n = g.getNodeCount(); return runOne (g, n, limit, irrat, r); } /** Go */ public static long runOne (Graph g, int limit, double irrat) throws GraphException { return runOne (g, limit, irrat, new Random (System.currentTimeMillis ())); } public static void runOne (String id, double prob, double aved, Graph g, int limit, double irrat, double clus, double avenc, int run, PrintStream out) throws IOException, GraphException { long t = runOne (g, limit, irrat); out.println (prob + "\t" + aved + "\t" + clus + "\t" + avenc + "\t" + irrat + "\t" + t); out.flush (); } public static void runLoop (String id, double prob, Graph g, int limit, int run, PrintStream out) throws GraphException, IOException, GraphException { // // Network attribute calculations // double aved = g.aveDistance (); double clus = g.clusterCoeff (); double avenc = g.averageNodeConnectivity (); double [] irrats = new double [] {0, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3}; for (int i = 0; i < irrats.length; i++) { double irrat = irrats [i]; runOne (id, prob, aved, g, limit, irrat, clus, avenc, run, out); } } public static void runLoop (String id, Graph g, int run, PrintStream out) throws GraphException, IOException, GraphException { int limit = 1000; double [] probs = new double [] {0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5}; for (int i = 0; i < probs.length; i++) { double prob = probs [i]; Random random = new Random (System.currentTimeMillis ()); // // Network rewiring calculations // Graph h = (Graph) g.clone (); boolean problemFound = h.rewire (prob, random); while (problemFound || ! h.isConnected ()) { System.err.println ("Retrying..."); h = (Graph) g.clone (); problemFound = h.rewire (prob, random); } runLoop (id, prob, h, limit, run, out); } } public static void main (String[] args) throws IOException, GraphException { BufferedReader in = new BufferedReader (new FileReader (IDENT + ".txt")); System.err.println ("Reading " + IDENT + ".txt"); Graph g = Graph.read (in); for (int i = 0; i < RUNS; i++) { runLoop (IDENT, g, i, System.out); } } } abstract class BrainStore { public final int desired; public final Random random; public final double irrationality; public boolean success = false; public String successString = null; public BrainStore (int des, Random r, double irr) { random = r; desired = des; irrationality = irr; } public abstract boolean tryNumber (int sq) throws GraphException; // return useful public boolean getSuccess () { return success; } public String getSuccessString () { return successString; } } class LagrangeStore extends BrainStore { private final int limit; private int [] sums; private int [] nums; private String [] data; private int size = 0; private Hashtable index = new Hashtable (); public int getSize () { return size; } public LagrangeStore (int des, int lim) { this (des, lim, null, 0); } public LagrangeStore (int des, int lim, Random r, double irr) { super (des, r, irr); limit = lim; sums = new int [limit]; nums = new int [limit]; data = new String [limit]; put (0, 0, ""); } private boolean put (int sum, int num, String s) { // returns OK if (success) return true; if (size == limit) return false; String label = sum + ":" + num; if (index.get (label) != null) return true; int i = size; size ++; sums [i] = sum; nums [i] = num; data [i] = s; index.put (label, new Integer (i)); return true; } private boolean tryAux (int sq) { // return new success if (success) return false; int root = (int) Math.round (Math.sqrt (sq)); if (sq != root * root) return false; int m = size; String extra = "" + sq; for (int j = 1; j <= 4; j++) { for (int i = 0; i < m; i++) { int sum = sums [i] + j * sq; int num = nums [i] + j; String s = (nums [i] == 0 ? "" : data [i] + "+") + extra; if (num == 4 && sum == desired) { success = true; successString = s + " = " + desired; //System.err.println ("... success: " + s); return true; } else if (num >= 4 || sum > desired) { // ignore } else { put (sum, num, s); } } extra = extra + "+" + sq; } return false; } public boolean tryNumber (int sq) { // return useful if (success) return false; int m = size; tryAux (sq); boolean useful = (success || size > m); //if (useful) System.err.println ("... really useful: " + sq); if (irrationality == 0) { return useful; } else { boolean wrong = random.nextDouble () < irrationality; return wrong ? (! useful) : useful; } } } ////////////////////////////////////////// // // // Cut-down version of Network package // // // ////////////////////////////////////////// class GraphException extends Exception { public GraphException() { super(); } public GraphException(String s) { super(s); } public GraphException(Exception ex) { super(ex instanceof GraphException ? ex.getMessage () : ex.toString ()); ex.printStackTrace (System.err); } } class Node { private final String name; public Node (String n) { name = n; } /** Return name of a graph element. */ public String getName () { return name; } public String toString () { return name; } } class Edge { private final Node from; private final Node to; private Edge inv = null; public String toString () { return from + " --- " + to; } private Edge (Node f, Node t) { from = f; to = t; } public void setInv () { if (inv == null) { Edge e = new Edge (to, from); inv = e; e.setInv (this); } } private void setInv (Edge e) { inv = e; } public Edge getInv () { return inv; } public static Edge create (Node f, Node t, Graph g) throws GraphException { Edge e = new Edge (f, t); e.setInv (); g.addEdge (e); return e; } public static Edge create (String s, Graph g) throws GraphException { int i = s.indexOf ("\t"); Node from = g.getNode (s.substring (0, i)); Node to = g.getNode (s.substring (i+1)); return create (from, to, g); } /** Where does this edge come from? */ public Node getFrom () { return from; } /** Where does this edge go to? */ public Node getTo () { return to; } } class Graph implements Cloneable { public static final int KAWACHI_CONSTANT = 10; private int nodecount = 0; private Vector nodes = new Vector (); private Vector edges = new Vector (); private Hashtable nodelookup = new Hashtable (); private Hashtable nodeindex = new Hashtable (); private Hashtable adj = new Hashtable (); private Hashtable revadj = new Hashtable (); /** Create a graph. */ public Graph () { } /** Create a graph. */ public Graph (Vector n, Vector e) { nodecount = n.size (); nodes = (Vector) n.clone (); edges = (Vector) e.clone (); rebuildAdj (); } public Object clone () { return new Graph (nodes, edges); } /** Return a vector of all the nodes in the graph. */ public Vector getAllNodes () { return nodes; } /** Return a vector of all the edges in the graph. */ public Vector getAllEdges () { return edges; } /** Return the Node object with a specific name. */ public Node getNode (String name) { Node node = (Node) nodelookup.get (name); if (node != null) { return node; } else { Node n = new Node (name); nodes.addElement (n); nodeindex.put (n, new Integer (nodecount)); nodecount++; nodelookup.put (name, n); return n; } } /** Return the Node object with a specific index. */ public Node getNode (int i) { return (Node) nodes.elementAt (i); } /** Return the index of a Node object. */ public int getIndex (Node n) { return ((Integer) nodeindex.get (n)).intValue (); } /** How many nodes does this graph contain? */ public int getNodeCount () { return nodecount; } public double clusterCoeff () { int n = getNodeCount(); double total = 0; for (int i = 0; i < n; i++) { boolean [] neigh = neighbourhood (this, i); total += clusterCount (this, neigh); } return total/n; } public double averageNodeConnectivity () { // not ported to this version return 0; } public int diameter () { double [] [] distances = shortestPathDistance (); double diam = 0; for (int i = 0; i < nodecount; i ++) { for (int j = i+1; j < nodecount; j ++) { if (distances [i] [j] > diam) diam = distances [i] [j]; } } return (int) Math.round (diam); } public double aveDistance () { double [] [] distances = shortestPathDistance (); double total = 0; int count = 0; for (int i = 0; i < nodecount; i ++) { for (int j = i+1; j < nodecount; j ++) { double d = distances [i] [j]; total += d; count++; } } return total/count; } private void rebuildAdj () { Enumeration enumeration = edges.elements (); while (enumeration.hasMoreElements ()) { Edge edge = (Edge) enumeration.nextElement (); addAdjacency (edge.getFrom (), edge, false); addAdjacency (edge.getTo (), edge.getInv (), true); } enumeration = nodes.elements (); int i = 0; while (enumeration.hasMoreElements ()) { Node node = (Node) enumeration.nextElement (); nodeindex.put (node, new Integer (i)); i++; nodelookup.put (node.getName (), node); } } private void addAdjacency (Node n, Edge edge, boolean rev) { Hashtable h = rev ? revadj : adj; Vector v = (Vector) adj.get (n); if (v == null) { v = new Vector (); h.put (n, v); } v.addElement (edge); } public void addEdge (Edge edge) throws GraphException { if (edge.getFrom() == edge.getTo()) { System.err.println ("Ignoring circular edge " + edge); return; } Edge x = edgeBetween (edge.getFrom(), edge.getTo()); if (x != null) { System.err.println ("Ignoring duplicate edge " + edge); return; } x = edgeBetween (edge.getTo(), edge.getFrom()); if (x != null) { System.err.println ("Ignoring duplicate (inv) edge " + edge); return; } addAdjacency (edge.getFrom (), edge, false); addAdjacency (edge.getTo (), edge.getInv (), true); edges.addElement (edge); } private void removeAdjacency (Node n, Edge edge, boolean rev) { Hashtable h = rev ? revadj : adj; Vector v = (Vector) adj.get (n); if (v == null) { v = new Vector (); h.put (n, v); } v.removeElement (edge); } public void deleteEdge (Edge edge) { removeAdjacency (edge.getFrom (), edge, false); removeAdjacency (edge.getTo (), edge.getInv (), true); edges.removeElement (edge); } /** Return the vector of edges from (or if flipped, to) this node. */ public Vector edgesFrom (Node node, boolean flip) { if (flip) return edgesTo (node); else return edgesFrom (node); } /** Return the vector of edges from this node. */ public Vector edgesFrom (Node node) { return (Vector) adj.get (node); } /** Return the vector of edges to this node. */ public Vector edgesTo (Node node) { return (Vector) revadj.get (node); } /** Return the degree of this node. */ public int degree (Node node) { return edgesFrom (node).size (); } /** Return the edge between two nodes, or null if there is no such edge. For the purposes of this method, it is assumed that there is only one such edge. */ public Edge edgeBetween (Node from, Node to) { Vector edges = edgesFrom (from); if (edges == null) return null; Enumeration enumeration = edges.elements (); while (enumeration.hasMoreElements ()) { Edge edge = (Edge) enumeration.nextElement (); if (edge.getTo() == to) return edge; } return null; } /** Return a matrix of edges between nodes (matrix [i] [j] is from node i --> node j). For the purposes of this method, it is assumed that there is only one such edge. */ public Edge [] [] matrix () { Edge [] [] m = new Edge [nodecount] [nodecount]; for (int i = 0; i < nodecount; i ++) { for (int j = 0; j < nodecount; j ++) { m [i] [j] = null; } } Enumeration enumeration = edges.elements (); while (enumeration.hasMoreElements ()) { Edge edge = (Edge) enumeration.nextElement (); Node from = edge.getFrom (); Node to = edge.getTo (); m [getIndex (from)] [getIndex (to)] = edge; } return m; } public boolean isConnected () { double [] [] distances = shortestPathDistance (); for (int i = 0; i < nodecount; i ++) { for (int j = i+1; j < nodecount; j ++) { if (distances [i] [j] >= Float.MAX_VALUE) return false; } } return true; } public double [] [] shortestPathDistance () { double [] [] distances = new double [nodecount] [nodecount]; for (int i = 0; i < nodecount; i ++) { for (int j = 0; j < nodecount; j ++) { distances [i] [j] = (i == j) ? 0.0 : Float.MAX_VALUE; } } Enumeration enumeration = getAllEdges ().elements (); while (enumeration.hasMoreElements ()) { Edge edge = (Edge) enumeration.nextElement (); int i = getIndex (edge.getFrom ()); int j = getIndex (edge.getTo ()); if (i != j) { if (distances [i] [j] > 1) { distances [i] [j] = 1; distances [j] [i] = 1; } } } for (int k = 0; k < nodecount; k ++) { for (int i = 0; i < nodecount; i ++) { for (int j = 0; j < nodecount; j ++) { double d = distances [i] [k] + distances [k] [j]; if (d < distances [i] [j]) { distances [i] [j] = d; } } } } return distances; } public boolean rewire (double p, Random r) throws GraphException { return rewireKawachi (p, this, r) <= 0; } public static int rewire (boolean watts, double prob, Graph g, Random random) throws GraphException { if (watts) return rewireWatts (prob, g, random); else return rewireKawachi (prob, g, random); } public static int rewireKawachi (double prob, Graph g, Random random) throws GraphException { int res = 0; for (int i = 0; i < KAWACHI_CONSTANT; i++) { res += rewireKawachiAux (prob/KAWACHI_CONSTANT, g, random); } return res; } public static int rewireKawachiAux (double prob, Graph g, Random random) throws GraphException { int n = g.getNodeCount(); if (n < 3) throw new GraphException ("Too few nodes for rewiring"); Vector edges = g.getAllEdges(); int sz = edges.size(); if (sz < 1) throw new GraphException ("Too few edges for rewiring"); Vector toDelete = new Vector(); int deleteCount = 0; Enumeration enumeration = edges.elements (); while (enumeration.hasMoreElements ()) { Edge edge = (Edge) enumeration.nextElement (); if (random.nextDouble () < prob) { toDelete.addElement (edge); deleteCount++; } } Vector problemNodes = new Vector(); System.err.println ("Deleting a batch of " + (toDelete.size ()) + " edges"); enumeration = toDelete.elements (); while (enumeration.hasMoreElements ()) { Edge edge = (Edge) enumeration.nextElement (); // delete one-by-one g.deleteEdge (edge); if (g.getAllEdges().size() != sz-1) throw new GraphException ("Oops! Rewiring delete failed."); int deg1 = g.degree (edge.getFrom()); int deg2 = g.degree (edge.getTo()); if (deg1 == 1) problemNodes.addElement (edge.getFrom()); if (deg2 == 1) problemNodes.addElement (edge.getTo()); Node z = null; if (problemNodes.size () > 0) { z = (Node) problemNodes.elementAt (0); //System.err.println ("Problem node " + z); problemNodes.removeElementAt (0); } else { z = deg1 > deg2 ? edge.getFrom() : edge.getTo(); } double [] pi = new double [n]; double tot = 0; for (int k = 0; k < n; k++) { pi [k] = 0; Node y = g.getNode (k); if (k != g.getIndex(z) && g.edgeBetween(z, y) == null) { int dg = 1 + g.degree (y); pi [k] = dg; tot += dg; } } for (int k = 0; k < n; k++) { pi [k] = pi [k] / tot; } tot = 0; for (int k = 0; k < n; k++) { double d = pi [k]; pi [k] += tot; tot += d; } double r = random.nextDouble (); Node x = null; for (int k = 0; k < n; k++) { if (r < pi [k]) { x = g.getNode (k); break; } } //System.err.println ("Connecting " + x + " & " + z); Edge.create (x, z, g); } if (g.getAllEdges().size() != sz) throw new GraphException ("Oops! Rewiring delete miscalculated."); return deleteCount; } public static int rewireWatts (double prob, Graph g, Random random) throws GraphException { int n = g.getNodeCount(); if (n < 3) throw new GraphException ("Too few nodes for rewiring"); Vector edges = g.getAllEdges(); int sz = edges.size(); if (sz < 1) throw new GraphException ("Too few edges for rewiring"); Vector toDelete = new Vector(); int deleteCount = 0; Enumeration enumeration = edges.elements (); while (enumeration.hasMoreElements ()) { Edge edge = (Edge) enumeration.nextElement (); if (random.nextDouble () < prob) { toDelete.addElement (edge); deleteCount++; } } System.err.println ("Deleting a batch of " + (toDelete.size ()) + " edges"); enumeration = toDelete.elements (); while (enumeration.hasMoreElements ()) { Edge edge = (Edge) enumeration.nextElement (); g.deleteEdge (edge); } if (g.getAllEdges().size() != sz-deleteCount) throw new GraphException ("Oops! Rewiring delete failed."); for (int k = 0; k < deleteCount; k++) { int i = 0; int j = 0; Node x = null; Node y = null; boolean connected = true; while (connected) { //System.err.print ("."); i = (int) (n * random.nextDouble ()); x = g.getNode (i); j = (int) (n * random.nextDouble ()); y = g.getNode (j); connected = (i == j) || g.edgeBetween(x, y) != null; } //System.err.println ("Connecting " + x + " & " + y); Edge.create (x, y, g); } return deleteCount; } private static boolean [] neighbourhood (Graph g, int k) { int n = g.getNodeCount(); boolean [] res = new boolean [n]; for (int i = 0; i < n; i++) { res [i] = false; } Node x = g.getNode (k); Enumeration enumeration = g.edgesFrom (x).elements (); while (enumeration.hasMoreElements ()) { Edge edge = (Edge) enumeration.nextElement (); Node y = edge.getTo (); res [g.getIndex (y)] = true; } return res; } private static double clusterCount (Graph g, boolean [] neigh) { int size = 0; int count = 0; for (int i = 0; i < neigh.length; i++) { if (neigh [i]) size++; } if (size <= 1) return 0; Enumeration enumeration = g.getAllEdges ().elements (); while (enumeration.hasMoreElements ()) { Edge edge = (Edge) enumeration.nextElement (); Node x = edge.getFrom (); Node y = edge.getTo (); if (neigh [g.getIndex (x)] && neigh[g.getIndex (y)]) count++; } return 2.0 * count / (size * (size - 1.0)); } /** Read a graph from a URL or file. @exception java.io.IOException (for I/O or parsing errors) */ public static Graph read (String s) throws GraphException, IOException { try { URL url = new URL (s); return read (url); } catch (MalformedURLException ex) { File f = new File (s); return read (f); } } /** Read a graph from a URL. @exception java.io.IOException (for I/O or parsing errors) */ public static Graph read (URL url) throws GraphException, IOException { BufferedReader b = new BufferedReader (new InputStreamReader (url.openStream ())); return read (b); } /** Read a graph from a file. @exception java.io.IOException (for I/O or parsing errors) */ public static Graph read (File in) throws GraphException, IOException { BufferedReader b = new BufferedReader (new FileReader (in)); return read (b); } /** Read a graph. @exception java.io.IOException (for I/O or parsing errors) */ public static Graph read (BufferedReader in) throws GraphException, IOException { Graph g = new Graph (); String line = in.readLine (); while (line != null) { Edge.create (line, g); line = in.readLine (); } in.close (); return g; } }