Setup and compilation

  • To begin with, create a directory for this project.
  • In order to save time, a few modules have already been written.
    Download these files (save link target as...): graph.mli, graph.ml, gfile.mli, gfile.ml, ftest.ml.
    We will explain these files soon.
  • Since you like downloading stuff, you may also save these files: graph1, graph1.svg.
  • The main file is ftest.ml so, we try to compile it, as taught in lesson 6:
    ocamlc -o ftest ftest.ml(try it) -o ftest indicates the name of the executable.
    Error: Unbound module Gfile
  • Look at the beginning of ftest.ml: we open a module Gfile.
    Lesson learnt: The modules used by the program must be compiled before the program.
  • ocamlc -c graph.ml gfile.ml-c means compile only, no executable is built.
    Error: Could not find the .cmi file for interface graph.mli
  • A file named graph.mli exists. It is the interface of module Graph
    Lesson learnt: foo.mli must be compiled before foo.ml.
  • ocamlc -c graph.mli graph.ml gfile.mli gfile.ml
    something works, at last.
  • See which files have been produced: ls -l (or more professionally, ls -alhv)
    Compilation of C files
    file.c compiled into file.o (by gcc)
    Compilation of OCaml files
    file.ml compiled into file.cmo (by ocamlc)
    file.mli compiled into file.cmi (by ocamlc)
  • ocamlc -o ftest ftest.ml
    Error: Required module `Gfile' is unavailable
  • This error does not come from the compiler but from the linker.
    Lesson learnt: When linking (building an executable), the modules must be explicitly given.
  • ocamlc -o ftest graph.cmo gfile.cmo ftest.ml
    Note: When linking, the modules must be explicitly given in the order of dependencies.
  • You have obtained an executable ftest.
  • ./ftest (You get a usage message.)
  • Whenever you modify a file, you will have to recompile it as well as all the files that depend on it.

A few more things about compilation

  • Recompiling everything is boring. This is why tools such as GNU make (for almost everything) or ant (for Java) exist. Actually, the java compiler already takes care of dependencies.
  • A couple of build tools exist for OCaml too. Let us give a try to ocamlbuild.
  • First, remove all the compilation files: rm -f *.cmi *.cmo ftest(In order to avoid confusion between tools, ocamlbuild refuses to work if it finds compiled files.)
  • Use ocamlbuild ftest.byte to build the bytecode executable, or ocamlbuild ftest.native to build the native executable. (You need only one of them).
    In case you ask, the compiled files can be found in a directory named _build.
  • ./ftest.byte or ./ftest.native(still prints a usage message, though).

Discover the project modules

  • You cannot compile the project using ocaml-top. Use any text editor instead, e.g. emacs (configured as explained at the beginning of OCaml - Lesson 6) or visual studio code, if you know how to launch it.
    You may enjoy A. Bit-Monnot's setup configuration for VSCode : ocaml-maxflow-project on github.
  • The base project contains two modules and a main program:
    • graph.mli and graph.ml which define a module Graph
    • gfile.mli and gfile.ml which define a module Gfile
    • ftest.ml, the main program.
  • Look at the interfaces of Graph (graph.mli) and Gfile
    All functions are already implemented in graph.ml and gfile.ml
    You must not modify the module Graph , but you will have to create new modules and to modify Gfile.
  • Try to understand the graph file format (look at the example graph1 and read quickly gfile.ml)
  • To ease the writing of algorithms, write a new module Tools , with the following signature (the signature must be in file tools.mli) :
    Interface file
    open Graph val clone_nodes: 'a graph -> 'b graph val gmap: 'a graph -> ('a -> 'b) -> 'b graph val add_arc: int graph -> id -> id -> int -> int graph
    Implementation file
    (* Yes, we have to repeat open Graph. *) open Graph (* assert false is of type ∀α.α, so the type-checker is happy. *) let clone_nodes gr = assert false let gmap gr f = assert false ...
    • clone_nodes gr returns a new graph having the same nodes than gr, but no arc. (code : one line)
      In order to find your errors more quickly, you may add an annotation : let clone_nodes (gr:'a graph) = ...
    • gmap gr f maps all arcs of gr by function f. (⩽3 lines)
    • add_arc g id1 id2 n adds n to the value of the arc between id1 and id2. If the arc does not exist, it is created.
  • In order to test, you may use an online graph editor (Create a graph), and download your graphs.
  • In order to visualize graphs, we will use the famous Graphviz library.
    Write a new function export in Gfile which writes a string graph in dot format (the format understood by graphviz). To understand the expected format, you just have to look at this example (click on the picture to get the source file).
    To generate an image from a dot file, you can use:
    dot -Tsvg your-dot-file > some-output-file.svg

To be done

Some technical support

Debugging

In order to activate debugging:
  • You must create a file named _tags in your project directory (this file is read by ocamlbuild).
    Add the following line:
    true: debug
  • In the terminal, set the variable OCAMLRUNPARAM to b (means: backtrace)
    export OCAMLRUNPARAM="b"then, you can launch your program.

Packages

  • If you use special modules, such as Unix (package name: unix) or Graphics (package name: graphics), you need to tell ocamlbuild to use the corresponding package. In the file _tags, add the following line:
    true: package(unix)
    or
    true: package(unix,graphics,...)
  • In order to build, you have to use:ocamlbuild -use-ocamlfind myprog.native
    ocamlfind (findlib) is a tool written by a prolific OCaml developper: Gerd Stolpmann. It is a library manager for OCaml.

How to start writing the Ford-Fulkerson algorithm (alert: SPOILER)

If you are stuck, you may start by writing a function which finds a path in a graph:

(* A path is a list of nodes. *) type path = id list (* find_path gr forbidden id1 id2 * returns None if no path can be found. * returns Some p if a path p from id1 to id2 has been found. * * forbidden is a list of forbidden nodes (they have already been visited) *) find_path: int graph -> id list -> id -> id -> path option