AE1PGP Coursework 2 – Matrix functions (Haskell)
Introduction
This is the second AE1PGP Coursework. It is worth 10% of the module mark for this module. It requires you to use Functional Programming and the Haskell programming language to write functions that can be used to manipulate mathematical matrices.
The deadline for this exercise is 18:00 on Thursday 17th May 2017.
Read the entire document before beginning the exercise.
If you have any questions about this exercise, please ask after a lecture, in a lab, or on the Q&A forum on Moodle. If any questions require this exercise to be clarified then this document will be updated and everyone will be notified via Moodle.
Version History
· Version 1.0 – 2018-05-03 – Original version.
· Version 1.1 – 2018-05-07 – Version number bumped due to confusion over Q7 typo.
Submission
You must submit a single file containing a Haskell script with all your code for this exercise. The file must be named after your student ID number (ie, 6512345.hs) and must not require any other functions outside of the standard Prelude, must work on the version of ghci installed on cslinux, and must not use any ghci extensions to Haskell. The first line of each file should be a comment which contains your student ID number, username, and full name, of the form:
— 6512345 zy12345 Joe Blogs
I will copy your file into a new directory. Your script must load and compile in ghci without warnings or errors when I use the command
ghci 6512345.hs
This command will be run on our Linux server cslinux. If it does not compile, for any reason, then you will lose all the marks for testing (common reasons in the past have been submitting a file with the wrong filename, or developing your solution on your personal computer without having tested it on our Linux server). If the file compiles but has warnings then you will lose some marks for not correcting the warnings.
The Haskell script file should be uploaded to the Coursework 2 Submission link on the AE1PGP Moodle page. You may submit as many times as you wish before the deadline (the last submission before the deadline will be used). After the deadline has passed, if you have already submitted your exercise then you will not be able to submit again. If you have not already submitted then you will be allowed to submit once.
Remember that you can only access the Linux server from the designated labs in SEB and SSB and that these labs are also heavily booked for teaching. Do not wait until the last moment to submit as you may find you cannot get into any of the rooms to access your source code files. Not being able to access the machine due to lack of planning is not an extenuating circumstance.
Late submissions: AE1PGP late submission policy is different from the standard university policy. Late submissions will lose 5 percentage points per hour, rounded up to the next whole hour. This is to better represent the large benefit a small amount of extra time can give at the end of a programming exercise. No late submissions will be accepted more than 24 hours after the exercise deadline. If you have extenuating circumstances you should file them before the deadline.
Plagiarism
You should complete this coursework on your own. Anyone suspected of plagiarism will be investigated and punished in accordance with the university policy on plagiarism (see your student handbook and the University Quality Manual). This may include a mark of zero for this coursework.
You should write the source code required for this assignment yourself. If you use code from other sources (books, web pages, etc), you should use comments to acknowledge this (and marks will be heavily adjusted down according to the amount of original work).
You must not copy or share source code with other students. You must not work together on your solution. You can informally talk about higher-level ideas but not to a level of detail that would allow you all to create the same source code.
It is your responsibility to keep your submission private. Do not share personal computers, usb drives, or any other circumstances which could reasonably be assumed to risk another student gaining access to your files.
Remember, it is quite easy for experienced lecturers to spot plagiarism in source code. If you are having problems you should ask questions rather then plagiarize. If you are not able to complete the exercise then you should still submit your incomplete program as that will still get you some of the marks for the parts you have done (but make sure your incomplete solution compiles and partially runs!).
Marking
Each question will be marked out of 2 marks, giving a maximum possible mark of 20. 1 mark will be awarded for correctness, 1 mark will be awarded for programming style. In particular, you should aim to make your definitions as simple, clear, and elegant as possible.
If a function specifies that it should use another function and you do not do that, you will receive 0/2 for that question.
If a function does not have the type given in the coursework, you will receive 0/2 for that question.
If a function does not have the name given in the coursework, you will receive 0/2 for that question.
The same principle applies to other situations; do not change things from the coursework.
Late Submissions: see submission section above.
Task
One way to represent a matrix in Haskell is to use nested lists. In this coursework, we will use the following representation: a r-by-c matrix containing values of type a has r rows and c columns and is represented as a list of r lists of c values of type a. Given as a Haskell type, this is:
type Matrix a = [[a]]
Some examples are:
— 4 x 2 matrix
eg1 :: Matrix Int
eg1 = [ [1, 3],
[0, 5],
[-3, 4],
[2, 2] ]
— 2 x 3 matrix
eg2 :: Matrix Int
eg2 = [ [3, 1, 4],
[-1, 0, 5] ]
— 0 x 0 matrix, an empty matrix
eg3 :: Matrix Int
eg3 = [ [] ]
— 2×1 matrix
eg4 :: Matrix Int
eg4 = [ [2],
[3] ]
eg5 :: Matrix Double
eg5 = [ [6.2, 4.3, 7.4, -7.3],
[9.3, 1.2, 0.4, -6.2] ]
Note that in our definition, the empty matrix with no values is a valid matrix represented as [[]], a list containing one empty list (which is not the same as [] which is just an empty list). You can assume that all Matrix inputs are correctly formed (each nested list is the same length, ensuring a square or rectangular matrix).
The aim of this coursework is to write a Haskell script to manipulate matrices using this representation. Lines starting with > are lines typed into the the ghci REPL. They are not lines which should appear in your Haskell script.
You should copy both the Matrix definition and the example matrices above into your script file. Do not change their definitions. Implement the functions below on your own. Do not import any other Haskell modules – you can only use the Prelude functions and functions you write yourself. You may write any helper functions you need and you may use the answers to any of the questions below in subsequent answers. You may wish to consult the Wikipedia page on Matrices if you wish to refresh your knowledge of basic matrix operations.
Functions
1. Define a function
2. empty :: Matrix a -> Bool
that returns True if the matrix is empty and False otherwise. For example:
> empty eg1
False
> empty eg2
False
> empty eg3
True
3. Define a function
4. rowCount :: Matrix a -> Int
that returns the number of rows in the matrix. Hint: this is easy, except in the case of an empty matrix (the empty matrix will be a special case of many functions in this coursework so remember to consider that situation). For example:
> rowCount eg1
4
> rowCount eg2
2
> rowCount eg3
0
5. Define a function
6. columnCount :: Matrix a -> Int
that returns the number of columns in the matrix. For example:
> columnCount eg1
2
> columnCount eg2
3
> columnCount eg3
0
7. Define a function
8. element’ :: Matrix a -> Int -> Int -> a
that returns the value in the matrix at the given row and column position. For this function you should assume that the row and column position will always be valid for the given matrix. For example:
> element’ eg1 0 1
3
> element’ eg1 2 0
-3
> element’ eg2 1 1
0
9. Define a function
10. element :: Matrix a -> Int -> Int -> Maybe a
that returns the value in the matrix at the given row and column position, or Nothing if that is not a valid position for the given matrix.
> element eg1 0 0
Just 1
> element eg1 0 1
Just 3
> element eg1 0 2
Nothing
11. The type
data Either a b = Left a | Right b
type can be used to represent values of either one of two potential types. It is often used in Haskell to represent the possibility of an error or an actual answer. This is similar to the Maybe type, but in the “error” case, Maybe can only say Nothing with no extra information about the reason why. Haskell convention is that the Left constructor is used to communicate information about the error and the Right constructor is used to give the actual answer (the way to remember is that “Right” is a synonym for “correct”). The Either type is part of the Standard Prelude.
Given the type:
data MatrixErr = NegIndex — represents an index being negative
| RowTooLarge — represents a row index being too large for the matrix
| ColTooLarge — represents a column index being too large for the matrix
deriving Show
define the function
elementEth :: Matrix a -> Int -> Int -> Either MatrixErr a
that returns the value in the matrix at the given row and column position, or a Left value with the appropriate error value from MatrixErr. Note: you will need to copy the definition of MatrixErr into your script. If multiple error conditions are true, you can return any of the appropriate errors.
> elementEth eg1 0 1
Right 3
> elementEth eg1 0 0
Right 1
> elementEth eg1 0 5
Left ColTooLarge
> elementEth eg1 5 0
Left RowTooLarge
> elementEth eg1 0 (-1)
Left NegIndex
12. Define a function
13. identity :: Int -> Maybe (Matrix Int)
that returns the Identity matrix for the given size. An Identity matrix is a square matrix which has zero for all values, except the values on the top-left to bottom-right diagonal which are all one. If the size is less than 1, then the identity matrix isn’t defined and Nothing should be returned. For example:
> identity 2
Just [[1,0],[0,1]]
> identity 5
Just [[1,0,0,0,0],[0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1]]
> identity 0
Nothing
> identity (-1)
Nothing
14. Define a function
15. addMat :: Num a => Matrix a -> Matrix a -> Maybe (Matrix a)
which takes two matrices and adds them together. Matrices may only be added together if they have the same number of rows and columns. If they do not, return Nothing. If they do, the values in the result matrix are the addition of the two values at the same positions in the two argument matrices. For example:
> addMat eg1 eg1
Just [[2,6],[0,10],[-6,8],[4,4]]
> addMat eg1 eg2
Nothing
> addMat eg3 eg3
Nothing
16. Define a higher-order function
17. mapMat :: (a -> b) -> Matrix a -> Matrix b
that takes a function and a matrix and returns the matrix with the function applied to each value in the matrix. This should be the Matrix equivalent of map over a list. For example:
> mapMat (+1) eg5
[[7.2,5.3,8.4,-6.3],[10.3,2.2,1.4,-5.2]]
> mapMat odd eg2
[[True,True,False],[True,False,True]]
> mapMat (+1) eg3
[[]]
18. Define a function
19. compress :: Num a => Matrix a -> Matrix a
that takes an r-by-c Matrix and returns a new r-by-1 Matrix where the values in each row have been replaced by a length 1 list containing the total value from adding together all the values in that row. For example:
> compress eg1
[[4],[5],[1],[4]]
> compress eg5
[[10.599999999999998],[4.7]]
> compress eg3
[[]]
END