Author: vive
Date: 2007-08-20 17:58:17 +0000 (Mon, 20 Aug 2007)
New Revision: 14808

Added:
   trunk/apps/load-balancing-sims/phase7/sim/SimpleEstimator.java
   trunk/apps/load-balancing-sims/phase7/sim/SimpleLink.java
   trunk/apps/load-balancing-sims/phase7/sim/SimplePeer.java
Modified:
   trunk/apps/load-balancing-sims/phase7/sim/SimpleGraph.java
   trunk/apps/load-balancing-sims/phase7/sim/SimpleNode.java
   trunk/apps/load-balancing-sims/phase7/sim/SimpleQuery.java
Log:
Updates on the simple model...


Added: trunk/apps/load-balancing-sims/phase7/sim/SimpleEstimator.java
===================================================================
--- trunk/apps/load-balancing-sims/phase7/sim/SimpleEstimator.java              
                (rev 0)
+++ trunk/apps/load-balancing-sims/phase7/sim/SimpleEstimator.java      
2007-08-20 17:58:17 UTC (rev 14808)
@@ -0,0 +1,81 @@
+package sim;
+
+class SimpleEstimator {
+
+    double[] length_times;         // Map from [0,1]->[0,maxt]
+    static double alpha = 0.1;     // Parameter for exponential moving average
+
+    /*
+     * @resolution: resolution on estimation in keyspace
+     */
+    public SimpleEstimator(int resolution) {
+       length_times = new double[resolution];
+    }
+
+    /*
+     * update estimator with an observed roundtrip time for a distance
+     *
+     * @dist: distance for service
+     * @rtt: observed roundtrip time when using this estimator
+     */
+    public void update(double dist,double rtt) {
+       // int i = (int) Math.floor(length_times.length * 2*dist);
+       int i = (int) Math.floor(length_times.length*dist);
+
+       if(length_times[i] == 0) {
+           length_times[i] = rtt;
+       } else {
+           length_times[i] = rtt*alpha + length_times[i]*(1-alpha);
+       }
+    }
+
+    /*
+     * estimate required time based on previously seen performance,
+     * simple linear average when no performance seen
+     *
+     * @dist: distance we wonder about
+     */
+    public double estimate(double dist) {
+       // int i = (int) Math.floor(length_times.length * 2*dist);
+       int i = (int) Math.floor(length_times.length*dist);
+
+       if(i==length_times.length)  // Boundary node, farthest away
+           i--;
+
+       // Direct estimation?
+       if (length_times[i] != 0) {
+           return length_times[i];
+       } else {
+
+           if((i==0) || (i==length_times.length-1)) {
+               return 0;
+           } else {
+               // Try interferring estimation from farther and nearer
+               double lower=0,upper=0;         
+
+               for (int j=i-1; j>0; j--) {
+                   if (length_times[j]>0) {
+                       lower = length_times[j];
+                       break;
+                   }
+               }
+
+               for (int j=i+1; j<(length_times.length-1); j++) {
+                   if (length_times[j]>0) {
+                       upper = length_times[j];
+                       break;
+                   }
+               }
+
+               if ((lower==0)||(upper==0)) {
+                   return 0;
+               } else {
+                   return ((lower+upper)/2);
+               }
+
+           }
+       }
+       
+    }
+
+}

Modified: trunk/apps/load-balancing-sims/phase7/sim/SimpleGraph.java
===================================================================
--- trunk/apps/load-balancing-sims/phase7/sim/SimpleGraph.java  2007-08-20 
17:00:45 UTC (rev 14807)
+++ trunk/apps/load-balancing-sims/phase7/sim/SimpleGraph.java  2007-08-20 
17:58:17 UTC (rev 14808)
@@ -1,37 +1,255 @@
-/*
- * Graph of SimpleNodes 
- */
+//
+// The graph holding the simulation network topology
+// Takes care of routing, position randomization, also some node configuration
+//

 package sim;
-
 import java.util.Random;
+import java.util.Vector;
+import java.util.LinkedList;

 class SimpleGraph {

-    SimpleNode[] nodes;
-    private Random rand;
-
-    public SimpleGraph(int n) {
-       nodes = new SimpleNode[n];
+    // SimpleNode[] nodes;
+    Vector<SimpleNode> nodes;           // Growable, but array should be more 
efficient
+    private static Random rand = new Random(System.currentTimeMillis() % 
10000);
+    private boolean delays = false;     // If to vary delays by default
+    
+    /*
+     * @n: number of nodes
+     * @estsize: size of estimation table
+     * @delays: if to sample delays between nodes, or to use unity
+     */
+    public SimpleGraph(int n, int estsize, boolean delays) {
+       this.delays = delays;
+       // nodes = new SimpleNode[n];
+       nodes = new Vector<SimpleNode>();
        for (int i=0; i<n; i++) {
-           nodes[i] = new SimpleNode((double)i/n);
+           double location = (double)i/n;
+           nodes.add(new SimpleNode(location, estsize));
        }
-       rand = new Random((int) (System.currentTimeMillis() % 10000));
     }

     /*
+     * To enable opennet for all nodes.
+     * @opennet_maxpeers: max peers that a node wants to take on opennet
+     */
+    public void enableOpennet(int opennet_maxpeers) {
+       for (SimpleNode n: nodes) {
+           n.enableOpennet(opennet_maxpeers);
+       }
+    }
+
+    /*
+     * To disable opennet for all nodes.
+     */
+    public void disableOpennet() {
+       for (SimpleNode n: nodes) {
+           n.disableOpennet();
+       }
+    }
+
+    /*
+     * Make a route in the network (possibly affecting opennet if enabled)
+     * Peers chosen uniformly at random
+     *
+     * @htl: Hops to Live
+     */
+    public void randomroute(int htl) {
+       SimpleNode src = nodes.get(rand.nextInt(nodes.size()));
+       SimpleNode dst = nodes.get(rand.nextInt(nodes.size()));
+       LinkedList<SimpleQuery> route = src.route(new 
SimpleQuery(src,null,dst,null,SimpleQuery.FORWARD,htl,false));
+       unlock(route);
+    }
+
+    /*
+     * Unlock simple trackkeeping of route participation
+     *
+     * @route: The route with all parcitipants
+     */
+
+    public void unlock(LinkedList<SimpleQuery> route) {
+       for (SimpleQuery rq: route) {
+           rq.source.busy = false;
+       }
+    }
+
+    /*
+     * Randomly assign new locations in [0,1) to nodes
+     */
+    public void randLocations() {
+       for (SimpleNode n: nodes) {
+           n.setLocation(rand.nextDouble());
+       }       
+    }
+
+    /*
+     * Randomly shuffle the existing positions
+     */
+    public void shuffleLocations() {
+       for(int j=0; j<nodes.size(); j++) {
+            int nn = rand.nextInt(nodes.size());
+            SimpleNode nj = nodes.get(j);
+            SimpleNode swapto = nodes.get(nn);
+
+            double tmp = nj.getLocation();
+           nj.setLocation(swapto.getLocation());
+            swapto.setLocation(tmp);
+
+            nodes.set(j,swapto);
+            nodes.set(nn,nj);
+        }
+    }
+
+    /*
+     * Generate a delay of 0.1 + a harmonic (1/x) distribution and with mean 1.
+     *
+     * fixme: try a Gaussian with mean 1? (restricted downwards by a min value)
+     */
+    public static double sampleDelay() {
+       double delaymin = 0.1;
+       // return (delaymin + 0.9*0.1*Math.pow(37.5,rand.nextDouble()));
+       return (delaymin + 0.9*0.0001*Math.pow(119000,rand.nextDouble()));
+       //return ((rand.nextDouble()<0.5)?0.1:1.9);
+       // return ((rand.nextDouble()<0.5)?0.5:1.5);
+    }
+
+    /*
+     * Patch a node into Kleinbergs original model
+     * Warning: this does NOT give a Kleinberg model, dont use this for 
growing networks
+     * Used to evaluate quick churn into the darknet model.
+     *
+     * @avgdeg: resulting average degree of the nodes
+     * @directed: if to use directed edges
+     * @local: if to add nearest-neighbor connections
+     * @inconst: if to keep the *indegree* constant (fixme)
+     *
+     */
+    public SimpleNode insertNode(double avgdeg, boolean directed, boolean 
local, boolean inconst) {
+
+       int i = rand.nextInt(nodes.size());
+       SimpleNode newnode = new SimpleNode(rand.nextDouble(),0);
+       nodes.insertElementAt(newnode,i);
+
+        double degree,delay;
+        if(directed) {
+            degree = avgdeg;
+        } else {
+            degree = avgdeg/2;
+        }
+
+       SimpleNode src = nodes.get(i);
+       if (local) {
+           SimpleNode dst;
+           SimpleLink link;
+
+           if(delays) 
+               delay = sampleDelay();
+           else
+               delay = 1;
+           dst = nodes.get((i+1)%nodes.size());
+           link = new SimpleLink(src,dst,delay);
+           src.connect(dst,link,SimplePeer.t_DARKNET);
+           dst.connect(src,link,SimplePeer.t_DARKNET);
+
+           if(delays) 
+               delay = sampleDelay();
+           else
+               delay = 1;
+           dst = nodes.get((i-1)%nodes.size());
+           link = new SimpleLink(src,dst,delay);
+           src.connect(dst,link,SimplePeer.t_DARKNET);
+           dst.connect(src,link,SimplePeer.t_DARKNET);
+       }
+
+       for(int j=0; j<degree; j++) {
+           boolean found = false;
+           while(!found) {
+               double r = rand.nextFloat();
+               int d = (int) Math.floor (Math.exp (r*Math.log 
(nodes.size()/2.0)));
+               int destpos;
+               
+               if (rand.nextFloat()<0.5) {
+                   destpos = i - d;
+               } else {
+                   destpos = i + d;
+               }
+               
+               if (destpos>=nodes.size()) {
+                   destpos = destpos - nodes.size();
+               } else if (destpos<0) {
+                   destpos = destpos + nodes.size();
+               }
+               
+               SimpleNode dst = nodes.get(destpos);
+               
+               if(delays)
+                   delay = sampleDelay();
+               else
+                   delay = 1;
+               
+               if (directed) {
+                   if (inconst) {
+                       if (!dst.hasNeighbor(src)) {
+                           found = true;
+                           SimpleLink sl = new SimpleLink(src,dst,delay);
+                           dst.connect(src,sl,SimplePeer.t_DARKNET);
+                       }
+                   } else {
+                       if (!src.hasNeighbor(dst)) {
+                           found = true;
+                           SimpleLink sl = new SimpleLink(dst,src,delay);
+                           src.connect(dst,sl,SimplePeer.t_DARKNET);
+                       }
+                   }
+               } else {
+                   if (!src.hasNeighbor(dst) && !dst.hasNeighbor(src)) {
+                       found = true;
+                       SimpleLink sl = new SimpleLink(src,dst,delay);
+                       src.connect(dst,sl,SimplePeer.t_DARKNET);
+                       dst.connect(src,sl,SimplePeer.t_DARKNET);
+                   }
+               }                                   
+           }
+       }
+
+       return newnode;
+    }
+
+    /*
+     * Remove a node from the graph
+     *
+     * fixme: also remove load stats/counters
+     * @n: node to remove
+     */
+    public void removeNode (SimpleNode n) {
+
+       Vector<SimpleNode> toDisconnect = new Vector<SimpleNode>();
+
+       for (SimplePeer peer: n.neighbors) {
+           peer.n.disconnect(n);
+           toDisconnect.add(peer.n);
+       }
+
+       for (SimpleNode disconnecter: toDisconnect) {
+           n.disconnect(disconnecter);
+       }
+
+       nodes.removeElement(n);
+    }
+
+    /*
      * Generating a Kleinberg small-world graph with shortcuts proportional to 
1/d
      *
      * @avgdeg: average degree of nodes
      * @directed: if to have directed edges
      * @local: if to have local edges (to nearest-neighbors in the lattice)
      * @inconst: if to have a constant number of indegree edges (when using 
directed edges),
-     *           else use constant outdegree
+     *           otherwise use constant outdegree (default)
      */
-
     public void CreateKleinbergGraph(double avgdeg, boolean directed, boolean 
local, boolean inconst) {

-       double degree;
+       double degree,delay;
        if(directed) {
            degree = avgdeg;
        } else {
@@ -39,28 +257,30 @@
        }

        if(local) {
-           for(int i=0; i<nodes.length; i++) {
-               SimpleNode src = nodes[i];
-               SimpleNode dst = nodes[(i+1)%nodes.length];
-               src.connect(dst);
-               dst.connect(src);
+           for(int i=0; i<nodes.size(); i++) {
+               if(delays) 
+                   delay = sampleDelay();
+               else
+                   delay = 1;
+               
+               SimpleNode src = nodes.get(i);
+               SimpleNode dst = nodes.get((i+1)%nodes.size());
+               SimpleLink link = new SimpleLink(src,dst,delay);
+               src.connect(dst,link,SimplePeer.t_DARKNET);
+               dst.connect(src,link,SimplePeer.t_DARKNET);
            }
        }

        System.err.println("Giving each node " + degree + " shortcuts" + (local 
? " and nearest-neighbor connections " : "") + " (average degree="+ (local ? 
(avgdeg+2) : avgdeg)  +")");

-       for(int i=0; i<nodes.length; i++) {
-           SimpleNode src = nodes[i];
-
+       for(int i=0; i<nodes.size(); i++) {
+           SimpleNode src = nodes.get(i);
            for(int j=0; j<degree; j++) {

                boolean found = false;
                while(!found) {
-
-                   // assuming nodes are equally spaced out in [0,1] we can 
use integers as offsets
-                   // fixme: option for sampling directly in [0,1]
                    double r = rand.nextFloat();
-                    int d = (int) Math.floor (Math.exp (r*Math.log 
(nodes.length/2.0)));
+                    int d = (int) Math.floor (Math.exp (r*Math.log 
(nodes.size()/2.0)));
                    int destpos;

                    if (rand.nextFloat()<0.5) {
@@ -69,40 +289,174 @@
                        destpos = i + d;
                    }

-                   if (destpos>=nodes.length) {
-                       destpos = destpos - nodes.length;
+                   if (destpos>=nodes.size()) {
+                       destpos = destpos - nodes.size();
                    } else if (destpos<0) {
-                       destpos = destpos + nodes.length;
+                       destpos = destpos + nodes.size();
                    }

-                   SimpleNode dst = nodes[destpos];
+                   SimpleNode dst = nodes.get(destpos);

+                   if(delays)
+                       delay = sampleDelay();
+                   else
+                       delay = 1;
+
                    if (directed) {
                        if (inconst) {
                            if (!dst.hasNeighbor(src)) {
                                found = true;
-                               dst.connect(src);
+                               SimpleLink sl = new SimpleLink(src,dst,delay);
+                               dst.connect(src,sl,SimplePeer.t_DARKNET);
                            }
                        } else {
                            if (!src.hasNeighbor(dst)) {
                                found = true;
-                               src.connect(dst);
+                               SimpleLink sl = new SimpleLink(dst,src,delay);
+                               src.connect(dst,sl,SimplePeer.t_DARKNET);
                            }
                        }
                    } else {
                        if (!src.hasNeighbor(dst) && !dst.hasNeighbor(src)) {
                            found = true;
-                           src.connect(dst);
-                           dst.connect(src);
+                           SimpleLink sl = new SimpleLink(src,dst,delay);
+                           src.connect(dst,sl,SimplePeer.t_DARKNET);
+                           dst.connect(src,sl,SimplePeer.t_DARKNET);
                        }
-                   }
-                   
-                   
+                   }                               
                }
+           }       
+       }
+    }        
+
+
+    /*
+     * Evaluate swap directly without no delay
+     *
+     * @randsteps: steps for random walk from source to dest (0=uniform 
selection)
+     * @randomize_interval: if to enable position randomization with a period 
of swaps
+     */
+    public void tryswap(int randsteps, int randomize_interval) {
+       
+       int i,j;
+       SimpleNode na=null,nb=null;
+
+       if (randsteps==0) {
+           do {
+               i = rand.nextInt(nodes.size());
+               j = rand.nextInt(nodes.size());
+           } while (i==j);
+
+           na = nodes.get(i);
+           nb = nodes.get(j);
+       } else {
+           // Fixme: let the node initialize the request itself
+           do {
+               i = rand.nextInt(nodes.size());
+               na = nodes.get(i);
+               int s = randsteps;
+               nb = na;
+               while (s > 0) {
+                   nb = nb.neighbors.get(rand.nextInt(nb.neighbors.size())).n;
+                   s--;
+               }
+           } while (na==nb);
+
+       }
+
+       if (randomize_interval > 0) {
+           if (na.rand_interval > 0) {
+               na.rand_interval--;
+           } else {
+               na.setLocation(rand.nextDouble());
+               na.rand_interval = na.rand_interval + randomize_interval;
            }
+       }

+       double p = 
Math.exp(na.logDists(na.getLocation())+nb.logDists(nb.getLocation())-na.logDists(nb.getLocation())-nb.logDists(na.getLocation()));
+
+       // Evaluate step
+       if (rand.nextDouble()<p) {
+           double tmp = na.getLocation();
+           na.setLocation(nb.getLocation());
+           nb.setLocation(tmp);            
        }
     }

+    /*
+     * Print nodes and edges between them
+     *
+     * @stdout: if stdout!= 0 we print to stdout, if stdout==0 print to stderr
+     */
+    public void printNodes(int stdout) {
+        for(int i=0; i<nodes.size(); i++) {
+            int start = (int) 
Math.round(nodes.size()*nodes.get(i).getLocation());
+            Vector<SimplePeer> neighbors = nodes.get(i).neighbors;

+            for (SimplePeer peer: nodes.get(i).neighbors) {
+                int stop = (int) Math.round(nodes.size()*peer.n.getLocation());
+
+                if(stdout==1) {
+                    System.out.println(start + "\t" + stop);
+                } else {
+                    System.err.println(start + "\t" + stop);
+                }
+            }
+        }
+    }
+
+    /*
+     * Print only locations for nodes (in topology order)
+     *
+     * @stdout: if stdout!= 0 we print to stdout, if stdout==0 print to stderr
+     */
+    public void printLocations(int stdout) {
+       for(SimpleNode n: nodes) {
+           ((stdout==0) ? System.err : System.out).println(n.getLocation());
+       }
+    }
+    
+    /*
+     * Enable all nodes for learning route delays
+     */
+    public void learning() {
+       for (SimpleNode n: nodes) {
+           n.learning = true;
+       }
+    }
+
+    /*
+     * Disable learning of route delays
+     */
+    public void unlearning() {
+       for (SimpleNode n: nodes) {
+           n.learning = false;
+       }
+    }
+
+    /*
+     * Use delays for generating edges
+     */
+    public void edgedelays() {
+       this.delays = true;
+    }
+    
+    /*
+     * Enable delay usage for nodes in routing
+     */
+    public void usedelays() {
+       for (SimpleNode n: nodes) {
+           n.delays = true;
+       }
+    }
+    
+    /*
+     * Disable delays usage for nodes in routing
+     */
+    public void nodelays() {
+       for (SimpleNode n: nodes) {
+           n.delays = false;
+       }
+    }
+
 }

Added: trunk/apps/load-balancing-sims/phase7/sim/SimpleLink.java
===================================================================
--- trunk/apps/load-balancing-sims/phase7/sim/SimpleLink.java                   
        (rev 0)
+++ trunk/apps/load-balancing-sims/phase7/sim/SimpleLink.java   2007-08-20 
17:58:17 UTC (rev 14808)
@@ -0,0 +1,42 @@
+package sim;
+
+import java.util.LinkedList;
+import java.util.Random;
+
+class SimpleLink {
+
+    double d;
+    SimpleNode a,b;
+    static boolean use_delays=false;
+    static final Random rand = new Random(System.currentTimeMillis() % 10000);
+    // fixme: event queue for delivery of messages    
+
+    public void use_delays() {
+       use_delays = true;
+    }
+
+    public SimpleLink(SimpleNode aend,SimpleNode bend) {
+       this(aend,bend,SimpleGraph.sampleDelay());
+    }
+
+    public SimpleLink(SimpleNode aend,SimpleNode bend,double delay) {
+       a = aend;
+       b = bend;
+       d = delay;
+    }
+
+    // Assumption: the application will know how to handle a link if its 
directed (fixme?)
+    public LinkedList<SimpleQuery> send(SimpleQuery q) {
+       
+       if (q.source==a) {
+           return b.recv(q);
+       } else if (q.source==b) {
+           return a.recv(q);
+       } else {
+           System.err.println("SimpleLink.send(): Simulation error, unknown 
target?");
+           System.exit(-1);
+       }
+       return null;
+    }
+
+}

Modified: trunk/apps/load-balancing-sims/phase7/sim/SimpleNode.java
===================================================================
--- trunk/apps/load-balancing-sims/phase7/sim/SimpleNode.java   2007-08-20 
17:00:45 UTC (rev 14807)
+++ trunk/apps/load-balancing-sims/phase7/sim/SimpleNode.java   2007-08-20 
17:58:17 UTC (rev 14808)
@@ -12,40 +12,86 @@
 class SimpleNode {

     boolean print_steps = false;
-    Random rand;
+    static Random rand;
+    boolean learning = false;
+    boolean delays = false;
+    int rand_interval;

     double location;
-    public Vector<SimpleNode> neighbors; // Symmetric for undirected, outgoing 
when directed
-    public Vector<SimpleNode> incoming;  // Incoming when directed
+    public Vector<SimplePeer> neighbors; // Symmetric for undirected, outgoing 
when directed
+    public Vector<SimplePeer> incoming;  // Incoming when directed
     public HashMap<SimpleNode,Integer> traffic_incoming;
     public HashMap<SimpleNode,Integer> traffic_outgoing;
     public HashMap<SimpleNode,Integer> init_local;       // Outgoing traffic, 
locally initiated
     public HashMap<SimpleNode,Integer> init_remote;      // Outgoing traffic, 
remotely initated
     public HashMap<SimpleNode,Integer> new_received;     // New messages 
received on an edge
     public HashMap<SimpleNode,Integer> new_sent;         // New messages sent 
on an edge
+    public HashMap<SimpleNode,SimpleEstimator> estimators;
+    // SimpleEstimator est;

+    /* Distance time estimation
+     * Let D(y) be the time to neighbor y
+     * In a Kleinberg network we know that E[T|routing via y to z] = D(y) + 
log(|z-y|)*c
+     * c is the unknown factor which we need to derive
+     * Route for a while: each time we route and get a response update c 
(running average)
+     * NB: possibly we need to estimate c depending on neighbors, varying 
connections to the network
+     */
+
     /*** stats ***/
     int load = 0;
-    int loadmem = 0;     // Internal 
-    boolean busy = false;
+    int loadmem = 0;            // Total load seen
+    boolean busy = false;       // Busy with current query?
+    boolean opennet = false;    // Participate or not
+    int opennet_maxpeers = 0;   // LRU-scheme to keep max this number
+    int estsize;                // Size of routing estimation table

     /*** constants ***/
     public static final int LOCALORIG = 0;
     public static final int REMOTEORIG = 1;

-    public SimpleNode(double x) {
+    /*
+     * @x: location to take (on circle)
+     * @estsize: size of routing delay-estimation table (set to 0 if not used)
+     */
+    public SimpleNode(double x, int estsize) {
        this.location = x;
-       neighbors = new Vector<SimpleNode>();
-       incoming = new Vector<SimpleNode>();
+       this.estsize = estsize;
+       neighbors = new Vector<SimplePeer>();
+       incoming = new Vector<SimplePeer>();
        traffic_incoming = new HashMap<SimpleNode,Integer>();
        traffic_outgoing = new HashMap<SimpleNode,Integer>();
        init_local = new HashMap<SimpleNode,Integer>();
        init_remote = new HashMap<SimpleNode,Integer>();
        new_received = new HashMap<SimpleNode,Integer>();
        new_sent = new HashMap<SimpleNode,Integer>();
-       rand = new Random ((int) (System.currentTimeMillis() % 10000));
+       // est = new SimpleEstimator(estsize);
+       // rand = new Random ((int) (System.currentTimeMillis() % 10000));
+       estimators = new HashMap<SimpleNode,SimpleEstimator>();
+       rand = new Random(System.currentTimeMillis() % 10000);
     }

+    /*
+     * Enable opennet peering/attempts for this node
+     *
+     * @desired_peers: number of desired peers before LRU-dropping
+     */
+    public void enableOpennet(int desired_peers) {
+       opennet = true;
+       opennet_maxpeers = desired_peers;
+    }
+
+    /*
+     * Disable opennet for this node
+     */
+    public void disableOpennet() {
+       opennet = false;
+    }
+
+    /*
+     * Keep incoming stats for a new query
+     *
+     * @inq: the query to register
+     */
     public void load_incoming(SimpleQuery inq) {
        load++;
        loadmem++;
@@ -71,6 +117,15 @@
        }
     }

+    /*
+     * Keep outgoing stats for a query to be sent
+     * 
+     * fixme: change this to be called from the level sending the query
+     * @n: target of the query
+     * @srctype: if source is local (SimpleNode.LOCALORIG) or if remote
+     *           (SimpleNode.REMOTEORIG), which is used to separate load
+     *           based on locally/remotely initiated query usage
+     */
     public void load_outgoing(SimpleNode n, int srctype) {
        // Total traffic
        if(!traffic_outgoing.containsKey(n)) {
@@ -97,13 +152,26 @@
            }
        }

-       // fixme: fresh taget going to target
+       // fixme: fresh taget going to target 
     }

+    /*
+     * Receive a generic query
+     */
+    public LinkedList<SimpleQuery> recv(SimpleQuery q) {
+       return route(q);
+    }
+
+    /*
+     * Route from q.source to q.target
+     *
+     * NB: if q.dest==null we count that the query q is internally handled as 
starting request
+     * @q: The query that specifies which target we're on to
+     */
     public LinkedList<SimpleQuery> route(SimpleQuery q) {
        int htl = q.htl;       // Internal htl value
        int init;              // local or remote origin?
-       //load_incoming(q.source);
+       SimpleLink srclink = q.link;
        load_incoming(q);

        if(q.source == this) {
@@ -112,112 +180,314 @@
            init = SimpleNode.REMOTEORIG;
        }

-       if(print_steps)
-           printQueryState(htl);
-
        LinkedList<SimpleQuery> result = new LinkedList<SimpleQuery>();

        if(this.location == q.target.getLocation()) {
-           result.addLast(new 
SimpleQuery(this,q.source,q.target,SimpleQuery.SUCCESS,0));
+           result.addLast(new 
SimpleQuery(this,q.source,q.target,srclink,SimpleQuery.SUCCESS,0,opennet_wantPeer()));
            load_outgoing(q.source, init);
            return result;
        }

        if(htl == 0) {      
-            result.addLast(new 
SimpleQuery(this,q.source,q.target,SimpleQuery.HTLSTOP,0));
+            result.addLast(new 
SimpleQuery(this,q.source,q.target,srclink,SimpleQuery.HTLSTOP,0,false));
            load_outgoing(q.source, init);
            return result;
        }

        if(busy) {
-           result.addLast(new 
SimpleQuery(this,q.source,q.target,SimpleQuery.REJECTLOOP,htl-1));
+           result.addLast(new 
SimpleQuery(this,q.source,q.target,srclink,SimpleQuery.REJECTLOOP,htl-1,false));
            load_outgoing(q.source, init);
            return result;
        }

        busy = true;

-       HashSet<SimpleNode> peers = new HashSet<SimpleNode>(neighbors); 
-       peers.remove(q.source);
+       /*
+        * Available peers
+        * fixme: do this lookup in a nicer way, get called directly from a 
peer event
+        */
+       HashSet<SimplePeer> peers = new HashSet<SimplePeer>(neighbors); 
+       SimplePeer srcpeer = null;
+       if((q.dest!=null) && (q.source!=this)) {  // Ignore internal and 
locally initiated
+           for (SimplePeer peer: neighbors) {
+               if(peer.n == q.source) {
+                   srcpeer = peer;
+                   break;
+               }
+           }
+           if(srcpeer == null) {
+               System.err.println("route(): at "+ this.getLocation() +": no 
source peer at " + q.source.getLocation());
+               EvalKleinbergLoad.printNeighbors(this);
+               System.exit(0);
+           }
+       }
+       peers.remove(srcpeer);

+       /*
+        * Make selection until hops left in query.
+        * If using delays, then routing on is still restricted to nodes closer
+        * to target (dont let time dominate query getting closer)
+        */         
        int time = 0;
        while(htl>0) {
-
-           if(print_steps && (time>0))
-               printQueryState(htl);
            time++;
-           
            double bestdist = Double.MAX_VALUE;
-           SimpleNode bestpeer = null;
+           double loc_bestdist = Double.MAX_VALUE;
+           SimplePeer bestpeer = null;
+           SimplePeer loc_bestpeer = null;

-           for (SimpleNode peer: peers) {
-               double dist = peer.distanceTo(q.target);
-               if(dist < bestdist) {
-                   bestdist = dist;
-                   bestpeer = peer;
-               } else if (dist == bestdist) {
-                   if(rand.nextDouble()<0.5) {
+           for (SimplePeer peer: peers) {
+               double dist,loc_dist;
+
+               /*
+               // Cheating for the optimal value *knowing* which neighbors 
have already seen query (avoid loops)
+               if(peer.n.busy)
+                   continue;
+               */
+
+               // dist = peer.l.d;
+               loc_dist = peer.n.distanceTo(q.target);
+
+               if (delays) {
+                   // Dist to location
+                   // dist = 2*peer.l.d + est.estimate(loc_dist);              
    
+                   // Dist but neighbor-sensitive
+                   // dist = 2*peer.l.d + 
estimators.get(peer.n).estimate(loc_dist);
+                   // Location-based
+                   dist = 2*peer.l.d + 
estimators.get(peer.n).estimate(q.target.getLocation());
+               } else {
+                   dist = loc_dist;
+               }
+
+               // Only consider a time-distance if node is physically closer
+               if (loc_dist<this.distanceTo(q.target)) {
+                   if (dist<bestdist) {
                        bestdist = dist;
                        bestpeer = peer;
+                   } else if ((dist==bestdist)&&(rand.nextDouble()<0.5)) {
+                       bestdist = dist;
+                       bestpeer = peer;
                    }
                }
+
+               // Always compute loc-best
+               if (loc_dist<loc_bestdist) {
+                   loc_bestdist = loc_dist;
+                   loc_bestpeer = peer;
+               } else if ((loc_dist==loc_bestdist)&&(rand.nextDouble()<0.5)) {
+                   loc_bestdist = loc_dist;
+                   loc_bestpeer = peer;
+               }
            }
-           
-           if(bestpeer == null) {
-               result.addLast(new 
SimpleQuery(this,q.source,q.target,SimpleQuery.DEADEND,htl-1));
+
+           if ((bestpeer==null) || (delays==false)) {
+               bestpeer = loc_bestpeer;                
+           }
+
+           // Do we have an option? Otherwise the final (internal) load.
+           if (bestpeer==null) {
+               result.addLast(new 
SimpleQuery(this,q.source,q.target,srclink,SimpleQuery.DEADEND,htl-1,false));
                load_outgoing(q.source, init);
                return result;
            }

-           result.addLast(new 
SimpleQuery(this,bestpeer,q.target,SimpleQuery.FORWARD,htl-1));  
+           // Pass on message and monitor timing (fixme: freenet HTL heuristic)
+           result.addLast(new 
SimpleQuery(this,bestpeer.n,q.target,bestpeer.l,SimpleQuery.FORWARD,htl-1,false));
            peers.remove(bestpeer);

-           if(print_steps)
-               System.err.println("sending to " + bestpeer.getLocation() + 
"(attempt #"+time+")");
-
-           // fixme: Freenet HTL heuristic
-           load_outgoing(bestpeer, init);
-           LinkedList<SimpleQuery> attempt = bestpeer.route(new 
SimpleQuery(this,bestpeer,q.target,SimpleQuery.FORWARD,htl-1));
+           // Send the packet: load outgoing, route, load incoming result
+           load_outgoing(bestpeer.n, init);
+           LinkedList<SimpleQuery> attempt = bestpeer.l.send(new 
SimpleQuery(this,bestpeer.n,q.target,bestpeer.l,SimpleQuery.FORWARD,htl-1,false));
            load_incoming(attempt.getLast());
-
            result.addAll(attempt);

+           // Learn from overheard/returned message (fixme: penalize when 
routes have been sent wrong/HTLSTOP/...)
+           if (this.learning && (attempt.getLast().type==SimpleQuery.SUCCESS)) 
{
+               double routedist = bestpeer.n.distanceTo(q.target);
+               double rtt = routeTime(result)-2*result.getLast().link.d;
+               if(rtt<0) {
+                   EvalKleinbergLoad.printRoute(attempt);
+                   EvalKleinbergLoad.printRoute(result);                   
+                   System.err.println("WTF?"); System.exit(0);
+               }
+
+               // Update estimators
+               // double rtt = routeTime(attempt)-2*attempt.getLast().link.d;
+               // est.update(routedist,rtt);
+               // estimators.get(bestpeer.n).update(routedist,rtt);
+               // 
estimators.get(bestpeer.n).update(q.target.getLocation(),rtt);
+               estimators.get(bestpeer.n).update(q.target.getLocation(),rtt);
+           }
+
+           // Pass back
            switch(attempt.getLast().type) {                // Did this branch 
succeed?
            case SimpleQuery.SUCCESS:           
                if(q.source != this) {
-                   result.addLast(new 
SimpleQuery(this,q.source,q.target,SimpleQuery.SUCCESS,0));
+                   result.addLast(new 
SimpleQuery(this,q.source,q.target,srclink,SimpleQuery.SUCCESS,0,q.openref));
                    load_outgoing(q.source, init);
                }
+
+               // fixme: more advanced decision on whether to let destsampling 
or not
+               if(opennet) {
+                   opennet_destsampling(q.target);
+               }
+
                return result;

-           case SimpleQuery.HTLSTOP:
+           case SimpleQuery.HTLSTOP:                       // No more Hops To 
Live
                if(q.source != this) {
-                   result.addLast(new 
SimpleQuery(this,q.source,q.target,SimpleQuery.HTLSTOP,0));
+                   result.addLast(new 
SimpleQuery(this,q.source,q.target,srclink,SimpleQuery.HTLSTOP,0,false));
                    load_outgoing(q.source, init);
                }
                return result;

-           default:
+           default:                                        // Continue as long 
as possible
                htl = attempt.getLast().htl;
                break;
            }
        }

-       if(q.source != this) {      
-           result.addLast(new 
SimpleQuery(this,q.source,q.target,SimpleQuery.HTLSTOP,0));
+       if(q.source != this) {                              // No more Hops To 
Live
+           result.addLast(new 
SimpleQuery(this,q.source,q.target,srclink,SimpleQuery.HTLSTOP,0,false));
            load_outgoing(q.source, init);
        }
        return result;
     }

+
+    /*
+     * Sample destination, using opennet connections as LRU
+     * fixme: support for directed edges
+     *
+     * 
+     1. Check if we want to drop some opennet peer for a new one
+     2. Check which one to drop
+     3. Connect to new node (ask if still wants us)
+     4. If success, disconnect the old node
+     */
+    void opennet_destsampling(SimpleNode dest) {
+       if (this.hasNeighbor(dest) || rand.nextDouble()>0.1) {
+           return;
+       }
+
+       SimpleNode dropnode = null;
+       if (!opennet_wantPeer()) {
+           dropnode = opennet_nodeToDrop();
+           if (dropnode==null) {
+               return;              // Cant drop a node at the moment
+           }
+           opennet_drop(dropnode);
+       }
+
+       if (dest.opennet_acceptpeer(this)) {
+           SimpleLink link = new SimpleLink(this,dest);        
+           this.connect(dest,link,SimplePeer.t_OPENNET);
+           dest.connect(this,link,SimplePeer.t_OPENNET);
+       } else {
+           System.err.println("NOT accepting peer!"); System.exit(-1); // fixme
+       }
+    }
+
+    // fixme: concurrency
+    public boolean opennet_wantPeer() {
+
+       int opennet_peers=0;
+       for (SimplePeer p: neighbors) {
+           if (p.t == SimplePeer.t_OPENNET) {
+               opennet_peers++;
+           }
+       }
+
+       if(opennet_peers<opennet_maxpeers) {
+           return true;
+       } else {
+           return false;
+       }       
+
+    }
+
+    // LRU-drop opennet nodes (Vector.add places on end)
+    public SimpleNode opennet_nodeToDrop() {
+       SimpleNode dropnode=null;
+       for (SimplePeer p: neighbors) {
+           if (p.t == SimplePeer.t_OPENNET) {
+               dropnode = p.n;
+               break;
+           }
+       }
+       return dropnode;
+    }
+
+    public boolean opennet_acceptpeer(SimpleNode n) {
+       if (this.hasNeighbor(n)) {      // fixme: concurrency
+           System.err.println("Error: dest target already has the peer"); 
System.exit(0); 
+       }
+
+        int opennet_peers=0;
+        for (SimplePeer p: neighbors) {
+            if (p.t == SimplePeer.t_OPENNET) {
+                opennet_peers++;
+            }
+        }
+
+       while (opennet_peers>=opennet_maxpeers) {
+           SimpleNode dropnode = opennet_nodeToDrop();
+           if (dropnode == null) { System.err.println("Error: cant find node 
to drop"); System.exit(-1); }
+           opennet_drop(dropnode);
+           opennet_peers--;
+       }
+
+       return true;
+    }
+
+    /*
+     * Drop a node that is assumed to be as an opennet peer
+     */
+    public void opennet_drop(SimpleNode dropnode) {
+       if (!hasNeighbor(dropnode)) {
+           System.err.println("Error: does not have neighbor to drop"); 
System.exit(-1);
+       }
+
+       if (nodeToPeer(dropnode).t!=SimplePeer.t_OPENNET) {
+           System.err.println("Error: not opennet peer"); System.exit(-1);
+       }
+           
+           
+
+       disconnect(dropnode);
+       dropnode.disconnect(this);
+    }
+
+
+    double routeTime(LinkedList<SimpleQuery> route) {
+       double t = 0;
+       for (SimpleQuery q: route) {
+           t = t + q.link.d;
+       }
+       return t;
+    }
+
+
     public double getLocation() {
        return location;
     }

+
+    public void setLocation(double loc) {
+       if((location < 0)||(location>=1)) {
+           System.err.println("setLocation(): setting location to " + 
location);
+           System.exit(0);
+       }
+
+       location = loc;
+    }
+
+
     public double distanceTo(SimpleNode target) {
        return distanceTo(target.location);
     }

+
     public double distanceTo(double pos) {
        if ((pos<0) || (pos>1)) {
            System.err.println("SimpleNode.distanceTo(): malformed distance"); 
System.exit(-1);
@@ -225,45 +495,135 @@
        return Math.min(Math.max(location,pos)-Math.min(location,pos), 
Math.min(location,pos)+1.0-Math.max(location,pos));
     }

-    public void connect(SimpleNode target) {
+
+    public double distanceFrom(double from,double to) {
+       return Math.min(Math.max(from,to)-Math.min(from,to), 
Math.min(from,to)+1.0-Math.max(from,to));
+    }
+
+
+    public void connect(SimpleNode target,SimpleLink link,int type) {
        if (target == this) {
            System.err.println("SimpleNode.connect(): connecting itself"); 
System.exit(-1);
        }
+       if (this.hasNeighbor(target)) {
+           System.err.println("SimpleNode.connect(): neighbor exists"); 
System.exit(-1);
+       }
+       if (!(type==SimplePeer.t_DARKNET || type==SimplePeer.t_OPENNET)) {
+           System.err.println("SimpleNode.connect(): unknown type " + type); 
System.exit(-1);
+       }

-       if (neighbors.contains(target)) {
-           System.err.println("SimpleNode.connect(): neighbors exists"); 
System.exit(-1);
-       }
-       neighbors.add(target);
-       target.connectIncoming(this);
+       neighbors.add(new SimplePeer(target,link,type));
+       target.connectIncoming(this,link,type);
+       estimators.put(target,new SimpleEstimator(this.estsize));
     }

+
     // This is for easy book-keeping when we are evaluating a directed model
-    public void connectIncoming(SimpleNode source) {
+    public void connectIncoming(SimpleNode source,SimpleLink link,int type) {
        if(source == this) {
            System.err.println("SimpleNode.connectIncoming(): connecting 
itself"); System.exit(-1);
        }
-       if(incoming.contains(source)) {
+       if(this.hasIncoming(source)) {
            System.err.println("SimpleNode.connectIncoming(): source exists"); 
System.exit(-1);
        }
-       incoming.add(source);
+       if (!(type==SimplePeer.t_DARKNET || type==SimplePeer.t_OPENNET)) {
+           System.err.println("SimpleNode.connect(): unknown type " + type); 
System.exit(-1);
+       }
+
+       incoming.add(new SimplePeer(source,link,SimplePeer.t_DARKNET));
     }

-    public boolean hasNeighbor(SimpleNode peer) {
-       return neighbors.contains(peer);
+
+    /*
+     * Disconnect a peer
+     * note: for directed edges the source should always disconnect
+     * fixme: load counting and disconnection
+     */
+    public void disconnect(SimpleNode target) {        
+       if (nodeToPeer(target)==null) {
+           System.err.println("SimpleNode.disconnect(): peer not in 
neighbors");
+           System.exit(-1);
+       }
+
+       for (SimplePeer p: neighbors) {
+           if (p.n == target) {
+               neighbors.remove(p);
+               break;
+           }
+       }
+
+       for (SimplePeer p: incoming) {
+           if (p.n == target) {
+               incoming.remove(p);
+               break;
+           }
+       }
+
+       traffic_incoming.remove(target);
+       traffic_outgoing.remove(target);
+       init_local.remove(target);
+       init_remote.remove(target);
+       new_received.remove(target);
+       new_sent.remove(target);
+       estimators.remove(target);
+
     }

+    public SimplePeer nodeToPeer(SimpleNode n) {
+       for (SimplePeer p: neighbors) {
+           if (p.n == n) {
+               return p;
+           }
+       }
+       return null;
+    }
+
+    public boolean hasNeighbor(SimpleNode node) {
+       //return neighbors.contains(peer);
+       for(SimplePeer p: neighbors) {
+           if(p.n == node)
+               return true;
+       }
+       return false;
+    }
+
+    
+    public boolean hasIncoming(SimpleNode node) {
+       for(SimplePeer p: incoming) {
+           if(p.n == node)
+               return true;
+       }
+       return false;
+    }
+
+
     public int getDegree() {
        return neighbors.size();
     }

+
+    public double logDists(double from) {
+       double sum = 0;
+
+       for (SimplePeer p: neighbors) {     
+           if (p.n.getLocation()==from) {
+               sum = sum + Math.log(distanceFrom(from,this.getLocation()));
+           } else {
+               sum = sum + Math.log(distanceFrom(from,p.n.getLocation()));
+           }
+       }
+       return sum;
+    }
+
+
     public void printQueryState(int htl) {
        System.err.print("[htl="+htl+"]");
        System.err.print("location " + location +":\t");
        System.err.print("[ ");
-       for(SimpleNode n: neighbors) {
+       for(SimplePeer p: neighbors) {
+           SimpleNode n = p.n;
            System.err.print(n.getLocation() + " ");
        }
        System.err.print("]\t");
     }
-
 }

Added: trunk/apps/load-balancing-sims/phase7/sim/SimplePeer.java
===================================================================
--- trunk/apps/load-balancing-sims/phase7/sim/SimplePeer.java                   
        (rev 0)
+++ trunk/apps/load-balancing-sims/phase7/sim/SimplePeer.java   2007-08-20 
17:58:17 UTC (rev 14808)
@@ -0,0 +1,34 @@
+//
+// Peer representation as seen from a SimpleNode
+//
+package sim;
+
+class SimplePeer {
+    
+    static final int t_DARKNET=0;
+    static final int t_OPENNET=1;
+
+    SimpleNode n;        // The peer node
+    SimpleLink l;        // Link to peer
+    int t;
+
+    /*
+     * @target: the node which will become the peer
+     * @link: the link to the peer
+     * @type: type of peer (t.DARKNET or t_OPENNET)
+     */
+    public SimplePeer(SimpleNode target,SimpleLink link,int type) {
+       n = target;
+       l = link;       
+       t = type;
+    }
+
+    public boolean drknetPeer() {
+       return (t==t_DARKNET);
+    }
+
+    public boolean opennetPeer() {
+       return (t==t_OPENNET);
+    }
+}
+

Modified: trunk/apps/load-balancing-sims/phase7/sim/SimpleQuery.java
===================================================================
--- trunk/apps/load-balancing-sims/phase7/sim/SimpleQuery.java  2007-08-20 
17:00:45 UTC (rev 14807)
+++ trunk/apps/load-balancing-sims/phase7/sim/SimpleQuery.java  2007-08-20 
17:58:17 UTC (rev 14808)
@@ -1,3 +1,6 @@
+//
+// Queries to track network traffic
+//
 package sim;

 class SimpleQuery {
@@ -2,2 +5,3 @@

+    // Message types
     public static final int SUCCESS=0;
@@ -11,15 +15,31 @@
     final SimpleNode source;
     final SimpleNode dest;
     final SimpleNode target;
+    final SimpleLink link;
     final int type;
     int htl;
+    boolean openref;

-    public SimpleQuery (SimpleNode source, SimpleNode dest, SimpleNode target, 
int type, int htl) {
+    /*
+     * @source: sender of query
+     * @dest: destination of query
+     * @target: ultimate target of query (ignored if without meaning)
+     * @l: the link over which the query is sent
+     * @type: type of query
+     * @htl: HTL left on query
+     * @opennet: if this contains an opennet reference (from a successful CHK)
+     */
+    public SimpleQuery (SimpleNode source, SimpleNode dest, SimpleNode target, 
SimpleLink l, int type, int htl, boolean opennet) {
        this.source = source;
        this.dest = dest;
        this.target = target;
+       this.link = l;
        this.type = type;
        this.htl = htl;
+       this.openref = opennet;
     }

+    public boolean internalQuery() {
+       return (link==null);
+    }
 }


Reply via email to