Sometimes, as we already saw with Sortacle, a where block just isn’t enough. When you are testing complex functions, or perhaps even relations (as in the case of this assignment), you will need to put time and effort into building testing oracles.
In this assignment, you will develop oracles to test purported solutions to the Stable Marriage Problem. You can learn more about the problem from Wikipedia, but a summary of the problem is as follows:
Assume you have two sets, A and B, each with n members. Every member in A wants to be matched with exactly one member of B and vice versa. Every member of each set ranks its preference for being matched with each member of the other set by assigning each one a unique number between 0 and n-1 (i.e., providing a total ordering of members of the other set).
As an application of this problem, imagine matching companies with candidates. Each company will keep an internal ranking of all the candidates, based on who they would prefer to hire (we assume that only one candidate will be hired per company). Each candidate also has an opinion about where they want to work and therefore ranks each company as well. A programmer is asked to design and implement a program that generates n pairings of n companies with n candidates. The program’s generated “hires” are stable if there are no two members, one from each set, that would prefer each other to their current match.
There are many problems of this nature. Consider assigning TAs to classes; matching residents with hospitals; pairing students for homeworks; and much more. Of course, some of these problems represent a slight variation on the theme (maybe the companies don’t rank every candidate, or are allowed to give some of them the same rank; maybe you have only partial information for making the assignment; etc.). Ultimately, however, this problem in its many guises has wide application.
Being confident that your software is correct involves more than just writing code you think is right. However, it’s difficult to test complex software entirely by hand. Naturally, a computer scientist’s solution to this problem is to automate testing. Your job in this assignment is to build an automated testing oracle for a hypothetical solution to the stable marriage problem.
Your oracle’s job is to generate and feed test inputs to this solution, and test the correctness of the output. In the past, excluding Sortacle, you did this by comparing the output to a precomputed correct answer. This assumes two things: that there is only one right answer, and that it is easy for you to find it. In the real world either or both of these can and will be false. (How do you know what the right answer to an arbitrary instance of the problem is if the original problem was to write a program to find it?)
You’ve got your work cut out for you: we give you a correct solution to the stable marriage problem to help you write and test your oracle. You submit your oracle, and we unleash hell’s own fury of incorrect solutions at it, each with its own subtle flaw.It’s worth thinking about when we can conclude that we’re “done”. We ought to have multiple correct matchmakers because there are multiple correct outputs for each input. How many suffice?
You have access to the data structures and functions Hire, hire, is-hire, and matchmaker from the support code, which is automatically imported in the template given below.
| hire(company :: Number, candidate :: Number)
fun matchmaker(companies :: List<List<Number>>,
candidates :: List<List<Number>>)
Each purported solution will be a function like matchmaker that
consumes two arguments, both of which are List<List<Number>> in
which every list (both inner and outer, for both lists) is of the same
size (call it n but n is naturally not fixed). The first list
provides the companies’ preferences, the second one those for the
candidates (though you should convince yourself it doesn’t matter
which one is which). Each inner list corresponds to a specific
candidate or company—
To do well on this assignment, you will want to spend time considering all the different ways that output could be either invalid or inconsistent with the original problem statement. This will be helpful when testing your oracle. Be thorough! That’s the name of the game when testing.
Write a function named generate-input that generates input. Above, we have described only the shapes of the inputs; you will have to infer the constraints we’ve left out. Your function must generate a list of rankings for a specified number of either candidates or companies. Make sure that your function always generate random values, so that over many iterations the oracle can test matchmaker with a broad spectrum of inputs.
fun generate-input(n :: Number) -> List<List<Number>>:
fun is-valid(companies :: List<List<Number>>,
candidates :: List<List<Number>>,
m :: Set<Hire>)
Using generate-input and is-valid, write a function named oracle that tests whether an algorithm is a valid solution to the stable marriage problem.
Remember, an algorithm may sometimes produce a correct solution even if it is an incorrect algorithm. At the same time, there are numerous ways for an algorithm to return an incorrect solution. Therefore, your oracle should try to only return true if an algorithm seems to always produce a stable set of hires (where “always” is quantified by some number of tests).
a-matchmaker :: (List<List<Number>>, List<List<Number>>
For this assignment, we are allowing you to use Pyret’s built-in List, Number, and Set libraries (in addition to anything in the course programming language instructions). Feel free to use any set functions or methods, including list-to-list-set and .to-list.
oracle-code.arr and oracle-tests.arr: form