Browse Source

First Commit

rlacroix 1 year ago
commit
9d4e63d467
12 changed files with 498 additions and 0 deletions
  1. 20
    0
      Makefile
  2. 21
    0
      README.md
  3. 3
    0
      _tags
  4. 24
    0
      graphs/graph1
  5. 106
    0
      graphs/graph1.svg
  6. 21
    0
      graphs/graph1.txt
  7. 37
    0
      graphs/graph2.txt
  8. 35
    0
      src/ftest.ml
  9. 101
    0
      src/gfile.ml
  10. 18
    0
      src/gfile.mli
  11. 49
    0
      src/graph.ml
  12. 63
    0
      src/graph.mli

+ 20
- 0
Makefile View File

@@ -0,0 +1,20 @@
1
+
2
+build:
3
+	@echo "\n==== COMPILING ====\n"
4
+	ocamlbuild ftest.native
5
+
6
+format:
7
+	ocp-indent --inplace src/*
8
+
9
+edit:
10
+	code . -n
11
+
12
+demo: build
13
+	@echo "\n==== EXECUTING ====\n"
14
+	./ftest.native graphs/graph1 1 2 outfile
15
+	@echo "\n==== RESULT ==== (content of outfile) \n"
16
+	@cat outfile
17
+
18
+clean:
19
+	-rm -rf _build/
20
+	-rm ftest.native

+ 21
- 0
README.md View File

@@ -0,0 +1,21 @@
1
+Base project for Ocaml project on Ford-Fulkerson. This project contains some simple configuration files to facilitate editing Ocaml in VSCode.
2
+
3
+To use, you should install the *OCaml* extension in VSCode. Other extensions might work as well but make sure there is only one installed.
4
+Then open VSCode in the root directory of this repository (command line: `code path/to/ocaml-maxflow-project`).
5
+
6
+Features :
7
+ - full compilation as VSCode build task (Ctrl+Shift+b)
8
+ - highlights of compilation errors as you type
9
+ - code completion
10
+ - automatic indentation on file save
11
+
12
+
13
+A makefile provides some useful commands:
14
+ - `make build` to compile. This creates an ftest.native executable
15
+ - `make demo` to run the `ftest` program with some arguments
16
+ - `make format` to indent the entire project
17
+ - `make edit` to open the project in VSCode
18
+ - `make clean` to remove build artifacts
19
+
20
+In case of trouble with the VSCode extension (e.g. the project does not build, there are strange mistakes), a common workaround is to (1) close vscode, (2) `make clean`, (3) `make build` and (4) reopen vscode (`make edit`).
21
+

+ 3
- 0
_tags View File

@@ -0,0 +1,3 @@
1
+<src/**>: include
2
+
3
+

+ 24
- 0
graphs/graph1 View File

@@ -0,0 +1,24 @@
1
+%% Test graph #1
2
+
3
+%% Nodes
4
+
5
+n 88 209     % This is node #0, with its coordinates (which are not used by the algorithms).
6
+n 408 183
7
+n 269 491
8
+n 261 297
9
+n 401 394
10
+n 535 294    % This is node #5.
11
+
12
+
13
+%% Edges
14
+
15
+e 3 1 11     % An edge from 3 to 1, labeled "11".
16
+e 3 2 2
17
+e 1 5 21
18
+e 4 5 14
19
+e 1 4 1
20
+e 0 1 7
21
+e 0 3 10
22
+e 3 4 5
23
+e 2 4 12
24
+e 0 2 8

+ 106
- 0
graphs/graph1.svg View File

@@ -0,0 +1,106 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
3
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" [
4
+ <!ATTLIST svg xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">
5
+]>
6
+<!-- Generated by dot version 2.7.20060111.0540 (Fri Sep  3 08:16:42 UTC 2010)
7
+     For user: (lebotlan) D. Le Botlan,,, -->
8
+<!-- Title: finite_state_machine Pages: 1 -->
9
+<svg width="689px" height="302px"
10
+ viewBox = "0 0 517 227"
11
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
12
+<g id="graph0" class="graph" style="font-family:Times-Roman;font-size:14.00;">
13
+<title>finite_state_machine</title>
14
+<polygon style="fill:white;stroke:white;" points="0,227 0,-11 528,-11 528,227 0,227"/>
15
+<!-- 0 -->
16
+<g id="node1" class="node"><title>0</title>
17
+<ellipse style="fill:none;stroke:black;" cx="29" cy="112" rx="18" ry="18"/>
18
+<text text-anchor="middle" x="29" y="119">0</text>
19
+</g>
20
+<!-- 2 -->
21
+<g id="node3" class="node"><title>2</title>
22
+<ellipse style="fill:none;stroke:black;" cx="259" cy="43" rx="18" ry="18"/>
23
+<text text-anchor="middle" x="259" y="50">2</text>
24
+</g>
25
+<!-- 0&#45;&gt;2 -->
26
+<g id="edge2" class="edge"><title>0&#45;&gt;2</title>
27
+<path style="fill:none;stroke:black;" d="M51,100C59,95 68,90 77,86 127,64 140,59 192,48 201,47 212,46 221,44"/>
28
+<polygon style="fill:black;stroke:black;" points="221,39 235,44 221,48 221,39"/>
29
+<text text-anchor="middle" x="144" y="52">8</text>
30
+</g>
31
+<!-- 3 -->
32
+<g id="node5" class="node"><title>3</title>
33
+<ellipse style="fill:none;stroke:black;" cx="144" cy="112" rx="18" ry="18"/>
34
+<text text-anchor="middle" x="144" y="119">3</text>
35
+</g>
36
+<!-- 0&#45;&gt;3 -->
37
+<g id="edge4" class="edge"><title>0&#45;&gt;3</title>
38
+<path style="fill:none;stroke:black;" d="M53,112C69,112 89,112 107,112"/>
39
+<polygon style="fill:black;stroke:black;" points="107,107 120,112 107,116 107,107"/>
40
+<text text-anchor="middle" x="87" y="108">10</text>
41
+</g>
42
+<!-- 1 -->
43
+<g id="node7" class="node"><title>1</title>
44
+<ellipse style="fill:none;stroke:black;" cx="259" cy="191" rx="18" ry="18"/>
45
+<text text-anchor="middle" x="259" y="198">1</text>
46
+</g>
47
+<!-- 0&#45;&gt;1 -->
48
+<g id="edge6" class="edge"><title>0&#45;&gt;1</title>
49
+<path style="fill:none;stroke:black;" d="M48,127C65,142 93,160 120,170 153,182 192,187 221,188"/>
50
+<polygon style="fill:black;stroke:black;" points="221,184 235,190 221,192 221,184"/>
51
+<text text-anchor="middle" x="144" y="162">7</text>
52
+</g>
53
+<!-- 4 -->
54
+<g id="node9" class="node"><title>4</title>
55
+<ellipse style="fill:none;stroke:black;" cx="373" cy="120" rx="18" ry="18"/>
56
+<text text-anchor="middle" x="373" y="127">4</text>
57
+</g>
58
+<!-- 2&#45;&gt;4 -->
59
+<g id="edge8" class="edge"><title>2&#45;&gt;4</title>
60
+<path style="fill:none;stroke:black;" d="M279,56C296,68 321,86 343,99"/>
61
+<polygon style="fill:black;stroke:black;" points="345,95 353,107 340,103 345,95"/>
62
+<text text-anchor="middle" x="316" y="72">12</text>
63
+</g>
64
+<!-- 3&#45;&gt;2 -->
65
+<g id="edge12" class="edge"><title>3&#45;&gt;2</title>
66
+<path style="fill:none;stroke:black;" d="M165,99C183,90 207,75 225,63"/>
67
+<polygon style="fill:black;stroke:black;" points="224,59 237,55 229,66 224,59"/>
68
+<text text-anchor="middle" x="201" y="71">2</text>
69
+</g>
70
+<!-- 3&#45;&gt;1 -->
71
+<g id="edge14" class="edge"><title>3&#45;&gt;1</title>
72
+<path style="fill:none;stroke:black;" d="M161,128C171,136 181,146 192,152 203,159 215,167 225,172"/>
73
+<polygon style="fill:black;stroke:black;" points="228,168 237,179 224,176 228,168"/>
74
+<text text-anchor="middle" x="201" y="148">11</text>
75
+</g>
76
+<!-- 3&#45;&gt;4 -->
77
+<g id="edge10" class="edge"><title>3&#45;&gt;4</title>
78
+<path style="fill:none;stroke:black;" d="M168,114C208,115 288,118 336,119"/>
79
+<polygon style="fill:black;stroke:black;" points="336,114 349,119 336,123 336,114"/>
80
+<text text-anchor="middle" x="259" y="114">5</text>
81
+</g>
82
+<!-- 1&#45;&gt;4 -->
83
+<g id="edge16" class="edge"><title>1&#45;&gt;4</title>
84
+<path style="fill:none;stroke:black;" d="M280,179C293,171 311,162 325,152 331,150 336,146 343,142"/>
85
+<polygon style="fill:black;stroke:black;" points="340,138 353,135 344,146 340,138"/>
86
+<text text-anchor="middle" x="316" y="148">1</text>
87
+</g>
88
+<!-- 5 -->
89
+<g id="node15" class="node"><title>5</title>
90
+<ellipse style="fill:none;stroke:black;" cx="488" cy="168" rx="18" ry="18"/>
91
+<text text-anchor="middle" x="488" y="175">5</text>
92
+</g>
93
+<!-- 1&#45;&gt;5 -->
94
+<g id="edge18" class="edge"><title>1&#45;&gt;5</title>
95
+<path style="fill:none;stroke:black;" d="M283,188C311,186 357,182 397,178 415,175 433,174 451,172"/>
96
+<polygon style="fill:black;stroke:black;" points="451,168 464,171 451,176 451,168"/>
97
+<text text-anchor="middle" x="373" y="174">21</text>
98
+</g>
99
+<!-- 4&#45;&gt;5 -->
100
+<g id="edge20" class="edge"><title>4&#45;&gt;5</title>
101
+<path style="fill:none;stroke:black;" d="M396,130C412,138 435,147 453,154"/>
102
+<polygon style="fill:black;stroke:black;" points="455,150 465,159 451,158 455,150"/>
103
+<text text-anchor="middle" x="431" y="138">14</text>
104
+</g>
105
+</g>
106
+</svg>

+ 21
- 0
graphs/graph1.txt View File

@@ -0,0 +1,21 @@
1
+% This is a graph.
2
+
3
+n 20 300 0
4
+n 200 300 1
5
+n 200 200 2
6
+n 200 400 3
7
+n 380 300 4
8
+n 380 200 5
9
+
10
+e 0 2 0 8
11
+e 0 3 1 10
12
+e 0 1 2 7
13
+e 2 4 3 12
14
+e 3 4 4 5
15
+e 3 2 5 2
16
+e 3 1 6 11
17
+e 1 4 7 1
18
+e 1 5 8 21
19
+e 4 5 9 14
20
+
21
+% End of graph

+ 37
- 0
graphs/graph2.txt View File

@@ -0,0 +1,37 @@
1
+% This is a graph.
2
+
3
+n 20 300 0
4
+n 200 300 1
5
+n 200 200 2
6
+n 200 400 3
7
+n 380 300 4
8
+n 380 200 5
9
+n 380 400 6
10
+n 380 100 7
11
+n 380 500 8
12
+n 560 300 9
13
+n 560 200 10
14
+n 560 400 11
15
+n 560 100 12
16
+
17
+e 0 3 0 2
18
+e 0 2 1 2
19
+e 0 1 2 4
20
+e 1 4 3 2
21
+e 1 6 4 2
22
+e 1 5 5 2
23
+e 4 10 6 2
24
+e 2 5 7 2
25
+e 10 12 8 6
26
+e 3 6 9 2
27
+e 3 8 10 2
28
+e 6 9 11 2
29
+e 8 11 12 2
30
+e 11 9 13 2
31
+e 9 10 14 4
32
+e 5 10 15 1
33
+e 5 7 16 1
34
+e 7 12 17 2
35
+e 7 10 18 1
36
+
37
+% End of graph

+ 35
- 0
src/ftest.ml View File

@@ -0,0 +1,35 @@
1
+open Gfile
2
+    
3
+let () =
4
+
5
+  (* Check the number of command-line arguments *)
6
+  if Array.length Sys.argv <> 5 then
7
+    begin
8
+      Printf.printf
9
+        "\n ✻  Usage: %s infile source sink outfile\n\n%s%!" Sys.argv.(0)
10
+        ("    🟄  infile  : input file containing a graph\n" ^
11
+         "    🟄  source  : identifier of the source vertex (used by the ford-fulkerson algorithm)\n" ^
12
+         "    🟄  sink    : identifier of the sink vertex (ditto)\n" ^
13
+         "    🟄  outfile : output file in which the result should be written.\n\n") ;
14
+      exit 0
15
+    end ;
16
+
17
+
18
+  (* Arguments are : infile(1) source-id(2) sink-id(3) outfile(4) *)
19
+  
20
+  let infile = Sys.argv.(1)
21
+  and outfile = Sys.argv.(4)
22
+  
23
+  (* These command-line arguments are not used for the moment. *)
24
+  and _source = int_of_string Sys.argv.(2)
25
+  and _sink = int_of_string Sys.argv.(3)
26
+  in
27
+
28
+  (* Open file *)
29
+  let graph = from_file infile in
30
+
31
+  (* Rewrite the graph that has been read. *)
32
+  let () = write_file outfile graph in
33
+
34
+  ()
35
+

+ 101
- 0
src/gfile.ml View File

@@ -0,0 +1,101 @@
1
+open Graph
2
+open Printf
3
+
4
+type path = string
5
+
6
+(* Format of text files:
7
+   % This is a comment
8
+
9
+   % A node with its coordinates (which are not used).
10
+   n 88.8 209.7
11
+   n 408.9 183.0
12
+
13
+   % The first node has id 0, the next is 1, and so on.
14
+
15
+   % Edges: e source dest label
16
+   e 3 1 11
17
+   e 0 2 8
18
+
19
+*)
20
+
21
+let write_file path graph =
22
+
23
+  (* Open a write-file. *)
24
+  let ff = open_out path in
25
+
26
+  (* Write in this file. *)
27
+  fprintf ff "%% This is a graph.\n\n" ;
28
+
29
+  (* Write all nodes (with fake coordinates) *)
30
+  n_iter_sorted graph (fun id -> fprintf ff "n %.1f 1.0\n" (float_of_int id)) ;
31
+  fprintf ff "\n" ;
32
+
33
+  (* Write all arcs *)
34
+  e_iter graph (fun id1 id2 lbl -> fprintf ff "e %d %d %s\n" id1 id2 lbl) ;
35
+
36
+  fprintf ff "\n%% End of graph\n" ;
37
+
38
+  close_out ff ;
39
+  ()
40
+
41
+(* Reads a line with a node. *)
42
+let read_node id graph line =
43
+  try Scanf.sscanf line "n %f %f" (fun _ _ -> new_node graph id)
44
+  with e ->
45
+    Printf.printf "Cannot read node in line - %s:\n%s\n%!" (Printexc.to_string e) line ;
46
+    failwith "from_file"
47
+
48
+(* Ensure that the given node exists in the graph. If not, create it. 
49
+ * (Necessary because the website we use to create online graphs does not generate correct files when some nodes have been deleted.) *)
50
+let ensure graph id = if node_exists graph id then graph else new_node graph id
51
+
52
+(* Reads a line with an arc. *)
53
+let read_arc graph line =
54
+  try Scanf.sscanf line "e %d %d %s"
55
+        (fun id1 id2 label -> new_arc (ensure (ensure graph id1) id2) id1 id2 label)
56
+  with e ->
57
+    Printf.printf "Cannot read arc in line - %s:\n%s\n%!" (Printexc.to_string e) line ;
58
+    failwith "from_file"
59
+
60
+(* Reads a comment or fail. *)
61
+let read_comment graph line =
62
+  try Scanf.sscanf line " %%" graph
63
+  with _ ->
64
+    Printf.printf "Unknown line:\n%s\n%!" line ;
65
+    failwith "from_file"
66
+
67
+let from_file path =
68
+
69
+  let infile = open_in path in
70
+
71
+  (* Read all lines until end of file. 
72
+   * n is the current node counter. *)
73
+  let rec loop n graph =
74
+    try
75
+      let line = input_line infile in
76
+
77
+      (* Remove leading and trailing spaces. *)
78
+      let line = String.trim line in
79
+
80
+      let (n2, graph2) =
81
+        (* Ignore empty lines *)
82
+        if line = "" then (n, graph)
83
+
84
+        (* The first character of a line determines its content : n or e. *)
85
+        else match line.[0] with
86
+          | 'n' -> (n+1, read_node n graph line)
87
+          | 'e' -> (n, read_arc graph line)
88
+
89
+          (* It should be a comment, otherwise we complain. *)
90
+          | _ -> (n, read_comment graph line)
91
+      in      
92
+      loop n2 graph2
93
+
94
+    with End_of_file -> graph (* Done *)
95
+  in
96
+
97
+  let final_graph = loop 0 empty_graph in
98
+
99
+  close_in infile ;
100
+  final_graph
101
+

+ 18
- 0
src/gfile.mli View File

@@ -0,0 +1,18 @@
1
+(* Read a graph from a file,
2
+ * Write a graph to a file. *)
3
+
4
+open Graph
5
+
6
+type path = string
7
+
8
+(* Values are read as strings. *)
9
+val from_file: path -> string graph
10
+
11
+(* Similarly, we write only a string graph.
12
+ * If necessary, use gmap (to be written by you) to prepare the input graph. *)
13
+val write_file: path -> string graph -> unit
14
+
15
+
16
+(* The format of files is compatible with the files generated by:
17
+   https://algorithms.discrete.ma.tum.de/graph-algorithms/flow-ford-fulkerson/index_en.html
18
+*)

+ 49
- 0
src/graph.ml View File

@@ -0,0 +1,49 @@
1
+type id = int
2
+
3
+type 'a out_arcs = (id * 'a) list
4
+
5
+(* A graph is just a list of pairs: a node & its outgoing arcs. *)
6
+type 'a graph = (id * 'a out_arcs) list
7
+
8
+exception Graph_error of string
9
+
10
+let empty_graph = []
11
+
12
+let node_exists gr id = List.mem_assoc id gr
13
+
14
+let out_arcs gr id =
15
+  try List.assoc id gr
16
+  with Not_found -> raise (Graph_error ("Node " ^ string_of_int id ^ " does not exist in this graph."))
17
+
18
+let find_arc gr id1 id2 =
19
+  let out = out_arcs gr id1 in
20
+  try Some (List.assoc id2 out)
21
+  with Not_found -> None
22
+
23
+let new_node gr id =
24
+  if node_exists gr id then raise (Graph_error ("Node " ^ string_of_int id ^ " already exists in the graph."))
25
+  else (id, []) :: gr
26
+
27
+let new_arc gr id1 id2 lbl =
28
+
29
+  (* Existing out-arcs *)
30
+  let outa = out_arcs gr id1 in
31
+
32
+  (* Update out-arcs.
33
+   * remove_assoc does not fail if id2 is not bound.  *)
34
+  let outb = (id2, lbl) :: List.remove_assoc id2 outa in
35
+  
36
+  (* Replace out-arcs in the graph. *)
37
+  let gr2 = List.remove_assoc id1 gr in
38
+  (id1, outb) :: gr2
39
+
40
+let n_iter gr f = List.iter (fun (id, _) -> f id) gr
41
+
42
+let n_iter_sorted gr f = n_iter (List.sort compare gr) f
43
+
44
+let n_fold gr f acu = List.fold_left (fun acu (id, _) -> f acu id) acu gr
45
+
46
+let e_iter gr f = List.iter (fun (id1, out) -> List.iter (fun (id2, x) -> f id1 id2 x) out) gr
47
+
48
+let e_fold gr f acu = List.fold_left (fun acu (id1, out) -> List.fold_left (fun acu (id2, x) -> f acu id1 id2 x) acu out) acu gr
49
+

+ 63
- 0
src/graph.mli View File

@@ -0,0 +1,63 @@
1
+
2
+(* Type of a directed graph in which arcs have labels of type 'a. *)
3
+type 'a graph
4
+
5
+(* Each node has a unique identifier (a number). *)
6
+type id = int
7
+
8
+exception Graph_error of string
9
+
10
+
11
+(**************  CONSTRUCTORS  **************)
12
+
13
+(* The empty graph. *)
14
+val empty_graph: 'a graph
15
+
16
+(* Add a new node with the given identifier.
17
+ * @raise Graph_error if the id already exists. *)
18
+val new_node: 'a graph -> id -> 'a graph
19
+
20
+(* new_arc gr id1 id2 lbl  : adds an arc from node id1 to node id2 with label lbl
21
+ * Both nodes must already exist in the graph.
22
+ * If the arc already exists, its label is replaced by lbl. 
23
+ * @raise Graph_error if node id1 or id2 does not exist in the graph. *)
24
+val new_arc: 'a graph -> id -> id -> 'a -> 'a graph
25
+
26
+
27
+(**************  GETTERS  *****************)
28
+
29
+(* node_exists gr id  indicates if the node with identifier id exists in graph gr. *)
30
+val node_exists: 'a graph -> id -> bool
31
+
32
+(* Type of lists of outgoing arcs of a node. 
33
+ * An arc is represented by a pair of the destination identifier and the arc label. *)
34
+type 'a out_arcs = (id * 'a) list
35
+
36
+(* Find the out_arcs of a node.
37
+ * @raise Graph_error if the id is unknown in the graph. *)
38
+val out_arcs: 'a graph -> id -> 'a out_arcs
39
+
40
+(* find_arc gr id1 id2  finds an arc between id1 and id2 and returns its label. Returns None if the arc does not exist. 
41
+* @raise Graph_error if id1 is unknown. *)
42
+val find_arc: 'a graph -> id -> id -> 'a option
43
+
44
+
45
+(**************  COMBINATORS, ITERATORS  **************)
46
+
47
+(* Iterate on all nodes, in no special order. *)
48
+val n_iter: 'a graph -> (id -> unit) -> unit
49
+
50
+(* Like n_iter, but the nodes are sorted. *)
51
+val n_iter_sorted: 'a graph -> (id -> unit) -> unit
52
+  
53
+(* Fold on all (unsorted) nodes. You must remember what List.fold_left does. *)
54
+val n_fold: 'a graph -> ('b -> id -> 'b) -> 'b -> 'b
55
+
56
+
57
+(* Iter on all arcs (edges) *)
58
+val e_iter: 'a graph -> (id -> id -> 'a -> unit) -> unit
59
+
60
+(* Fold on all arcs (edges) *)
61
+val e_fold: 'a graph -> ('b -> id -> id -> 'a -> 'b) -> 'b -> 'b
62
+
63
+

Loading…
Cancel
Save