Peano
Loading...
Searching...
No Matches
test_propagate_use_statements.py
Go to the documentation of this file.
2from peano4.api.taskgraph import Task
3from peano4.api.taskgraph.compiler.PropagateUseStatements import propagate_use_statements
4
5# propagate_use_statements() works on compiled step sequences, i.e. on lists
6# of peano4.api.solversteps.Step. Below, a "graph" is such a list and its
7# "tasks" are the Steps within it (one Step per task, as
8# peano4.api.taskgraph.compiler.Sequential.translate() yields them).
9
10
11READ_AND_DISCARD = Task.DataAccess.READ_AND_DISCARD
12WRITE = Task.DataAccess.WRITE
13READ_AND_WRITE = Task.DataAccess.READ_AND_WRITE
14TEMPORARY = Task.DataAccess.TEMPORARY
15
16
18 """
19 Minimal stand-in for a peano4.api.datamodel.Patch: propagate_use_statements()
20 only ever needs submodel identity, never anything Patch-specific.
21 """
22
23 def __init__(self, name):
24 self.name = name
25
26 def __repr__(self):
27 return self.name
28
29
30def _chain_graph(name_prefix, length):
31 """A step sequence of `length` steps. Returned twice: once as the
32 sequence handed to propagate_use_statements(), once as the list of
33 steps the tests inspect - both are the same list object."""
34 steps = [
35 peano4.api.solversteps.Step(name=f"{name_prefix}-{i}", add_user_defined_actions=False)
36 for i in range(length)
37 ]
38 return steps, steps
39
40
41def _use_cell(step, submodel, qualifier):
42 step.use_cell(submodel)
43 step.cell_data_access[submodel] = qualifier
44
45
46def _use_face(step, submodel, qualifier):
47 step.use_face(submodel)
48 step.face_data_access[submodel] = qualifier
49
50
51def _use_vertex(step, submodel, qualifier):
52 step.use_vertex(submodel)
53 step.vertex_data_access[submodel] = qualifier
54
55
56def _access(task, attribute_name="cell_data_access"):
57 return getattr(task, attribute_name)
58
59
60# ---------------------------------------------------------------------------
61# Single-graph, no edges: the "distance" rule itself.
62# ---------------------------------------------------------------------------
63
64
66 """Distance 1, WRITE then READ_AND_DISCARD: nothing to do."""
67 graph, tasks = _chain_graph("A", 2)
68 submodel = _FakeSubmodel("Q")
69 _use_cell(tasks[0], submodel, WRITE)
70 _use_cell(tasks[1], submodel, READ_AND_DISCARD)
71
72 changed = propagate_use_statements([graph])
73
74 assert changed is False
75 assert tasks[1].cell_data.count(submodel) == 1 # not duplicated
76
77
79 """Distance 1, WRITE then READ_AND_WRITE: also fine."""
80 graph, tasks = _chain_graph("A", 2)
81 submodel = _FakeSubmodel("Q")
82 _use_cell(tasks[0], submodel, WRITE)
83 _use_cell(tasks[1], submodel, READ_AND_WRITE)
84
85 changed = propagate_use_statements([graph])
86
87 assert changed is False
88
89
91 """Distance 1, WRITE then WRITE again: the second WRITE is a fresh,
92 independent production (e.g. ProjectPatchOntoFaces recomputing face
93 data from scratch every time step) - it does not need to read what
94 the first WRITE produced, so there is nothing to protect and nothing
95 to do, even though the two are immediately adjacent."""
96 graph, tasks = _chain_graph("A", 2)
97 submodel = _FakeSubmodel("Q")
98 _use_cell(tasks[0], submodel, WRITE)
99 _use_cell(tasks[1], submodel, WRITE)
100
101 changed = propagate_use_statements([graph])
102
103 assert changed is False
104
105
107 """A WRITE at N is immediately followed by another WRITE at N+1 - N's
108 own search stops right there with nothing to do (superseded). N+1 is
109 itself a WRITE, though, so it is independently checked too, on its
110 own terms: N+1 to the READ_AND_DISCARD at N+2 is distance 1 (odd),
111 already fine."""
112 graph, tasks = _chain_graph("A", 3)
113 submodel = _FakeSubmodel("Q")
114 _use_cell(tasks[0], submodel, WRITE)
115 _use_cell(tasks[1], submodel, WRITE)
116 _use_cell(tasks[2], submodel, READ_AND_DISCARD)
117
118 changed = propagate_use_statements([graph])
119
120 assert changed is False
121
122
124 """Worked example 1: task N writes, task N+2 reads, nothing in between
125 mentions the submodel (distance 2, even) - fixed by inserting a
126 READ_AND_WRITE declaration on task N+1."""
127 graph, tasks = _chain_graph("A", 3)
128 submodel = _FakeSubmodel("Q")
129 _use_cell(tasks[0], submodel, WRITE)
130 _use_cell(tasks[2], submodel, READ_AND_DISCARD)
131
132 changed = propagate_use_statements([graph])
133
134 assert changed is True
135 assert submodel in tasks[1].cell_data
136 assert _access(tasks[1])[submodel] == READ_AND_WRITE
137
138
140 """Worked example 2: task N writes, task N+3 reads (distance 3, odd) -
141 already fine as it stands."""
142 graph, tasks = _chain_graph("A", 4)
143 submodel = _FakeSubmodel("Q")
144 _use_cell(tasks[0], submodel, WRITE)
145 _use_cell(tasks[3], submodel, READ_AND_DISCARD)
146
147 changed = propagate_use_statements([graph])
148
149 assert changed is False
150 assert submodel not in tasks[1].cell_data
151 assert submodel not in tasks[2].cell_data
152
153
155 """Worked example 3: task N writes, task N+4 reads (distance 4, even) -
156 fixed by inserting a READ_AND_WRITE declaration on task N+3, not N+1."""
157 graph, tasks = _chain_graph("A", 5)
158 submodel = _FakeSubmodel("Q")
159 _use_cell(tasks[0], submodel, WRITE)
160 _use_cell(tasks[4], submodel, READ_AND_DISCARD)
161
162 changed = propagate_use_statements([graph])
163
164 assert changed is True
165 assert submodel not in tasks[1].cell_data
166 assert submodel not in tasks[2].cell_data
167 assert submodel in tasks[3].cell_data
168 assert _access(tasks[3])[submodel] == READ_AND_WRITE
169
170
172 """A TEMPORARY use in between is treated exactly like a task that does
173 not mention the submodel at all: task N writes, task N+1 already has
174 a (user-declared) TEMPORARY use of the same submodel, task N+3 reads -
175 the distance that matters is still N to N+3 (distance 3, odd, fine),
176 not N to N+1 (which would wrongly look like an immediate non-read)."""
177 graph, tasks = _chain_graph("A", 4)
178 submodel = _FakeSubmodel("Q")
179 _use_cell(tasks[0], submodel, WRITE)
180 _use_cell(tasks[1], submodel, TEMPORARY)
181 _use_cell(tasks[3], submodel, READ_AND_DISCARD)
182
183 changed = propagate_use_statements([graph])
184
185 assert changed is False
186 assert submodel not in tasks[2].cell_data
187
188
190 """A READ_AND_DISCARD never obliges anything downstream - even an
191 immediately following task that does not mention the submodel at all
192 is fine."""
193 graph, tasks = _chain_graph("A", 3)
194 submodel = _FakeSubmodel("Q")
195 _use_cell(tasks[0], submodel, READ_AND_DISCARD)
196
197 changed = propagate_use_statements([graph])
198
199 assert changed is False
200 assert submodel not in tasks[1].cell_data
201 assert submodel not in tasks[2].cell_data
202
203
205 """Same for TEMPORARY."""
206 graph, tasks = _chain_graph("A", 3)
207 submodel = _FakeSubmodel("Q")
208 _use_cell(tasks[0], submodel, TEMPORARY)
209
210 changed = propagate_use_statements([graph])
211
212 assert changed is False
213
214
216 """READ_AND_WRITE both reads and writes, so - like a plain WRITE - it
217 still obliges whatever comes next (distance 2 here, needs a relay)."""
218 graph, tasks = _chain_graph("A", 3)
219 submodel = _FakeSubmodel("Q")
220 _use_cell(tasks[0], submodel, READ_AND_WRITE)
221 _use_cell(tasks[2], submodel, READ_AND_DISCARD)
222
223 changed = propagate_use_statements([graph])
224
225 assert changed is True
226 assert _access(tasks[1])[submodel] == READ_AND_WRITE
227
228
230 """A second call once everything is already admissible must change
231 nothing and report False."""
232 graph, tasks = _chain_graph("A", 5)
233 submodel = _FakeSubmodel("Q")
234 _use_cell(tasks[0], submodel, WRITE)
235 _use_cell(tasks[4], submodel, READ_AND_DISCARD)
236
237 first_changed = propagate_use_statements([graph])
238 state_after_first = [list(task.cell_data) for task in tasks]
239 second_changed = propagate_use_statements([graph])
240 state_after_second = [list(task.cell_data) for task in tasks]
241
242 assert first_changed is True
243 assert second_changed is False
244 assert state_after_first == state_after_second
245
246
248 graph, tasks = _chain_graph("A", 3)
249 cell_submodel = _FakeSubmodel("Cell")
250 face_submodel = _FakeSubmodel("Face")
251 vertex_submodel = _FakeSubmodel("Vertex")
252 _use_cell(tasks[0], cell_submodel, WRITE)
253 _use_cell(tasks[2], cell_submodel, READ_AND_DISCARD)
254 _use_face(tasks[0], face_submodel, WRITE)
255 _use_face(tasks[2], face_submodel, READ_AND_DISCARD)
256 _use_vertex(tasks[0], vertex_submodel, WRITE)
257 _use_vertex(tasks[2], vertex_submodel, READ_AND_DISCARD)
258
259 propagate_use_statements([graph])
260
261 assert tasks[1].cell_data == [cell_submodel]
262 assert tasks[1].face_data == [face_submodel]
263 assert tasks[1].vertex_data == [vertex_submodel]
264
265
267 """A graph does not need any edge at all to be analysed - it is simply
268 treated as a self-contained sequence, exactly like the old
269 PropagateUseStatements used to for a single already-compiled Step
270 sequence."""
271 graph, tasks = _chain_graph("A", 3)
272 submodel = _FakeSubmodel("Q")
273 _use_cell(tasks[0], submodel, WRITE)
274 _use_cell(tasks[2], submodel, READ_AND_DISCARD)
275
276 changed = propagate_use_statements([graph]) # no edges at all
277
278 assert changed is True
279 assert submodel in tasks[1].cell_data
280
281
282# ---------------------------------------------------------------------------
283# Cross-graph edges, including self-edges (replacing the old Graph.cyclic).
284# ---------------------------------------------------------------------------
285
286
288 """A's last task writes; B's first task is at distance 2 across the
289 (A, B) edge (one silent task in between, in A) - the relay has to be
290 inserted into A (the task exactly one before B's first task), not B."""
291 graph_a, tasks_a = _chain_graph("A", 2)
292 graph_b, tasks_b = _chain_graph("B", 1)
293 submodel = _FakeSubmodel("Q")
294 _use_cell(tasks_a[0], submodel, WRITE)
295 _use_cell(tasks_b[0], submodel, READ_AND_DISCARD)
296
297 changed = propagate_use_statements([graph_a, graph_b], edges=[(graph_a, graph_b)])
298
299 assert changed is True
300 assert submodel in tasks_a[1].cell_data
301 assert _access(tasks_a[1])[submodel] == READ_AND_WRITE
302 assert tasks_b[0].cell_data.count(submodel) == 1 # unchanged, still just the original read
303
304
306 """A (graph, graph) self-edge is what replaces the old Graph.cyclic
307 flag: a graph's last task writing and its own first task reading, at
308 distance 1 across the wrap-around, is already valid."""
309 graph, tasks = _chain_graph("A", 3)
310 submodel = _FakeSubmodel("Q")
311 _use_cell(tasks[0], submodel, READ_AND_DISCARD)
312 _use_cell(tasks[2], submodel, WRITE)
313
314 changed = propagate_use_statements([graph], edges=[(graph, graph)])
315
316 assert changed is False # distance from tasks[2] to tasks[0] (wrap) is 1, and tasks[0] reads
317
318
320 """Same wrap-around, but with a genuine gap needing a relay: the last
321 task writes, the graph's own first task is silent, and the real reader
322 is the *second* task - distance 2 via the wrap (last -> first (silent,
323 distance 1) -> second (read, distance 2)), matching worked example 1
324 but reached through the self-edge instead of a plain successor."""
325 graph, tasks = _chain_graph("A", 3)
326 submodel = _FakeSubmodel("Q")
327 _use_cell(tasks[1], submodel, READ_AND_DISCARD)
328 _use_cell(tasks[2], submodel, WRITE)
329
330 changed = propagate_use_statements([graph], edges=[(graph, graph)])
331
332 assert changed is True
333 assert submodel in tasks[0].cell_data
334 assert _access(tasks[0])[submodel] == READ_AND_WRITE
335
336
338 """Without a (graph, graph) edge, a graph's own last task is a dead
339 end - nothing wraps back to its first task, so a write at the last
340 task with no further use is simply left alone."""
341 graph, tasks = _chain_graph("A", 2)
342 submodel = _FakeSubmodel("Q")
343 _use_cell(tasks[1], submodel, WRITE)
344
345 changed = propagate_use_statements([graph]) # no self-edge
346
347 assert changed is False
348 assert submodel not in tasks[0].cell_data
349
350
352 """Setup -> TimeStep and Setup -> Plot: a submodel Setup last writes has
353 to reach a real reader down *either* branch, and each branch is fixed
354 on its own terms (here: TimeStep's reader sits at distance 2, needing a
355 relay; Plot's sits at distance 1, already valid)."""
356 setup, setup_tasks = _chain_graph("Setup", 1)
357 time_step, time_step_tasks = _chain_graph("TimeStep", 2)
358 plot, plot_tasks = _chain_graph("Plot", 1)
359 submodel = _FakeSubmodel("Q")
360 _use_cell(setup_tasks[0], submodel, WRITE)
361 _use_cell(time_step_tasks[1], submodel, READ_AND_DISCARD) # distance 2 from setup
362 _use_cell(plot_tasks[0], submodel, READ_AND_DISCARD) # distance 1 from setup
363
364 changed = propagate_use_statements(
365 [setup, time_step, plot],
366 edges=[(setup, time_step), (setup, plot)],
367 )
368
369 assert changed is True
370 assert submodel in time_step_tasks[0].cell_data
371 assert _access(time_step_tasks[0])[submodel] == READ_AND_WRITE
372 assert plot_tasks[0].cell_data.count(submodel) == 1
373
374
376 """TimeStep and Plot may follow one another in either order - a
377 submodel TimeStep's last task writes and only Plot's first task reads,
378 at distance 1, is already valid down that path. (Self-edges are
379 deliberately left out here - a single-task graph whose only task both
380 writes and self-repeats would conflict with itself immediately, which
381 is a separate scenario covered by the self-edge tests above.)"""
382 time_step, time_step_tasks = _chain_graph("TimeStep", 1)
383 plot, plot_tasks = _chain_graph("Plot", 1)
384 submodel = _FakeSubmodel("Q")
385 _use_cell(time_step_tasks[0], submodel, WRITE)
386 _use_cell(plot_tasks[0], submodel, READ_AND_DISCARD)
387
388 changed = propagate_use_statements(
389 [time_step, plot],
390 edges=[(time_step, plot), (plot, time_step)],
391 )
392
393 assert changed is False
394
395
396# ---------------------------------------------------------------------------
397# Upgrading a WRITE that immediately follows a READ_AND_WRITE.
398# ---------------------------------------------------------------------------
399
400
402 """A READ_AND_WRITE explicitly asks for its value to be kept around -
403 a WRITE immediately after it would otherwise silently discard that
404 value (predicateForLoad false for a plain WRITE), so it gets upgraded
405 to READ_AND_WRITE instead of being left as a superseding WRITE."""
406 graph, tasks = _chain_graph("A", 2)
407 submodel = _FakeSubmodel("Q")
408 _use_cell(tasks[0], submodel, READ_AND_WRITE)
409 _use_cell(tasks[1], submodel, WRITE)
410
411 changed = propagate_use_statements([graph])
412
413 assert changed is True
414 assert _access(tasks[1])[submodel] == READ_AND_WRITE
415 assert tasks[1].cell_data.count(submodel) == 1 # upgraded in place, not duplicated
416
417
419 """Contrast with the above: a WRITE immediately after another plain
420 WRITE is the legitimate "recompute this fresh every time" pattern and
421 is deliberately left alone - only a READ_AND_WRITE predecessor (which
422 explicitly asked to survive) triggers the upgrade."""
423 graph, tasks = _chain_graph("A", 2)
424 submodel = _FakeSubmodel("Q")
425 _use_cell(tasks[0], submodel, WRITE)
426 _use_cell(tasks[1], submodel, WRITE)
427
428 changed = propagate_use_statements([graph])
429
430 assert changed is False
431 assert _access(tasks[1])[submodel] == WRITE
432
433
435 """The same upgrade, but with the READ_AND_WRITE the last task of one
436 graph and the WRITE the first task of a successor graph."""
437 graph_a, tasks_a = _chain_graph("A", 1)
438 graph_b, tasks_b = _chain_graph("B", 1)
439 submodel = _FakeSubmodel("Q")
440 _use_cell(tasks_a[0], submodel, READ_AND_WRITE)
441 _use_cell(tasks_b[0], submodel, WRITE)
442
443 changed = propagate_use_statements([graph_a, graph_b], edges=[(graph_a, graph_b)])
444
445 assert changed is True
446 assert _access(tasks_b[0])[submodel] == READ_AND_WRITE
447
448
450 """The wrap-around counterpart of test_write_immediately_after_write_is_not_upgraded():
451 a single-task graph whose one task WRITEs and self-repeats (e.g.
452 ProjectPatchOntoFaces recomputing face data fresh every time step) must
453 not get upgraded just because it is - via the self-edge - immediately
454 "after" its own previous WRITE."""
455 graph, tasks = _chain_graph("A", 1)
456 submodel = _FakeSubmodel("Q")
457 _use_cell(tasks[0], submodel, WRITE)
458
459 changed = propagate_use_statements([graph], edges=[(graph, graph)])
460
461 assert changed is False
462 assert _access(tasks[0])[submodel] == WRITE
463
464
466 """Mirrors exahype2.api.fv.RusanovWithExplicitEuler's own task graph:
467 task 0 READ_AND_WRITEs, task 1 WRITEs (upgraded to READ_AND_WRITE by
468 this pass), task 2 READ_AND_WRITEs again - once task 1 is upgraded,
469 every remaining gap is distance 1 (odd), so no relay is ever needed."""
470 graph, tasks = _chain_graph("A", 3)
471 submodel = _FakeSubmodel("Q")
472 _use_cell(tasks[0], submodel, READ_AND_WRITE)
473 _use_cell(tasks[1], submodel, WRITE)
474 _use_cell(tasks[2], submodel, READ_AND_WRITE)
475
476 changed = propagate_use_statements([graph])
477
478 assert changed is True
479 assert _access(tasks[1])[submodel] == READ_AND_WRITE
480 assert tasks[0].cell_data.count(submodel) == 1 # unchanged, no relay inserted
481 assert tasks[2].cell_data.count(submodel) == 1 # unchanged, no relay inserted
482
483
485 """A TEMPORARY use does not relay the submodel through the stacks. If
486 the relay has to go onto a step that already declares the submodel
487 TEMPORARY, that declaration is upgraded to READ_AND_WRITE."""
488 graph, tasks = _chain_graph("A", 3)
489 submodel = _FakeSubmodel("Q")
490 _use_cell(tasks[0], submodel, WRITE)
491 _use_cell(tasks[1], submodel, TEMPORARY)
492 _use_cell(tasks[2], submodel, READ_AND_DISCARD)
493
494 changed = propagate_use_statements([graph])
495
496 assert changed is True
497 assert tasks[1].cell_data.count(submodel) == 1
498 assert _access(tasks[1])[submodel] == READ_AND_WRITE
499
500
501# ---------------------------------------------------------------------------
502# Distances count compiled mesh traversals, not tasks.
503# ---------------------------------------------------------------------------
504
505
507 """Four tasks (cell WRITE, vertex, cell, vertex READ_AND_DISCARD) are
508 three tasks apart, which would be fine. FuseSteps however compiles them
509 into the traversals [A], [B, C], [D], i.e. the read is two traversals
510 after the write, and a relay is required on the fused step [B, C]."""
511 from peano4.api.taskgraph import Graph
512 from peano4.api.taskgraph.compiler import FuseSteps
513
514 submodel = _FakeSubmodel("Q")
515 graph = Graph()
516 task_a = Task("A", Task.GridEntity.CELL)
517 task_b = Task("B", Task.GridEntity.VERTEX)
518 task_c = Task("C", Task.GridEntity.CELL)
519 task_d = Task("D", Task.GridEntity.VERTEX)
520 for task in (task_a, task_b, task_c, task_d):
521 graph.add_task(task)
522 task_b.add_dependency(task_a)
523 task_c.add_dependency(task_b)
524 task_d.add_dependency(task_c)
525 task_a.use_cell(submodel, WRITE)
526 task_d.use_cell(submodel, READ_AND_DISCARD)
527
528 steps = FuseSteps.translate(graph, "Test", None)
529 assert [step.name for step in steps] == ["Test_A", "Test_B__C", "Test_D"]
530
531 changed = propagate_use_statements([steps])
532
533 assert changed is True
534 assert _access(steps[1])[submodel] == READ_AND_WRITE
535
536
537# ---------------------------------------------------------------------------
538# Submodels that are read before they are ever written.
539# ---------------------------------------------------------------------------
540
541
543 """Setup never touches Q, the time step reads it first: the last setup
544 step creates it."""
545 setup, setup_steps = _chain_graph("Setup", 2)
546 time_step, time_step_steps = _chain_graph("TimeStep", 2)
547 submodel = _FakeSubmodel("Q")
548 _use_cell(time_step_steps[0], submodel, READ_AND_WRITE)
549 _use_cell(time_step_steps[1], submodel, WRITE)
550
551 changed = propagate_use_statements(
552 [setup, time_step],
553 edges=[(setup, time_step), (time_step, time_step)],
554 )
555
556 assert changed is True
557 assert _access(setup_steps[1])[submodel] == WRITE
558 assert submodel not in _access(setup_steps[0])
559
560
562 """Setup may be followed by a plot first, which does not use Q: the
563 WRITE goes into setup, and the plot relays Q to the time step."""
564 setup, setup_steps = _chain_graph("Setup", 1)
565 time_step, time_step_steps = _chain_graph("TimeStep", 2)
566 plot, plot_steps = _chain_graph("Plot", 1)
567 submodel = _FakeSubmodel("Q")
568 _use_cell(time_step_steps[0], submodel, READ_AND_WRITE)
569 _use_cell(time_step_steps[1], submodel, READ_AND_WRITE)
570
571 propagate_use_statements(
572 [setup, time_step, plot],
573 edges=[
574 (setup, time_step),
575 (setup, plot),
576 (time_step, plot),
577 (plot, time_step),
578 (time_step, time_step),
579 (plot, plot),
580 ],
581 )
582
583 assert _access(setup_steps[0])[submodel] == WRITE
584 assert _access(plot_steps[0])[submodel] == READ_AND_WRITE
585
586
588 """There is no earlier sequence to create Q in: nothing changes."""
589 setup, setup_steps = _chain_graph("Setup", 2)
590 submodel = _FakeSubmodel("Q")
591 _use_cell(setup_steps[0], submodel, READ_AND_DISCARD)
592
593 changed = propagate_use_statements([setup])
594
595 assert changed is False
596 assert submodel not in _access(setup_steps[1])
597
598
600 """Setup writes Q, so nothing has to be created."""
601 setup, setup_steps = _chain_graph("Setup", 2)
602 time_step, time_step_steps = _chain_graph("TimeStep", 1)
603 submodel = _FakeSubmodel("Q")
604 _use_cell(setup_steps[0], submodel, WRITE)
605 _use_cell(time_step_steps[0], submodel, READ_AND_WRITE)
606
607 propagate_use_statements(
608 [setup, time_step],
609 edges=[(setup, time_step), (time_step, time_step)],
610 )
611
612 assert submodel not in _access(setup_steps[1]) or _access(setup_steps[1])[submodel] == READ_AND_WRITE
613
614
615# ---------------------------------------------------------------------------
616# READ_AND_DISCARD followed by another read.
617# ---------------------------------------------------------------------------
618
619
621 """Two sibling consumers in subsequent steps: the first must keep Q."""
622 graph, tasks = _chain_graph("A", 3)
623 submodel = _FakeSubmodel("Q")
624 _use_cell(tasks[0], submodel, WRITE)
625 _use_cell(tasks[1], submodel, READ_AND_DISCARD)
626 _use_cell(tasks[2], submodel, READ_AND_DISCARD)
627
628 changed = propagate_use_statements([graph])
629
630 assert changed is True
631 assert _access(tasks[1])[submodel] == READ_AND_WRITE
632 assert _access(tasks[2])[submodel] == READ_AND_DISCARD
633
634
636 """The next use produces Q afresh: discarding is fine."""
637 graph, tasks = _chain_graph("A", 3)
638 submodel = _FakeSubmodel("Q")
639 _use_cell(tasks[0], submodel, WRITE)
640 _use_cell(tasks[1], submodel, READ_AND_DISCARD)
641 _use_cell(tasks[2], submodel, WRITE)
642
643 propagate_use_statements([graph])
644
645 assert _access(tasks[1])[submodel] == READ_AND_DISCARD
646
647
649 """The upgraded declaration is an origin like any other: a reader two
650 steps later gets a relay in between."""
651 graph, tasks = _chain_graph("A", 4)
652 submodel = _FakeSubmodel("Q")
653 _use_cell(tasks[0], submodel, WRITE)
654 _use_cell(tasks[1], submodel, READ_AND_DISCARD)
655 _use_cell(tasks[3], submodel, READ_AND_DISCARD)
656
657 propagate_use_statements([graph])
658
659 assert _access(tasks[1])[submodel] == READ_AND_WRITE
660 assert _access(tasks[2])[submodel] == READ_AND_WRITE
One task within a peano4.api.taskgraph.Graph.
Definition Task.py:8
Minimal stand-in for a peano4.api.datamodel.Patch: propagate_use_statements() only ever needs submode...
The Python-side model of "what happens during one mesh sweep".
Definition __init__.py:1
Compilers are simple functions.
Definition __init__.py:1
The task graph abstraction allows users to formalise their solver steps over small task graphs which ...
Definition __init__.py:1
test_write_before_read_needs_no_creation()
Setup writes Q, so nothing has to be created.
test_write_immediately_after_readwrite_across_an_edge_is_upgraded()
The same upgrade, but with the READ_AND_WRITE the last task of one graph and the WRITE the first task...
test_immediate_read_after_write_is_already_valid()
Distance 1, WRITE then READ_AND_DISCARD: nothing to do.
test_time_step_and_plot_composed_in_both_directions()
TimeStep and Plot may follow one another in either order - a submodel TimeStep's last task writes and...
test_immediate_readwrite_after_write_is_already_valid()
Distance 1, WRITE then READ_AND_WRITE: also fine.
test_distance_three_needs_no_change()
Worked example 2: task N writes, task N+3 reads (distance 3, odd) - already fine as it stands.
test_readwrite_origin_is_treated_like_write()
READ_AND_WRITE both reads and writes, so - like a plain WRITE - it still obliges whatever comes next ...
test_is_idempotent()
A second call once everything is already admissible must change nothing and report False.
test_branching_successor_graphs_are_each_fixed_independently()
Setup -> TimeStep and Setup -> Plot: a submodel Setup last writes has to reach a real reader down eit...
test_without_the_self_edge_the_wrap_around_is_not_considered()
Without a (graph, graph) edge, a graph's own last task is a dead end - nothing wraps back to its firs...
_chain_graph(name_prefix, length)
A step sequence of length steps.
test_discard_followed_by_read_at_even_distance_gets_a_relay()
The upgraded declaration is an origin like any other: a reader two steps later gets a relay in betwee...
test_relay_upgrades_a_temporary_declaration()
A TEMPORARY use does not relay the submodel through the stacks.
_access(task, attribute_name="cell_data_access")
test_read_before_write_within_entry_sequence_is_not_changed()
There is no earlier sequence to create Q in: nothing changes.
test_read_origin_triggers_nothing()
A READ_AND_DISCARD never obliges anything downstream - even an immediately following task that does n...
test_discard_followed_by_read_is_upgraded()
Two sibling consumers in subsequent steps: the first must keep Q.
test_distance_two_inserts_readwrite_in_between()
Worked example 1: task N writes, task N+2 reads, nothing in between mentions the submodel (distance 2...
test_discard_followed_by_write_is_not_upgraded()
The next use produces Q afresh: discarding is fine.
test_gap_across_a_cross_graph_edge_is_fixed_in_the_right_graph()
A's last task writes; B's first task is at distance 2 across the (A, B) edge (one silent task in betw...
test_write_immediately_after_readwrite_is_upgraded()
A READ_AND_WRITE explicitly asks for its value to be kept around - a WRITE immediately after it would...
test_temporary_origin_triggers_nothing()
Same for TEMPORARY.
test_distance_four_inserts_readwrite_immediately_before_the_read()
Worked example 3: task N writes, task N+4 reads (distance 4, even) - fixed by inserting a READ_AND_WR...
test_write_immediately_after_write_is_not_upgraded()
Contrast with the above: a WRITE immediately after another plain WRITE is the legitimate "recompute t...
test_distances_count_fused_steps_not_tasks()
Four tasks (cell WRITE, vertex, cell, vertex READ_AND_DISCARD) are three tasks apart,...
test_read_before_any_write_adds_write_to_last_step_of_entry_sequence()
Setup never touches Q, the time step reads it first: the last setup step creates it.
test_self_edge_propagates_across_the_wrap_around()
A (graph, graph) self-edge is what replaces the old Graph.cyclic flag: a graph's last task writing an...
test_read_before_any_write_via_plot_detour_also_relays_through_plot()
Setup may be followed by a plot first, which does not use Q: the WRITE goes into setup,...
test_self_edge_wrap_around_gap_is_fixed()
Same wrap-around, but with a genuine gap needing a relay: the last task writes, the graph's own first...
test_graph_with_no_edges_is_analysed_on_its_own()
A graph does not need any edge at all to be analysed - it is simply treated as a self-contained seque...
test_write_then_write_then_read_finds_the_real_read_past_the_superseding_write()
A WRITE at N is immediately followed by another WRITE at N+1 - N's own search stops right there with ...
test_temporary_declaration_is_transparent_to_the_search()
A TEMPORARY use in between is treated exactly like a task that does not mention the submodel at all: ...
test_upgrade_runs_before_relay_insertion_so_no_relay_is_needed()
Mirrors exahype2.api.fv.RusanovWithExplicitEuler's own task graph: task 0 READ_AND_WRITEs,...
test_write_immediately_after_write_via_self_edge_is_not_upgraded()
The wrap-around counterpart of test_write_immediately_after_write_is_not_upgraded(): a single-task gr...
test_immediate_write_after_write_supersedes_needs_no_relay()
Distance 1, WRITE then WRITE again: the second WRITE is a fresh, independent production (e....