| 1 | component ListExercise |
|---|
| 2 | export Executable |
|---|
| 3 | import List.{...} |
|---|
| 4 | |
|---|
| 5 | (* The example graph in this exercise is cribbed from |
|---|
| 6 | "Fast Decision Procedures Based on Congruence Closure", |
|---|
| 7 | by G. Nelson and D. C. Oppen. They give a *far* more efficient |
|---|
| 8 | algorithm for solving the same problem! |
|---|
| 9 | |
|---|
| 10 | The initial graph looks like this: |
|---|
| 11 | |
|---|
| 12 | f |
|---|
| 13 | | |
|---|
| 14 | f |
|---|
| 15 | | |
|---|
| 16 | f g |
|---|
| 17 | | / \ |
|---|
| 18 | f g | |
|---|
| 19 | | / \ | |
|---|
| 20 | f g | | |
|---|
| 21 | | / \|/ |
|---|
| 22 | a b |
|---|
| 23 | |
|---|
| 24 | Initially we assert that the following nodes are equivalent: |
|---|
| 25 | a = f(f(f(a))) |
|---|
| 26 | a = f(f(f(f(f(a))))) |
|---|
| 27 | a = g(a, b) |
|---|
| 28 | |
|---|
| 29 | We end up discovering that all the nodes except b are equivalent! |
|---|
| 30 | *) |
|---|
| 31 | |
|---|
| 32 | (* EXERCISE 1: samePairs *) |
|---|
| 33 | |
|---|
| 34 | (* Fill in the function samePairs that checks that its two input lists |
|---|
| 35 | contain the same pairs of values (either list may contain |
|---|
| 36 | duplicates, and order doesn't matter). |
|---|
| 37 | |
|---|
| 38 | You will find BIG AND, BIG OR, and IN operators useful here. |
|---|
| 39 | Also, note that AND: and OR: yield short-circuit evaluation. |
|---|
| 40 | *) |
|---|
| 41 | samePairs(a: List[\ (Node,Node) \], b: List[\ (Node,Node) \]): Boolean = |
|---|
| 42 | fail("Ex 1, samePairs, not yet started; " |
|---|
| 43 | "the other exercises won't check without it!") |
|---|
| 44 | |
|---|
| 45 | (* EXERCISE 2: uniq *) |
|---|
| 46 | |
|---|
| 47 | (* Fill in the function uniq that is passed a list of Nodes, and removes elements of the |
|---|
| 48 | list that are equal to preceding elements. |
|---|
| 49 | |
|---|
| 50 | There are many ways to do this; a particularly nice (parallel) one |
|---|
| 51 | makes use of the split() method on lists. Another possibility is to use |
|---|
| 52 | |xs| and indexing to extract sublists: x[0], x[1:], and so forth. *) |
|---|
| 53 | uniq(xs: List[\(Node,Node)\]) = |
|---|
| 54 | fail("Ex 2, uniq, not yet started.") |
|---|
| 55 | |
|---|
| 56 | (* EXERCISE 3: symmetricTransitiveClosure *) |
|---|
| 57 | |
|---|
| 58 | (* Fill in the function symmetricTransitiveClosure, that given a list |
|---|
| 59 | of pairs computes the symmetric transitive closure of that list. |
|---|
| 60 | The list is symmetrically closed if for each pair (x,y) the list |
|---|
| 61 | also contains (y,x). The list is transitively closed if, when it |
|---|
| 62 | contains (x,y) and (y,z), it also contains (x,z) when x =/= z. You |
|---|
| 63 | can decide whether to include reflexive pairs (x,x) as well; this |
|---|
| 64 | exercise is somewhat easier if you do so. |
|---|
| 65 | |
|---|
| 66 | Note that you'll need to decide when to terminate. You can use |
|---|
| 67 | samePairs to decide this, or if you're careful you can use list |
|---|
| 68 | equality xs=ys (which is cheaper) or a check for emptiness |
|---|
| 69 | (xs.isEmpty, cheaper still). |
|---|
| 70 | |
|---|
| 71 | You may find this problem easiest if you symmetrically close a, |
|---|
| 72 | then compute the transitive closure of the result. We've provided |
|---|
| 73 | an appropriate type signature to help you along. *) |
|---|
| 74 | symmetricTransitiveClosure(a: List[\ (Node,Node) \]): List[\ (Node,Node) \] = |
|---|
| 75 | fail("Ex 3, symmetricTransitiveClosure, not yet started.") |
|---|
| 76 | |
|---|
| 77 | (* Assumption: a is symmetrically closed. *) |
|---|
| 78 | transitiveClosure(a: List[\ (Node,Node) \]): List[\ (Node,Node) \] = |
|---|
| 79 | fail("transitiveClosure is used but not implemented.") |
|---|
| 80 | |
|---|
| 81 | (* EXERCISE 4: equivalence by operator *) |
|---|
| 82 | |
|---|
| 83 | (* a list of Nodes, find the symmetric, transitively |
|---|
| 84 | closed list of Nodes with the same operator. The operator of a |
|---|
| 85 | node n can be obtained by writing n.op (so you'll want to write |
|---|
| 86 | code of the form n.op = m.op) |
|---|
| 87 | *) |
|---|
| 88 | operatorClosure(nodes: List[\Node\]): List[\(Node,Node)\] = |
|---|
| 89 | fail("Ex 4, operatorClosure, not yet started.") |
|---|
| 90 | |
|---|
| 91 | (* EXERCISE 5: graph equivalence closure *) |
|---|
| 92 | |
|---|
| 93 | (* Given a graph, we define its *equivalence closure*, a |
|---|
| 94 | symmetric, transitively closed list for which the following are true: |
|---|
| 95 | If (a,b) is in initEquivs, it is in the equivalence closure. |
|---|
| 96 | If two nodes a and b have the same operator (they're in the operatorClosure), |
|---|
| 97 | and corresponding pairs of children are equal or in the equivalence closure, |
|---|
| 98 | then (a,b) are also in the equivalence closure. |
|---|
| 99 | For this exercise you'll want to use transitiveClosure, operatorClosure, |
|---|
| 100 | and zip. |
|---|
| 101 | xs.zip[\T\](ys) generates corresponding pairs of elements from xs and ys; for example |
|---|
| 102 | <| x | (x,y) <- <|1,2,3|>.zip[\ZZ32\]<|1,4,3,2|>, x=y |> |
|---|
| 103 | generates the list of elements <1,3>. |
|---|
| 104 | To obtain a list of the children of node n, simply write n.children. |
|---|
| 105 | *) |
|---|
| 106 | equivalenceClosure(graph: List[\Node\], initEquivs: List[\(Node,Node)\]): List[\(Node,Node)\] = |
|---|
| 107 | fail("Ex 5, equivalenceClosure, not yet started.") |
|---|
| 108 | |
|---|
| 109 | (************************************************************ |
|---|
| 110 | * Built-in utility code down here |
|---|
| 111 | ************************************************************) |
|---|
| 112 | |
|---|
| 113 | value object Node(name: String, op: String, children: List[\Node\]) extends Equality[\Node\] |
|---|
| 114 | getter asString(): String = name |
|---|
| 115 | opr =(self, other:Node): Boolean = name = other.name |
|---|
| 116 | end |
|---|
| 117 | |
|---|
| 118 | node(op: String, children: List[\Node\]): Node = do |
|---|
| 119 | name = op "(" (BIG ||[c <- children] (", " c.name))[2:] ")" |
|---|
| 120 | Node(name, op, children) |
|---|
| 121 | end |
|---|
| 122 | |
|---|
| 123 | node(op: String, childNodes: Node...): Node = |
|---|
| 124 | node(op, list[\Node\](childNodes)) |
|---|
| 125 | |
|---|
| 126 | leaf(name: String): Node = Node(name, name, <|[\Node\] |>) |
|---|
| 127 | |
|---|
| 128 | showEquiv(eqvs: List[\(Node,Node)\]): String = |
|---|
| 129 | BIG //[(x,y) <- eqvs, |x.name| < |y.name| OR: x.name < y.name] x.name " = " y.name |
|---|
| 130 | |
|---|
| 131 | (************************************************************ |
|---|
| 132 | * TESTS |
|---|
| 133 | ************************************************************) |
|---|
| 134 | |
|---|
| 135 | run(): () = do |
|---|
| 136 | a = leaf("a") |
|---|
| 137 | fa = node("f",a) |
|---|
| 138 | ffa = node("f",fa) |
|---|
| 139 | fffa = node("f",ffa) |
|---|
| 140 | ffffa = node("f",fffa) |
|---|
| 141 | fffffa = node("f",ffffa) |
|---|
| 142 | b = leaf("b") |
|---|
| 143 | gab = node("g",a,b) |
|---|
| 144 | gfab = node("g",fa,b) |
|---|
| 145 | ggfab = node("g",gfab,b) |
|---|
| 146 | graph = <|[\Node\] a,fa,ffa,fffa,ffffa,fffffa,b,gab,gfab,ggfab |> |
|---|
| 147 | initEquivs = <|[\(Node,Node)\] (a,fffa), (a,fffffa), (a,gab)|> |
|---|
| 148 | iE = <|[\(Node,Node)\] (a,gab), (a,fffa), (a,fffffa) |> |
|---|
| 149 | eE = <|[\(Node,Node)\] |> |
|---|
| 150 | (* EXERCISE 1: samePairs *) |
|---|
| 151 | assert(samePairs(iE, iE), true, "Ex 1: Should be samePairs",iE,iE) |
|---|
| 152 | assert(samePairs(initEquivs, iE), true, "Ex 1: Should be samePairs",initEquivs,iE) |
|---|
| 153 | assert(samePairs(eE, eE), true, "Ex 1: Should be samePairs",eE,eE) |
|---|
| 154 | iE2 = initEquivs || iE |
|---|
| 155 | assert(samePairs(iE, iE2), true, "Ex 1: Should be samePairs",iE,iE2) |
|---|
| 156 | assert(samePairs(eE, iE), false, "Ex 1: Not samePairs", eE, iE) |
|---|
| 157 | iEr = iE[1:] |
|---|
| 158 | assert(samePairs(iE2, iEr), false, "Ex 1: Not samePairs", iE2, iEr) |
|---|
| 159 | println("EXERCISE 1 Complete") |
|---|
| 160 | (* EXERCISE 2: uniq *) |
|---|
| 161 | assert(initEquivs, uniq(initEquivs), "Ex 2: Uniq is different") |
|---|
| 162 | assert(initEquivs, uniq(initEquivs || iE), "Ex 2: Uniq is different") |
|---|
| 163 | assert(eE, uniq(eE), "Ex 2: Uniq should be empty!") |
|---|
| 164 | println("EXERCISE 2 Complete") |
|---|
| 165 | (* EXERCISE 3: symmetricTransitiveClosure *) |
|---|
| 166 | initEquivs_closed = symmetricTransitiveClosure(initEquivs) |
|---|
| 167 | reflexives = <|[\(Node,Node)\] (a,a), (fffa, fffa), (fffffa, fffffa), (gab, gab) |> |
|---|
| 168 | expected_closure = |
|---|
| 169 | ( initEquivs || reflexives || |
|---|
| 170 | <|[\(Node,Node)\] (fffa, a), (fffa,fffffa), (fffa, gab), |
|---|
| 171 | (fffffa, fffa), (fffffa, a), (fffffa, gab), |
|---|
| 172 | (gab, fffa), (gab, fffffa), (gab, a) |> ) |
|---|
| 173 | assert(samePairs(initEquivs_closed || reflexives, expected_closure), true, |
|---|
| 174 | "Ex 3: Closure doesn't have same pairs as expected\n", |
|---|
| 175 | initEquivs_closed, "\n", expected_closure) |
|---|
| 176 | println("EXERCISE 3 Complete") |
|---|
| 177 | (* EXERCISE 4: closure by op *) |
|---|
| 178 | op_closure = operatorClosure(graph) |
|---|
| 179 | reflexive_graph = <|[\(Node,Node)\] (n,n) | n <- graph |> |
|---|
| 180 | expected_op_closure = |
|---|
| 181 | symmetricTransitiveClosure( reflexive_graph || |
|---|
| 182 | <|[\(Node,Node)\] |
|---|
| 183 | (fa,ffa),(fa,fffa),(fa,ffffa),(fa,fffffa), |
|---|
| 184 | (gab,gfab),(gab,ggfab) |> ) |
|---|
| 185 | assert(samePairs(op_closure || reflexive_graph, expected_op_closure), true, |
|---|
| 186 | "Ex 4: Closure by operator doesn't have same pairs as expected\n", |
|---|
| 187 | op_closure, "\n", expected_op_closure) |
|---|
| 188 | println("EXERCISE 4 Complete") |
|---|
| 189 | (* EXERCISE 5: equivalence closure *) |
|---|
| 190 | fullClose = <| (n1,n2) | n1 <- graph, n1.name =/= "b", n2 <- graph, n2.name =/= "b" |> |
|---|
| 191 | equiv_closure = equivalenceClosure(graph, initEquivs) |
|---|
| 192 | println("Initial equivalences:" // showEquiv(initEquivs)) |
|---|
| 193 | println("\nFinal equivalences:" // showEquiv(equiv_closure)) |
|---|
| 194 | assert(samePairs(fullClose||reflexive_graph, equiv_closure||reflexive_graph), true, |
|---|
| 195 | "Ex 5: Closure by operator is missing some pairs!\n", |
|---|
| 196 | fullClose, "\n", equiv_closure) |
|---|
| 197 | println("EXERCISE 5 Complete") |
|---|
| 198 | end |
|---|
| 199 | |
|---|
| 200 | end |
|---|