cottax.core.maths.executable module

The proof that a graph is executable: every entry, at every depth, is answered by one problem whose edges lie right against its cycle. Typed separately: an ExecutableGraph is always executable.

body_of[G: NestingGraph](g: G)[source]

The graph without its problems: what is run, never answered.

Return type:

G

check_no_requirements(b: ExecutableGraph)[source]

No requirement is left: every condition is a problem’s, held by unknowns.

N_P = N_C

Return type:

Iterator[Violation]

check_body_is_dag(b: ExecutableGraph)[source]

With the problems taken out no cycle is left: functionally acyclic.

|S_k| = 1 ∀ S_k ∊ SCC[G N_P]

Return type:

Iterator[Violation]

check_one_problem_per_level(b: ExecutableGraph)[source]

No two problems nested in the same thing – both outermost, or both answered inside one problem – are on one cycle.

The mirror of check_nesting_ends_on_one_cycle: a nested pair must share a cycle at the outer’s level, siblings must not.

q ≁ q’ in N A(q) ∀ q ≠ q’ ∊ N_P with A(q) = A(q’)

Same parent (within) is the same A(·): in a forest, equal ancestor sets means equal chains.

Return type:

Iterator[Violation]

check_all_conditions_on_cycle(b: ExecutableGraph)[source]

Every problem iterates something, and every condition it holds is produced there: on its cycle.

|S_p| > 1 and C_p ⊆ ⋃_{k ∊ S_p} O_k ∀ p ∊ N_P

A condition nothing on the cycle produces is a constant to the problem: no unknown can move it. A read nothing produces at all (a boundary input) is one.

Return type:

Iterator[Violation]

check_other_reads_off_cycle(b: ExecutableGraph)[source]

What a problem reads beyond its conditions is not produced on its cycle.

(I_p C_p) ∩ O(S_p) = ∅ ∀ p ∊ N_P

Return type:

Iterator[Violation]

check_other_owns_off_cycle(b: ExecutableGraph)[source]

What a problem owns beyond its (iterable) unknowns is not read on its cycle. (O_p U_p) ∩ I(S_p) = ∅ ∀ p ∊ N_P

Return type:

Iterator[Violation]

class ExecutableGraph[N: Label, V: Place](graph: NestingGraph[N, V])[source]

Bases: ProvedGraph[N, V], Generic[N, V]

A graph proved executable: one problem per level, its body a DAG, and every problem’s edges against its cycle – conditions on it, everything else off it.

checks: PerLevel[tuple[Callable[[Any], object], ...]] = (<function check_no_requirements>, <function check_one_problem_per_level>, <function check_body_is_dag>, <function check_all_conditions_on_cycle>, <function check_other_reads_off_cycle>, <function check_other_owns_off_cycle>)

Callable [self] -> iterable of violations, which are raised at construction unless skipped (see unchecked).

class RequirementUnanswered(node: Label, conditions: tuple[Place, ...])[source]

Bases: Violation

node: Label
conditions: tuple[Place, ...]
template: AbstractClassVar[str] = '{node!r} holds {conditions} against nothing: it has no unknowns, so no driver can move it -- `Determine` it by a variable on its cycle, or drop it'

e.g. ‘output {place!r} is owned by both {other!r} and {node!r}’

Type:

Wording

class SeveralOutermost(cycle: tuple[Label, ...], problems: tuple[Label, ...])[source]

Bases: Violation

cycle: tuple[Label, ...]
problems: tuple[Label, ...]
template: AbstractClassVar[str] = 'cycle {cycle} declares several problems ({problems}) with nothing saying which is outer -- one driver answers one problem, so `Combine` them into a single problem over every unknown, or `Nest` the others inside one of them. Which is a modelling decision, not something the blocking can read off the graph'

e.g. ‘output {place!r} is owned by both {other!r} and {node!r}’

Type:

Wording

class NoProblem(cycle: tuple[Label, ...])[source]

Bases: Violation

cycle: tuple[Label, ...]
template: AbstractClassVar[str] = 'coupled cycle {cycle} declares no problem, so it has no conditions and there is nothing to hand a driver -- `Cut` a variable in the cycle and drive the iteration from outside, or declare the problem to drive within the cycle'

e.g. ‘output {place!r} is owned by both {other!r} and {node!r}’

Type:

Wording

class StillCyclic(problem: Label, cycle: tuple[Label, ...])[source]

Bases: Violation

problem: Label
cycle: tuple[Label, ...]
template: AbstractClassVar[str] = "inside {problem!r}'s iteration, {cycle} is still cyclic with no problem on it, so there is no condition map to build for it. Cut a variable and pick a driver (inside / outside), or nest a problem that answers it in {problem!r}"

e.g. ‘output {place!r} is owned by both {other!r} and {node!r}’

Type:

Wording

class OutsideCycle(problem: Label, unknowns: tuple[Place, ...])[source]

Bases: Violation

problem: Label
unknowns: tuple[Place, ...]
template: AbstractClassVar[str] = '{problem!r} has no body and is in no cycle at its level: nothing it reads depends on {unknowns}, so no iteration can determine them -- a problem is answered by a condition computed from its unknowns (e.g. r_u(u) = 0), not by a value'

e.g. ‘output {place!r} is owned by both {other!r} and {node!r}’

Type:

Wording

class Constant(problem: Label, places: tuple[Place, ...])[source]

Bases: Violation

problem: Label
places: tuple[Place, ...]
template: AbstractClassVar[str] = '{problem!r} reads {places} as a condition, but nothing in its cycle produces it, so no unknown of the problem can move it -- a constant is not a condition; compute it from the unknowns, or drop it'

e.g. ‘output {place!r} is owned by both {other!r} and {node!r}’

Type:

Wording

class ReadInsideCycle(problem: Label, places: tuple[Place, ...])[source]

Bases: Violation

problem: Label
places: tuple[Place, ...]
template: AbstractClassVar[str] = '{problem!r} reads {places} without holding against it, and it is produced inside the block it drives -- a driver reads its data before the block runs, so it must come from outside (an earlier block, or a boundary input)'

e.g. ‘output {place!r} is owned by both {other!r} and {node!r}’

Type:

Wording

class OwnReadInsideCycle(problem: Label, places: tuple[Place, ...], readers: tuple[Label, ...])[source]

Bases: Violation

problem: Label
places: tuple[Place, ...]
readers: tuple[Label, ...]
template: AbstractClassVar[str] = '{problem!r} owns {places} without answering it, and it is read by {readers} inside the block it drives -- a report is bound after convergence, so a reader on the cycle would iterate against a value that does not exist yet'

e.g. ‘output {place!r} is owned by both {other!r} and {node!r}’

Type:

Wording