1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
|
(**************************************************************************)
(* *)
(* Copyright (C) 2012 Johannes 'josch' Schauer <j.schauer@email.de> *)
(* Copyright (C) 2012 Pietro Abate <pietro.abate@pps.jussieu.fr> *)
(* *)
(* This library is free software: you can redistribute it and/or modify *)
(* it under the terms of the GNU Lesser General Public License as *)
(* published by the Free Software Foundation, either version 3 of the *)
(* License, or (at your option) any later version. A special linking *)
(* exception to the GNU Lesser General Public License applies to this *)
(* library, see the COPYING file for more information. *)
(**************************************************************************)
module OCAMLHashtbl = Hashtbl
open! ExtLib
open Dose_common
open Dose_algo
#define __label __FILE__
let label = __label ;;
include Util.Logging(struct let label = label end) ;;
let timer_johnson = Util.Timer.create "GraphUtils.FindCycles.johnson"
module GraphUtils (G : Graph.Sig.I) = struct
module C = Graph.Components.Make(G)
module VSet = Set.Make(G.V)
module O = Defaultgraphs.GraphOper(G)
let v_list_mem v l = List.exists (fun p -> (G.V.compare p v) = 0) l;;
let scc_with_vertex g v =
let _,scc = C.scc g in
let n = scc v in
let sg = G.create () in
G.iter_vertex (fun v1 ->
if (scc v1) = n then
List.iter (fun e ->
if scc (G.E.dst e) = n then
G.add_edge_e sg e
) (G.succ_e g v1)
) g;
sg
;;
let copy_graph g =
let g1 = G.create () in
G.iter_edges_e (fun e -> G.add_edge_e g1 e) g;
g1
;;
let find_strong_articulation_points g =
let scc = List.filter_map (function [] | [_] -> None | s -> Some(O.subgraph g s)) (C.scc_list g) in
List.fold_left (fun acc scc ->
(* test if this scc has a strong articulation point *)
let l = G.fold_vertex (fun v acc ->
(* test if the scc without this vertex is split or not *)
let og = copy_graph g in
G.remove_vertex og v;
let num_scc = List.length (C.scc_list og) in
if num_scc = 1 then acc else (v,num_scc)::acc
) scc [] in
l@acc
) [] scc
;;
let find_strong_bridges g =
let scc = List.filter_map (function [] | [_] -> None | s -> Some(O.subgraph g s)) (C.scc_list g) in
List.fold_left (fun acc scc ->
(* test if this scc has a strong articulation point *)
let l = G.fold_edges_e (fun e acc ->
(* test if the scc without this vertex is split or not *)
let og = copy_graph g in
G.remove_edge_e og e;
let num_scc = List.length (C.scc_list og) in
if num_scc = 1 then acc else (e,num_scc)::acc
) scc [] in
l@acc
) [] scc
;;
let get_partial_order g =
let module Dfs = Graph.Traverse.Dfs(G) in
if Dfs.has_cycle g then
failwith "need a DAG without cycles as input for partial order";
let module Hashtbl = OCAMLHashtbl.Make(G.V) in
(* find all vertices with no successors (those that have all dependencies
* fulfilled) *)
let init = G.fold_vertex (fun v acc ->
match G.succ g v with
| [] -> v::acc
| _ -> acc
) g [] in
let result = ref [init] in
let processed = Hashtbl.create (G.nb_vertex g) in
let tocheck = Hashtbl.create (G.nb_vertex g) in
(* fill the two hashtables, starting from the initial result *)
List.iter (fun v ->
Hashtbl.replace processed v ();
G.iter_pred (fun pred ->
Hashtbl.replace tocheck pred ()
) g v
) init;
while Hashtbl.length tocheck > 0 do
let localprocessed = Hashtbl.create (G.nb_vertex g) in
(* iterate over the to-be-checked vertices and check if all of their
* dependencies are already in the result *)
Hashtbl.iter (fun v _ ->
let satisfied = G.fold_succ (fun succ acc ->
if acc then Hashtbl.mem processed succ else acc
) g v true in
(* if yes, remove this vertex from tocheck, add it to the result and
* add its predecessors to tocheck*)
if satisfied then begin
Hashtbl.remove tocheck v;
Hashtbl.replace localprocessed v ();
G.iter_pred (fun pred ->
Hashtbl.replace tocheck pred ()
) g v;
end;
) tocheck;
(* add results of this round to global variables *)
let l = Hashtbl.fold (fun v _ acc ->
Hashtbl.replace processed v ();
v::acc
) localprocessed [] in
result := l::!result
done;
List.rev !result
;;
exception Found of G.V.t
let find_vertex_option prop g =
try
G.iter_vertex (fun v -> if prop v then raise (Found v)) g;
None
with Found v -> Some(v)
;;
exception Empty
let first_vertex g =
try
G.iter_vertex (fun v -> raise (Found v)) g;
raise Empty
with Found v -> v
;;
end
module FindCycles (G : Graph.Sig.I) = struct
module C = Graph.Components.Make(G)
module GraphUtilsG = GraphUtils(G)
module O = Defaultgraphs.GraphOper(G)
let edge_cycle_from_vertex_cycle g cycle =
let fst, tail = match cycle with
| h::t -> h,t
| [] -> raise List.Empty_list
in
let rec get_edges vertices last acc =
match vertices with
| [] ->
(* edge from last to fst to close the cycle *)
let e = G.find_edge g last fst in
e::acc
| h::t ->
(*edge from last to h *)
let e = G.find_edge g last h in
get_edges t h (e::acc)
in
let edges = get_edges tail fst [] in
List.rev edges
;;
let get_cycles_per_edge_ht g cycles =
let hist = Hashtbl.create (G.nb_edges g) in
List.iter (fun cycle ->
let edges = edge_cycle_from_vertex_cycle g cycle in
List.iter (fun edge ->
Hashtbl.replace hist edge ((Hashtbl.find_default hist edge 0)+1);
) edges;
) cycles;
hist
;;
let to_edges g cycle =
let rec aux (acc,first,last) = function
|[] ->
begin match (acc,first,last) with
|acc,Some f,Some l -> List.rev ((G.find_edge g l f) :: acc)
|[],None,None -> []
|_,_,_ -> failwith "not a cycle (empty)" end
|h1::h2::t ->
begin match (acc,first,last) with
|[],None,None ->
let e = G.find_edge g h1 h2 in
aux (e::acc,Some h1,Some h2) t
|acc,Some f,Some l ->
let e1 = G.find_edge g l h1 in
let e2 = G.find_edge g h1 h2 in
aux (e2::e1::acc,Some f,Some h2) t
|_,_,_ -> failwith "not a cycle (full)" end
|_ -> failwith "not a cycle (impossible)"
in aux ([],None,None) cycle
let johnson ?(maxlength=0) ?(maxamount=0) g =
Util.Timer.start timer_johnson;
let path = Stack.create () in
(* vertex: blocked from search *)
let blocked = Hashtbl.create 1023 in
(* graph portions that yield no elementary circuit *)
let b = Hashtbl.create 1023 in
(* list to accumulate the circuits found *)
let result = ref [] in
let resultlen = ref 0 in
let i = ref 0 in
let rec unblock n =
if Hashtbl.find blocked n then begin
Hashtbl.replace blocked n false;
List.iter unblock (Hashtbl.find b n);
Hashtbl.replace b n [];
end
in
let stack_to_list s =
let l = ref [] in
Stack.iter (fun e -> l:= e::!l) s;
!l
in
let vertex_set = G.fold_vertex (fun v l -> v::l) g [] in
let vertex_set_len = List.length vertex_set in
let rec circuit thisnode startnode component =
let closed = ref false in
if (maxlength = 0 || (Stack.length path) < maxlength) && (maxamount = 0 || !resultlen < maxamount) then begin
Stack.push thisnode path;
Hashtbl.replace blocked thisnode true;
G.iter_succ (fun nextnode ->
if G.V.equal nextnode startnode then begin
result := ((stack_to_list path))::!result;
incr resultlen;
debug "[%d/%d] found cycles: %d\r%!" !i vertex_set_len !resultlen;
closed := true;
end else begin if not(Hashtbl.find blocked nextnode) then
if circuit nextnode startnode component then begin
closed := true;
end
end
) component thisnode;
if !closed then begin
unblock thisnode
end
else
G.iter_succ (fun nextnode ->
let l = Hashtbl.find b nextnode in
if not(List.mem thisnode l) then
Hashtbl.replace b nextnode (thisnode::l)
) component thisnode;
ignore(Stack.pop path);
end;
!closed
in
debug "[%d/%d] found cycles: %d\r%!" 0 vertex_set_len 0;
let non_degenerate_scc = List.filter (function [] | [_] -> incr i; false | _ -> true) (C.scc_list g) in
let non_degenerate_subgraphs = List.map (fun scc -> O.subgraph g scc) non_degenerate_scc in
List.iter (fun g ->
let vertex_set = G.fold_vertex (fun v l -> v::l) g [] in
List.iter (fun s ->
incr i;
debug "[%d/%d] found cycles: %d\r%!" !i vertex_set_len !resultlen;
if maxamount = 0 || !resultlen < maxamount then begin
let component = GraphUtilsG.scc_with_vertex g s in
if G.nb_edges component > 0 then begin
G.iter_vertex (fun node ->
Hashtbl.replace blocked node false;
Hashtbl.replace b node [];
) component;
ignore(circuit s s component);
end;
G.remove_vertex g s;
end
) (List.sort ~cmp:G.V.compare vertex_set);
) non_degenerate_subgraphs;
debug "[%d/%d] found cycles: %d\n%!" !i vertex_set_len !resultlen;
Util.Timer.stop timer_johnson (List.rev !result)
;;
end
module FAS (G : Graph.Sig.I) = struct
module EdgeSet = Set.Make(G.E)
module FindCyclesG = FindCycles(G)
module Dfs = Graph.Traverse.Dfs(G)
module GraphUtilsG = GraphUtils(G)
module Int = struct type t = int let compare = Stdlib.compare end
module IntSet = Set.Make(Int)
module T = Graph.Topological.Make(G)
(* decide whether a given feedback arc set is minimal for the supplied graph.
* A feedback arc set is minimal if the reinsertion of any edge in the set
* into the graph would make the graph cyclic again
* this is FALSE but written as such in:
* Brandenburg, K. H. F., & Hanauer, K. (2011). Sorting Heuristics for the
* Feedback Arc Set Problem. *)
let isminimal fas g =
let g = GraphUtilsG.copy_graph g in
EdgeSet.iter (G.remove_edge_e g) fas;
if Dfs.has_cycle g then
failwith "the input is not a feedback arc set for the supplied graph";
EdgeSet.for_all (fun e ->
G.add_edge_e g e;
let res = Dfs.has_cycle g in
G.remove_edge_e g e;
res
) fas
;;
(* turn a feedback arc set into a vertex ordering
* since there are many topological orderings for a given acyclic graph, take
* care to choose the order which keeps the feedback arc set small
* for this reason, instead of using Graph.Topological,
* GraphUtils.get_partial_order is used. All vertices within each group
* returned by this function are then ordered such that the cardinality of the
* given feedback arc set is reduced. *)
let getorder fas g =
(* fasverts is a hashtable which maps vertices which are the source of edges
* in the feedback arc set to a list of vertices which are destinations of
* edges in the feedback arc set. *)
let fasverts = Hashtbl.create (EdgeSet.cardinal fas) in
EdgeSet.iter (fun e ->
let src = G.E.src e in let dst = G.E.dst e in
if src <> dst then (* ignore selfcycles as they don't influence the order *)
Hashtbl.add fasverts src (dst,e)
) fas;
(* go through all vertex lists returned by GraphUtilsG.get_partial_order and
* sort all vertices in this list which make edges in the feedback arc set,
* such that those edges are removed *)
List.fold_left (fun acc l ->
(* get all the edges that are part of this list for lookup later *)
let localverts = Hashtbl.create (List.length l) in
List.iter (fun v -> Hashtbl.add localverts v ()) l;
(* create a graph that only contains those feedback arcs whose source and
* destination are in the current vertex list. The resulting graph might
* be cyclic if the feedback arc set was really bad but at this point we
* don't care*)
let g = G.create () in
List.iter (fun v1 ->
List.iter (fun (v2,e) ->
if Hashtbl.mem localverts v2 then G.add_edge_e g e
) (Hashtbl.find_all fasverts v1)
) l;
if Dfs.has_cycle g then
warning "fas has forward and backward edge (creating a cycle) we don't handle this yet";
(* get the topological order of the vertices in the graph *)
let vlist = T.fold (fun v acc -> v::acc) g [] in
(* concatenate all vertices that are not part of the graph above *)
let acc = List.fold_left (fun acc v ->
if not (List.mem v vlist) then v::acc else acc
) acc l in
List.rev_append vlist acc
) [] (GraphUtilsG.get_partial_order g)
;;
let ordertofas order g =
(* check if the order can match the graph *)
if (List.length order) <> (G.nb_vertex g) then
failwith "invalid vertex order (length differs)";
let seen = Hashtbl.create (List.length order) in
List.fold_left (fun acc v ->
Hashtbl.add seen v (); (* because of edges in self cycles *)
G.fold_succ_e (fun edge acc ->
if Hashtbl.mem seen (G.E.dst edge) then
EdgeSet.add edge acc (* this vertex has already been processed, so it is a backarc*)
else
acc
) g v acc
) EdgeSet.empty order
;;
(* cost function for an order equals the size of the fas *)
let costoforder order g =
EdgeSet.cardinal (ordertofas order g)
;;
let cyclefas ?(maxlength=4) g =
let g = GraphUtilsG.copy_graph g in
(* given a graph and a list of cycles in it, return a set of edges that
* remove all those cycles by iteratively removing the edge that is shared
* by most cycles. *)
let calculate_partial_fas cycles =
let hist = Hashtbl.create (G.nb_edges g) in
(* create a hashtable mapping edges to a set of integers where each
* integer maps to the cycle that this edge is part of *)
List.iteri (fun i cycle ->
let edges = FindCyclesG.edge_cycle_from_vertex_cycle g cycle in
List.iter (fun edge ->
Hashtbl.replace hist edge (IntSet.add i (Hashtbl.find_default hist edge IntSet.empty));
) edges;
) cycles;
let rec remove_most_popular_edge acc =
(* get the edge that is part of the most cycles *)
match List.of_enum (Hashtbl.enum hist) with
| [] -> acc (* it might be that no cycles of this length can be broken *)
| hd::tl -> begin
let max_edge,cids = List.fold_left (fun (k1,v1) (k2,v2) ->
if (IntSet.cardinal v1) > (IntSet.cardinal v2) then k1,v1 else k2,v2
) hd tl in
(* end if the edge with the most cycles has zero cycles *)
if (IntSet.cardinal cids) = 0 then
acc
else begin
(* remove those cycle ids from all sets *)
Hashtbl.iter (fun edge set ->
Hashtbl.replace hist edge (IntSet.diff set cids)
) hist;
(* add edge to feedback arc set *)
remove_most_popular_edge (EdgeSet.add max_edge acc)
end
end
in
remove_most_popular_edge EdgeSet.empty
in
let remove_edgeset = EdgeSet.iter (G.remove_edge_e g) in
(* find selfcycles *)
let selfcycles = G.fold_edges_e (fun e acc ->
if (G.E.src e) = (G.E.dst e) then EdgeSet.add e acc else acc
) g EdgeSet.empty in
(* remove the found edges from the graph *)
remove_edgeset selfcycles;
(* apply calculate_partial_fas on the graph, remove the resulting edges and
* increment the max cycle length each time until the graph is loop free *)
let rec foo ml acc =
if Dfs.has_cycle g then begin
let cycles = FindCyclesG.johnson ~maxlength:ml g in
match cycles with
| [] -> foo (ml+2) acc
| l ->
let partial_fas = calculate_partial_fas l in
remove_edgeset partial_fas;
foo (ml+2) (EdgeSet.union partial_fas acc)
end else
acc
in
let fas = foo maxlength EdgeSet.empty in
(* instead of calculating the union of the set of selfcycle edges and the
* edges in the calculated feedback arc set, calculate the induced vertex
* order. The back arcs in this order are of either equal or less amount
* than the edges picked above. *)
getorder fas g
;;
let sortingfaswrapper algo g_orig =
let g = GraphUtilsG.copy_graph g_orig in
(* first, remove all selfcycles *)
let selfcycles = G.fold_edges_e (fun e acc ->
if (G.E.src e) = (G.E.dst e) then EdgeSet.add e acc else acc
) g EdgeSet.empty in
(* remove the found edges from the graph *)
EdgeSet.iter (G.remove_edge_e g) selfcycles;
algo g
;;
(* implementation of algorithm presented in
* Peter Eades, Xuemin Lin, W.F. Smyth, A fast and effective heuristic for
* the feedback arc set problem, Information Processing Letters, Volume 47,
* Issue 6, 18 October 1993, Pages 319-323, ISSN 0020-0190,
* 10.1016/0020-0190(93)90079-O.
*
* by setting improved:true it becomes the implementation of an improvement
* of the eades algo as presented in
* Tom Coleman and Anthony Wirth. 2010. Ranking tournaments: Local search
* and a new algorithm. J. Exp. Algorithmics 14, Article 6 (January 2010),
* .62 pages. DOI=10.1145/1498698.1537601
*
* the implementation is rather slow as focus was put on correctness and
* readability rather than execution speed
* *)
let eadesfas ?(improved=false) g =
let aux g =
(* instead of appending to s1, we will prepend to s1 and reverse s1 in the
* end*)
let s1 = ref [] in
let s2 = ref [] in
let is_sink v = G.out_degree g v = 0 in
let is_source v = G.in_degree g v = 0 in
let delta = if improved then begin
fun v -> abs ((G.out_degree g v) - (G.in_degree g v))
end else begin
fun v -> (G.out_degree g v) - (G.in_degree g v)
end in
while G.nb_vertex g > 0 do
(* add all sinks to s2 *)
try
while true do
match GraphUtilsG.find_vertex_option is_sink g with
| Some v -> begin
s2 := v::(!s2);
G.remove_vertex g v;
end
| None -> raise Exit
done
with Exit -> ();
(* add all sources to s1 *)
try
while true do
match GraphUtilsG.find_vertex_option is_source g with
| Some v -> begin
s1 := v::(!s1);
G.remove_vertex g v;
end
| None -> raise Exit
done
with Exit -> ();
(* add vertex with maximum δ = d_{out} - d_{in} to s1 *)
if G.nb_vertex g > 0 then begin
let v = GraphUtilsG.first_vertex g in
let v,_ = G.fold_vertex (fun v (vmax, d) ->
let dn = delta v in
if dn > d then (v,dn) else (vmax,d)
) g (v, delta v) in
if improved && (G.in_degree g v) > (G.out_degree g v) then
s2 := v::(!s2) (* treat as sink *)
else
s1 := v::(!s1); (* treat as source *)
G.remove_vertex g v;
end
done;
List.rev_append !s1 !s2
in
(* now figure out the feedback arc set from this ordering *)
sortingfaswrapper aux g
;;
(* cost function for an order equals the size of the fas *)
let costoforder2l l e pos g =
let seen = Hashtbl.create ((List.length l) + 1) in
let res = ref EdgeSet.empty in (* could be speeded up by using a list instead of a set *)
let gather = G.iter_succ_e (fun edge ->
if Hashtbl.mem seen (G.E.dst edge) then
res := EdgeSet.add edge !res (* this vertex has already been processed, so it is a backarc*)
) g in
List.iteri (fun i v ->
(* once we are at position i, add e in *)
if i = pos then begin
Hashtbl.add seen e ();
gather e;
end;
Hashtbl.add seen v (); (* because of edges in self cycles *)
gather v;
) l;
(* if e was to be added at the end: do it now *)
if pos = (List.length l) then begin
Hashtbl.add seen e ();
gather e;
end;
EdgeSet.cardinal !res
;;
let handlevertexorder g = function
| None -> G.fold_vertex (fun v acc -> v::acc) g []
| Some l ->
let l = List.filter (G.mem_vertex g) l in
if (List.length l) <> (G.nb_vertex g) then
failwith "invalid vertex order (length differs)"
else l
;;
let sortingfas ?(order=None) algo g =
let aux g =
let l = handlevertexorder g order in
algo l g
in
sortingfaswrapper aux g
;;
(* find the position to insert v into l with the least cost *)
let insert g v = function
| [] -> [v]
| l -> begin
let min = ref max_int in
let res = ref 0 in
for i=0 to (List.length l) do
let m = costoforder2l l v i g in
if m < !min then begin
min := m;
res := i;
end
done;
match !res with
| 0 -> v::l
| _ -> begin
let l1, l2 = List.split_nth !res l in
l1@[v]@l2
end
end
;;
(* TODO: move this further down to the insertsort section *)
(* this version of insertion sort has an order as input and output
* it does not care about selfcycles *)
let insertorderfas order g =
(* find the position to insert v into l with the least cost *)
let rec sort l =
match l with
| h::tl -> insert g h (sort tl)
| [] -> []
in
sort order
;;
let rec convergefas g algo res min =
let res = algo res g in
let newmin = costoforder res g in
if newmin = min then
res (* convergence! *)
else if newmin < min then begin
convergefas g algo res newmin (* try harder! *)
end else
failwith "minimum increased" (* apparently algo is not monotone... *)
;;
let revfas ?(order=None) algo g =
let aux g =
let res = handlevertexorder g order in
(* then use algo until convergence is reached *)
let c1 = convergefas g algo res max_int in
(* now use this solution as an input to an algorithm which repeatedly
* reverses that solution, applies one step of insertion sort and then
* applies algo until convergence *)
let rec converge2 res min =
let res = List.rev res in
(* after a reversal MUST come an insertionsort - otherwise an improvement
* of the result is not guaranteed *)
let res = insertorderfas res g in
let newmin = costoforder res g in
let res = convergefas g algo res newmin in
let newmin = costoforder res g in
if newmin = min then
res (* convergence! *)
else if newmin < min then begin
debug "current best result: %d\n%!" newmin;
converge2 res newmin
end else
failwith "minimum increased"
in
converge2 c1 max_int
in
sortingfaswrapper aux g
;;
let multifas ?(order=None) algo g =
let aux g =
let res = handlevertexorder g order in
convergefas g algo res max_int
in
sortingfaswrapper aux g
;;
(************************ INSERT HEURISTIC ************************)
(*
* simplified version (no reversal, just iterative insertion) of
* Chanas, S., & Kobylański, P. (1996). A new heuristic algorithm solving
* the linear ordering problem. Computational optimization and applications,
* 6(2), 191-205.
*
* everything is very imperative but this way of doing it outperformed any
* recursive solution
*
* the complexity is O(n^2) as usual for insertion sort
*
* runs insertion sort on a default order
* *)
let insertfas g =
sortingfas insertorderfas g
;;
let insertmultifas g =
multifas insertorderfas g
;;
let insertrevfas g =
revfas insertorderfas g
;;
(************************ SIFTING HEURISTIC ************************)
(* TODO: find a paper with the origin of sifting
* the term sifting comes from the paper
* Brandenburg, K. H. F., & Hanauer, K. (2011). Sorting Heuristics for the
* Feedback Arc Set Problem.
* *)
let siftorderfas order g =
(* iterate over all vertices of the graph and try to find the best position
* in the list for each vertex *)
G.fold_vertex (fun v acc ->
(* remove v from the list - TODO: work around this somehow...*)
let acc = List.filter (fun e -> e <> v) acc in
insert g v acc
) g order
;;
let siftfas g =
sortingfas siftorderfas g
;;
let siftmultifas g =
multifas siftorderfas g
;;
(*
* this combined algorithm was introduced in
* Brandenburg, K. H. F., & Hanauer, K. (2011). Sorting Heuristics for the
* Feedback Arc Set Problem.
*
* similar to insertrevfas with the difference of
* - using sifting instead of insertion sort
* - applying one step of insertion sort after reversal
* o this is necessary because sifting a reversed order does not
* necessarily improve the result but insertion sorting a reversed
* order does
* o that reasoning can also be read about in
* Brandenburg, K. H. F., & Hanauer, K. (2011). Sorting Heuristics
* for the Feedback Arc Set Problem.
*)
let siftrevfas g =
revfas siftorderfas g
;;
(************************ MOVE HEURISTIC ************************)
(*
* the move heuristic comes from
* Coleman, T., & Wirth, A. (2009). Ranking tournaments: Local search and a
* new algorithm. Journal of Experimental Algorithmics (JEA), 14, 6.
* and is there called chanas-both because of it's similarity to the insertion
* sort algorithm above. It differs in that it allows each element to be moved
* to either side. It was called move in
* Brandenburg, K. H. F., & Hanauer, K. (2011). Sorting Heuristics for the
* Feedback Arc Set Problem.
* *)
let moveorderfas order g =
(* iterate over all positions in the order and try to find the best position
* for the vertex in that postion *)
let rec aux acc i =
if List.length acc = i then acc else begin
let l1, v, l2 = match List.split_nth i acc with
| _, [] -> failwith "impossible"
| l1, h::tl -> l1,h,tl
in
aux (insert g v (l1@l2)) (i+1)
end
in
aux order 0
;;
let movefas g =
sortingfas moveorderfas g
;;
let movemultifas g =
multifas moveorderfas g
;;
let moverevfas g =
revfas moveorderfas g
(************************ CYCLE HYBRIDS ************************)
let cycleinsert ?(maxlength=4) g =
let order = cyclefas ~maxlength g in
sortingfas ~order:(Some order) insertorderfas g
;;
let cycleinsertmulti ?(maxlength=4) g =
let order = cyclefas ~maxlength g in
multifas ~order:(Some order) insertorderfas g
;;
let cycleinsertrev ?(maxlength=4) g =
let order = cyclefas ~maxlength g in
revfas ~order:(Some order) insertorderfas g
;;
let cyclesift ?(maxlength=4) g =
let order = cyclefas ~maxlength g in
sortingfas ~order:(Some order) siftorderfas g
;;
let cyclesiftmulti ?(maxlength=4) g =
let order = cyclefas ~maxlength g in
multifas ~order:(Some order) siftorderfas g
;;
let cyclesiftrev ?(maxlength=4) g =
let order = cyclefas ~maxlength g in
revfas ~order:(Some order) siftorderfas g
;;
let cyclemove ?(maxlength=4) g =
let order = cyclefas ~maxlength g in
sortingfas ~order:(Some order) moveorderfas g
;;
let cyclemovemulti ?(maxlength=4) g =
let order = cyclefas ~maxlength g in
multifas ~order:(Some order) moveorderfas g
;;
let cyclemoverev ?(maxlength=4) g =
let order = cyclefas ~maxlength g in
revfas ~order:(Some order) moveorderfas g
;;
(************************ EADES HYBRIDS ************************)
let eadesinsert g =
let order = eadesfas g in
sortingfas ~order:(Some order) insertorderfas g
;;
let eadesinsertmulti g =
let order = eadesfas g in
multifas ~order:(Some order) insertorderfas g
;;
let eadesinsertrev g =
let order = eadesfas g in
revfas ~order:(Some order) insertorderfas g
;;
let eadessift g =
let order = eadesfas g in
sortingfas ~order:(Some order) siftorderfas g
;;
let eadessiftmulti g =
let order = eadesfas g in
multifas ~order:(Some order) siftorderfas g
;;
let eadessiftrev g =
let order = eadesfas g in
revfas ~order:(Some order) siftorderfas g
;;
let eadesmove g =
let order = eadesfas g in
sortingfas ~order:(Some order) moveorderfas g
;;
let eadesmovemulti g =
let order = eadesfas g in
multifas ~order:(Some order) moveorderfas g
;;
let eadesmoverev g =
let order = eadesfas g in
revfas ~order:(Some order) moveorderfas g
;;
end
|