Your backtracking code will backtrack forever and visit every node if 
necessary ... could this skew the simulation results? I suggest you increment 
depth just before calling n.request() in request(), this would be a closer 
approximation with the same simulation performance, and more accurate path 
lengths.

Also, do you use the request rate code?

On Friday 23 May 2008 22:58, mrogers at freenetproject.org wrote:
> Author: mrogers
> Date: 2008-05-23 21:58:34 +0000 (Fri, 23 May 2008)
> New Revision: 20085
> 
> Added:
>    trunk/apps/freenet-caching/
>    trunk/apps/freenet-caching/CachingSim.java
>    trunk/apps/freenet-caching/FifoNode.java
>    trunk/apps/freenet-caching/LruNode.java
>    trunk/apps/freenet-caching/Node.java
>    trunk/apps/freenet-caching/RandomNode.java
>    trunk/apps/freenet-caching/RequestGenerator.java
>    trunk/apps/freenet-caching/UTest.java
>    trunk/apps/freenet-caching/UniformRequestGenerator.java
>    trunk/apps/freenet-caching/ZipfRequestGenerator.java
> Log:
> Caching simulations (LRU vs FIFO vs random replacement).
> 
> 
> Added: trunk/apps/freenet-caching/CachingSim.java
> ===================================================================
> --- trunk/apps/freenet-caching/CachingSim.java                                
> (rev 
0)
> +++ trunk/apps/freenet-caching/CachingSim.java        2008-05-23 21:58:34 UTC 
> (rev 
20085)
> @@ -0,0 +1,141 @@
> +// This software has been placed in the public domain by its author
> +
> +import java.lang.reflect.Field;
> +import java.util.ArrayList;
> +import java.util.HashSet;
> +
> +class CachingSim
> +{
> +     public static int NODES = 1000, DEGREE = 20, KEYS = 200000;
> +     
> +     ArrayList<Node> nodes;
> +     RequestGenerator requests;
> +     
> +     CachingSim()
> +     {
> +             nodes = new ArrayList<Node> (NODES);
> +             for (int i = 0; i < NODES; i++) nodes.add (Node.create());
> +             makeKleinbergNetwork();
> +             ArrayList<Double> keys = new ArrayList<Double> (KEYS);
> +             for (int i = 0; i < KEYS; i++) keys.add (Math.random());
> +             requests = RequestGenerator.create();
> +             requests.init (keys);
> +             storeKeys (keys);
> +     }
> +     
> +     void run()
> +     {
> +             double success = 0.0, depth = 0.0, cost = 0.0;
> +             // Request each key 100 times
> +             for (int i = 0; i < KEYS * 100; i++) {
> +                     // Reset the counters halfway through the simulation
> +                     if (i == KEYS * 50) success = depth = cost = 0.0;
> +                     int random = (int) (Math.random() * nodes.size());
> +                     Node node = nodes.get (random);
> +                     double key = requests.generateRequest();
> +                     HashSet<Node> visited = new HashSet<Node>();
> +                     int d = node.request (key, 0, visited);
> +                     if (d != -1) {
> +                             success++; // Success rate should be close to 1
> +                             depth += d; // Depth of successful searches
> +                             cost += visited.size() - 1; // Bandwidth cost
> +                     }
> +             }
> +             System.out.println (
> +                     success / (KEYS * 50) + " " +
> +                     depth / success + " " +
> +                     cost / success
> +             );
> +     }
> +     
> +     void makeKleinbergNetwork()
> +     {
> +             for (Node a : nodes) {
> +                     // Normalise the probabilities
> +                     double norm = 0.0;
> +                     for (Node b : nodes) {
> +                             if (a.loc == b.loc) continue;
> +                             norm += 1.0 / distance (a.loc, b.loc);
> +                     }
> +                     // Create DEGREE/2 outgoing connections
> +                     for (Node b : nodes) {
> +                             if (a.loc == b.loc) continue;
> +                             double p = 1.0 / distance (a.loc, b.loc) / norm;
> +                             for (int i = 0; i < DEGREE / 2; i++) {
> +                                     if (Math.random() < p) {
> +                                             a.neighbours.add (b);
> +                                             b.neighbours.add (a);
> +                                             break;
> +                                     }
> +                             }
> +                     }
> +             }
> +     }
> +     
> +     static double distance (double x, double y)
> +     {
> +             if (x > y) return Math.min (x - y, y - x + 1.0);
> +             else return Math.min (y - x, x - y + 1.0);
> +     }
> +     
> +     // Store each key permanently on the node nearest its location
> +     void storeKeys (ArrayList<Double> keys)
> +     {
> +             for (double key : keys) {
> +                     double bestDistance = Double.POSITIVE_INFINITY;
> +                     Node bestNode = null;
> +                     for (Node node : nodes) {
> +                             double distance = distance (node.loc, key);
> +                             if (distance < bestDistance) {
> +                                     bestDistance = distance;
> +                                     bestNode = node;
> +                             }
> +                     }
> +                     bestNode.store.add (key);
> +             }
> +     }
> +     
> +     public static void main (String[] args)
> +     {
> +             // Command line args are used to set static values by reflection
> +             try {
> +                     for (String arg : args) {
> +                             // Arguments are in the form class.field=value
> +                             String[] cfv = arg.split ("=");
> +                             String[] cf = cfv[0].split ("\\.");
> +                             Class c = Class.forName (cf[0]);
> +                             Field f = c.getField (cf[1]);
> +                             String v = cfv[1];
> +                             // Determine the type and parse the value
> +                             Class type = f.getType();
> +                             if (type == Boolean.TYPE) {
> +                                     boolean b = Boolean.parseBoolean (v);
> +                                     f.setBoolean (null, b);
> +                             }
> +                             else if (type == Double.TYPE) {
> +                                     double d = Double.parseDouble (v);
> +                                     f.setDouble (null, d);
> +                             }
> +                             else if (type == Integer.TYPE) {
> +                                     int i = Integer.parseInt (v);
> +                                     f.setInt (null, i);
> +                             }
> +                             else if (type == Class.class) {
> +                                     ClassLoader cl
> +                                     = ClassLoader.getSystemClassLoader();
> +                                     f.set (null, cl.loadClass (v));
> +                             }
> +                     }
> +             }
> +             catch (Exception e) {
> +                     System.err.println (e);
> +                     System.exit (1);
> +             }
> +             // Print the arguments so we can tell the output files apart
> +             System.out.print ("# args:");
> +             for (String arg : args) System.out.print (" " + arg);
> +             System.out.println();
> +             // Run the simulation
> +             new CachingSim().run();
> +     }
> +}
> 
> Added: trunk/apps/freenet-caching/FifoNode.java
> ===================================================================
> --- trunk/apps/freenet-caching/FifoNode.java                          (rev 0)
> +++ trunk/apps/freenet-caching/FifoNode.java  2008-05-23 21:58:34 UTC (rev 
20085)
> @@ -0,0 +1,32 @@
> +// This software has been placed in the public domain by its author
> +
> +import java.util.LinkedHashMap;
> +import java.util.Map;
> +
> +class FifoNode extends Node
> +{
> +     FifoMap<Double,Double> cache = new FifoMap<Double,Double>();
> +     
> +     boolean cacheGet (double key)
> +     {
> +             return cache.containsKey (key);
> +     }
> +     
> +     void cachePut (double key)
> +     {
> +             cache.put (key, null);
> +     }
> +     
> +     class FifoMap<Key,Value> extends LinkedHashMap<Key,Value>
> +     {
> +             FifoMap()
> +             {
> +                     super (CACHE_SIZE, 0.75f, false);
> +             }
> +             
> +             protected boolean removeEldestEntry (Map.Entry<Key,Value> entry)
> +             {
> +                     return size() > CACHE_SIZE;
> +             }
> +     }
> +}
> 
> Added: trunk/apps/freenet-caching/LruNode.java
> ===================================================================
> --- trunk/apps/freenet-caching/LruNode.java                           (rev 0)
> +++ trunk/apps/freenet-caching/LruNode.java   2008-05-23 21:58:34 UTC (rev 
20085)
> @@ -0,0 +1,33 @@
> +// This software has been placed in the public domain by its author
> +
> +import java.util.LinkedHashMap;
> +import java.util.Map;
> +
> +class LruNode extends Node
> +{
> +     LruMap<Double,Double> cache = new LruMap<Double,Double>();
> +     
> +     boolean cacheGet (double key)
> +     {
> +             // Use get() rather than containsKey() to update the LRU order
> +             return cache.get (key) != null;
> +     }
> +     
> +     void cachePut (double key)
> +     {
> +             cache.put (key, key);
> +     }
> +     
> +     class LruMap<Key,Value> extends LinkedHashMap<Key,Value>
> +     {
> +             LruMap()
> +             {
> +                     super (CACHE_SIZE, 0.75f, true);
> +             }
> +             
> +             protected boolean removeEldestEntry (Map.Entry<Key,Value> entry)
> +             {
> +                     return size() > CACHE_SIZE;
> +             }
> +     }
> +}
> 
> Added: trunk/apps/freenet-caching/Node.java
> ===================================================================
> --- trunk/apps/freenet-caching/Node.java                              (rev 0)
> +++ trunk/apps/freenet-caching/Node.java      2008-05-23 21:58:34 UTC (rev 
> 20085)
> @@ -0,0 +1,65 @@
> +// This software has been placed in the public domain by its author
> +
> +import java.util.ArrayList;
> +import java.util.Collections;
> +import java.util.Comparator;
> +import java.util.HashSet;
> +     
> +abstract class Node implements Comparator<Node>
> +{
> +     public static int CACHE_SIZE = 1000, MAX_DEPTH = 10;
> +     public static Class CLASS = null; // Factory class
> +     
> +     // Factory method
> +     static Node create()
> +     {
> +             try {
> +                     return (Node) CLASS.newInstance();
> +             }
> +             catch (Exception e) {
> +                     System.err.println (e);
> +                     System.exit (2);
> +                     return null;
> +             }
> +     }
> +     
> +     double loc = Math.random(), target = 0.0;
> +     HashSet<Node> neighbours = new HashSet<Node>();
> +     HashSet<Double> store = new HashSet<Double>();
> +     
> +     abstract boolean cacheGet (double key);
> +     abstract void cachePut (double key);
> +     
> +     // Return the depth at which the request succeeded, or -1 if it failed
> +     int request (double key, int depth, HashSet<Node> visited)
> +     {
> +             if (!visited.add (this)) return -1; // Loop
> +             if (cacheGet (key) || store.contains (key)) return depth;
> +             if (depth == MAX_DEPTH) return -1; // DNF
> +             target = key; // Sort neighbours by closeness to requested key
> +             ArrayList<Node> nbrs = new ArrayList<Node> (neighbours);
> +             Collections.sort (nbrs, this);
> +             for (Node n : nbrs) {
> +                     int d = n.request (key, depth + 1, visited);
> +                     if (d != -1) {
> +                             cachePut (key);
> +                             return d;
> +                     }
> +             }
> +             return -1; // RNF
> +     }
> +     
> +     public int compare (Node n1, Node n2)
> +     {
> +             double d1 = CachingSim.distance (n1.loc, target);
> +             double d2 = CachingSim.distance (n2.loc, target);
> +             if (d1 < d2) return -1;
> +             if (d1 > d2) return 1;
> +             return 0;
> +     }
> +     
> +     public boolean equals (Node n1, Node n2)
> +     {
> +             return compare (n1, n2) == 0;
> +     }
> +}
> 
> Added: trunk/apps/freenet-caching/RandomNode.java
> ===================================================================
> --- trunk/apps/freenet-caching/RandomNode.java                                
> (rev 
0)
> +++ trunk/apps/freenet-caching/RandomNode.java        2008-05-23 21:58:34 UTC 
> (rev 
20085)
> @@ -0,0 +1,24 @@
> +// This software has been placed in the public domain by its author
> +
> +class RandomNode extends Node
> +{
> +     double[] cache = new double[CACHE_SIZE];
> +     double salt = Math.random();
> +     
> +     boolean cacheGet (double key)
> +     {
> +             return cache[hash (key)] == key;
> +     }
> +     
> +     void cachePut (double key)
> +     {
> +             cache[hash (key)] = key;
> +     }
> +     
> +     int hash (double key)
> +     {
> +             // Throw away the low-order bits because Math.random() sucks
> +             long bits = Double.doubleToLongBits (key + salt) >> 8;
> +             return (int) (bits & 0x7FFFFFFF) % CACHE_SIZE;
> +     }
> +}
> 
> Added: trunk/apps/freenet-caching/RequestGenerator.java
> ===================================================================
> --- trunk/apps/freenet-caching/RequestGenerator.java                          
(rev 0)
> +++ trunk/apps/freenet-caching/RequestGenerator.java  2008-05-23 21:58:34 UTC 
(rev 20085)
> @@ -0,0 +1,80 @@
> +// This software has been placed in the public domain by its author
> +
> +import java.util.ArrayList;
> +import java.util.TreeSet;
> +
> +abstract class RequestGenerator
> +{
> +     public static Class CLASS = null; // Factory class
> +     
> +     TreeSet<Request> queue = new TreeSet<Request>();
> +     double now = 0.0;
> +     
> +     // Factory method
> +     static RequestGenerator create()
> +     {
> +             try {
> +                     return (RequestGenerator) CLASS.newInstance();
> +             }
> +             catch (Exception e) {
> +                     System.err.println (e);
> +                     System.exit (2);
> +                     return null;
> +             }
> +     }
> +     
> +     // Schedule the first request for each key
> +     void init (ArrayList<Double> keys)
> +     {
> +             for (int i = 0, size = keys.size(); i < size; i++) {
> +                     double rate = requestRate();
> +                     double time = now - Math.log (Math.random()) / rate;
> +                     queue.add (new Request (keys.get (i), rate, time));
> +             }
> +     }
> +     
> +     // Return the rate at which a given key will be requested
> +     abstract double requestRate();
> +     
> +     // Return a key and schedule the next request for that key
> +     double generateRequest()
> +     {
> +             Request r = queue.first();
> +             queue.remove (r);
> +             now = r.time;
> +             double time = now - Math.log (Math.random()) / r.rate;
> +             queue.add (new Request (r.key, r.rate, time));
> +             return r.key;
> +     }
> +     
> +     class Request implements Comparable<Request>
> +     {
> +             final double key, rate, time;
> +             
> +             Request (double key, double rate, double time)
> +             {
> +                     this.key = key;
> +                     this.rate = rate;
> +                     this.time = time;
> +             }
> +             
> +             // Must be consistent with compareTo()
> +             public boolean equals (Request r)
> +             {
> +                     return key == r.key && rate == r.rate && time == r.time;
> +             }
> +             
> +             // Must be consistent with equals()
> +             public int compareTo (Request r)
> +             {
> +                     if (time < r.time) return -1;
> +                     if (time > r.time) return 1;
> +                     // Arbitrarily break ties using rate, then key
> +                     if (rate < r.rate) return -1;
> +                     if (rate > r.rate) return 1;
> +                     if (key < r.key) return -1;
> +                     if (key > r.key) return 1;
> +                     return 0;
> +             }
> +     }
> +}
> 
> Added: trunk/apps/freenet-caching/UTest.java
> ===================================================================
> --- trunk/apps/freenet-caching/UTest.java                             (rev 0)
> +++ trunk/apps/freenet-caching/UTest.java     2008-05-23 21:58:34 UTC (rev 
20085)
> @@ -0,0 +1,184 @@
> +// This software has been placed in the public domain by its author
> +
> +import java.io.*;
> +import java.util.ArrayList;
> +import java.util.Collections;
> +
> +class UTest
> +{
> +     static final double zCritical = 2.576; // P = 0.01, two-tailed
> +     // static final double zCritical = 1.96; // P = 0.05, two-tailed
> +
> +     
> +     static final int SMALLER = -1, INCONCLUSIVE = 0, LARGER = 1;
> +     
> +     static void die (String message)
> +     {
> +             System.err.println (message);
> +             System.exit (1);
> +     }
> +     
> +     static ArrayList <Double> readFile (String filename)
> +     {
> +             ArrayList <Double> arr = new ArrayList <Double> ();
> +             try {
> +                     BufferedReader in;
> +                     in = new BufferedReader (new FileReader (filename));
> +                     while (true) {
> +                             String s = in.readLine();
> +                             if (s == null) break;
> +                             arr.add (new Double (s));
> +                     }
> +                     in.close();
> +             }
> +             catch (FileNotFoundException fnf) {
> +                     die (filename + " not found");
> +             }
> +             catch (IOException io) {
> +                     die ("Error reading from " + filename);
> +             }
> +             catch (NumberFormatException nf) {
> +                     die ("Invalid data in " + filename);
> +             }
> +             return arr;
> +     }
> +     
> +     // The method used here is explained at
> +     // http://faculty.vassar.edu/lowry/ch11a.html
> +     static int test (String f1, String f2)
> +     {
> +             ArrayList <Double> a = readFile (f1);
> +             ArrayList <Double> b = readFile (f2);
> +             int nA = a.size(), nB = b.size();               
> +             if (nA < 5 || nB < 5) {
> +                     System.err.println ("Too few samples for U test\n");
> +                     return INCONCLUSIVE;
> +             }
> +             
> +             /*
> +             // Calculate the total rank of each set (old method, O(n^2))
> +             ArrayList <Double> merged = new ArrayList <Double> (nA + nB);
> +             merged.addAll (a);
> +             merged.addAll (b);
> +             Collections.sort (merged);
> +             double tA = 0.0, tB = 0.0;                              
> +             for (double x : a) {
> +                     int lessThan = 0, equalTo = 0;
> +                     for (double y : merged) {
> +                             if (y < x) lessThan++;
> +                             else if (y == x) equalTo++;
> +                             else break;
> +                     }
> +                     tA += lessThan + (equalTo + 1) / 2.0;
> +             }
> +             for (double x : b) {
> +                     int lessThan = 0, equalTo = 0;
> +                     for (double y : merged) {
> +                             if (y < x) lessThan++;
> +                             else if (y == x) equalTo++;
> +                             else break;
> +                     }
> +                     tB += lessThan + (equalTo + 1) / 2.0;
> +             }
> +             System.out.println ("Old method: " + tA + " " + tB);
> +             */
> +             
> +             // Calculate the total rank of each set (new method, O(n log n))
> +             double tA = 0.0, tB = 0.0;
> +             Collections.sort (a);
> +             Collections.sort (b);
> +             int iA = 0, iB = 0, lessThan = 0;
> +             while (iA < nA || iB < nB) {
> +                     int ties = 0, tieRanks = 0;
> +                     double currA, currB;
> +                     if (iA < nA) currA = a.get (iA);
> +                     else currA = Double.POSITIVE_INFINITY;
> +                     if (iB < nB) currB = b.get (iB);
> +                     else currB = Double.POSITIVE_INFINITY;
> +                     // If there are no more bs or next a is less than next b
> +                     if (currA < currB) {
> +                             // Count ties in a
> +                             while (iA + ties < nA &&
> +                                     a.get (iA + ties) == currA) {
> +                                     ties++;
> +                                     tieRanks += ties;
> +                             }
> +                             // Increase a's total rank and update the index
> +                             tA += tieRanks + ties * lessThan;
> +                             iA += ties;
> +                     }
> +                     // Same the other way round
> +                     else if (currA > currB) {
> +                             // Count ties in b
> +                             while (iB + ties < nB &&
> +                                     b.get (iB + ties) == currB) {
> +                                     ties++;
> +                                     tieRanks += ties;
> +                             }
> +                             // Increase b's total rank and update the index
> +                             tB += tieRanks + ties * lessThan;
> +                             iB += ties;
> +                     }
> +                     // Next a equal to next b: this is the tricky one
> +                     else {
> +                             int tiesA = 0, tiesB = 0;
> +                             // Count ties in a
> +                             while (iA + tiesA < nA &&
> +                                     a.get (iA + tiesA) == currA) {
> +                                     tiesA++;
> +                                     ties++;
> +                                     tieRanks += ties;
> +                             }
> +                             // Count ties in b
> +                             while (iB + tiesB < nB &&
> +                                     b.get (iB + tiesB) == currB) {
> +                                     tiesB++;
> +                                     ties++;
> +                                     tieRanks += ties;
> +                             }
> +                             double mean = tieRanks / (double) ties;
> +                             // Increase a's total rank and update the index
> +                             tA += tiesA * (mean + lessThan);
> +                             iA += tiesA;
> +                             // Increase b's total rank and update the index
> +                             tB += tiesB * (mean + lessThan);
> +                             iB += tiesB;
> +                     }
> +                     lessThan += ties;
> +             }
> +             
> +             // The standard deviation of both total ranks is the same
> +             double sigma = Math.sqrt (nA * nB * (nA + nB + 1.0) / 12.0);
> +             // Means of the distributions of the total ranks
> +             double muA = nA * (nA + nB + 1.0) / 2.0;
> +             double muB = nB * (nA + nB + 1.0) / 2.0;
> +             // Z scores
> +             double zA, zB;
> +             if (tA > muA) zA = (tA - muA - 0.5) / sigma;
> +             else zA = (tA - muA + 0.5) / sigma;
> +             if (tB > muB) zB = (tB - muB - 0.5) / sigma;
> +             else zB = (tB - muB + 0.5) / sigma;
> +             
> +             if (zA > zCritical) return LARGER;
> +             else if (zB > zCritical) return SMALLER;
> +             else return INCONCLUSIVE;
> +     }
> +     
> +     public static void main (String[] args)
> +     {
> +             if (args.length != 2) die ("usage: UTest <file1> <file2>");
> +             switch (test (args[0], args[1])) {
> +                     case SMALLER:
> +                     System.out.println (args[0] + " is smaller");
> +                     break;
> +                     
> +                     case INCONCLUSIVE:
> +                     System.out.println ("No significant difference");
> +                     break;
> +                     
> +                     case LARGER:
> +                     System.out.println (args[0] + " is larger");
> +                     break;
> +             }
> +     }
> +}
> 
> Added: trunk/apps/freenet-caching/UniformRequestGenerator.java
> ===================================================================
> --- trunk/apps/freenet-caching/UniformRequestGenerator.java                   
>         
(rev 0)
> +++ trunk/apps/freenet-caching/UniformRequestGenerator.java   2008-05-23 
21:58:34 UTC (rev 20085)
> @@ -0,0 +1,9 @@
> +// This software has been placed in the public domain by its author
> +
> +class UniformRequestGenerator extends RequestGenerator
> +{
> +     double requestRate()
> +     {
> +             return 1.0; // All keys are requested at the same rate
> +     }
> +}
> 
> Added: trunk/apps/freenet-caching/ZipfRequestGenerator.java
> ===================================================================
> --- trunk/apps/freenet-caching/ZipfRequestGenerator.java                      
>         
(rev 0)
> +++ trunk/apps/freenet-caching/ZipfRequestGenerator.java      2008-05-23 
> 21:58:34 
UTC (rev 20085)
> @@ -0,0 +1,9 @@
> +// This software has been placed in the public domain by its author
> +
> +class ZipfRequestGenerator extends RequestGenerator
> +{
> +     double requestRate()
> +     {
> +             return 1.0 / Math.random(); // Zipf distribution
> +     }
> +}
> 
> _______________________________________________
> cvs mailing list
> cvs at freenetproject.org
> http://emu.freenetproject.org/cgi-bin/mailman/listinfo/cvs
> 
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
URL: 
<https://emu.freenetproject.org/pipermail/devl/attachments/20080805/3873f821/attachment.pgp>

Reply via email to