1 """Unit tests for jazzparser.formalisms.music_halfspan.semantics.
2
3 """
4 """
5 ============================== License ========================================
6 Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding
7
8 This file is part of The Jazz Parser.
9
10 The Jazz Parser is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 The Jazz Parser is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with The Jazz Parser. If not, see <http://www.gnu.org/licenses/>.
22
23 ============================ End license ======================================
24
25 """
26 __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"
27
28 import unittest, os
29 from jazzparser import settings
30
31 from jazzparser.formalisms.music_halfspan.semantics import Semantics, \
32 EnharmonicCoordinate, List, semantics_from_string, ListCat, \
33 Variable, FunctionApplication, Leftonto, Rightonto, \
34 LambdaAbstraction, Coordination, apply, compose, \
35 list_lf_to_coordinates
36
38 """
39 Tests for building LFs using C{semantics_from_string}.
40
41 """
48
50 sem = semantics_from_string("[<1,2>]")
51
52 self.assertIsInstance(sem.lf, List)
53
54 self.assertEqual(len(sem.lf), 1)
55
56 self.assertIsInstance(sem.lf[0], EnharmonicCoordinate)
57 self.assertEqual((sem.lf[0].x,sem.lf[0].y), (1,2))
58
59
60 sem = semantics_from_string("[<1,2>,<3,4>]")
61
62 self.assertIsInstance(sem.lf, List)
63
70
90
101
103 sem = semantics_from_string("\\$X.$X")
104
105 self.assertIsInstance(sem.lf, LambdaAbstraction)
106
107 self.assertIsInstance(sem.lf.variable, Variable)
108
109 self.assertIsInstance(sem.lf.expression, Variable)
110
111
112 sem = semantics_from_string("\\$X.<1,2>")
113 self.assertIsInstance(sem.lf.expression, EnharmonicCoordinate)
114
115
116 sem = semantics_from_string("\\$X,$Y.$X")
117
118 self.assertIsInstance(sem.lf, LambdaAbstraction)
119
120 self.assertIsInstance(sem.lf.variable, Variable)
121
122 self.assertIsInstance(sem.lf.expression, LambdaAbstraction)
123 self.assertIsInstance(sem.lf.expression.variable, Variable)
124
126 sem = semantics_from_string("($X $X)")
127
128 self.assertIsInstance(sem.lf, FunctionApplication)
129
130 self.assertIsInstance(sem.lf.functor, Variable)
131
132 self.assertIsInstance(sem.lf.argument, Variable)
133
134 sem = semantics_from_string("($X <1,2>)")
135
136 self.assertIsInstance(sem.lf, FunctionApplication)
137
138 self.assertIsInstance(sem.lf.functor, Variable)
139
140 self.assertIsInstance(sem.lf.argument, EnharmonicCoordinate)
141
142 sem = semantics_from_string("(($X <1,2>) <3,4>)")
143
144 self.assertIsInstance(sem.lf, FunctionApplication)
145
146 self.assertIsInstance(sem.lf.functor, FunctionApplication)
147
148 self.assertIsInstance(sem.lf.argument, EnharmonicCoordinate)
149
151
152 sem = semantics_from_string(r"(\$x.leftonto($x)) & (\$x.leftonto($x))")
153 self.assertIsInstance(sem.lf, Coordination)
154 self.assertEqual(len(sem.lf), 2)
155
156
157 sem = semantics_from_string(r"(\$x.leftonto($x)) & (\$x.rightonto($x)) & (\$x.leftonto($x))")
158 self.assertIsInstance(sem.lf, Coordination)
159
160
161 self.assertEqual(len(sem.lf), 2)
162 self.assertIsInstance(sem.lf[1], Coordination)
163 self.assertEqual(len(sem.lf[1]), 2)
164
173
184
186 """
187 Tests for creating and reducing the predicates.
188
189 """
191
192 sem = semantics_from_string("leftonto([<1,2>])")
193 sem.beta_reduce()
194 self.assertIsInstance(sem.lf, List)
195 self.assertIsInstance(sem.lf[0], FunctionApplication)
196 self.assertIsInstance(sem.lf[0].functor, Leftonto)
197
198
199 sem = semantics_from_string("leftonto([<1,2>,<3,4>])")
200 sem.beta_reduce()
201 self.assertIsInstance(sem.lf, List)
202 self.assertIsInstance(sem.lf[0], FunctionApplication)
203 self.assertIsInstance(sem.lf[0].functor, Leftonto)
204
205
206 sem = semantics_from_string("leftonto(leftonto([<1,2>]))")
207 sem.beta_reduce()
208 self.assertIsInstance(sem.lf, List)
209 self.assertIsInstance(sem.lf[0], FunctionApplication)
210 self.assertIsInstance(sem.lf[0].functor, Leftonto)
211 self.assertIsInstance(sem.lf[0].argument, FunctionApplication)
212 self.assertIsInstance(sem.lf[0].argument.functor, Leftonto)
213
214
215 sem = semantics_from_string("rightonto([<1,2>,<3,4>])")
216 sem.beta_reduce()
217 self.assertIsInstance(sem.lf, List)
218 self.assertIsInstance(sem.lf[0], FunctionApplication)
219 self.assertIsInstance(sem.lf[0].functor, Rightonto)
220
222
223 sem0 = semantics_from_string(r"(\$x.$x <1,2>)")
224 sem0.beta_reduce()
225 sem1 = semantics_from_string("<1,2>")
226 self.assertEqual(sem0, sem1)
227
228
229 sem0 = semantics_from_string(r"(\$x.<1,2> $y)")
230 sem0.beta_reduce()
231 sem1 = semantics_from_string("<1,2>")
232 self.assertEqual(sem0, sem1)
233
234
235 sem0 = semantics_from_string(r"((\$x,$y.$x <1,2>) <3,4>)")
236 sem0.beta_reduce()
237 sem1 = semantics_from_string("<1,2>")
238 self.assertEqual(sem0, sem1)
239
241 """
242 Tests for use of semantic coordination.
243
244 """
246 """
247 Check the flattening of coordinations during beta-reduction is working
248
249 """
250
251 sem0 = semantics_from_string(r"(\$x.leftonto($x)) & (\$x.rightonto($x)) & (\$x.leftonto($x))")
252
253
254 sem0.beta_reduce()
255 self.assertIsInstance(sem0.lf, Coordination)
256 self.assertEqual(len(sem0.lf), 3)
257
258
259
260 sem1 = semantics_from_string(r"((\$x.leftonto($x)) & (\$x.rightonto($x))) & (\$x.leftonto($x))")
261 sem1.beta_reduce()
262 self.assertIsInstance(sem1.lf, Coordination)
263 self.assertEqual(len(sem1.lf), 3)
264
265 self.assertEqual(sem0, sem1)
266
267
268 sem0 = semantics_from_string(r"<1,2> & ((\$x.rightonto($x)) & (\$x.leftonto($x))) & <3,4> & <5,6>")
269 sem1 = semantics_from_string(r"<1,2> & (\$x.rightonto($x)) & (\$x.leftonto($x)) & <3,4> & <5,6>")
270 sem0.beta_reduce()
271 sem1.beta_reduce()
272 self.assertEqual(sem0, sem1)
273
275 """
276 General tests for the semantics module.
277
278 """
280 """
281 Generates the example that we used in the paper (from Alice in
282 Wonderland) as if it's coming from the combinators. Checks that
283 the right overall LF comes out.
284
285 This is pretty insane, but a great test that the semantics is
286 behaving correctly in the contexts in which we'll be using it.
287
288 """
289 sem = semantics_from_string
290
291
292 sem_0_1 = sem(r"[<0,0>]")
293 sem_1_2 = sem(r"\$x.$x")
294
295 sem_0_2 = apply(sem_1_2, sem_0_1)
296 self.assertTrue(sem_0_2.alpha_equivalent(sem(r"[<0,0>]")))
297
298
299 sem_2_3 = sem(r"\$x.leftonto($x)")
300 sem_3_4 = sem(r"\$x.leftonto($x)")
301
302 sem_2_4 = compose(sem_2_3, sem_3_4)
303 self.assertTrue(sem_2_4.alpha_equivalent(
304 sem(r"\$x.leftonto(leftonto($x))")))
305
306
307 sem_4_5 = sem(r"\$x.leftonto($x)")
308
309 sem_2_5 = compose(sem_2_4, sem_4_5)
310 self.assertTrue(sem_2_5.alpha_equivalent(
311 sem(r"\$x.leftonto(leftonto(leftonto($x)))")))
312
313
314 sem_5_6 = sem(r"\$x.leftonto($x)")
315
316 sem_2_6 = compose(sem_2_5, sem_5_6)
317 self.assertTrue(sem_2_6.alpha_equivalent(
318 sem(r"\$x.leftonto(leftonto(leftonto(leftonto($x))))")))
319
320
321 sem_6_7 = sem(r"\$x.leftonto($x)")
322
323 sem_2_7 = compose(sem_2_6, sem_6_7)
324 self.assertTrue(sem_2_7.alpha_equivalent(
325 sem(r"\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))")))
326
327
328 sem_7_8 = sem(r"\$x.leftonto($x)")
329 sem_8_9 = sem(r"\$x.$x")
330
331 sem_7_9 = compose(sem_7_8, sem_8_9)
332 self.assertTrue(sem_7_9.alpha_equivalent(
333 sem(r"\$x.leftonto($x)")))
334
335
336 sem_9_10 = sem(r"\$x.leftonto($x)")
337
338 sem_7_10 = compose(sem_7_9, sem_9_10)
339 self.assertTrue(sem_7_10.alpha_equivalent(
340 sem(r"\$x.leftonto(leftonto($x))")))
341
342
343 sem_2_10 = Semantics(Coordination([sem_2_7.lf, sem_7_10.lf]))
344 sem_2_10.beta_reduce()
345 semtest = sem(r"(\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) "\
346 "& (\$x.leftonto(leftonto($x)))")
347 semtest.beta_reduce()
348 self.assertTrue(sem_2_10.alpha_equivalent(semtest))
349
350
351 sem_10_11 = sem(r"\$x.leftonto($x)")
352
353 sem_2_11 = compose(sem_2_10, sem_10_11)
354 semtest = sem(r"\$y.("\
355 "((\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) "\
356 "& (\$x.leftonto(leftonto($x)))) leftonto($y))")
357 semtest.beta_reduce()
358 self.assertTrue(sem_2_11.alpha_equivalent(semtest))
359
360
361 sem_11_13 = sem(r"\$x.leftonto(leftonto($x))")
362
363 sem_2_13 = Semantics(Coordination([sem_2_11.lf, sem_11_13.lf]))
364 sem_2_13.beta_reduce()
365 semtest = sem(r"(\$y.("\
366 "((\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) "\
367 "& (\$x.leftonto(leftonto($x)))) leftonto($y))) "\
368 "& (\$x.leftonto(leftonto($x)))")
369 semtest.beta_reduce()
370 self.assertTrue(sem_2_13.alpha_equivalent(semtest))
371
372
373 sem_13_14 = sem(r"[<0,0>]")
374
375 sem_2_14 = apply(sem_2_13, sem_13_14)
376 semtest = sem(r"((\$y.("\
377 "((\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) "\
378 "& (\$x.leftonto(leftonto($x)))) leftonto($y))) "\
379 "& (\$x.leftonto(leftonto($x))) [<0,0>])")
380 semtest.beta_reduce()
381 self.assertTrue(sem_2_14.alpha_equivalent(semtest))
382
383
384 sem_0_14 = Semantics(ListCat([sem_0_2.lf, sem_2_14.lf]))
385 sem_0_14.beta_reduce()
386 semtest = sem(r"[<0,0>, "\
387 "((\$y.("\
388 "((\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) "\
389 "& (\$x.leftonto(leftonto($x)))) leftonto($y))) "\
390 "& (\$x.leftonto(leftonto($x))) <0,0>)]")
391 semtest.beta_reduce()
392 self.assertTrue(sem_0_14.alpha_equivalent(semtest))
393
395 """
396 Tests for producing a tonal space path from a logical form.
397
398 """
400 """
401 Builds a load of logical forms and gives their expected tonal space
402 paths.
403
404 """
405 self.longMessage = True
406 sem = semantics_from_string
407
408
409 self.basic = [
410 (sem(r"[leftonto(leftonto(<0,0>))]"),
411 [(2,0),(1,0),(0,0)]),
412 (sem(r"[leftonto(leftonto(leftonto(<0,0>)))]"),
413 [(3,0),(2,0),(1,0),(0,0)]),
414 (sem(r"[rightonto(rightonto(<0,0>))]"),
415 [(2,2),(3,2),(4,2)]),
416 (sem(r"[<0,0>,<0,1>]"),
417 [(0,0),(0,1)]),
418 (sem(r"[<4,-1>,<4,0>]"),
419 [(0,0),(0,1)]),
420 ]
421
422 self.develop = [
423 (sem(r"[<0,0>,leftonto(leftonto(<0,0>))]"),
424 [(0,0),(2,0),(1,0),(0,0)]),
425 (sem(r"[<0,0>,leftonto(leftonto(leftonto(<0,0>)))]"),
426 [(0,0),(-1,1),(-2,1),(-3,1),(-4,1)]),
427 (sem(r"[<0,0>,leftonto(leftonto(leftonto(<0,0>)))]"),
428 [(0,0),(-1,1),(-2,1),(-3,1),(-4,1)]),
429 (sem(r"[<-4,-2>,leftonto(leftonto(leftonto(<0,0>)))]"),
430 [(0,0),(-1,1),(-2,1),(-3,1),(-4,1)]),
431 (sem(r"[<0,0>, leftonto(leftonto(leftonto(<0,0>))), "\
432 "leftonto(leftonto(leftonto(<0,0>)))]"),
433 [(0,0),(-1,1),(-2,1),(-3,1),(-4,1),(-5,2),(-6,2),(-7,2),(-8,2)]),
434 (sem(r"[<1,2>,leftonto(leftonto(leftonto(<0,0>)))]"),
435 [(1, 2), (-1, 1), (-2, 1), (-3, 1), (-4, 1)]),
436 ]
437
438 self.coordination = [
439 (sem(r"[((\$x.leftonto($x)) & (\$x.leftonto($x)) <0,0>)]"),
440 [(1,0),(1,0),(0,0)]),
441 (sem(r"[((\$x.rightonto($x)) & (\$x.leftonto($x)) <0,0>)]"),
442 [(3,2),(5,2),(4,2)]),
443 (sem(r"[((\$x.leftonto($x)) & (\$x.leftonto(leftonto(leftonto($x)))) <0,0>)]"),
444 [(1,0),(3,0),(2,0),(1,0),(0,0)]),
445 (sem(r"[((\$x.leftonto($x)) & (\$x.leftonto(leftonto(leftonto(leftonto($x))))) <0,0>)]"),
446 [(1,0),(4,0),(3,0),(2,0),(1,0),(0,0)]),
447 ]
448
449 self.nested_coordination = [
450 (sem(r"[((\$x.leftonto($x)) & (\$x.leftonto((((\$y.leftonto($y)) & (\$y.leftonto(leftonto($y)))) $x))) <0,0>)]"),
451 [(1,0),(2,0),(1,0),(2,0),(1,0),(0,0)]),
452
453 (sem(r"[<0,0>, (((\$y.(((\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) & (\$x.leftonto(leftonto($x)))) leftonto($y))) & (\$x.leftonto(leftonto($x)))) <0,0>)]"),
454 [(0,0),(-2,-1),(-3,-1),(-4,-1),(-5,-1),(-6,-1),(-5,-1),(-6,-1),(-7,-1),(-6,-1),(-7,-1),(-8,-1)]),
455 ]
456
458 self.assertEqual(output, correct, msg="Expected output: %s. Got: %s. LF was %s" \
459 % (correct, output, sems))
460
465
470
475
480
482 """
483 Tests for certain bits of
484 L{jazzparser.formalisms.music_halfspan.semantics.EnharmonicCoordinate}.
485
486 """
488 """
489 This is a particularly difficult bit of EnharmonicCoordinate's
490 behaviour to get right, so it's worth testing a good few examples to
491 make sure it's behaving right.
492
493 """
494 self.longMessage = False
495
496
497 TESTS = [
498
499
500 ((0,0), (0,0), (0,0)),
501 ((0,0), (2,0), (2,0)),
502 ((0,0), (-2,0), (-2,0)),
503 ((0,0), (1,1), (1,1)),
504
505 ((0,0), (2,2), (-2,0)),
506 ((0,0), (-2,-2), (2,0)),
507 ]
508
509 for base,candidate,correct in TESTS:
510
511 base_crd = EnharmonicCoordinate.from_harmonic_coord(base)
512 candidate_crd = EnharmonicCoordinate.from_harmonic_coord(candidate)
513
514 result_crd = base_crd.nearest(candidate_crd)
515 result = result_crd.harmonic_coord
516
517 self.assertEqual(result, correct, msg="nearest instance of %s "\
518 "[%s] to %s should have been %s, got %s" % \
519 (candidate,
520 candidate_crd.zero_coord,
521 base,
522 correct,
523 result))
524