test_drawing.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919
  1. from sympy.categories.diagram_drawing import _GrowableGrid, ArrowStringDescription
  2. from sympy.categories import (DiagramGrid, Object, NamedMorphism,
  3. Diagram, XypicDiagramDrawer, xypic_draw_diagram)
  4. from sympy.sets.sets import FiniteSet
  5. def test_GrowableGrid():
  6. grid = _GrowableGrid(1, 2)
  7. # Check dimensions.
  8. assert grid.width == 1
  9. assert grid.height == 2
  10. # Check initialization of elements.
  11. assert grid[0, 0] is None
  12. assert grid[1, 0] is None
  13. # Check assignment to elements.
  14. grid[0, 0] = 1
  15. grid[1, 0] = "two"
  16. assert grid[0, 0] == 1
  17. assert grid[1, 0] == "two"
  18. # Check appending a row.
  19. grid.append_row()
  20. assert grid.width == 1
  21. assert grid.height == 3
  22. assert grid[0, 0] == 1
  23. assert grid[1, 0] == "two"
  24. assert grid[2, 0] is None
  25. # Check appending a column.
  26. grid.append_column()
  27. assert grid.width == 2
  28. assert grid.height == 3
  29. assert grid[0, 0] == 1
  30. assert grid[1, 0] == "two"
  31. assert grid[2, 0] is None
  32. assert grid[0, 1] is None
  33. assert grid[1, 1] is None
  34. assert grid[2, 1] is None
  35. grid = _GrowableGrid(1, 2)
  36. grid[0, 0] = 1
  37. grid[1, 0] = "two"
  38. # Check prepending a row.
  39. grid.prepend_row()
  40. assert grid.width == 1
  41. assert grid.height == 3
  42. assert grid[0, 0] is None
  43. assert grid[1, 0] == 1
  44. assert grid[2, 0] == "two"
  45. # Check prepending a column.
  46. grid.prepend_column()
  47. assert grid.width == 2
  48. assert grid.height == 3
  49. assert grid[0, 0] is None
  50. assert grid[1, 0] is None
  51. assert grid[2, 0] is None
  52. assert grid[0, 1] is None
  53. assert grid[1, 1] == 1
  54. assert grid[2, 1] == "two"
  55. def test_DiagramGrid():
  56. # Set up some objects and morphisms.
  57. A = Object("A")
  58. B = Object("B")
  59. C = Object("C")
  60. D = Object("D")
  61. E = Object("E")
  62. f = NamedMorphism(A, B, "f")
  63. g = NamedMorphism(B, C, "g")
  64. h = NamedMorphism(D, A, "h")
  65. k = NamedMorphism(D, B, "k")
  66. # A one-morphism diagram.
  67. d = Diagram([f])
  68. grid = DiagramGrid(d)
  69. assert grid.width == 2
  70. assert grid.height == 1
  71. assert grid[0, 0] == A
  72. assert grid[0, 1] == B
  73. assert grid.morphisms == {f: FiniteSet()}
  74. # A triangle.
  75. d = Diagram([f, g], {g * f: "unique"})
  76. grid = DiagramGrid(d)
  77. assert grid.width == 2
  78. assert grid.height == 2
  79. assert grid[0, 0] == A
  80. assert grid[0, 1] == B
  81. assert grid[1, 0] == C
  82. assert grid[1, 1] is None
  83. assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(),
  84. g * f: FiniteSet("unique")}
  85. # A triangle with a "loop" morphism.
  86. l_A = NamedMorphism(A, A, "l_A")
  87. d = Diagram([f, g, l_A])
  88. grid = DiagramGrid(d)
  89. assert grid.width == 2
  90. assert grid.height == 2
  91. assert grid[0, 0] == A
  92. assert grid[0, 1] == B
  93. assert grid[1, 0] is None
  94. assert grid[1, 1] == C
  95. assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), l_A: FiniteSet()}
  96. # A simple diagram.
  97. d = Diagram([f, g, h, k])
  98. grid = DiagramGrid(d)
  99. assert grid.width == 3
  100. assert grid.height == 2
  101. assert grid[0, 0] == A
  102. assert grid[0, 1] == B
  103. assert grid[0, 2] == D
  104. assert grid[1, 0] is None
  105. assert grid[1, 1] == C
  106. assert grid[1, 2] is None
  107. assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(),
  108. k: FiniteSet()}
  109. assert str(grid) == '[[Object("A"), Object("B"), Object("D")], ' \
  110. '[None, Object("C"), None]]'
  111. # A chain of morphisms.
  112. f = NamedMorphism(A, B, "f")
  113. g = NamedMorphism(B, C, "g")
  114. h = NamedMorphism(C, D, "h")
  115. k = NamedMorphism(D, E, "k")
  116. d = Diagram([f, g, h, k])
  117. grid = DiagramGrid(d)
  118. assert grid.width == 3
  119. assert grid.height == 3
  120. assert grid[0, 0] == A
  121. assert grid[0, 1] == B
  122. assert grid[0, 2] is None
  123. assert grid[1, 0] is None
  124. assert grid[1, 1] == C
  125. assert grid[1, 2] == D
  126. assert grid[2, 0] is None
  127. assert grid[2, 1] is None
  128. assert grid[2, 2] == E
  129. assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(),
  130. k: FiniteSet()}
  131. # A square.
  132. f = NamedMorphism(A, B, "f")
  133. g = NamedMorphism(B, D, "g")
  134. h = NamedMorphism(A, C, "h")
  135. k = NamedMorphism(C, D, "k")
  136. d = Diagram([f, g, h, k])
  137. grid = DiagramGrid(d)
  138. assert grid.width == 2
  139. assert grid.height == 2
  140. assert grid[0, 0] == A
  141. assert grid[0, 1] == B
  142. assert grid[1, 0] == C
  143. assert grid[1, 1] == D
  144. assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(),
  145. k: FiniteSet()}
  146. # A strange diagram which resulted from a typo when creating a
  147. # test for five lemma, but which allowed to stop one extra problem
  148. # in the algorithm.
  149. A = Object("A")
  150. B = Object("B")
  151. C = Object("C")
  152. D = Object("D")
  153. E = Object("E")
  154. A_ = Object("A'")
  155. B_ = Object("B'")
  156. C_ = Object("C'")
  157. D_ = Object("D'")
  158. E_ = Object("E'")
  159. f = NamedMorphism(A, B, "f")
  160. g = NamedMorphism(B, C, "g")
  161. h = NamedMorphism(C, D, "h")
  162. i = NamedMorphism(D, E, "i")
  163. # These 4 morphisms should be between primed objects.
  164. j = NamedMorphism(A, B, "j")
  165. k = NamedMorphism(B, C, "k")
  166. l = NamedMorphism(C, D, "l")
  167. m = NamedMorphism(D, E, "m")
  168. o = NamedMorphism(A, A_, "o")
  169. p = NamedMorphism(B, B_, "p")
  170. q = NamedMorphism(C, C_, "q")
  171. r = NamedMorphism(D, D_, "r")
  172. s = NamedMorphism(E, E_, "s")
  173. d = Diagram([f, g, h, i, j, k, l, m, o, p, q, r, s])
  174. grid = DiagramGrid(d)
  175. assert grid.width == 3
  176. assert grid.height == 4
  177. assert grid[0, 0] is None
  178. assert grid[0, 1] == A
  179. assert grid[0, 2] == A_
  180. assert grid[1, 0] == C
  181. assert grid[1, 1] == B
  182. assert grid[1, 2] == B_
  183. assert grid[2, 0] == C_
  184. assert grid[2, 1] == D
  185. assert grid[2, 2] == D_
  186. assert grid[3, 0] is None
  187. assert grid[3, 1] == E
  188. assert grid[3, 2] == E_
  189. morphisms = {}
  190. for m in [f, g, h, i, j, k, l, m, o, p, q, r, s]:
  191. morphisms[m] = FiniteSet()
  192. assert grid.morphisms == morphisms
  193. # A cube.
  194. A1 = Object("A1")
  195. A2 = Object("A2")
  196. A3 = Object("A3")
  197. A4 = Object("A4")
  198. A5 = Object("A5")
  199. A6 = Object("A6")
  200. A7 = Object("A7")
  201. A8 = Object("A8")
  202. # The top face of the cube.
  203. f1 = NamedMorphism(A1, A2, "f1")
  204. f2 = NamedMorphism(A1, A3, "f2")
  205. f3 = NamedMorphism(A2, A4, "f3")
  206. f4 = NamedMorphism(A3, A4, "f3")
  207. # The bottom face of the cube.
  208. f5 = NamedMorphism(A5, A6, "f5")
  209. f6 = NamedMorphism(A5, A7, "f6")
  210. f7 = NamedMorphism(A6, A8, "f7")
  211. f8 = NamedMorphism(A7, A8, "f8")
  212. # The remaining morphisms.
  213. f9 = NamedMorphism(A1, A5, "f9")
  214. f10 = NamedMorphism(A2, A6, "f10")
  215. f11 = NamedMorphism(A3, A7, "f11")
  216. f12 = NamedMorphism(A4, A8, "f11")
  217. d = Diagram([f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12])
  218. grid = DiagramGrid(d)
  219. assert grid.width == 4
  220. assert grid.height == 3
  221. assert grid[0, 0] is None
  222. assert grid[0, 1] == A5
  223. assert grid[0, 2] == A6
  224. assert grid[0, 3] is None
  225. assert grid[1, 0] is None
  226. assert grid[1, 1] == A1
  227. assert grid[1, 2] == A2
  228. assert grid[1, 3] is None
  229. assert grid[2, 0] == A7
  230. assert grid[2, 1] == A3
  231. assert grid[2, 2] == A4
  232. assert grid[2, 3] == A8
  233. morphisms = {}
  234. for m in [f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12]:
  235. morphisms[m] = FiniteSet()
  236. assert grid.morphisms == morphisms
  237. # A line diagram.
  238. A = Object("A")
  239. B = Object("B")
  240. C = Object("C")
  241. D = Object("D")
  242. E = Object("E")
  243. f = NamedMorphism(A, B, "f")
  244. g = NamedMorphism(B, C, "g")
  245. h = NamedMorphism(C, D, "h")
  246. i = NamedMorphism(D, E, "i")
  247. d = Diagram([f, g, h, i])
  248. grid = DiagramGrid(d, layout="sequential")
  249. assert grid.width == 5
  250. assert grid.height == 1
  251. assert grid[0, 0] == A
  252. assert grid[0, 1] == B
  253. assert grid[0, 2] == C
  254. assert grid[0, 3] == D
  255. assert grid[0, 4] == E
  256. assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(),
  257. i: FiniteSet()}
  258. # Test the transposed version.
  259. grid = DiagramGrid(d, layout="sequential", transpose=True)
  260. assert grid.width == 1
  261. assert grid.height == 5
  262. assert grid[0, 0] == A
  263. assert grid[1, 0] == B
  264. assert grid[2, 0] == C
  265. assert grid[3, 0] == D
  266. assert grid[4, 0] == E
  267. assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(),
  268. i: FiniteSet()}
  269. # A pullback.
  270. m1 = NamedMorphism(A, B, "m1")
  271. m2 = NamedMorphism(A, C, "m2")
  272. s1 = NamedMorphism(B, D, "s1")
  273. s2 = NamedMorphism(C, D, "s2")
  274. f1 = NamedMorphism(E, B, "f1")
  275. f2 = NamedMorphism(E, C, "f2")
  276. g = NamedMorphism(E, A, "g")
  277. d = Diagram([m1, m2, s1, s2, f1, f2], {g: "unique"})
  278. grid = DiagramGrid(d)
  279. assert grid.width == 3
  280. assert grid.height == 2
  281. assert grid[0, 0] == A
  282. assert grid[0, 1] == B
  283. assert grid[0, 2] == E
  284. assert grid[1, 0] == C
  285. assert grid[1, 1] == D
  286. assert grid[1, 2] is None
  287. morphisms = {g: FiniteSet("unique")}
  288. for m in [m1, m2, s1, s2, f1, f2]:
  289. morphisms[m] = FiniteSet()
  290. assert grid.morphisms == morphisms
  291. # Test the pullback with sequential layout, just for stress
  292. # testing.
  293. grid = DiagramGrid(d, layout="sequential")
  294. assert grid.width == 5
  295. assert grid.height == 1
  296. assert grid[0, 0] == D
  297. assert grid[0, 1] == B
  298. assert grid[0, 2] == A
  299. assert grid[0, 3] == C
  300. assert grid[0, 4] == E
  301. assert grid.morphisms == morphisms
  302. # Test a pullback with object grouping.
  303. grid = DiagramGrid(d, groups=FiniteSet(E, FiniteSet(A, B, C, D)))
  304. assert grid.width == 3
  305. assert grid.height == 2
  306. assert grid[0, 0] == E
  307. assert grid[0, 1] == A
  308. assert grid[0, 2] == B
  309. assert grid[1, 0] is None
  310. assert grid[1, 1] == C
  311. assert grid[1, 2] == D
  312. assert grid.morphisms == morphisms
  313. # Five lemma, actually.
  314. A = Object("A")
  315. B = Object("B")
  316. C = Object("C")
  317. D = Object("D")
  318. E = Object("E")
  319. A_ = Object("A'")
  320. B_ = Object("B'")
  321. C_ = Object("C'")
  322. D_ = Object("D'")
  323. E_ = Object("E'")
  324. f = NamedMorphism(A, B, "f")
  325. g = NamedMorphism(B, C, "g")
  326. h = NamedMorphism(C, D, "h")
  327. i = NamedMorphism(D, E, "i")
  328. j = NamedMorphism(A_, B_, "j")
  329. k = NamedMorphism(B_, C_, "k")
  330. l = NamedMorphism(C_, D_, "l")
  331. m = NamedMorphism(D_, E_, "m")
  332. o = NamedMorphism(A, A_, "o")
  333. p = NamedMorphism(B, B_, "p")
  334. q = NamedMorphism(C, C_, "q")
  335. r = NamedMorphism(D, D_, "r")
  336. s = NamedMorphism(E, E_, "s")
  337. d = Diagram([f, g, h, i, j, k, l, m, o, p, q, r, s])
  338. grid = DiagramGrid(d)
  339. assert grid.width == 5
  340. assert grid.height == 3
  341. assert grid[0, 0] is None
  342. assert grid[0, 1] == A
  343. assert grid[0, 2] == A_
  344. assert grid[0, 3] is None
  345. assert grid[0, 4] is None
  346. assert grid[1, 0] == C
  347. assert grid[1, 1] == B
  348. assert grid[1, 2] == B_
  349. assert grid[1, 3] == C_
  350. assert grid[1, 4] is None
  351. assert grid[2, 0] == D
  352. assert grid[2, 1] == E
  353. assert grid[2, 2] is None
  354. assert grid[2, 3] == D_
  355. assert grid[2, 4] == E_
  356. morphisms = {}
  357. for m in [f, g, h, i, j, k, l, m, o, p, q, r, s]:
  358. morphisms[m] = FiniteSet()
  359. assert grid.morphisms == morphisms
  360. # Test the five lemma with object grouping.
  361. grid = DiagramGrid(d, FiniteSet(
  362. FiniteSet(A, B, C, D, E), FiniteSet(A_, B_, C_, D_, E_)))
  363. assert grid.width == 6
  364. assert grid.height == 3
  365. assert grid[0, 0] == A
  366. assert grid[0, 1] == B
  367. assert grid[0, 2] is None
  368. assert grid[0, 3] == A_
  369. assert grid[0, 4] == B_
  370. assert grid[0, 5] is None
  371. assert grid[1, 0] is None
  372. assert grid[1, 1] == C
  373. assert grid[1, 2] == D
  374. assert grid[1, 3] is None
  375. assert grid[1, 4] == C_
  376. assert grid[1, 5] == D_
  377. assert grid[2, 0] is None
  378. assert grid[2, 1] is None
  379. assert grid[2, 2] == E
  380. assert grid[2, 3] is None
  381. assert grid[2, 4] is None
  382. assert grid[2, 5] == E_
  383. assert grid.morphisms == morphisms
  384. # Test the five lemma with object grouping, but mixing containers
  385. # to represent groups.
  386. grid = DiagramGrid(d, [(A, B, C, D, E), {A_, B_, C_, D_, E_}])
  387. assert grid.width == 6
  388. assert grid.height == 3
  389. assert grid[0, 0] == A
  390. assert grid[0, 1] == B
  391. assert grid[0, 2] is None
  392. assert grid[0, 3] == A_
  393. assert grid[0, 4] == B_
  394. assert grid[0, 5] is None
  395. assert grid[1, 0] is None
  396. assert grid[1, 1] == C
  397. assert grid[1, 2] == D
  398. assert grid[1, 3] is None
  399. assert grid[1, 4] == C_
  400. assert grid[1, 5] == D_
  401. assert grid[2, 0] is None
  402. assert grid[2, 1] is None
  403. assert grid[2, 2] == E
  404. assert grid[2, 3] is None
  405. assert grid[2, 4] is None
  406. assert grid[2, 5] == E_
  407. assert grid.morphisms == morphisms
  408. # Test the five lemma with object grouping and hints.
  409. grid = DiagramGrid(d, {
  410. FiniteSet(A, B, C, D, E): {"layout": "sequential",
  411. "transpose": True},
  412. FiniteSet(A_, B_, C_, D_, E_): {"layout": "sequential",
  413. "transpose": True}},
  414. transpose=True)
  415. assert grid.width == 5
  416. assert grid.height == 2
  417. assert grid[0, 0] == A
  418. assert grid[0, 1] == B
  419. assert grid[0, 2] == C
  420. assert grid[0, 3] == D
  421. assert grid[0, 4] == E
  422. assert grid[1, 0] == A_
  423. assert grid[1, 1] == B_
  424. assert grid[1, 2] == C_
  425. assert grid[1, 3] == D_
  426. assert grid[1, 4] == E_
  427. assert grid.morphisms == morphisms
  428. # A two-triangle disconnected diagram.
  429. f = NamedMorphism(A, B, "f")
  430. g = NamedMorphism(B, C, "g")
  431. f_ = NamedMorphism(A_, B_, "f")
  432. g_ = NamedMorphism(B_, C_, "g")
  433. d = Diagram([f, g, f_, g_], {g * f: "unique", g_ * f_: "unique"})
  434. grid = DiagramGrid(d)
  435. assert grid.width == 4
  436. assert grid.height == 2
  437. assert grid[0, 0] == A
  438. assert grid[0, 1] == B
  439. assert grid[0, 2] == A_
  440. assert grid[0, 3] == B_
  441. assert grid[1, 0] == C
  442. assert grid[1, 1] is None
  443. assert grid[1, 2] == C_
  444. assert grid[1, 3] is None
  445. assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), f_: FiniteSet(),
  446. g_: FiniteSet(), g * f: FiniteSet("unique"),
  447. g_ * f_: FiniteSet("unique")}
  448. # A two-morphism disconnected diagram.
  449. f = NamedMorphism(A, B, "f")
  450. g = NamedMorphism(C, D, "g")
  451. d = Diagram([f, g])
  452. grid = DiagramGrid(d)
  453. assert grid.width == 4
  454. assert grid.height == 1
  455. assert grid[0, 0] == A
  456. assert grid[0, 1] == B
  457. assert grid[0, 2] == C
  458. assert grid[0, 3] == D
  459. assert grid.morphisms == {f: FiniteSet(), g: FiniteSet()}
  460. # Test a one-object diagram.
  461. f = NamedMorphism(A, A, "f")
  462. d = Diagram([f])
  463. grid = DiagramGrid(d)
  464. assert grid.width == 1
  465. assert grid.height == 1
  466. assert grid[0, 0] == A
  467. # Test a two-object disconnected diagram.
  468. g = NamedMorphism(B, B, "g")
  469. d = Diagram([f, g])
  470. grid = DiagramGrid(d)
  471. assert grid.width == 2
  472. assert grid.height == 1
  473. assert grid[0, 0] == A
  474. assert grid[0, 1] == B
  475. def test_DiagramGrid_pseudopod():
  476. # Test a diagram in which even growing a pseudopod does not
  477. # eventually help.
  478. A = Object("A")
  479. B = Object("B")
  480. C = Object("C")
  481. D = Object("D")
  482. E = Object("E")
  483. F = Object("F")
  484. A_ = Object("A'")
  485. B_ = Object("B'")
  486. C_ = Object("C'")
  487. D_ = Object("D'")
  488. E_ = Object("E'")
  489. f1 = NamedMorphism(A, B, "f1")
  490. f2 = NamedMorphism(A, C, "f2")
  491. f3 = NamedMorphism(A, D, "f3")
  492. f4 = NamedMorphism(A, E, "f4")
  493. f5 = NamedMorphism(A, A_, "f5")
  494. f6 = NamedMorphism(A, B_, "f6")
  495. f7 = NamedMorphism(A, C_, "f7")
  496. f8 = NamedMorphism(A, D_, "f8")
  497. f9 = NamedMorphism(A, E_, "f9")
  498. f10 = NamedMorphism(A, F, "f10")
  499. d = Diagram([f1, f2, f3, f4, f5, f6, f7, f8, f9, f10])
  500. grid = DiagramGrid(d)
  501. assert grid.width == 5
  502. assert grid.height == 3
  503. assert grid[0, 0] == E
  504. assert grid[0, 1] == C
  505. assert grid[0, 2] == C_
  506. assert grid[0, 3] == E_
  507. assert grid[0, 4] == F
  508. assert grid[1, 0] == D
  509. assert grid[1, 1] == A
  510. assert grid[1, 2] == A_
  511. assert grid[1, 3] is None
  512. assert grid[1, 4] is None
  513. assert grid[2, 0] == D_
  514. assert grid[2, 1] == B
  515. assert grid[2, 2] == B_
  516. assert grid[2, 3] is None
  517. assert grid[2, 4] is None
  518. morphisms = {}
  519. for f in [f1, f2, f3, f4, f5, f6, f7, f8, f9, f10]:
  520. morphisms[f] = FiniteSet()
  521. assert grid.morphisms == morphisms
  522. def test_ArrowStringDescription():
  523. astr = ArrowStringDescription("cm", "", None, "", "", "d", "r", "_", "f")
  524. assert str(astr) == "\\ar[dr]_{f}"
  525. astr = ArrowStringDescription("cm", "", 12, "", "", "d", "r", "_", "f")
  526. assert str(astr) == "\\ar[dr]_{f}"
  527. astr = ArrowStringDescription("cm", "^", 12, "", "", "d", "r", "_", "f")
  528. assert str(astr) == "\\ar@/^12cm/[dr]_{f}"
  529. astr = ArrowStringDescription("cm", "", 12, "r", "", "d", "r", "_", "f")
  530. assert str(astr) == "\\ar[dr]_{f}"
  531. astr = ArrowStringDescription("cm", "", 12, "r", "u", "d", "r", "_", "f")
  532. assert str(astr) == "\\ar@(r,u)[dr]_{f}"
  533. astr = ArrowStringDescription("cm", "", 12, "r", "u", "d", "r", "_", "f")
  534. assert str(astr) == "\\ar@(r,u)[dr]_{f}"
  535. astr = ArrowStringDescription("cm", "", 12, "r", "u", "d", "r", "_", "f")
  536. astr.arrow_style = "{-->}"
  537. assert str(astr) == "\\ar@(r,u)@{-->}[dr]_{f}"
  538. astr = ArrowStringDescription("cm", "_", 12, "", "", "d", "r", "_", "f")
  539. astr.arrow_style = "{-->}"
  540. assert str(astr) == "\\ar@/_12cm/@{-->}[dr]_{f}"
  541. def test_XypicDiagramDrawer_line():
  542. # A linear diagram.
  543. A = Object("A")
  544. B = Object("B")
  545. C = Object("C")
  546. D = Object("D")
  547. E = Object("E")
  548. f = NamedMorphism(A, B, "f")
  549. g = NamedMorphism(B, C, "g")
  550. h = NamedMorphism(C, D, "h")
  551. i = NamedMorphism(D, E, "i")
  552. d = Diagram([f, g, h, i])
  553. grid = DiagramGrid(d, layout="sequential")
  554. drawer = XypicDiagramDrawer()
  555. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  556. "A \\ar[r]^{f} & B \\ar[r]^{g} & C \\ar[r]^{h} & D \\ar[r]^{i} & E \n" \
  557. "}\n"
  558. # The same diagram, transposed.
  559. grid = DiagramGrid(d, layout="sequential", transpose=True)
  560. drawer = XypicDiagramDrawer()
  561. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  562. "A \\ar[d]^{f} \\\\\n" \
  563. "B \\ar[d]^{g} \\\\\n" \
  564. "C \\ar[d]^{h} \\\\\n" \
  565. "D \\ar[d]^{i} \\\\\n" \
  566. "E \n" \
  567. "}\n"
  568. def test_XypicDiagramDrawer_triangle():
  569. # A triangle diagram.
  570. A = Object("A")
  571. B = Object("B")
  572. C = Object("C")
  573. f = NamedMorphism(A, B, "f")
  574. g = NamedMorphism(B, C, "g")
  575. d = Diagram([f, g], {g * f: "unique"})
  576. grid = DiagramGrid(d)
  577. drawer = XypicDiagramDrawer()
  578. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  579. "A \\ar[d]_{g\\circ f} \\ar[r]^{f} & B \\ar[ld]^{g} \\\\\n" \
  580. "C & \n" \
  581. "}\n"
  582. # The same diagram, transposed.
  583. grid = DiagramGrid(d, transpose=True)
  584. drawer = XypicDiagramDrawer()
  585. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  586. "A \\ar[r]^{g\\circ f} \\ar[d]_{f} & C \\\\\n" \
  587. "B \\ar[ru]_{g} & \n" \
  588. "}\n"
  589. # The same diagram, with a masked morphism.
  590. assert drawer.draw(d, grid, masked=[g]) == "\\xymatrix{\n" \
  591. "A \\ar[r]^{g\\circ f} \\ar[d]_{f} & C \\\\\n" \
  592. "B & \n" \
  593. "}\n"
  594. # The same diagram with a formatter for "unique".
  595. def formatter(astr):
  596. astr.label = "\\exists !" + astr.label
  597. astr.arrow_style = "{-->}"
  598. drawer.arrow_formatters["unique"] = formatter
  599. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  600. "A \\ar@{-->}[r]^{\\exists !g\\circ f} \\ar[d]_{f} & C \\\\\n" \
  601. "B \\ar[ru]_{g} & \n" \
  602. "}\n"
  603. # The same diagram with a default formatter.
  604. def default_formatter(astr):
  605. astr.label_displacement = "(0.45)"
  606. drawer.default_arrow_formatter = default_formatter
  607. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  608. "A \\ar@{-->}[r]^(0.45){\\exists !g\\circ f} \\ar[d]_(0.45){f} & C \\\\\n" \
  609. "B \\ar[ru]_(0.45){g} & \n" \
  610. "}\n"
  611. # A triangle diagram with a lot of morphisms between the same
  612. # objects.
  613. f1 = NamedMorphism(B, A, "f1")
  614. f2 = NamedMorphism(A, B, "f2")
  615. g1 = NamedMorphism(C, B, "g1")
  616. g2 = NamedMorphism(B, C, "g2")
  617. d = Diagram([f, f1, f2, g, g1, g2], {f1 * g1: "unique", g2 * f2: "unique"})
  618. grid = DiagramGrid(d, transpose=True)
  619. drawer = XypicDiagramDrawer()
  620. assert drawer.draw(d, grid, masked=[f1*g1*g2*f2, g2*f2*f1*g1]) == \
  621. "\\xymatrix{\n" \
  622. "A \\ar[r]^{g_{2}\\circ f_{2}} \\ar[d]_{f} \\ar@/^3mm/[d]^{f_{2}} " \
  623. "& C \\ar@/^3mm/[l]^{f_{1}\\circ g_{1}} \\ar@/^3mm/[ld]^{g_{1}} \\\\\n" \
  624. "B \\ar@/^3mm/[u]^{f_{1}} \\ar[ru]_{g} \\ar@/^3mm/[ru]^{g_{2}} & \n" \
  625. "}\n"
  626. def test_XypicDiagramDrawer_cube():
  627. # A cube diagram.
  628. A1 = Object("A1")
  629. A2 = Object("A2")
  630. A3 = Object("A3")
  631. A4 = Object("A4")
  632. A5 = Object("A5")
  633. A6 = Object("A6")
  634. A7 = Object("A7")
  635. A8 = Object("A8")
  636. # The top face of the cube.
  637. f1 = NamedMorphism(A1, A2, "f1")
  638. f2 = NamedMorphism(A1, A3, "f2")
  639. f3 = NamedMorphism(A2, A4, "f3")
  640. f4 = NamedMorphism(A3, A4, "f3")
  641. # The bottom face of the cube.
  642. f5 = NamedMorphism(A5, A6, "f5")
  643. f6 = NamedMorphism(A5, A7, "f6")
  644. f7 = NamedMorphism(A6, A8, "f7")
  645. f8 = NamedMorphism(A7, A8, "f8")
  646. # The remaining morphisms.
  647. f9 = NamedMorphism(A1, A5, "f9")
  648. f10 = NamedMorphism(A2, A6, "f10")
  649. f11 = NamedMorphism(A3, A7, "f11")
  650. f12 = NamedMorphism(A4, A8, "f11")
  651. d = Diagram([f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12])
  652. grid = DiagramGrid(d)
  653. drawer = XypicDiagramDrawer()
  654. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  655. "& A_{5} \\ar[r]^{f_{5}} \\ar[ldd]_{f_{6}} & A_{6} \\ar[rdd]^{f_{7}} " \
  656. "& \\\\\n" \
  657. "& A_{1} \\ar[r]^{f_{1}} \\ar[d]^{f_{2}} \\ar[u]^{f_{9}} & A_{2} " \
  658. "\\ar[d]^{f_{3}} \\ar[u]_{f_{10}} & \\\\\n" \
  659. "A_{7} \\ar@/_3mm/[rrr]_{f_{8}} & A_{3} \\ar[r]^{f_{3}} \\ar[l]_{f_{11}} " \
  660. "& A_{4} \\ar[r]^{f_{11}} & A_{8} \n" \
  661. "}\n"
  662. # The same diagram, transposed.
  663. grid = DiagramGrid(d, transpose=True)
  664. drawer = XypicDiagramDrawer()
  665. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  666. "& & A_{7} \\ar@/^3mm/[ddd]^{f_{8}} \\\\\n" \
  667. "A_{5} \\ar[d]_{f_{5}} \\ar[rru]^{f_{6}} & A_{1} \\ar[d]^{f_{1}} " \
  668. "\\ar[r]^{f_{2}} \\ar[l]^{f_{9}} & A_{3} \\ar[d]_{f_{3}} " \
  669. "\\ar[u]^{f_{11}} \\\\\n" \
  670. "A_{6} \\ar[rrd]_{f_{7}} & A_{2} \\ar[r]^{f_{3}} \\ar[l]^{f_{10}} " \
  671. "& A_{4} \\ar[d]_{f_{11}} \\\\\n" \
  672. "& & A_{8} \n" \
  673. "}\n"
  674. def test_XypicDiagramDrawer_curved_and_loops():
  675. # A simple diagram, with a curved arrow.
  676. A = Object("A")
  677. B = Object("B")
  678. C = Object("C")
  679. D = Object("D")
  680. f = NamedMorphism(A, B, "f")
  681. g = NamedMorphism(B, C, "g")
  682. h = NamedMorphism(D, A, "h")
  683. k = NamedMorphism(D, B, "k")
  684. d = Diagram([f, g, h, k])
  685. grid = DiagramGrid(d)
  686. drawer = XypicDiagramDrawer()
  687. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  688. "A \\ar[r]_{f} & B \\ar[d]^{g} & D \\ar[l]^{k} \\ar@/_3mm/[ll]_{h} \\\\\n" \
  689. "& C & \n" \
  690. "}\n"
  691. # The same diagram, transposed.
  692. grid = DiagramGrid(d, transpose=True)
  693. drawer = XypicDiagramDrawer()
  694. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  695. "A \\ar[d]^{f} & \\\\\n" \
  696. "B \\ar[r]^{g} & C \\\\\n" \
  697. "D \\ar[u]_{k} \\ar@/^3mm/[uu]^{h} & \n" \
  698. "}\n"
  699. # The same diagram, larger and rotated.
  700. assert drawer.draw(d, grid, diagram_format="@+1cm@dr") == \
  701. "\\xymatrix@+1cm@dr{\n" \
  702. "A \\ar[d]^{f} & \\\\\n" \
  703. "B \\ar[r]^{g} & C \\\\\n" \
  704. "D \\ar[u]_{k} \\ar@/^3mm/[uu]^{h} & \n" \
  705. "}\n"
  706. # A simple diagram with three curved arrows.
  707. h1 = NamedMorphism(D, A, "h1")
  708. h2 = NamedMorphism(A, D, "h2")
  709. k = NamedMorphism(D, B, "k")
  710. d = Diagram([f, g, h, k, h1, h2])
  711. grid = DiagramGrid(d)
  712. drawer = XypicDiagramDrawer()
  713. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  714. "A \\ar[r]_{f} \\ar@/^3mm/[rr]^{h_{2}} & B \\ar[d]^{g} & D \\ar[l]^{k} " \
  715. "\\ar@/_7mm/[ll]_{h} \\ar@/_11mm/[ll]_{h_{1}} \\\\\n" \
  716. "& C & \n" \
  717. "}\n"
  718. # The same diagram, transposed.
  719. grid = DiagramGrid(d, transpose=True)
  720. drawer = XypicDiagramDrawer()
  721. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  722. "A \\ar[d]^{f} \\ar@/_3mm/[dd]_{h_{2}} & \\\\\n" \
  723. "B \\ar[r]^{g} & C \\\\\n" \
  724. "D \\ar[u]_{k} \\ar@/^7mm/[uu]^{h} \\ar@/^11mm/[uu]^{h_{1}} & \n" \
  725. "}\n"
  726. # The same diagram, with "loop" morphisms.
  727. l_A = NamedMorphism(A, A, "l_A")
  728. l_D = NamedMorphism(D, D, "l_D")
  729. l_C = NamedMorphism(C, C, "l_C")
  730. d = Diagram([f, g, h, k, h1, h2, l_A, l_D, l_C])
  731. grid = DiagramGrid(d)
  732. drawer = XypicDiagramDrawer()
  733. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  734. "A \\ar[r]_{f} \\ar@/^3mm/[rr]^{h_{2}} \\ar@(u,l)[]^{l_{A}} " \
  735. "& B \\ar[d]^{g} & D \\ar[l]^{k} \\ar@/_7mm/[ll]_{h} " \
  736. "\\ar@/_11mm/[ll]_{h_{1}} \\ar@(r,u)[]^{l_{D}} \\\\\n" \
  737. "& C \\ar@(l,d)[]^{l_{C}} & \n" \
  738. "}\n"
  739. # The same diagram with "loop" morphisms, transposed.
  740. grid = DiagramGrid(d, transpose=True)
  741. drawer = XypicDiagramDrawer()
  742. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  743. "A \\ar[d]^{f} \\ar@/_3mm/[dd]_{h_{2}} \\ar@(r,u)[]^{l_{A}} & \\\\\n" \
  744. "B \\ar[r]^{g} & C \\ar@(r,u)[]^{l_{C}} \\\\\n" \
  745. "D \\ar[u]_{k} \\ar@/^7mm/[uu]^{h} \\ar@/^11mm/[uu]^{h_{1}} " \
  746. "\\ar@(l,d)[]^{l_{D}} & \n" \
  747. "}\n"
  748. # The same diagram with two "loop" morphisms per object.
  749. l_A_ = NamedMorphism(A, A, "n_A")
  750. l_D_ = NamedMorphism(D, D, "n_D")
  751. l_C_ = NamedMorphism(C, C, "n_C")
  752. d = Diagram([f, g, h, k, h1, h2, l_A, l_D, l_C, l_A_, l_D_, l_C_])
  753. grid = DiagramGrid(d)
  754. drawer = XypicDiagramDrawer()
  755. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  756. "A \\ar[r]_{f} \\ar@/^3mm/[rr]^{h_{2}} \\ar@(u,l)[]^{l_{A}} " \
  757. "\\ar@/^3mm/@(l,d)[]^{n_{A}} & B \\ar[d]^{g} & D \\ar[l]^{k} " \
  758. "\\ar@/_7mm/[ll]_{h} \\ar@/_11mm/[ll]_{h_{1}} \\ar@(r,u)[]^{l_{D}} " \
  759. "\\ar@/^3mm/@(d,r)[]^{n_{D}} \\\\\n" \
  760. "& C \\ar@(l,d)[]^{l_{C}} \\ar@/^3mm/@(d,r)[]^{n_{C}} & \n" \
  761. "}\n"
  762. # The same diagram with two "loop" morphisms per object, transposed.
  763. grid = DiagramGrid(d, transpose=True)
  764. drawer = XypicDiagramDrawer()
  765. assert drawer.draw(d, grid) == "\\xymatrix{\n" \
  766. "A \\ar[d]^{f} \\ar@/_3mm/[dd]_{h_{2}} \\ar@(r,u)[]^{l_{A}} " \
  767. "\\ar@/^3mm/@(u,l)[]^{n_{A}} & \\\\\n" \
  768. "B \\ar[r]^{g} & C \\ar@(r,u)[]^{l_{C}} \\ar@/^3mm/@(d,r)[]^{n_{C}} \\\\\n" \
  769. "D \\ar[u]_{k} \\ar@/^7mm/[uu]^{h} \\ar@/^11mm/[uu]^{h_{1}} " \
  770. "\\ar@(l,d)[]^{l_{D}} \\ar@/^3mm/@(d,r)[]^{n_{D}} & \n" \
  771. "}\n"
  772. def test_xypic_draw_diagram():
  773. # A linear diagram.
  774. A = Object("A")
  775. B = Object("B")
  776. C = Object("C")
  777. D = Object("D")
  778. E = Object("E")
  779. f = NamedMorphism(A, B, "f")
  780. g = NamedMorphism(B, C, "g")
  781. h = NamedMorphism(C, D, "h")
  782. i = NamedMorphism(D, E, "i")
  783. d = Diagram([f, g, h, i])
  784. grid = DiagramGrid(d, layout="sequential")
  785. drawer = XypicDiagramDrawer()
  786. assert drawer.draw(d, grid) == xypic_draw_diagram(d, layout="sequential")