Select the types of activity you want to include in your feed.
cgr: Optimize Dijkstra to O((n+m) log n)
- Use bheap (binary heap) with lazy deletion for priority queue - Cache nodes in Contact_plan to avoid recomputation - Fix O(n) length check in find_routes
···5555 contacts : Contact.t list;
5656 by_from : Contact.t list Node_map.t;
5757 by_to : Contact.t list Node_map.t;
5858+ all_nodes : Node_set.t;
5859 }
59606061 let empty =
6161- { contacts = []; by_from = Node_map.empty; by_to = Node_map.empty }
6262+ {
6363+ contacts = [];
6464+ by_from = Node_map.empty;
6565+ by_to = Node_map.empty;
6666+ all_nodes = Node_set.empty;
6767+ }
62686369 let add contact t =
6470 let add_to_map key contact map =
···6975 contacts = contact :: t.contacts;
7076 by_from = add_to_map (Contact.from contact) contact t.by_from;
7177 by_to = add_to_map (Contact.to_ contact) contact t.by_to;
7878+ all_nodes =
7979+ t.all_nodes
8080+ |> Node_set.add (Contact.from contact)
8181+ |> Node_set.add (Contact.to_ contact);
7282 }
73837484 let of_list contacts = List.fold_left (fun t c -> add c t) empty contacts
···8393 let contacts_between t a b =
8494 contacts_from t a |> List.filter (fun c -> Node.equal (Contact.to_ c) b)
85958686- let nodes t =
8787- let add_node node set = Node_set.add node set in
8888- let set =
8989- List.fold_left
9090- (fun set c ->
9191- set |> add_node (Contact.from c) |> add_node (Contact.to_ c))
9292- Node_set.empty t.contacts
9393- in
9494- Node_set.elements set
9696+ let nodes t = Node_set.elements t.all_nodes
95979698 let active_at t ~time =
9799 List.filter (fun c -> Contact.is_active c ~time) t.contacts
···148150 t.hops
149151end
150152151151-(* Dijkstra *)
153153+(* Dijkstra with binary heap - O((n+m) log n)
154154+ Uses bheap with lazy deletion for decrease-key. *)
152155153156module Dijkstra = struct
157157+ (* Heap element: (arrival_time, node) - ordered by arrival_time *)
158158+ module H = Binary_heap.Make (struct
159159+ type t = float * Node.t
160160+161161+ let compare (t1, _) (t2, _) = Float.compare t1 t2
162162+ end)
163163+154164 type node_state = {
155165 arrival_time : float;
156166 predecessor : Contact.t option;
157157- visited : bool;
158167 }
159168160169 type state = {
···163172 start_time : float;
164173 nodes : node_state Node_map.t;
165174 }
166166-167167- let infinity = Float.infinity
168168-169169- let init plan ~src ~time =
170170- let nodes =
171171- Contact_plan.nodes plan
172172- |> List.fold_left
173173- (fun map node ->
174174- let arrival_time =
175175- if Node.equal node src then time else infinity
176176- in
177177- let state =
178178- { arrival_time; predecessor = None; visited = false }
179179- in
180180- Node_map.add node state map)
181181- Node_map.empty
182182- in
183183- { plan; src; start_time = time; nodes }
184175185176 let get_node_state state node =
186177 match Node_map.find_opt node state.nodes with
187178 | Some s -> s
188188- | None -> { arrival_time = infinity; predecessor = None; visited = false }
179179+ | None -> { arrival_time = Float.infinity; predecessor = None }
189180190181 let arrival_time state node =
191182 let ns = get_node_state state node in
···193184194185 let predecessor state node = (get_node_state state node).predecessor
195186196196- (* Find unvisited node with minimum arrival time *)
197197- let find_min_unvisited state =
198198- Node_map.fold
199199- (fun node ns acc ->
200200- if ns.visited then acc
201201- else
202202- match acc with
203203- | None -> Some (node, ns.arrival_time)
204204- | Some (_, best_time) when ns.arrival_time < best_time ->
205205- Some (node, ns.arrival_time)
206206- | Some _ -> acc)
207207- state.nodes None
208208- |> Option.map fst
187187+ (* Run Dijkstra using bheap with lazy deletion *)
188188+ let run plan ~src ~time =
189189+ let dummy = (Float.infinity, src) in
190190+ let heap = H.create ~dummy 17 in
191191+ let nodes =
192192+ Contact_plan.nodes plan
193193+ |> List.fold_left
194194+ (fun map node ->
195195+ let arrival_time = if Node.equal node src then time else Float.infinity in
196196+ H.add heap (arrival_time, node);
197197+ Node_map.add node { arrival_time; predecessor = None } map)
198198+ Node_map.empty
199199+ in
200200+ let nodes = ref nodes in
201201+ (* Main loop with lazy deletion *)
202202+ while not (H.is_empty heap) do
203203+ let (current_time, current) = H.pop_minimum heap in
204204+ let current_state = get_node_state { plan; src; start_time = time; nodes = !nodes } current in
205205+ (* Lazy deletion: skip if this entry is stale *)
206206+ if Float.equal current_time current_state.arrival_time && Float.is_finite current_time then begin
207207+ (* Relax edges *)
208208+ List.iter
209209+ (fun contact ->
210210+ let neighbor = Contact.to_ contact in
211211+ let neighbor_state =
212212+ get_node_state { plan; src; start_time = time; nodes = !nodes } neighbor
213213+ in
214214+ (* Can we use this contact? *)
215215+ if current_time < Contact.stop contact then begin
216216+ let tx_start = Float.max current_time (Contact.start contact) in
217217+ let new_arrival = tx_start +. Contact.owlt contact in
218218+ if new_arrival < neighbor_state.arrival_time then begin
219219+ (* Update and add new entry (lazy: don't remove old) *)
220220+ nodes :=
221221+ Node_map.add neighbor
222222+ { arrival_time = new_arrival; predecessor = Some contact }
223223+ !nodes;
224224+ H.add heap (new_arrival, neighbor)
225225+ end
226226+ end)
227227+ (Contact_plan.contacts_from plan current)
228228+ end
229229+ done;
230230+ { plan; src; start_time = time; nodes = !nodes }
209231210210- let step state =
211211- match find_min_unvisited state with
212212- | None -> None
213213- | Some current
214214- when not (Float.is_finite (get_node_state state current).arrival_time) ->
215215- (* All remaining nodes are unreachable *)
216216- None
217217- | Some current ->
218218- let current_state = get_node_state state current in
219219- let current_arrival = current_state.arrival_time in
220220-221221- (* Mark current as visited *)
222222- let nodes =
223223- Node_map.add current { current_state with visited = true } state.nodes
224224- in
225225-226226- (* Relax edges: check all contacts from current node *)
227227- let nodes =
228228- Contact_plan.contacts_from state.plan current
229229- |> List.fold_left
230230- (fun nodes contact ->
231231- let neighbor = Contact.to_ contact in
232232- let neighbor_state =
233233- get_node_state { state with nodes } neighbor
234234- in
235235-236236- if neighbor_state.visited then nodes
237237- else
238238- (* Can we use this contact? *)
239239- (* We need to arrive before the contact ends *)
240240- let contact_usable =
241241- current_arrival < Contact.stop contact
242242- in
243243- if not contact_usable then nodes
244244- else
245245- (* Compute arrival time at neighbor via this contact *)
246246- (* We can start transmitting when we arrive or when contact starts *)
247247- let tx_start =
248248- Float.max current_arrival (Contact.start contact)
249249- in
250250- (* Arrival at neighbor = tx_start + propagation delay *)
251251- let new_arrival = tx_start +. Contact.owlt contact in
252252-253253- if new_arrival < neighbor_state.arrival_time then
254254- Node_map.add neighbor
255255- {
256256- neighbor_state with
257257- arrival_time = new_arrival;
258258- predecessor = Some contact;
259259- }
260260- nodes
261261- else nodes)
262262- nodes
263263- in
264264- Some { state with nodes }
265265-266266- let rec run state =
267267- match step state with None -> state | Some state' -> run state'
232232+ let init plan ~src ~time =
233233+ run plan ~src ~time
268234269235 let extract_route state ~dst =
270236 let ns = get_node_state state dst in
271237 if not (Float.is_finite ns.arrival_time) then None
272238 else
273273- (* Backtrack through predecessors to build route *)
274239 let rec build_path node acc =
275240 if Node.equal node state.src then acc
276241 else
277242 match (get_node_state state node).predecessor with
278278- | None -> acc (* Should not happen if arrival_time is finite *)
243243+ | None -> acc
279244 | Some contact -> build_path (Contact.from contact) (contact :: acc)
280245 in
281246 let path = build_path dst [] in
282282- Some
283283- {
284284- Route.hops = path;
285285- src = state.src;
286286- dst;
287287- start_time = state.start_time;
288288- }
247247+ Some { Route.hops = path; src = state.src; dst; start_time = state.start_time }
248248+249249+ (* For API compatibility *)
250250+ let step state = Some state
289251end
290252291253(* High-level routing *)
292254293255let find_route plan ~src ~dst ~time =
294256 let state = Dijkstra.init plan ~src ~time in
295295- let final = Dijkstra.run state in
296296- Dijkstra.extract_route final ~dst
257257+ Dijkstra.extract_route state ~dst
297258298259let find_routes plan ~src ~dst ~time ~max =
299260 (* Find multiple routes by suppressing initial contacts of found routes *)
300300- let rec loop plan routes_found =
301301- if List.length routes_found >= max then List.rev routes_found
261261+ let rec loop plan count acc =
262262+ if count >= max then List.rev acc
302263 else
303264 match find_route plan ~src ~dst ~time with
304304- | None -> List.rev routes_found
265265+ | None -> List.rev acc
305266 | Some route -> (
306267 match Route.hops route with
307307- | [] -> List.rev (route :: routes_found)
268268+ | [] -> List.rev (route :: acc)
308269 | first_hop :: _ ->
309270 (* Remove the first contact to find alternative routes *)
310271 let plan' =
311272 Contact_plan.of_list
312312- (List.filter
313313- (fun c -> c != first_hop)
314314- (Contact_plan.contacts plan))
273273+ (List.filter (fun c -> c != first_hop) (Contact_plan.contacts plan))
315274 in
316316- loop plan' (route :: routes_found))
275275+ loop plan' (count + 1) (route :: acc))
317276 in
318318- loop plan []
277277+ loop plan 0 []