|
| 1 | +# Modules Modelling Matrices |
| 2 | +We define the signature |
| 3 | + |
| 4 | +```ocaml |
| 5 | +module type Ring = sig |
| 6 | + type t |
| 7 | + val zero : t |
| 8 | + val one : t |
| 9 | + val add : t -> t -> t |
| 10 | + val mul : t -> t -> t |
| 11 | + val compare : t -> t -> int |
| 12 | + val to_string : t -> string |
| 13 | +end |
| 14 | +``` |
| 15 | + |
| 16 | +of an [algebraic ring structure](https://en.wikipedia.org/wiki/Ring_(mathematics)), where `add` and `mul` are the two binary operations on the set of elements of type `t`. Values `zero` and `one` represent the identity elements with respect to *addition* and *multiplication*. The function `compare` establishes a total order on the elements of the set. The auxiliary function `to_string` is used to generate a string representation of an element. This signature is available in `ring/libRing.ml`. |
| 17 | + |
| 18 | +Furthermore, a matrix is given by the signature: |
| 19 | + |
| 20 | +```ocaml |
| 21 | +module type Matrix = sig |
| 22 | + type elem |
| 23 | + type t |
| 24 | + val create : int -> int -> t |
| 25 | + val identity : int -> t |
| 26 | + val from_rows : elem list list -> t |
| 27 | + val row_count : t -> int |
| 28 | + val col_count : t -> int |
| 29 | + val set : int -> int -> elem -> t -> t |
| 30 | + val get : int -> int -> t -> elem |
| 31 | + val transpose : t -> t |
| 32 | + val add : t -> t -> t |
| 33 | + val mul : t -> t -> t |
| 34 | + val to_string : t -> string |
| 35 | +end |
| 36 | +``` |
| 37 | + |
| 38 | +Here, `elem` and `t` represent the types of the elements of the matrix and the matrix itself, respectively. The functions have the following semantics: |
| 39 | + |
| 40 | +- `create n m` creates an empty (all zeroes) $n\times m$ matrix. |
| 41 | +- `identity n` creates an $n\times n$ identity matrix. |
| 42 | +- `row_count m` and `col_count m` return the number of rows and columns in `m`, respectively. |
| 43 | +- `from_rows l` creates a matrix from the given list of rows (lists of elements). You may assume that `l` is non-empty and all lists have the same length. |
| 44 | +- `set r c v m` sets the element at row `r` and column `c` in matrix `m` to value `v`. |
| 45 | +- `get r c m` returns the element at row `r` and column `c` from matrix `m`. |
| 46 | +- `transpose m` returns the transpose of matrix `m`. |
| 47 | +- `add a b` adds matrices `a` and `b` component-wise. |
| 48 | +- `mul a b` computes the matrix multiplication of `a` and `b`. |
| 49 | +- `to_string m` produces a string representation of `m`. Columns shall be separated by single whitespaces and rows by a newline character `\n`. |
| 50 | + |
| 51 | +This signature is available in `matrix/libMatrix.ml`. |
| 52 | + |
| 53 | +Perform these tasks: |
| 54 | + |
| 55 | +- **IntRing** |
| 56 | + In the file `intring/libIntRing.ml`: Implement a module `IntRing` that implements the `Ring` signature for the `int` type. [0.5 points] |
| 57 | +- **FloatRing** |
| 58 | + In the file `floatring/libFloatRing.ml`: Implement a module `FloatRing` that implements the `Ring` signature for the `float` type. [0.5 points] |
| 59 | +- **FiniteRing** |
| 60 | + In the file `finitering/libFiniteRing.ml`: Define a signature `FiniteRing` that extends the `Ring` signature with a value `elems` that represents a list of all elements of the ring's finite set. Make sure that everything in the `Ring` signature is part of `FiniteRing` (without copy-pasting them)! [0.5 points] |
| 61 | +- **BoolRing** |
| 62 | + In the file `boolring/libBoolRing.ml`: Implement a module `BoolRing` that implements the `FiniteRing` signature for the `bool` type. The multiplication in a bool ring is *conjunction*, the addition is the *exclusive or*. [0.5 points] |
| 63 | +- **SetRing** |
| 64 | + In the file `setring/libSetRing.ml`: Implement a functor `SetRing` that models a ring over the power set of the set in the `FiniteRing` passed as the functor's argument. `SetRing` has to implement the `Ring` signature. We use union and intersection as `add` and `mul` operations. The representation produced by `to_string` is $\{e_1,\dots,e_n\}$. For `compare` we use a bit of an unconventional order. First, we order the elements in the set by their own order and then compare the sets lexicographically, e.g. $\emptyset<\{1,2,3\}<\{2\}<\{2,3\}<\{3\}$. The type `t` in `SetRing` should be `D.t list`, if `D` is the module that is passed to the functor. Make sure that this information about `t` is exposed to the outside. [1.5 points] |
| 65 | +- **DenseMatrix** |
| 66 | + In the file `densematrix/libDenseMatrix.ml`: Implement a functor `DenseMatrix` that satisfies the `Matrix` signature. The argument of the functor is a `Ring` that provides everything to implement the matrix operations. The `DenseMatrix` is supposed to store all elements in the matrix in a list of rows, which are lists of elements. [3 points] |
| 67 | +- **SparseMatrix** |
| 68 | + In the file `sparsematrix/libSparseMatrix.ml`: Implement a functor `SparseMatrix` that satisfies the `Matrix` signature. The argument of the functor is a `Ring` that provides everything to implement the matrix operations. The `SparseMatrix` stores only those elements of the matrix that are non-zero. More precisely, the representation of a sparse matrix must have memory usage proportional to the number of non-zero entries in the matrix ($\mathcal{O}(n)$). [3.5 points] |
| 69 | + |
| 70 | +*Hint: You may assume that all inputs are valid, e.g. no out-of-bounds access. If you wish to handle invalid input, just throw an exception.* |
| 71 | + |
| 72 | +*Hint: The function implementations need not be particularly efficient or tail-recursive, just keep your implementations simple where possible.* |
| 73 | + |
| 74 | +## Assignment Repository Layout and Tests |
| 75 | +Testing your solutions is more difficult this week as a test cannot compile if any module it depends on is not correctly implemented or a module signature is incorrectly defined. Thus, we have separated each module into its own `dune` library (one per directory). This way, we can compile and test each library independently. However, since some tasks require implementations of previous tasks, we have introduced dependencies between the libraries. If building any dependency of a library fails, we cannot test that library (and the corresponding task will receive zero points) as building it would fail too. You can consult the `dune` files or the graph below to see exactly what the dependencies are. If you don't want to submit a solution for some task, check Artemis to make sure that the public tests for the tasks that you do want to submit pass. In particular, look for the `*:build` tests, which pass only if that library can be tested and tell you why it cannot otherwise. |
| 76 | + |
| 77 | +Write any code you want to share between tasks in `common/common.ml`. |
| 78 | + |
| 79 | +To not cause issues with the tests, you should not change the provided `dune` files (the tests on Artemis will ignore your `dune` files). |
| 80 | + |
| 81 | +<details> |
| 82 | + <summary>Overview of dependencies</summary> |
| 83 | + Example: If your implementation of `libFiniteRing` can't compile, then `libBoolRing` and `libSetRing` will not be tested and receive zero points. |
| 84 | + |
| 85 | + Example: You can reuse any implementations you write in `libMatrix` in `libDenseMatrix` and `libSparseMatrix`, but don't change the `Matrix` signature. |
| 86 | + |
| 87 | + *Tip: You can generate similar graphs for any dune project by installing dune-deps with opam install dune-deps, then running e.g. `dune-deps | dot -Tpng > deps.png`* |
| 88 | +</details> |
0 commit comments