/* * DCS 520 - Matthew Stone - Spring 2001 * HW 3 * Decisions and policies. * Out 4/09/2001 * Due 4/26/2001 * * Skeleton code for reinforcement-learning "network router" assignment. * Based on Justin Boyan and Michael Littman's simulator * from NIPS 94, "Packet routing in dynamically changing networks: * A reinforcement learning approach" * * The idea for this program is to apply Markov decision procedures to * network routing, as follows. * * Each packet is an agent. At any point, the state of the packet is * defined by the node in the network that the packet is currently at, and * the node in the network that the packet is destined for. At each state, * we have a choice of action: which link to follow from the current node * to the next location along the path. We see the router at each node as * making this choice in order to minimize the overall cost of dealing with * packets. * * The immediate cost of this action is the time that it takes to travel to * the next node, plus the length of time the packet spends waiting in the * queue at the next node. The overall cost of the action is the time that * it takes for the packet to reach its destination. * * The idea of temporal difference learning, which we use here, is to * maintain an estimate for how long we expect a packet to take from any * node to any other node via each possible link. We update this estimate * each time we send a packet. In the simplest case (QL), the next node * downstream relays back to you its estimate of how long the packet will * take, starting from you. You factor this in. In a more complicated * case (VL), you query all neighbor nodes how long they expect this packet * to take; then you update all your estimates, decide where to send the * packet, and update in response. * * Your homework is to fill in the skeleton code here. The functions you * have to write are mapped out in SECTION 1 below. * * The organization of the file is as follows. * SECTION 0. General definitions you'll need for your stuff. * SECTION 1. Placeholders for you. * SECTION 2. Definitions that the simulator needs. * SECTION 3. Basic network and simulator operations * SECTION 4. Initialization stuff (including main) * SECTION 5. Advanced simulator goop * SECTION 6. Outputting simulation results * SECTION 7. Random number procedure definitions */ /* ********************************************************* * SECTION 0. General header information ********************************************************* */ /* Includes. */ #include #include #include #include /* * The following functions for making stochastic decisions * are useful for carrying out balanced policies: */ /* make a random number between 0 and maxnum */ int create(int maxnum); /* returns true with probability 1/n */ int one_in(int n); /* returns true with probability p */ int with_prob(double p); /* * Given a node and dest, choose a link. * To allow the code allows other methods for doing this * besides learning, this calls the do_policy function * you define, as well as a number of other alternatives. */ int choose_link(int n, int dest); /* * Send packet indexed by E from node N over LINK. * Return value is reply of how long next node * expects delivery to take. */ double send_with_t_relay(int e, int n, int link); /* * Query what will happen to E if sent from N over LINK. * Return value is reply of how long next node * expects delivery to take. */ double get_t_relay(int e, int n, int link); /* * The network is stored in a global predefined array. * There can be at most MAXNODES nodes, * and at most MAXLINKS connections out * of any nodes, for a total of MAXEDGES * connections in the network. */ #define MAXNODES 128 #define MAXLINKS 9 #define MAXEDGES (MAXNODES*MAXLINKS) int nnodes; /* How many nodes? */ int links[MAXNODES][MAXLINKS]; /* Who connected to? */ int nlinks[MAXNODES]; /* How many connected to? */ /* * You can use this to keep track of whether you're * using basic Q-learning, in which you only get feedback * about actions you perform; or whether you're using * the fancier V-learning, in which you are allowed to * query all your neighbors for a time estimate before * deciding where to send this packet. */ #define QL 5 #define VL 6 int learntype = VL; /* What learning type (V/Q)? */ /* * The learner has three other parameters: * epsilon: an "exploration" factor: * how much worse than best can an option be, * and still you're willing to risk it, to * get better information about the space. * eta: a fraction, indicating how much you * weight new information against the * old information you already have. * vprob: probability of getting the relay * packet from your neighbor in V-learning. */ double epsilon = 0.0; /* Tolerance in Q for link choice. */ double eta = 0.7; /* How fast to learn? */ double vprob = 1.0; /* prob of random echo packet. */ /* ********************************************************* * SECTION 1. You Fill This In ********************************************************* */ /* * Declare a variable to store your value estimates. */ /* * Define this function, which will be called to initialize * your value estimates. */ void setup_value_estimates(void); /* * Define this function, which will be called whenever * you have a new estimate of the time a packet is * taking to be delivered. * * Given that your packet started at node N, traveled * out connection LINK, headed ultimately to DEST, * incorporate the new estimate NEW_EST of time * to arrival. * * Remember to weight by eta. */ void do_learn(int n, int link, int dest, double new_est); /* * Define this function, which should return the * best guess of how long a packet will take to * travel from node N_FROM to node N_TO, assuming * we follow the optimal policy given our current * value estimates. */ double current_estimate(int n_from, int n_to); /* * Define this function, which should return * the best guess of how long it will take a * packet to travel from node N_FROM to node * N_TO, assuming that it takes TIME_TO_NEXT * to travel its first hop, that it arrives * at node N_NEXT at the end of that hop, and * that we then follow the optimal policy given * our current estimates. */ double improved_estimate(double time_to_next, int n_next, int n_dest); /* * Define this function, to decide the appropriate * routing for a packet now at node N, ultimately * heading for node DEST, based on our current * value estimates. Return the index of the * outgoing LINK connection from N that the packet * will follow. * * Remember to choose randomly among alternatives * that seem equally good (in being within epsilon * of optimal). */ int do_policy(int n, int dest); /* * Define this function, which lets NODE process one packet E along towards * destination DEST. First, * for VL learning, ask for a better estimate for every possible link, and * use them. Remember to use vprob to simulate the fact that you may not * always get an answer. * * Then pick the outgoing link to use; send the packet and get an estimate * of the arrival time relayed back. Remember to use choose_link so that * your program works with all applicable strategies, not just learning. * * Finally, update your estimate of how long it takes to send packets from * here to this destination along this path using the new estimate. */ int bump_packet(int e, int n, int dest); /* ********************************************************* * SECTION 2. Definitions for simulator ********************************************************* */ /* Defines. */ #define QUEUELEVELS 60 #define MAXEVENTS 5000 /* Strategies. */ #define RANDOM 0 /* Pick a link, any link. */ #define LEARN 1 /* Best according to learning. */ #define BEST 2 /* Any (random) shortest path. */ #define BESTATIC 3 /* One (fixed) shortest path. */ /* For tcl stuff. */ int link_activity[MAXNODES][MAXNODES]; int down[MAXNODES][MAXNODES]; int edge_from[MAXEDGES]; int edge_to[MAXEDGES]; int nedges; double xnode[MAXNODES],ynode[MAXNODES]; /* Coordinates of nodes. */ /* Event structure. */ struct { int dest; /* Where headed? */ int source; /* Where created or special event type. */ int node; /* Where now? */ double birth; /* When originally created? */ int hops; /* Count number of requeues. */ int left; /* Left heap or free list. */ int right; /* Right heap. */ double qtime; /* When inserted. */ double etime; /* When removed. */ } events[MAXEVENTS]; int queuetop; int freelist; double enqueued[MAXNODES]; /* Time of last in line. */ int nenqueued[MAXNODES]; /* How many in queue? */ /* Special events. */ #define INJECT -1 #define REPORT -2 #define END_SIM -3 #define UNKNOWN -4 /* Define. */ #define Nil -1 /* Represent shortest path. */ int shortest[MAXNODES][MAXNODES]; /* Returns link. */ int distance[MAXNODES][MAXNODES]; /* Returns steps. */ int interqueuen[MAXNODES]; /* Initialized to interqueue. */ /* User variables. */ int strat = LEARN; /* How choose actions? */ char *graphname = "lata_network"; /* Where get the network? */ int maxpackets = 1000; /* Packet limit in network. */ int quit_criteria = 3; /* How many successful reports */ /* before quitting? */ int queuelimit = 1000; /* Max elements per queue. */ int queuelevels = 10; /* Queue levels to perceptron. */ double callmean = 1.0; /* When next call? */ double callstd = 0.2; char *polfile = ""; /* Place to store the policy. */ double interreport = 100.0; /* Time between reports. */ double interqueue = 1.0; /* Time between handlings. (init) */ double internode = 1.0; /* Time in transit. */ /* Report variables. */ int routed_packets = 0; int total_routing_time = 0; int active_packets = 0; int converge = 0; double best_transit = 10000.0; int queue_full = 0; int necho_packets = 0; int total_hops = 0; int send_fail = 0; /* Functions. */ /* Macros. */ #define DOUBLE(x) ((double)(x)) #define Max(x,y) (((x)<(y))?(y):(x)) #define Min(x,y) (((x)<(y))?(x):(y)) extern long int lrand48(); extern double erand48(); extern char *malloc(); short random_seed48[3]; void init_randomizer(); double fran(), bran(); struct timeval barf_o_ghetti; void get_globals(); /* Processes cmd line args. */ void init_graph(); /* Setup graph parameters. */ void do_sim_setup(); /* Prepare to run the simulation. */ void do_sim(); /* Run the simulation for awhile. */ int start_packet(); /* Starts another packet going. */ int push_event(); /* Add an event to the queue. */ int pop_event(); /* Remove an entry. Return packet. */ int create_event(); /* Returns a new event struct. */ void free_event(); /* Add event to free list. */ void compute_best(); /* All shortest paths. */ void interactive_report(); /* Get some stats. Get set again. */ double poisson(); /* Get next arrival time. */ void dump_dest(); /* Policy for a given dest. */ /* ********************************************************* * SECTION 3. Basic Network and Simulator Operations ********************************************************* */ /* Sends node n's top of queue along given link. If the destination */ /* node has a full queue, we send the node's packet to itself. */ /* Returns an estimate of the time to destination. Packet must get */ /* consumed if sent to its destination. Returns a success or failure */ /* signal as well. */ double send_with_t_relay(e, n, link) int e, n, link; { double curtime = events[e].etime; /* What time is it? */ /* How long waiting? */ double time_in_queue = curtime-events[e].qtime-internode; double nexttime; /* Next arrival time. */ int n_to; double new_est; /* Figure out neighbor. */ if ((link < 0) || (link >= nlinks[n])) /* No such link! */ n_to = n; else n_to = links[n][link]; /* Neighbor node. */ if (down[n][n_to]) n_to = n; /* Link is down! */ /* Record activity. */ link_activity[n][n_to]++; if (n_to == events[e].dest) { /* Destination matches neighbor! */ routed_packets++; nenqueued[n]--; total_routing_time += curtime-events[e].birth+internode; total_hops += events[e].hops + 1; new_est = improved_estimate(time_in_queue+internode, n_to, events[e].dest); /* It has waited and is now done. */ active_packets--; free_event(e); return(new_est); } /* Is sending node full? If so, change destination to be the node */ /* itself. */ if (nenqueued[n_to] >= queuelimit) { send_fail++; n_to = n; } /* Do the send! */ events[e].node = n_to; events[e].hops++; /* nexttime = Max(enqueued[n_to], curtime+internode) + interqueue; */ /* Is the queue empty (and recouped) when the packet arrives? */ /* Which comes last? Queue is ready or packet arrives? */ nexttime = Max(enqueued[n_to]+interqueuen[n_to], curtime+internode); events[e].etime = nexttime; enqueued[n_to] = nexttime; push_event(e, curtime); nenqueued[n_to]++; /* Keep counts. */ nenqueued[n]--; /* Ok, the packet has moved one link. */ /* It took the wait + the send + however long it will take from there. */ new_est = improved_estimate(time_in_queue+internode, n_to, events[e].dest); necho_packets++; return(new_est); } /* Pretends to send node n's top of queue along given link. */ /* Returns an estimate of the time to destination. Returns a success or failure */ /* signal as well. */ double get_t_relay(e, n, link) int e, n, link; { double curtime = events[e].etime; /* What time is it? */ /* How long waiting? */ double time_in_queue = curtime-events[e].qtime-internode; int n_to; double new_est; /* Figure out neighbor. */ if ((link < 0) || (link >= nlinks[n])) /* No such link! */ n_to = n; else n_to = links[n][link]; /* Neighbor node. */ if (down[n][n_to]) n_to = n; /* Link is down! */ if (n_to == events[e].dest) { /* Destination matches neighbor! */ /* It has waited and is now done. */ new_est = improved_estimate(time_in_queue+internode, n_to, events[e].dest); return(new_est); } /* Is sending node full? If so, change destination to be the node */ /* itself. */ if (nenqueued[n_to] >= queuelimit) { n_to = n; } /* Ok, the packet pretended to move one link. */ /* It took the wait + the send + however long it will take from there. */ new_est = improved_estimate(time_in_queue+internode, n_to, events[e].dest); necho_packets++; return(new_est); } /* Compute an outgoing link on which to send n's topmost packet in */ /* order to route it to dest. */ int choose_link(n, dest) int n, dest; { switch (strat) { case LEARN: /* True learning. */ return(do_policy(n,dest)); case RANDOM: /* Just go. */ return(create(nlinks[n])); case BEST: /* Always take one of the shortest paths (at random). */ { int best = distance[n][dest]; int i, best_count = 0, best_link; for (i = 0; i < nlinks[n]; i++) { if (1+distance[links[n][i]][dest] == best) { best_count++; if (one_in(best_count)) best_link = i; } } return(best_link); } case BESTATIC: return(shortest[n][dest]); default: fprintf(stderr, "Unknown action strategy: %d\n", strat); } } /* ********************************************************* * SECTION 4. Initialization ********************************************************* */ /* Main. */ void main(argc, argv) int argc; char *argv[]; { get_globals(argc, argv); init_randomizer(); /* Set it up. */ init_graph(graphname); /* Setup graph parameters. */ if ((strat == BEST)||(strat == BESTATIC)) compute_best(); /* Initialize shortest paths. */ setup_value_estimates(); /* Setup learning parameters. */ do_sim_setup(); do_sim(); } /* Given an argc and an argv, process the arguments and set global */ /* variables. Some of these only make sense at startup. Others */ /* don't make sense at start up. */ void get_globals(argc, argv) int argc; char *argv[]; { /* Command line params. */ while (argc > 1) { /* Two argument switches. */ if (argc == 2) { fprintf(stderr, "\"%s\" argument needs a value.\n", argv[1]); exit(-1); } if (!strcmp(argv[1], "strat")) { if (!strcmp(argv[2], "RANDOM")) strat = RANDOM; else if (!strcmp(argv[2], "LEARN")) strat = LEARN; else if (!strcmp(argv[2], "BEST")) strat = BEST; else if (!strcmp(argv[2], "BESTATIC")) strat = BESTATIC; else { fprintf(stderr, "strat \"%s\" not recognized.\n", argv[2]); exit(-1); } argc -= 2; argv += 2; } else if (!strcmp(argv[1], "learntype")) { if (!strcmp(argv[2], "QL")) learntype = QL; else if (!strcmp(argv[2], "VL")) learntype = VL; else { fprintf(stderr, "learntype \"%s\" not recognized.\n", argv[2]); exit(-1); } argc -= 2; argv += 2; } else if (!strcmp(argv[1], "interreport")) { interreport = DOUBLE(atof(argv[2])); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "quit_criteria")) { quit_criteria = atoi(argv[2]); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "queuelimit")) { queuelimit = atoi(argv[2]); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "maxpackets")) { maxpackets = atoi(argv[2]); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "graphname")) { graphname = argv[2]; argc -= 2; argv += 2; } else if (!strcmp(argv[1], "eta")) { eta = DOUBLE(atof(argv[2])); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "vprob")) { vprob = DOUBLE(atof(argv[2])); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "internode")) { internode = DOUBLE(atof(argv[2])); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "callmean")) { callmean = DOUBLE(atof(argv[2])); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "callstd")) { callstd = DOUBLE(atof(argv[2])); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "epsilon")) { epsilon = DOUBLE(atof(argv[2])); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "queuelevels")) { queuelevels = atoi(argv[2]); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "interqueue")) { interqueue = DOUBLE(atof(argv[2])); argc -= 2; argv += 2; } else if (!strcmp(argv[1], "polfile")) { polfile = argv[2]; argc -= 2; argv += 2; } else if (!strcmp(argv[1], "up")) { /* no sense at startup. */ int edge, n1, n2; edge = atoi(argv[2]); n1 = edge_from[edge]; n2 = edge_to[edge]; down[n1][n2] = 0; down[n2][n1] = 0; argc -= 2; argv += 2; } else if (!strcmp(argv[1], "down")) { /* no sense at startup. */ int edge, n1, n2; edge = atoi(argv[2]); n1 = edge_from[edge]; n2 = edge_to[edge]; down[n1][n2] = 1; down[n2][n1] = 1; argc -= 2; argv += 2; } else { fprintf(stderr, "Switch not recognized: %s\n", argv[1]); exit(-1); } } } /* Setup graph parameters. Abandon netpad. */ void init_graph(filename) char *filename; { char line[300]; /* Line from the file. */ FILE *graph; int link, node, node1, node2; int dummy; double x, y; graph = fopen(filename, "r"); /* Try to open it. */ if (graph == NULL) { fprintf(stderr, "Couldn't open %s\n", filename); exit(-1); } nnodes = 0; nedges = 0; while (fscanf(graph, "%[^\n]\n", line) != EOF) { if(!strncmp(line, "1000 ", 5)) { /* Node declaration. */ sscanf(line, "1000 %d %f %f %d", &node, &x, &y, &dummy); if (node != nnodes) { fprintf(stderr, "bad graph format: got node %d after %d, expected %d.\n", node, nnodes-1, nnodes); exit(-1); } /* Check for overflow. */ if (nnodes == MAXNODES) { fprintf(stderr, "MAXNODES too small for this graph.\n"); exit(-1); } /* Init new node. */ xnode[nnodes] = x; ynode[nnodes] = y; nlinks[nnodes] = 0; nnodes++; } else if (!strncmp(line, "2000 ", 5)) { /* Link declaration. */ sscanf(line, "2000 %d %d %d ", &node1, &node2, &dummy); /* Check if broken. */ if ((node1 >= nnodes) || (node2 >= nnodes) || (node1 < 0) || (node2 < 0)) { fprintf(stderr, "Link bad nodes (too big or too small):\n%s\n.", line); fprintf(stderr, "parsed: %d, %d\n", node1, node2); exit(-1); } /* Check for link overflow. */ if (nlinks[node1] == MAXLINKS) { fprintf(stderr, "MAXLINKS too small for node: %d\n", node1); exit(-1); } links[node1][nlinks[node1]] = node2; nlinks[node1]++; /* Add bidirectional link. */ if (nlinks[node2] == MAXLINKS) { fprintf(stderr, "MAXLINKS too small for node: %d\n", node2); exit(-1); } links[node2][nlinks[node2]] = node1; nlinks[node2]++; /* Add the edge. Should check for nedges overflow. */ edge_from[nedges] = node1; edge_to[nedges] = node2; nedges++; } /* Skip any other type of line. */ } /* Done reading. What did we get? */ } /* Compute all shortest paths. */ void compute_best() { int changing = 1; int i, j, k; /* Initialize distances. */ for (i = 0; i < nnodes; i++) for (j = 0; j < nnodes; j++) { if (i == j) distance[i][j] = 0; else distance[i][j] = nnodes+1; shortest[i][j] = -1; } /* Keep updating until no more changes. */ while (changing) { changing = 0; for (i = 0; i < nnodes; i++) { for (j = 0; j < nnodes; j++) { /* Update our estimate of distance for sending from i to j. */ if (i != j) { /* Not self. */ for (k = 0; k < nlinks[i]; k++) { if (distance[i][j] > 1+distance[links[i][k]][j]) { distance[i][j] = 1+distance[links[i][k]][j]; /* Better. */ shortest[i][j] = k; changing = 1; } } /* Next link. */ } } /* Next "to" */ } /* Next "from" */ } /* Next iteration. */ } /* Before calling do_sim. */ void do_sim_setup() { int e; /* Event to handle. */ int i, j; /* Set up the "hardware". */ for (i = 0; i < nnodes; i++) interqueuen[i] = interqueue; /* Links are "up" to start. */ for (i = 0; i < nnodes; i++) for (j = 0; j < nnodes; j++) down[i][j] = 0; /* Initialize some structures. */ freelist = Nil; /* Create free list. */ for (e = 0; e < MAXEVENTS; e++) free_event(e); /* Put in initial injection and report events. */ queuetop = Nil; e = create_event(0.0, 0); events[e].source = REPORT; events[e].etime = 0.0; /* Do this right away. */ push_event(e, 0.0); e = create_event(0.0, 0); events[e].source = INJECT; events[e].etime = poisson(callmean); push_event(e, 0.0); /* Empty all node queues. */ for (i = 0; i < nnodes; i++) { enqueued[i] = 0.0; nenqueued[i] = 0; } } void init_randomizer() { int i; struct timeval *tp = &barf_o_ghetti; struct timezone *tzp = 0; gettimeofday(tp, tzp); random_seed48[0] = (tp->tv_usec) & 0177777; random_seed48[1] = getpid(); random_seed48[2] = getppid(); /* exercise out any startup transients */ for (i=0;i<87;++i) create(10); } /* ********************************************************* * SECTION 5. SIMULATOR FUNCTIONS ********************************************************* */ void do_sim(timelimit) double timelimit; { int e; /* Event to handle. */ double curtime; int i; while(1) { /* Until time runs out... */ /* Epoch. */ e = pop_event(); curtime = events[e].etime; if (events[e].source == INJECT) { events[e].etime += poisson(callmean); push_event(e, curtime); e = start_packet(curtime); /* Nil on failure. */ } if ((e != Nil) && (events[e].source == REPORT)) { /* Recursively descend heap? */ interactive_report(curtime, e); } else if (e != Nil) bump_packet(e, events[e].node, events[e].dest); } } /* Starts another packet going. */ int start_packet(time) double time; { int n_from, n_to; /* From and to are uniform over nodes. */ int e; if (active_packets >= maxpackets) return(Nil); n_to = create(nnodes); /* Find a node (different from starting loc). */ for (n_from = create(nnodes); n_from==n_to; n_from = create(nnodes)); /* Reject packet, leaving one space for the system (if full). */ if (nenqueued[n_from] >= queuelimit-1) { queue_full++; return(Nil); } else nenqueued[n_from]++; /* Plop it onto the queue. */ active_packets++; e = create_event(time, n_to); /* Make the new packet. */ if (e == Nil) { fprintf(stderr, "Too many events, failing...\n"); exit(-1); } /* Now that we know where this packet began, set source. */ events[e].source = events[e].node = n_from; return(e); } /* Returns a new event number (off the free list). */ int create_event(time, dest) double time; int dest; { int e; if (freelist == Nil) { fprintf(stderr, "Can't create new event, free list empty\n"); return(-1); } /* Pop it off the free list. */ e = freelist; freelist = events[e].left; /* Initialize new event. */ events[e].dest = dest; events[e].source = UNKNOWN; events[e].node = UNKNOWN; events[e].birth = time; events[e].hops = 0; events[e].left = Nil; events[e].right = Nil; events[e].etime = time; events[e].qtime = time; return(e); } /* Returns 0 on failure. */ int push_event(e, time) int e; double time; { events[e].qtime = time; queuetop = meldheap(queuetop, e); return(1); } /* Remove an entry from the priority queue. Return the */ /* event which was removed. Returns Nil if event queue empty. */ int pop_event() { int e; e = queuetop; if (e == Nil) return(e); queuetop = meldheap(events[e].left, events[e].right); events[e].left = Nil; events[e].right = Nil; return(e); } /* Given a used event structure, return it to the free list. */ void free_event(e) int e; { events[e].left = freelist; freelist = e; } /* Makes a new heap out of two heaps. */ int meldheap(h1, h2) int h1, h2; { int h3; if (h1 == Nil) return (h2); if (h2 == Nil) return (h1); /* So h1 is the smaller one. */ if (events[h1].etime > events[h2].etime) { /* Swap. */ h3 = h1; h1 = h2; h2 = h3; } /* So branches are equally likely. */ if (one_in(2)) { h3 = events[h1].left; events[h1].left = events[h1].right; events[h1].right = h3; } events[h1].right = meldheap(events[h1].right, h2); return(h1); } /* ********************************************************* * SECTION 6. OUTPUT ********************************************************* */ /* Report stuff. */ int nodes_alive; double oldest_node; double total_age; #define MEAN(x) (DOUBLE(x)/DOUBLE(nodes_alive)) void interactive_report(time, e) double time; int e; { int argc; char *argv[100]; char commands[1000], word[1000]; double next_report = time+1000.0; int i, badcmd; int n1, n2, edge; char *ptr; if (time > 0.0) { printf("{%f %f %f} ", time, DOUBLE(total_routing_time)/DOUBLE(routed_packets), DOUBLE(total_hops)/DOUBLE(routed_packets)); printf("{"); for (i = 0; i < nedges; i++) printf("%d ", link_activity[edge_from[i]][edge_to[i]] +link_activity[edge_to[i]][edge_from[i]]); printf("}\n"); fflush(stdout); } routed_packets = 0; /* Reset. */ total_routing_time = 0; total_hops = 0; for (i = 0; i < nedges; i++) link_activity[edge_from[i]][edge_to[i]] = link_activity[edge_to[i]][edge_from[i]] = 0; badcmd = 1; /* loop until a continuation command appears. */ while (badcmd) { gets(commands); /* Parse into argc, argv (trailing blank counts as an argument). */ argc = 1; /* argv[0] doesn't count. */ ptr = commands; while (*ptr) { while (*ptr && *ptr == ' ') ptr++; /* Skip leading spaces. */ argv[argc] = ptr; /* That's the start. */ while (*ptr && *ptr != ' ') ptr++; /* Skip non-spaces. */ if (*ptr) *(ptr++) = '\0'; /* Mark the end. */ argc++; } /* Catch policy request. (ignores other commands on line). */ if ((argc >= 3) && (!strcmp("pol", argv[1]))) { dump_dest(atoi(argv[2])); } else badcmd = 0; } get_globals(argc, argv); events[e].etime += interreport; push_event(e, time); } void dump_dest(node) int node; { int j, l; printf("{"); for (j = 0; j < nnodes; j++) { l = choose_link(j, node); printf("%d ", links[j][l]); } printf("}\n"); fflush(stdout); } /* ********************************************************* * SECTION 7. RANDOM NUMBERS... ********************************************************* */ int create(maxnum) int maxnum; { return nrand48(random_seed48)%Max(1,maxnum); } int one_in(n) int n; { if (create(n)==0) return 1; else return 0; } double fran() { return erand48(random_seed48); } double bran(lo, hi) double lo, hi; { return (hi-lo) * fran() + lo; } int with_prob(p) double p; { if (fran()<=p) return 1; else return 0; } double poisson(arr) double arr; { return(-log(bran(0.0,1.0))/arr); }