[1973] | 1 | (* a few constants *)
|
---|
| 2 | let population_size = 20
|
---|
[1980] | 3 | and max_stagnant_iterations = 10000
|
---|
[1973] | 4 | and mutation_rate = 0.05;;
|
---|
| 5 |
|
---|
[1979] | 6 | (* the type definitions. note that Caml has trouble with mutually recursive
|
---|
| 7 | data structures. you can define them, you just can't ever instantiate them.
|
---|
| 8 | this is why the fields in wi are all loose references by way of strings *)
|
---|
[1973] | 9 | type wi = {
|
---|
[1979] | 10 | wi_name: string;
|
---|
| 11 | wi_nodename: string;
|
---|
| 12 | wi_essid: string;
|
---|
[1973] | 13 | };;
|
---|
[1979] | 14 | type group = {
|
---|
| 15 | group_essid: string;
|
---|
| 16 | mutable group_wis: wi list;
|
---|
| 17 | };;
|
---|
| 18 | type node = {
|
---|
| 19 | node_name: string;
|
---|
| 20 | node_wis: wi list;
|
---|
| 21 | };;
|
---|
[1973] | 22 |
|
---|
| 23 | let nodes = Hashtbl.create 4;;
|
---|
| 24 | let groups = Hashtbl.create 4;;
|
---|
| 25 |
|
---|
[1979] | 26 | (* some convenience functions *)
|
---|
[1973] | 27 | let compose f g = fun x -> f(g(x));;
|
---|
| 28 | let ($) = compose;;
|
---|
| 29 | let maketuple a b = (a, b);;
|
---|
[1979] | 30 | let head = List.hd;;
|
---|
| 31 | let tail = List.tl;;
|
---|
[1973] | 32 | (* given a hashtable, return all the keys as a list *)
|
---|
| 33 | let keys t = Hashtbl.fold (fun k d a -> k::a) t [];;
|
---|
| 34 | (* given a hashtable, return all the values as a list *)
|
---|
| 35 | let values t = Hashtbl.fold (fun k d a -> d::a) t [];;
|
---|
[1980] | 36 | let copyarray src dest = Array.blit src 0 dest 0 (Array.length src);;
|
---|
[1973] | 37 |
|
---|
| 38 | (* given a list, return a list of pairs with all possible combinations of
|
---|
| 39 | items from the given list *)
|
---|
| 40 | let rec combinations l =
|
---|
| 41 | match l with
|
---|
| 42 | [] -> []
|
---|
[1979] | 43 | | x::xs -> (List.map (maketuple x) xs)@(combinations xs);;
|
---|
[1973] | 44 |
|
---|
| 45 | (* given a configuration and two wi's, return the score *)
|
---|
| 46 | let wi_score c wi1 wi2 =
|
---|
[1979] | 47 | let scoretable = [ ((<=) 2, 1);
|
---|
[1973] | 48 | ((==) 2, -1);
|
---|
| 49 | ((==) 1, -5);
|
---|
| 50 | ((==) 0, -10) ] in
|
---|
[1979] | 51 | let channel1 = c wi1.wi_essid in
|
---|
| 52 | let channel2 = c wi2.wi_essid in
|
---|
[1973] | 53 | let diff = abs (channel1 - channel2) in
|
---|
| 54 | let rec runtable t = match t with
|
---|
| 55 | [] -> assert false
|
---|
| 56 | | (cond, s)::xs -> if (cond diff) then s
|
---|
| 57 | else runtable xs in
|
---|
| 58 | runtable scoretable;;
|
---|
| 59 |
|
---|
[1979] | 60 | (* given a configuration and a node, return the score. this is simply the sum of
|
---|
| 61 | the scores of all the combinations of interfaces, written down as a fold for
|
---|
| 62 | efficiency *)
|
---|
[1973] | 63 | let node_score c n =
|
---|
| 64 | let foldfunc acc (wi1, wi2) = acc + (wi_score c wi1 wi2) in
|
---|
[1980] | 65 | let base_score = List.fold_left foldfunc 0 (combinations n.node_wis) in
|
---|
| 66 | base_score * (List.length n.node_wis);;
|
---|
[1973] | 67 |
|
---|
| 68 | let score_configuration c (ns: node list) =
|
---|
[1979] | 69 | let foldfunc acc n = acc + (node_score (Hashtbl.find c) n) in
|
---|
[1980] | 70 | let nodescores = List.fold_left foldfunc 0 ns in
|
---|
| 71 | nodescores;;
|
---|
[1973] | 72 |
|
---|
| 73 | (* given a filename, return a list of all the lines in the file with the given
|
---|
| 74 | filename *)
|
---|
| 75 | let snarf_lines fname =
|
---|
| 76 | let infile = open_in fname in
|
---|
| 77 | let result = ref [] in
|
---|
| 78 | try
|
---|
| 79 | while true do
|
---|
| 80 | result := (input_line infile)::!result
|
---|
| 81 | done;
|
---|
| 82 | !result (* never gets here *)
|
---|
| 83 | with End_of_file -> !result
|
---|
| 84 |
|
---|
[1979] | 85 | let parse_pair nodename (wname, gname) =
|
---|
| 86 | let new_wi = { wi_name = wname; wi_nodename = nodename; wi_essid = gname} in
|
---|
| 87 | let foo = try
|
---|
| 88 | let group = Hashtbl.find groups gname in
|
---|
| 89 | group.group_wis <- new_wi::group.group_wis;
|
---|
| 90 | with Not_found ->
|
---|
| 91 | let group = { group_essid = gname; group_wis = [ new_wi ] } in
|
---|
| 92 | Hashtbl.add groups gname group in
|
---|
| 93 | new_wi
|
---|
| 94 |
|
---|
| 95 | let parse_fields fields =
|
---|
| 96 | let nodename = head fields in
|
---|
| 97 | let rec makepairs l = match l with
|
---|
| 98 | [] -> []
|
---|
| 99 | | x::[] -> assert false
|
---|
| 100 | | a::b::xs -> (a, b)::(makepairs xs) in
|
---|
| 101 | let wis = List.map (parse_pair nodename) (makepairs (tail fields)) in
|
---|
| 102 | let sorted_wis = List.sort compare wis in
|
---|
| 103 | let node = { node_name = nodename; node_wis = sorted_wis } in
|
---|
| 104 | Hashtbl.add nodes nodename node
|
---|
| 105 |
|
---|
[1973] | 106 | let parse_file fname =
|
---|
[1979] | 107 | let spacere = Str.regexp " " in
|
---|
| 108 | List.iter (parse_fields $ (Str.split spacere)) (snarf_lines fname)
|
---|
[1973] | 109 | ;;
|
---|
| 110 |
|
---|
[1980] | 111 | (** Return a random configuration. For some reason, if this function accesses
|
---|
| 112 | the global 'groups' hash instead of getting it passed in from above, that
|
---|
| 113 | hash is empty. *)
|
---|
[1979] | 114 | let random_configuration groups =
|
---|
[1973] | 115 | let conf = Hashtbl.create 30 in
|
---|
[1980] | 116 | Hashtbl.iter (fun k _ -> Hashtbl.add conf k (1 + (Random.int 12))) groups;
|
---|
[1973] | 117 | conf
|
---|
| 118 |
|
---|
[1979] | 119 | (* Mutate the configuration in the given population at the given offset *)
|
---|
[1973] | 120 | let mutate p i =
|
---|
[1980] | 121 | Hashtbl.iter (fun essid _ -> let f = Random.float 1.0 in
|
---|
| 122 | let group = Hashtbl.find groups essid in
|
---|
| 123 | let maxchannel = if (List.length group.group_wis) == 1 then 11
|
---|
| 124 | else 13 in
|
---|
| 125 | if (f < mutation_rate) then
|
---|
| 126 | Hashtbl.replace p.(i) essid (1 + (Random.int maxchannel))) p.(i);;
|
---|
[1973] | 127 |
|
---|
[1979] | 128 | let print_conf conf =
|
---|
| 129 | let print_wi wi = wi.wi_name ^ ": " ^ (string_of_int (Hashtbl.find conf wi.wi_essid)) in
|
---|
| 130 | let wis node = List.fold_left (fun acc wi -> acc ^ " " ^ (print_wi wi)) "" node.node_wis in
|
---|
| 131 | let sorted_nodes = List.sort (fun a b -> compare (a.node_name) (b.node_name)) (values nodes) in
|
---|
| 132 | List.iter (fun n -> print_string (n.node_name ^ ":" ^ (wis n) ^ "\n")) sorted_nodes
|
---|
| 133 |
|
---|
[1980] | 134 | let parse_nodeclusters fname =
|
---|
| 135 | let spacere = Str.regexp " " in
|
---|
| 136 | (* handle a row of fields. the first field is the supernode name, the
|
---|
| 137 | rest are the subnode names. create a new node for the supernode,
|
---|
| 138 | stuff all the wi's of the subnodes under it, names prefixed with
|
---|
| 139 | their original node's names for clarity, and remove the subnodes
|
---|
| 140 | from the nodes hashtable *)
|
---|
| 141 | let do_fields f = let nodename = head f in
|
---|
| 142 | let subnodenames = tail f in
|
---|
| 143 | let subnodes = List.map (Hashtbl.find nodes) subnodenames in
|
---|
| 144 | List.iter (Hashtbl.remove nodes) subnodenames;
|
---|
| 145 | let prefixed_wis n = List.map (fun w -> { w with wi_name = n.node_name ^ "." ^ w.wi_name}) n.node_wis in
|
---|
| 146 | let wis = List.fold_left (fun a s -> a@(prefixed_wis s)) [] subnodes in
|
---|
| 147 | let node = { node_name = nodename; node_wis = wis } in
|
---|
| 148 | Hashtbl.add nodes nodename node in
|
---|
| 149 | List.iter (do_fields $ (Str.split spacere)) (snarf_lines fname);;
|
---|
| 150 |
|
---|
[1973] | 151 | let main =
|
---|
[1979] | 152 | parse_file Sys.argv.(1);
|
---|
[1980] | 153 | parse_nodeclusters "coupled.conf";
|
---|
[1973] | 154 | Random.self_init();
|
---|
[1979] | 155 | let population = Array.init population_size (fun _ -> random_configuration groups) in
|
---|
[1973] | 156 | let last_high_score = ref (-1000000) in
|
---|
| 157 | let iterations_since_new_high_score = ref 0 in
|
---|
[1979] | 158 | let generation = ref 0 in
|
---|
[1973] | 159 | let all_nodes = values nodes in
|
---|
| 160 | while !iterations_since_new_high_score < max_stagnant_iterations do
|
---|
| 161 | (* mutate the population *)
|
---|
| 162 | for i = 0 to (population_size / 2 - 1) do
|
---|
| 163 | let i2 = i + population_size / 2 in
|
---|
| 164 | population.(i2) <- Hashtbl.copy population.(i);
|
---|
| 165 | mutate population (i2)
|
---|
| 166 | done;
|
---|
| 167 | (* sort the populations according to score. to do this, make
|
---|
| 168 | a list of (score, solution) tuples *)
|
---|
| 169 | let score_pop = Array.map (fun c -> ((score_configuration c all_nodes), c)) population in
|
---|
| 170 | (* sort on the first field *)
|
---|
[1979] | 171 | Array.sort (fun x y -> compare (fst y) (fst x)) score_pop;
|
---|
[1980] | 172 | (* extract the, now sorted, configuration and put it into population *)
|
---|
| 173 | copyarray (Array.map snd score_pop) population;
|
---|
[1973] | 174 | (* now look at the best score and update the highscore if
|
---|
| 175 | necessary *)
|
---|
| 176 | let high_score = fst score_pop.(0) in
|
---|
| 177 | if high_score > !last_high_score then begin
|
---|
| 178 | last_high_score := high_score;
|
---|
| 179 | iterations_since_new_high_score := 0
|
---|
| 180 | end;
|
---|
[1979] | 181 | assert (!last_high_score >= high_score);
|
---|
| 182 | if (!generation mod 10) == 0 then begin
|
---|
| 183 | print_int !generation;
|
---|
| 184 | print_string ": ";
|
---|
| 185 | print_int !last_high_score;
|
---|
| 186 | print_newline();
|
---|
| 187 | end;
|
---|
| 188 | incr iterations_since_new_high_score;
|
---|
| 189 | incr generation
|
---|
[1973] | 190 | done;
|
---|
[1979] | 191 | print_conf population.(0);;
|
---|
[1973] | 192 |
|
---|
| 193 | main
|
---|