Part II · Grammar as a Computational Object

Chapter 7

Operational Grammars

mixed The central definitions of the program (provisional); their components are standard, and the chapter says precisely which.

Learning objectives
  • Define an operational grammar and its three layers precisely.
  • Define what it means for a representation to be adequate for a grammar, and why the SDK tests adequacy.
  • Distinguish operational grammars (which generate operations over a structure) from structural grammars (which generate the structure).
  • Write the grammar of each structure of Part I in one common format.
  • State the five mechanisms of advantage and the mechanism thesis, and say why the thesis is a conjecture.
  • Locate the definitions with respect to labelled transition systems, Kleene algebra with tests, attribute grammars and automatic structures.

7.1 The object

Part I described structures one at a time. Each had a set of operations (“moves”), a meaning for each move, a notion of which sequences of moves are allowed, and a representation with costs. The following definition collects these components.

Definition 7.1 (Operational grammar)CGT-DEF-003

An operational grammar is a tuple

G=(Σ,A,⟦⋅⟧,L)G = (\Sigma, \Str, \sem{\cdot}, \Lang)

where A\Str is a computational structure with set of states AA; Σ\Sigma is a finite move signature, each move with an arity; ⟦a⟧⊆A×A\sem{a} \subseteq A \times A is the relational semantics of move aa (for moves with arguments, one relation per argument tuple); and L⊆Γ∗\Lang \subseteq \Gamma^*, for a subset Γ⊆Σ\Gamma \subseteq \Sigma, is the admissibility language, given by a finite description (a regular expression, an automaton, a context-free grammar). The class of GG is the class of L\Lang (regular, context-free, …).

A word w∈Σ∗w \in \Sigma^* (with arguments) is an operation expression. It is

  • syntactically admissible if its moves are in Σ\Sigma with correct arities and its projection πΓ(w)\pi_\Gamma(w) onto Γ\Gamma lies in L\Lang;
  • semantically defined at xx if ⟦w⟧(x)≠∅\sem{w}(x) \neq \varnothing, where ⟦w⟧\sem{w} is the composition of Definition Definition 6.1;
  • executed by a representation at a cost given by a cost profile (Definition 7.4).

Three remarks fix the reading.

  1. Admissibility is a projection. The language constrains only the moves it mentions; observer moves such as here or read are free unless named. This is a definitional choice (CGT-D-015); without it, every constraint would have to enumerate every observer.
  2. Admissibility and definedness are different. pop on an empty stack is admissible under the universal language and undefined; (a b)* a may reject a b although ⟦ab⟧(x)\sem{ab}(x) is non-empty. Syntax is decided by L\Lang without looking at the state; semantics depends on the state.
  3. The finite description of L\Lang is a conventional formal grammar (Σ,N,P,S)(\Sigma, N, P, S) or an automaton. What the definition adds is the structure-dependent semantics ⟦⋅⟧\sem{\cdot} and, in Definition 7.4, a cost model.

7.2 Adequate representations

A representation implements the moves on concrete memory. It is correct when it computes what the semantics says.

Definition 7.2 (Adequacy)

A representation ρ\rho of a structure with grammar GG is adequate if for every state xx and every expression ww: ρ\rho executes ww to completion iff ⟦w⟧(x)≠∅\sem{w}(x) \neq \varnothing, the values it returns at each call are those of the semantics, and when ww is undefined, ρ\rho reports undefinedness at the first call ii at which the prefix w≤iw_{\le i} becomes undefined.

Adequacy is the standard notion of a correct implementation of an abstract data type (an abstraction function relating concrete and abstract states). The SDK makes it executable: every structure has a pure reference semantics denote on its abstract value and an instrumented apply on its representation, and execute compares them call by call. The test suite checks adequacy for every structure on thousands of random expressions, including undefined ones (CGT-D-014).

Here is the whole interface, used to define a new structure from scratch: a counter that can be incremented and decremented within bounds, with an admissibility language forbidding two decrements in a row.

Python · executed by the book's example checker
from cgtsdk import Cost, Inadmissible, Move, Structure

class BoundedCounter(Structure):
    """A counter in [0, K]. Abstract value: the integer."""
    name = "counter"
    SIGNATURE = (Move("inc", (), "add one"), Move("dec", (), "subtract one"), Move("value", (), "read"))

    def __init__(self, K, start=0):
        self.K, self.v = K, start

    def abstract(self):
        return (self.K, self.v)

    @classmethod
    def denote(cls, state, call):
        K, v = state
        if call.name == "inc":
            if v == K: raise Inadmissible("counter at its maximum")
            return (K, v + 1), None
        if call.name == "dec":
            if v == 0: raise Inadmissible("counter at zero")
            return (K, v - 1), None
        return state, v

    def apply(self, call, cost):
        cost.add("compare", 1)
        if call.name == "value":
            cost.add("read", 1); return self.v
        step = 1 if call.name == "inc" else -1
        if not 0 <= self.v + step <= self.K:
            raise Inadmissible("counter at its maximum" if step > 0 else "counter at zero")
        cost.add("write", 1); self.v += step

for expr in ["inc inc dec value", "inc dec dec", "dec", "inc inc inc inc", "inc value inc dec value"]:
    g = BoundedCounter(K=3).grammar(admissible="(inc | dec inc)* (dec | ε)")   # fresh state each time
    r = g.execute(expr)
    status = (f"syntax: {r.syntax.error}" if not r.syntax.ok else
              f"undefined at call {r.undefined_at}: {r.reason}" if not r.defined else f"values {r.values}")
    print(f"{expr:24s} -> {status}  [adequate={r.adequate}]")
Output (checked)
inc inc dec value        -> values [None, None, None, 1]  [adequate=True]
inc dec dec              -> syntax: move word inc dec dec is not in the admissibility language  [adequate=None]
dec                      -> undefined at call 0: counter at zero  [adequate=True]
inc inc inc inc          -> undefined at call 3: counter at its maximum  [adequate=True]
inc value inc dec value  -> values [None, 1, None, None, 1]  [adequate=True]

Execution mutates the representation, so each expression starts from a fresh counter at zero. The second expression is rejected before execution because dec dec is not admissible; the third is admissible and undefined (the counter starts at zero); the fourth runs off the top. The last shows projection at work: value is not mentioned by the language, so it does not interfere with it.

7.3 The grammar of each structure

With the definition in hand, the structures of Part I can be written in one format. The table is a summary of Chapters 2–6; the SDK implements each row.

Structure States Moves Σ\Sigma Defined when Admissibility class (from empty) Typical representation and move cost
array sequences get(i), set(i,x), insert, delete, append, pop, len index in range universal contiguous: O(1)\Oh(1) access, Θ(n−i)\Theta(n-i) shifts
linked list sequences + cursor get(i)… or next, prev index in range / neighbour exists universal records: Θ(i)\Theta(i) access
stack sequences push(x), pop, top, empty non-empty for pop, top context-free, not regular (Dyck prefixes) array: amortized O(1)\Oh(1)
queue / deque sequences enqueue, dequeue, … non-empty same language as the stack ring buffer on ZC\Z_C: amortized O(1)\Oh(1)
map / set finite functions put, get, contains, delete key present for get, delete universal hashing: O(1)\Oh(1) expected; trie: O(∣k∣)\Oh(\lvert k \rvert)
heap multisets push, pop_min, peek non-empty Dyck-type, as for stacks array with heap numeration: O(log⁡n)\Oh(\log n)
binary tree trees + cursor L, R, U, goto(w), read target node exists universal; addresses form a prefix-closed language pointers: Θ(∣w∣)\Theta(\lvert w \rvert); heap numerals: O(⌈h/w⌉)\Oh(\lceil h/w \rceil)
search tree sorted sets insert, delete, contains, min key present for delete universal Θ(h)\Theta(h); AVL: O(log⁡n)\Oh(\log n)
labelled graph vertex sets one move per label, at(v), here image non-empty any regular / context-free L\Lang by choice adjacency lists: Θ(edges inspected)\Theta(\text{edges inspected})
automaton states one move per letter transition defined — table: O(1)\Oh(1) per letter

The table makes visible what is common — every structure is a set of states with partial moves — and what is not: the admissibility class (universal, regular, context-free), the dependence of definedness on state, and above all the cost column.

7.4 Structural grammars

An operational grammar generates operations over a structure. A second kind of grammar generates the structure itself.

Definition 7.3 (Structural grammar)CGT-DEF-004

A structural grammar for a structure A\Str is a grammar whose unique derivation (or unique generated object) is A\Str or its representation: a minimal DAG of a tree, a straight-line program for a string, a tree straight-line program, a hyperedge-replacement grammar for a graph. Its size is the total length of its productions.

The two kinds combine: navigating a grammar-compressed tree is an operational grammar whose states are given by a structural grammar (Chapter 5, CGT-THM-005). Grammar-based compression is an established field [Charikar et al. 2005; Lohrey 2015; Bille et al. 2011]; random access into a grammar-compressed string of length NN takes O(log⁡N)\Oh(\log N) time [Bille et al. 2011], and lower bounds show that for polynomial-space structures this cannot in general be improved to constant time [Verbin & Yu 2013]. This book uses these objects and does not claim new compression results.

7.5 Cost profiles

Definition 7.4 (Resolution scheme and cost profile)CGT-DEF-005

For a class of structures and a family of queries, a resolution scheme is a triple of algorithms (Build, Query, Update) on a fixed machine model. Its cost profile is the tuple

C=(Tbuild,  S,  Tquery,  Tupdate,  ∣out∣),\mathcal{C} = \big(T_{\mathrm{build}},\; S,\; T_{\mathrm{query}},\; T_{\mathrm{update}},\; |\mathrm{out}|\big),

each a function of the structure size nn, the grammar size gg, the query size qq, update parameters uu and output size oo. A grammatical index is a resolution scheme whose Build takes a grammar as input; a grammar-based algorithm is one whose control flow follows derivations (product constructions, fixpoint evaluation of productions, descent through a structural grammar).

The notation T(n,g,q,u,o)T(n, g, q, u, o) is a reporting discipline, not yet a theory: whether grammar parameters yield distinctions not already captured by parameterized complexity or by preprocessing–query trade-offs is an open problem (CGT-OPEN-005).

7.6 Five mechanisms

Every advantage observed so far in this research — in the chapters of Part I and in the ten experiments of the ledger — traces to one of five mechanisms.

Definition 7.5 (Mechanisms of advantage)CGT-DEF-009
Code Mechanism Where it appears
M-ARITH an arithmetic numeration of a dense address space replaces stored structure by computation arrays; ring buffers; heaps (CGT-THM-003); compiled words (CGT-THM-006)
M-PRE precomputation: answers are stored and later retrieved closures, LCA tables, perfect hashing — bounded below by information content (CGT-THM-002)
M-SHARE repeated substructure is represented once minimal DAGs, minimal automata — fragile under updates (CGT-THM-005)
M-RESTRICT a restriction of the instance class makes structure exploitable interval labels on forests; 2-SAT
M-PRUNE the admissibility language excludes moves before they are executed product construction — and it can also multiply work (CGT-THM-001)
Conjecture 7.6 (Mechanism thesis)ConjectureCGT-CONJ-001

Every asymptotic advantage of a grammatical resolution scheme over the best conventional scheme for the same queries, machine model and cost component is attributable to a combination of M-ARITH, M-PRE, M-SHARE, M-RESTRICT and M-PRUNE.

The thesis is not yet a mathematical statement: “conventional scheme” and “attributable” are undefined, and making them precise is an open problem (CGT-OPEN-001). It is nevertheless falsifiable in practice — by a grammar-based scheme with an advantage none of the five explains — and it organizes the rest of the book. Chapters 8–14 examine each mechanism with its cost profile and its failure cases.

7.7 Relationship to established theory

The components of an operational grammar are not new, and it is important to say which existing theories each comes from.

What the program adds is not a new object but a discipline: every claim filed under one layer, every cost stated as a profile in an explicit model, every advantage attributed to a mechanism and paired with the inputs that defeat it.

Exercise 7.1

Extend BoundedCounter with a move reset and an admissibility language in which reset may only occur after at least two inc. Write the language as a regular expression and check your answer with the SDK.

Exercise 7.2

Prove that adequacy is preserved under sequential composition: if a representation is adequate for every single-call expression from every reachable state, it is adequate for all expressions.

Solution to Exercise 7.2

Induction on the length of w=w′cw = w' c. By hypothesis the representation agrees with the semantics on w′w', including the point of first undefinedness; if w′w' is defined, the representation reaches a state whose abstraction is the semantic state yy reached by w′w', and the single-call hypothesis at yy gives agreement on cc. The point of first undefinedness of ww is that of w′w' if w′w' is undefined, and otherwise is the last call iff ⟦c⟧(y)=∅\sem{c}(y) = \varnothing.

Exercise 7.3

Give an operational grammar (states, moves, semantics, admissibility) for a traffic light with moves next and emergency, in which emergency is admissible only from green. Which component of the definition carries the “only from green” condition — syntax or semantics? Argue for both choices.

Exercise 7.4

For each mechanism in Definition 7.5, give one example from outside this book (a database index, a compiler optimization, a cache) and say which cost component of the profile it improves and which it worsens.

Summary
  • An operational grammar G=(Σ,A,⟦⋅⟧,L)G = (\Sigma, \Str, \sem{\cdot}, \Lang) consists of moves, relational semantics on a structure, and an admissibility language constraining the projection of move words.
  • Syntax (admissibility), semantics (definedness and values) and execution (cost) are separate layers; a representation is adequate when its execution agrees with the semantics, and the SDK tests this.
  • Structural grammars generate the structure; operational grammars generate operations over it.
  • Costs are reported as profiles (Tbuild,S,Tquery,Tupdate,∣out∣)(T_{\mathrm{build}}, S, T_{\mathrm{query}}, T_{\mathrm{update}}, |\mathrm{out}|).
  • Five mechanisms — arithmetic addressing, precomputation, sharing, restriction, pruning — account for every advantage observed so far; that they account for all advantages is a conjecture.
  • The components are standard; the program’s contribution is the discipline that keeps them apart.

References cited in this chapter

  1. Barrett et al. 2000 C. Barrett, R. Jacob, M. Marathe (2000). Formal-language-constrained path problems. SIAM Journal on Computing 30(3):809–837. doi:10.1137/S0097539798337716.
  2. Bille et al. 2011 P. Bille, G. M. Landau, R. Raman, K. Sadakane, S. R. Satti, O. Weimann (2011). Random access to grammar-compressed strings. Proc. 22nd ACM-SIAM SODA, pp. 373–389. link.
  3. Blumensath & Grädel 2000 A. Blumensath, E. Grädel (2000). Automatic structures. Proc. 15th IEEE LICS, pp. 51–62.
  4. Charikar et al. 2005 M. Charikar, E. Lehman, D. Liu, R. Panigrahy, M. Prabhakaran, A. Sahai, A. Shelat (2005). The smallest grammar problem. IEEE Transactions on Information Theory 51(7):2554–2576. doi:10.1109/TIT.2005.850116.
  5. Epstein et al. 1992 D. B. A. Epstein, J. W. Cannon, D. F. Holt, S. V. F. Levy, M. S. Paterson, W. P. Thurston (1992). Word Processing in Groups. Jones and Bartlett (later A K Peters).
  6. Khoussainov & Nerode 1995 B. Khoussainov, A. Nerode (1995). Automatic presentations of structures. Logic and Computational Complexity, LNCS 960.
  7. Knuth 1968 D. E. Knuth (1968). Semantics of context-free languages. Mathematical Systems Theory 2(2):127–145. doi:10.1007/BF01692511.
  8. Kozen 1997 D. Kozen (1997). Kleene algebra with tests. ACM Transactions on Programming Languages and Systems 19(3):427–443. doi:10.1145/256167.256195.
  9. Lohrey 2015 M. Lohrey (2015). Grammar-based tree compression. Proc. DLT 2015 (LNCS). doi:10.1007/978-3-319-21500-6_3.
  10. Mendelzon & Wood 1995 A. O. Mendelzon, P. T. Wood (1995). Finding regular simple paths in graph databases. SIAM Journal on Computing 24(6):1235–1258.
  11. Reps 1998 T. Reps (1998). Program analysis via graph reachability. Information and Software Technology 40(11–12):701–726. doi:10.1016/S0950-5849(98)00093-7.
  12. Verbin & Yu 2013 E. Verbin, W. Yu (2013). Data structure lower bounds on random access to grammar-compressed strings. Proc. CPM 2013 (LNCS). link.
  13. Yannakakis 1990 M. Yannakakis (1990). Graph-theoretic methods in database theory. Proc. 9th ACM PODS, pp. 230–242. doi:10.1145/298514.298576.

Report an erratum or an unclear passage in this chapter