Idea Transcript
Graph Transformation and Pointer Structures
Mike Dodds
Submitted for the degree of Doctor of Philosophy
The University of York Department of Computer Science
September 2008
Abstract This thesis is concerned with the use of graphtransformation rules to specify and manipulate pointer structures. In it, we show that graph transformation can form the basis of a practical and wellformalised approach to specifying pointer properties. We also show that graph transformation rules can be used as an efficient mechanism for checking the properties of graphs. We make contextsensitive graph transformation rules more practical for specifying structures, by improving their worstcase application time. We define syntactic conditions ensuring faster application of rules, and we show how these conditions improve the application time of sequences of rules. We apply these fast graph transformation systems to the problem of recognising graph languages in linear time, and show that several interesting contextsensitive languages can be recognised using this approach. We examine the relationship between pointer specification using contextfree graph transformation and separation logic, an alternative approach to reasoning about pointers. We show that formulas in a fragment of separation logic can be translated into a restricted class of hyperedge replacement grammars, and vice versa, showing that these two approaches are of equivalent power. This means that our fragment inherits the formal properties of hyperedgereplacement grammars, such as inexpressibility results. We show that several operators of full separation logic cannot be expressed using hyperedge replacement. We define a Clike language that uses graph transformation rules to ensure pointer safety. This language includes graph transformation constructs for defining and rewriting pointer structures. These constructs can be statically checked for shape safety by modelling them as graph transformation rules. We give both an abstract graphtransformation semantics and a concrete executable semantics for our new constructs, and prove that the semantics correspond.
1
Contents I
Introduction and preliminaries
13
1 Introduction
14
1.1
Background and motivation . . . . . . . . . . . . . . . . . . .
15
1.2
Contribution . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
1.3
Thesis structure . . . . . . . . . . . . . . . . . . . . . . . . . .
21
1.4
Publication history . . . . . . . . . . . . . . . . . . . . . . . .
22
2 Preliminaries
II
23
2.1
Graphs and morphisms . . . . . . . . . . . . . . . . . . . . . .
23
2.2
Doublepushout graph rewriting . . . . . . . . . . . . . . . . .
26
2.3
Hyperedgereplacement graph rewriting . . . . . . . . . . . .
29
2.4
Graph signatures . . . . . . . . . . . . . . . . . . . . . . . . .
32
Fast graph transformation and recognition
3 Fast graph transformation
36 37
3.1
The problems of graph transformation . . . . . . . . . . . . .
38
3.2 3.3
Fast leftconnected graph transformation . . . . . . . . . . . . Fast rooted graph transformation . . . . . . . . . . . . . . . .
42 50
3.4
Multistep graph transformation . . . . . . . . . . . . . . . .
54
4 Efficient graph recognition
65
4.1
Recognition by rooted reduction . . . . . . . . . . . . . . . .
66
4.2
Noncontextfree RGRS languages . . . . . . . . . . . . . . .
76
4.3
Recognition by leftconnected reduction . . . . . . . . . . . .
91
4.4
Comparison between LGRSs and RGRSs
. . . . . . . . . . .
95
4.5
Developing and validating GRSs . . . . . . . . . . . . . . . .
96
2
5 Other approaches tofast graph transformation
III
99
5.1 5.2
Efficient direct derivations . . . . . . . . . . . . . . . . . . . . 99 Efficient multistep derivation . . . . . . . . . . . . . . . . . . 101
5.3
Efficient recognition and special reduction systems . . . . . . 103
Graph grammars and separation logic
6 Semantics of formulas and grammars
108 109
6.1
Separation logic syntax and semantics . . . . . . . . . . . . . 111
6.2
Flattening separation logic formulas . . . . . . . . . . . . . . 122
6.3
Heapgraphs and mapping between domains . . . . . . . . . . 127
6.4
Heapgraph grammars and source normalisation . . . . . . . . 129
7 Mapping between formulas and grammars 137 7.1 Intuitive relationship . . . . . . . . . . . . . . . . . . . . . . . 137 7.2
Mapping from formulas to grammars . . . . . . . . . . . . . . 139
7.3
Proving the correctness of mapping g . . . . . . . . . . . . . . 145
7.4
Mapping from grammars to formulas . . . . . . . . . . . . . . 156
7.5
Proving the correctness of mapping s . . . . . . . . . . . . . . 158
8 Consequences and limitations
IV
164
8.1
Inexpressible separation logic operators
8.2
Extending the heap model . . . . . . . . . . . . . . . . . . . . 169
8.3
Consequences of the correspondence . . . . . . . . . . . . . . 171
8.4
Other related work . . . . . . . . . . . . . . . . . . . . . . . . 175
A language for shape safety
9 CGRS: A language for shape safety
. . . . . . . . . . . . 164
176 177
9.1
Safe pointers by graph transformation . . . . . . . . . . . . . 178
9.2
CGRS: a language for safe pointers . . . . . . . . . . . . . . . 182
9.3
Example: tree insertion and rebalancing . . . . . . . . . . . . 193
9.4
Code size in CGRS . . . . . . . . . . . . . . . . . . . . . . . . 201
10 Semantics of CGRS and shape safety
205
10.1 Extraction of GRSs and rules from CGRS . . . . . . . . . . . 205 10.2 Translating CGRS to C . . . . . . . . . . . . . . . . . . . . . 208 3
10.3 Syntax and semantics of µC . . . . . . . . . . . . . . . . . . . 221 10.4 Translating from memory states to graphs . . . . . . . . . . . 229 10.5 Correctness of translations . . . . . . . . . . . . . . . . . . . . 234 10.6 Shape safety guarantees in CGRS . . . . . . . . . . . . . . . . 242 10.7 Implementing and optimising CGRS . . . . . . . . . . . . . . 244 11 Other approaches to shape safety
245
11.1 Shape types and structured gamma . . . . . . . . . . . . . . . 245 11.2 Specifying structures using logic
. . . . . . . . . . . . . . . . 246
11.3 Shape analysis . . . . . . . . . . . . . . . . . . . . . . . . . . 249 11.4 Specifying structures by graph transformation . . . . . . . . . 252
V
Conclusion
255
12 Conclusions and further work
256
12.1 Thesis summary . . . . . . . . . . . . . . . . . . . . . . . . . 256 12.2 Further work . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 A Balanced binary trees are not MSexpressible
264
Bibliography
267
4
List of Figures 2.1
Commutative diagrams defining a pushout. . . . . . . . . . .
26
2.2
Commutative diagrams defining pullback. . . . . . . . . . . .
26
2.3
Two pushouts defining a derivation. . . . . . . . . . . . . . .
27
3.1
Rooted rule removing an element from a linked list.
. . . . .
52
4.1
RGRS CL for rooted cyclic lists. . . . . . . . . . . . . . . . .
70
4.2
Balanced binary tree. . . . . . . . . . . . . . . . . . . . . . . .
76
4.3
Accepting graph and rules for the rooted GRS RBB. . . . . .
79
4.4
Accepting graph and rules for the rooted GRS RBB (cont). .
80
4.5 4.6
Example reduction sequence for a member of L(RBB). . . . . BBT including three rootpointerpredecessors. . . . . . . . .
81 82
4.7
Rooted grid. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
85
4.8
Grid RGRS rules and accepting graph. . . . . . . . . . . . . .
86
4.9
Rooted binary DAG. . . . . . . . . . . . . . . . . . . . . . . .
87
4.10 Linear RGRS recognising rooted binary DAGs. . . . . . . . .
89
4.11 Rules and accepting graph for LGRS BB. . . . . . . . . . . .
94
5.1
A special reduction system for Stars. . . . . . . . . . . . . . . 105
6.1
Simple heaps illustrating separation logic satisfaction. . . . . 114
6.2
Definition of satisfaction for separation logic. . . . . . . . . . 117
6.3
Flattening function flat. . . . . . . . . . . . . . . . . . . . . . 124
6.4
Rewriting function lift. . . . . . . . . . . . . . . . . . . . . . . 125
6.5
Heap and corresponding heapgraph . . . . . . . . . . . . . . 129
6.6
Grammar producing nonheap graphs. . . . . . . . . . . . . . 130
7.1
Mapping from formulas to grammars. . . . . . . . . . . . . . 143
7.2
Transforming a predicate into a grammar. . . . . . . . . . . . 146
5
7.3
Transforming a predicate into a grammar (cont). . . . . . . . 147
7.4 7.5
Mapping from grammars to formulas. . . . . . . . . . . . . . 157 Transforming a grammar into a formula. . . . . . . . . . . . . 159
8.1
Definition of satisfaction for omitted operators. . . . . . . . . 165
9.1
Rooted GRS defining the language of binary trees. . . . . . . 180
9.2
Graph transformation rule Insert. . . . . . . . . . . . . . . . 181
9.3
Syntax for signatures and shapes. . . . . . . . . . . . . . . . . 186
9.4
CGRS signature and shape declarations. . . . . . . . . . . . . 188
9.5
Syntax for transformers and reducers. . . . . . . . . . . . . . 189
9.6
Textual syntax for transformer functions. . . . . . . . . . . . 192
9.7
Reducer branchleaf. . . . . . . . . . . . . . . . . . . . . . . 194
9.8
Unbalanced tree with AVL labels . . . . . . . . . . . . . . . . 195
9.9
GRS for unbalanced trees with AVL labels. . . . . . . . . . . 196
9.10 Balanced AVL tree with stack. . . . . . . . . . . . . . . . . . 196 9.11 Signature and shape declarations for the avl shape. . . . . . 197 9.12 Transformer rup and corresponding rule . . . . . . . . . . . . 199 9.13 Transformer rdouble and corresponding rule . . . . . . . . . 200 9.14 Transformer relim and corresponding rule . . . . . . . . . . . 201 9.15 Transformer rsingle and corresponding rule . . . . . . . . . 202 10.1 Transformer tree insert and corresponding rule. . . . . . . 208 10.2 Translation to C types for CGRS signatures and types. . . . . 210 10.3 Source code produced from bin and tree. . . . . . . . . . . . 211 10.4 Translation to C for CGRS shapes. . . . . . . . . . . . . . . . 214 10.5 Translation from CGRS to C for transformers. . . . . . . . . 217 10.6 Source code produced from tree goleft. . . . . . . . . . . . 218 10.7 Abstract syntax of µC. . . . . . . . . . . . . . . . . . . . . . . 222 10.8 SOS semantics of µC control flow. . . . . . . . . . . . . . . . 226 10.9 SOS semantics of µC assignment and dereferencing. . . . . . 227 10.10SOS semantics of µC memory handling. . . . . . . . . . . . . 229 10.11Large σstructure . . . . . . . . . . . . . . . . . . . . . . . . . 232 10.12Correctness requirement for translation function. . . . . . . . 236 11.1 Graph type for a doublylinked list node. . . . . . . . . . . . 247
6
Index of Definitions W S, 133
µC state, 224
W S ′,
µC type schema, 223
133
µC type schema
example, 134
example, 224
W S0 , 133 Cpreserving, 29
⇀, 25
Σrule, 34
σ nodepair, 230 example, 231
example, 35
σschema, 230
Σσ , 206
example, 230
example, 207 •,
σstructure, 231
165
example, 231
example, 166 ◦,
⇓, 140
166
example, 140
example, 166
̺rooted Σrule, 67
α, 128
example, 67
example, 129 αt , 141
flat, 124
example, 141 βσ , 233
example, 124 lift, 124
example, 233
example, 124
⊲⊳, 141
abstract declaration, 206
example, 141
example, 206
κ, 148
attachment node, 29
example, 148 ≤p , 118
balanced binary tree, 76
example, 118
example, 76
⌊−⌋, 131
branching factor, 46
L′R , 95
example, 46
LL , 95 Clike rooted graph, 183
LR , 95
example, 183 7
Clike rooted rule, 183
edge enumeration, 43
example, 183 compatible predicate, 132
example, 43 equation system, 149
example, 133
example, 149
complete binary tree, 104
expose, 142
Condition 1, 48
example, 142
Condition 2, 48
external node, 29
Condition 3, 49
fast Σrule, 91
Condition R1, 53
example, 91
Condition R2, 53
flat formula, 122
Condition R3, 54
example, 122
confluence, 71 example, 72
graph, 23
connected component, 24
example, 24
connected graph, 24
graph class, 29
connected nodes, 24
graph environment, 148
correspondence, 150
example, 148
example, 150
graph evaluation, 148
corresponding graph signature, 206
example, 148
example, 207
graph matching problem, 42 graph morphism, 25
dangling condition, 27
example, 25
dangling location, 112
graph properties, 24
degree, 23
example, 24
derivation (doublepushout), 27
graph reduction specification, 179
example, 28
example, 180
derivation (hyperedge replacement),
graph signature, 33
30 descendant edge, 74
example, 33 GRS type, 178
example, 74 descendant node, 74
handle, 29
example, 74
heap, 112
direct derivation (doublepushout), 27
example, 112 direct derivation (hyperedge replace heap fusion, 117 ment), 30 example, 118 heapgraph, 128
edge, 23 8
morphism correspondence, 237
example, 128
example, 237 morphism extension, 43
heapgraph grammar, 129 HR grammar, 31
example, 44
hyperedge, 29
multistep graph transformation prob
hyperedge replacement, 30
lem, 55
example, 31 hyperedge replacement grammar, 31
natural pushout, 26
example, 32
node (graph), 23
hypergraph, 29
node (hypergraph), 29
example, 30
nodepair, 230 example, 231
image (heap), 112 indegree, 23
outdegree, 23
inclusion, 25 initial graph, 31
partial morphism, 25
injective morphism, 25
polynomially terminating GRS, 179
interface graph, 26
example, 180
inverse (rule), 26
predicate interpretation, 116
irreducible, 27
example, 116
isomorphic, 25
preserves undefinedness, 25 production, 30
join, 141
projection (hyperedge), 132
example, 141
pullback, 26
label alphabet, 23
pushout, 26
language (hyperedge replacement), 31 language family, 31
reachable node, 24 replacement, 30
leftconnected graph reduction spec
result morphism, 27
ification (LGRS), 92
RGRS recognition problem, 69
leftconnected rule, 42
righthand side, 26
lefthand side, 26 LGRS recognition problem, 92
root label, 51 root node, 51
linear LGRS, 92
rootpointerpredecessor, 80
example, 93
example, 80
linear RGRS, 71
rooted binary DAG, 87
example, 71
example, 87 rooted cyclic list
matching morphism, 27 9
example, 140
example, 70
rooted graph reduction specification variable interpretation, 115 (RGRS), 68 example, 115 rooted grid, 84 vertex (graph), 23 example, 84 vertex (hypergraph), 29 rooted rule, 51 example, 52 rule, 26
wellsourced, 128 welltyped state, 224
example, 28
example, 224
rule application problem, 40 separating edge, 24 shape safe location, 242 shape safety preservation, 242 shapesafe rule, 181 singleton hypergraph, 29 size (rule), 26 source, 29 source normalisation, 131 example, 136 string heapgraph, 166 example, 166 stringgraph, 165 example, 166 strong Vstructure, 100 example, 100 subgraph, 23 surjective morphism, 25 tagged graph, 139 example, 139 trackmorphism, 27 tupleheap, 169 example, 170 tupletype schema, 169 example, 170 unify tags, 140 10
Acknowledgements I am grateful to my supervisor, Detlef Plump, for his advice, support and guidance during this research, and for giving me the benefit of his invaluable technical understanding. I would like to thank my examiners, Arend Rensink and Colin Runciman, whose clear and helpful comments have improved this thesis immensely. I would also like to thank Colin for his advice and assistance during my PhD, and for getting me interested in mathematical computer science during my time as an undergraduate. I would like to thank the University of York Department of Computer Science for acting as my home for the past eight years, and for providing a friendly, intellectually stimulating environment in which to do research. I would like to thank the members of the Programming Languages and Systems group, and especially Neil Mitchell, Sandra Steinert, Greg Manning, and Matthew Naylor, for their help, advice and friendship. I would like to thank all of the people, including but not limited to those mentioned above, who have commented on various aspects of this work, and especially those who have pointed out my mistakes. This thesis would be much poorer without you. Finally, I would like to thank my partner Lindsay, my parents Alison and Dave, my family, and my friends. This thesis is dedicated to Jess, Grace, Abby and Darcy. Sleep well girls.
11
Author’s declaration I declare that all the work in this thesis is my own, except where attributed and cited to another author. Several sections of this thesis have been published previously. For details, please see the end of Chapter 1.
12
Part I
Introduction and preliminaries
13
Chapter 1
Introduction This thesis is concerned with the use of graph transformation rules to specify and manipulate pointer structures. It aims to show that graph transformation can form the basis of a practical and wellformalised approach to specifying pointer properties, and that graph transformation rules can be used as an efficient mechanism for checking the properties of graphs. In this thesis we address three main questions. First, how can graph transformation rules be made an efficient mechanism for checking the properties of graph structures?
While graph transformation rules are well
understood and semantically clean, their worstcase execution time is too high for many applications, including checking whether a graph is a member of a graph language. We define syntactic conditions ensuring fast application of rules, and apply these conditions to give efficient graph recognition systems. Second, how do approaches based on graph transformation rules relate formally to other approaches to specifying pointer properties? By examining the relationship between graph transformation and other approaches we can better understand the formal properties of both graph grammars and of other approaches. In this thesis we show that contextfree graph grammars are closely related to separation logic [69], probably the most active current approach to specifying pointer properties. Third, how can graph grammars be used to specify the shape of pointer structures in a practical Clike programming language? Pointer structures are widely used in programming languages, but their properties have proved difficult to specify and verify. We develop a language that uses graph gram
14
mars to specify the properties of pointer structures. Graph grammars have previously been used to abstractly specify classes of pointer structures, but applying them to a practical language remains a challenge. This chapter gives an introduction and overview of the research presented in the thesis. Section 1.1 provides context and motivates our work. Section 1.2 describes the general approach and major research contributions of the thesis. Section 1.3 describes in detail the structure of the thesis. Finally, Section 1.4 describes the prior publication history of the work presented in this thesis.
1.1 1.1.1
Background and motivation Graph grammars and graph transformation
Graph transformation rules define rewrites over graphs. They generalise to graphs the string rewrite rules familiar from contextfree and contextsensitive string grammars. Several different formulations have been proposed (see [71] for an overview), but most can be classed as either contextfree or contextsensitive, depending on their power. A major advantage of graph transformation rules is that they have been the subject of a large amount of research into their properties, and they are consequently very well understood formally. Graph transformation rules can be used to specify grammars in the same way that string rewrite rules specify string grammars. Graph grammars define languages of graphs with common properties, by applying a set of productions to a finite initial element. The various kinds of graph grammar form a powerful and general framework for specifying the properties of graphs. This thesis makes considerable use of the doublepushout approach to graph transformation, a contextsensitive approach. Rules in this approach consist of a left and righthand side graph, with an interface between them. Derivations match the lefthand side in the target graph and rewrite it to correspond to the righthand side. Grammars based on doublepushout rules are generally powerful, in the sense that they can define any recursively enumerable language of graphs. A major disadvantage of doublepushout graph transformation is that each rule derivation has a polynomial worstcase time complexity given fixed 15
rules. This is a result of the high cost of matching a rule’s lefthand side to a subgraph in the target graph. As a consequence, graph grammars and graph recognition systems based on contextsensitive rules have a polynomial worstcase complexity. In this thesis, we address this problem by defining syntactic conditions ensuring improved application time. This thesis also makes use of the hyperedgereplacement approach to graph transformation, a contextfree approach. Hyperedge replacement productions replace single nonterminal edges with graphs. The hyperedge replacement approach is less expressive than the doublepushout approach, in that grammars based on doublepushout rules can express languages that are inexpressible by hyperedge replacement grammars. However, hyperedge replacement grammars have attractive theoretical properties, such as decidable language membership.
1.1.2
Pointer structures and shape safety
Software, unlike most other engineered artifacts, rarely does exactly what we want. The designers of bridges or car engines can generally ensure that their designs will work as intended. In contrast, even welltested software often behaves in unexpected ways, often by crashing or corrupting information. The consequences of defective software can be severe, including millions of pounds of economic damage and substantial numbers of deaths. The aim of verification is to ensure that software behaves as we expect. Programs cannot simply be tested to ensure correct behaviour; there are simply too many possible inputs. Instead we must reason formally about programs to show that they conform to specification. Programming languages such as Java and C store complex data as socalled pointer structures. Programs that use pointers are, however, amongst the most difficult to verify, because pointers permit aliasing, which means that local operations can have global effects. While some progress has been made in verifying other programs, verifying pointer manipulating programs is one of the major outstanding challenges in computer science. For a pointer program to behave according to its specification the program’s pointer structures must have the properties that are expected of them: that they are trees, or are acyclic, or are lists of lists of cyclic lists, and so on. We call these largescale properties the shape properties of a structure, and we call verifying that these properties always hold the prob16
lem of shape safety. Verifying shape safety is a necessary precondition for verifying the correctness of most pointer programs. Shape safety verification is currently a highly active area of research. One active approach is verification based on separation logic [69]. This is a recentlydeveloped logic for specifying the properties of heaps that extends normal firstorder logic with a socalled separating conjunction. This allows a formula to specify the spatial relationships between assertions in the heap. Recent work based on separation logic has made considerable progress in verifying pointermanipulating programs [20]. An important measure for evaluating shapesafety approaches is the expressiveness of the approach used for specifying structures – that is, the properties that can be expressed by the approach. The expressiveness of graphtransformation based systems are wellunderstood, but surprisingly little work has been done on the expressiveness of other shapesafety approaches, including separation logic.
1.1.3
Specifying pointer properties using graph grammars
Graph grammars can be used as a mechanism for abstractly specifying and checking the shape properties of pointer structures. The Safe Pointers by Graph Transformation (SPGT) project [4, 3] is a recent approach to ensuring the safety of pointer manipulations. This approach uses doublepushout graph transformation rules to define graph reduction systems (GRS), a variant on graph grammars. A GRS consists of a terminal element and a set of reduction rules. Structures are members of the language of a GRS if they can be reduced to the terminal element by the reduction rules. The SPGT approach uses GRSs to specify the shape properties of pointer structures. Pointer rewrites are then modelled as graph transformation rules. An algorithm has been developed that checks the safety of pointer rewrites expressed as graph transformation rules against a given GRS. Reduction systems have the desirable property that they come with an implied algorithm for membership checking, which consists of simply applying the reduction rules to the input graph. However, the high cost of applying doublepushout rules means that this checking algorithm can at best achieve a polynomial worstcase termination time, even when reduction is confluent. Prior work on the SPGT approach focused on abstractly specifying and 17
checking shape properties [4, 3]. Shape checking in this work was defined over graphs resembling pointer structures, rather than over pointer structures. No attempt was made in this prior work to implement the approach with real executable programs.
1.2
Contribution
In this section we describe the major research contributions of this thesis. These fall into three areas: the development of a framework for efficient graph transformation and recognition; the definition of a relationship between hyperedge replacement grammars and a fragment of separation logic; the development of a new language for ensuring shape safety.
1.2.1
Fast graph transformation and recognition
The SPGT project defines the properties of pointer structures using reduction systems based on graph transformation rules. Contextsensitive graph transformation rules in general require at worst polynomial time for each individual application. This has consequences for their use in some practical applications where polynomial time may be too expensive. In particular, as a result of the high worstcase cost of graph transformation, reduction systems generally require polynomial time or worse for graph recognition, which makes runtime checking of shape properties expensive. The first objective of this thesis is to show that graph transformation rules can be an efficient approach to checking the properties of graphs. To achieve this objective we first develop a approach to graph transformation that improves the application time of individual rules. The syntactic approach we develop is not just suitable for specifying classes of pointer structures; it is a general approach to faster graph transformation. Our approach is based on using syntactic conditions to restrict the possible searchspace for a rule match. We first describe the semantic properties of languages and rulesets that permit efficient rewriting, and then develop syntactic conditions that ensure that these semantic properties hold. Our work on syntactic conditions continues and generalises the work of D¨orr on strong Vstructures [25, 26]. (See §5.1 for a comparison between this work and ours).
18
Our conditions break down into two classes. First, rooted conditions, which require the existence of a uniquelyidentified root. These ensure constant time derivation, given fixed rules. Second, leftconnected conditions, which do not require roots. These ensure lineartime derivation, again with fixed rules. We show that rooted rules can be used for efficient multistep graph transformation. We also show that by sharing information between derivation steps, we can achieve lineartime multistep derivation even with rules that require linear time individually. To do this we modify an algorithm of Arnborg and Courcelle [1] to amortise the cost of application over the whole derivation. We then apply this work to the problem of graph recognition by reduction, and define rooted and leftconnected approaches to lineartime reduction. We present several examples of complex contextsensitive languages that can be recognised in linear time, including grid graphs, balanced binary trees, and rooted binary DAGs. We also prove some interesting results about the expressiveness of fast graph transformation systems. We have treated graph transformation entirely abstractly, without applying it to a particular problem. Our work has defined a class of rules / rule systems with an improved worstcase termination behavior. However, heuristic approaches may perform better than our approach in practice for particular problem domains. In §12.2.1 we discuss some of our objectives for testing our system in practice.
1.2.2
Graph transformation and separation logic
The second objective of this thesis is to examine the relationship between shape specification using graph transformation and other approaches to pointer verification. We have chosen to focus on separation logic, and have shown that a close relationship exists between hyperedge replacement grammars and separation logic. We describe two effective translations between restricted hyperedge replacement grammars and formulas in a fragment of separation logic. These translations preserve the semantics of formulas and grammars. The translations exist because (1) the recursive definitions commonly used in separation logic closely resemble hyperedge replacement productions, and (2) the separating property enforced by separating conjunction 19
corresponds to the contextfree property of hyperedgereplacement grammars. The translations demonstrate that formulas in our fragment of separation logic are of corresponding expressive power to HR grammars under our restrictions. As a consequence, formal results for hyperedge replacement languages, such as inexpressibility results, can be imported into the fragment of separation logic. For example, the languages of redblack trees, balanced binary trees, grid graphs are all known to be HRinexpressible. Consequently, they are also inexpressible in our fragment of separation logic. We have proved that the operators omitted from our fragment of separation logic cannot be simulated in general by a corresponding hyperedge replacement grammar. Notably conjunction corresponds to language intersection, and negation to language complement, both of which are known to be HRinexpressible.
1.2.3
A new language for shape safety
The third objective of this thesis is to show that graph transformation rules can be used to specify the properties of pointer structures in a practical Clike language. Our foundation is the work of the SPGT project on abstractly specifying pointer structures using graph reduction systems. We have defined the language CGRS that applies SPGT checking to the C programming language. Our approach in the design of CGRS is to extend C with constructs that explicitly correspond to those used in SPGTstyle checking. In this, we generalise the approach of Fradet and Le M´etayer [31], who define a shapesafety approach based on contextfree graph transformation and extend C with constructs corresponding to their shape safety approach. (See §11.1 for a comparison between Fradet and Le M´etayer’s work and CGRS). Pointer structures used in the CGRS program have a declared shape that specifies the possible form of the pointer structure. Shapes are declared using a textual syntax that is deliberately close to the abstract definition of a GRS. Shape structures in CGRS are manipulated by transformers. These have a textual syntax corresponding closely to graph transformation rules. CGRS deliberately adopts C conventions for its constructs, for example by requiring that transformers are deterministic. CGRS has been designed to work as an extension to C, and CGRS constructs can be used alongside 20
conventional C pointer structures. In addition, the fact that CGRS uses a syntax close to graph transformation rules means that large rewrites can be clearly expressed in CGRS. The constructs of CGRS have two semantics. First, they have an abstract semantics defined by mapping transformers to graph transformation rules, and shape declarations to GRSs. Second, constructs have a concrete executable semantics, defined by mapping them inplace to chunks of C code. We have proved that the concrete implementation corresponds to the abstract graphtransformation model. To do this, we define a concrete semantics for a fragment of C, and prove that the implementation of a construct corresponds to its abstract model. This proof means that the abstract semantics of CGRS constructs can be used to check the shape safety of the concrete implementation of the constructs. In this thesis we have focused on the correctness of the modelling and semantics. We have defined an operational semantics for CGRS and proved the correctness of this semantics with respect to the abstract semantics. However, we have not yet developed a complete implementation of CGRS. In §10.7 we discuss some of our future objectives for implementing and testing CGRS.
1.3
Thesis structure
The thesis structure is as follows. Chapter 2 gives the basic definitions of graph transformation used in the rest of the thesis. Then the thesis is broken into three parts, reflecting our three areas of research. Part II examines fast graph transformation and efficient recognition. Chapter 3 presents syntactic conditions ensuring fast execution of graph transformation rules, both individually and in sequence. Chapter 4 presents two classes of fast recognition systems with lineartime membership tests. It also presents several example recognition systems including systems recognising balanced binary trees and grid graphs. Chapter 5 compares our work to other approaches to fast graph transformation and efficient recognition. Part III examines the correspondence between hyperedgereplacement grammars and separation logic. Chapter 6 introduces separation logic and hyperedgereplacement graph grammars, defines a semantics to our fragment of separation logic, and defines a translation between separation logic states 21
and heapgraphs. Chapter 7 describes a translation from separation logic to hyperedge replacement and back, and proves that the translations are semanticspreserving. Chapter 8 proves that some constructs must be omitted from our fragment of separation logic and describes the consequences of our translation for both separation logic and hyperedge replacement. In Part IV we describe CGRS, our language for shape safety. Chapter 9 gives the syntax and an informal semantics for CGRS. Examples are given of CGRS programs. Chapter 10 gives a concrete and an abstract semantics for CGRS constructs, and proves that the two correspond. The shape safety guarantees implied by this correspondence are described. Chapter 11 compares CGRS with other approaches to shape safety. The thesis concludes with Chapter 12, which describes our major contributions and suggests possible areas of future work.
1.4
Publication history
Portions of the material on fast graph transformation and efficient recognition presented in Part II appear in my paper Graph Transformation in Constant Time, written with Detlef Plump. This paper presents the rooted syntactic conditions ensuring fast singlestep derivations and efficient recognition. It does not include the work on leftconnected derivation and recognition. This paper was presented to the 2006 International Conference on Graph Transformation [23]. A overview of the material presented in Part III is given in the extended abstract From Separation Logic to Hyperedge Replacement and Back. This short paper omits almost all of the technical detail given in this thesis, including the definitions of the translation functions and the proofs of correctness. It was presented to the Doctoral symposium at the 2008 International Conference on Graph Transformation [21]. A long version written with Detlef Plump will be published in the Doctoral Symposium proceedings [24]. An early version of the material presented in Part IV appears in my paper Extending C for Shape Checking Safety, written with Detlef Plump. This paper includes the syntax of our language CGRS, but omits most of the semantics and proofs of correctness. It was presented to the 2005 workshop on Graph Transformation for Verification and Concurrency [22]. 22
Chapter 2
Preliminaries This chapter defines the basic technical background used in the rest of the thesis.
2.1
Graphs and morphisms
In this section we define the notion of a graph, which is used in doublepushout graph transformation. (In §2.3 we define the notion of a hypergraph, for use in hyperedgereplacement rewriting. In general a (labelled) graph is a (labelled) hypergraph with edges of arity exactly two. It would therefore be possible to define only the single notion of a hypergraph. However, the notations commonly used in doublepushout rewriting and hyperedge replacement research are quite different, and the two approaches are used in entirely disjoint sections of the thesis. For this reason, we define the two notions separately.) Definition 2.1 (label alphabet). A label alphabet is a pair C = hCV , CE i of finite sets CV and CE . The elements of CV and CE serve as node labels and edge labels, respectively. For this section and the next, we assume a fixed alphabet C. Definition 2.2 (graph). A graph G = hVG , EG , sG , tG , lG , mG i over C consists of a finite set VG of nodes (or vertices), a finite set EG of edges, source and target functions sG , tG : EG → VG , a partial node labelling function lG : VG → CV and an edge labelling function mG : EG → CE . The size of G, denoted by G, is the number of nodes plus the number of
23
edges. The outdegree of a node v, denoted by outdegG (v), is the number of edges with source v, while the indegree, denoted by indegG (v) is the number of edges with target v. The degree of a node v, denoted degG (v), is equal to indegG (v) + outdegG (v). We write outlabG (v, l) for {e ∈ EG  sG (e) = v ∧ mG (e) = l}, the set of edges with label l outgoing from v, and inlabG (v, l) for {e ∈ EG  tG (e) = v ∧ mG (e) = l}. A graph H is a subgraph of G if VH ⊆ VG and EH ⊆ VG and sG = sH , tG = tH , lG = lH and mG = mH for the nodes and edges in H. Definition 2.3 (graph properties). A node v ′ is reachable from a node v if v = v ′ or if there are edges e1 , . . . , en such that sG (e1 ) = v, tG (en ) = v ′ and for i = 1, . . . , n − 1, tG (ei ) = sG (ei+1 ). A graph is reachable from some node v if every node in VG is reachable from v. A pair of nodes v and v ′ are connected if v = v ′ or if v is reachable from v ′ or v ′ is reachable from v. A graph is connected if every pair of nodes v, v ′ ∈ VG are connected. A connected component is a maximal subgraph such that every pair of nodes in the subgraph are connected and no node outside the component is connected to a node in the component. We write G \ l for the graph G with edge l removed. An edge l is separating in G if G\l has more connected components than G. Example 2.1 (graph, graph properties). The diagram given below shows a graph G with nodeset VG = {vJ , vK , vL , vM , vN , vP }, edgeset EG = {ea , eb , ec , ed , ee , ef }, and node and edgelabelling functions corresponding to the given node and edge subscripts. a
L
J b
K
c
M
N
d e
f
P The node vM has indegree 3, outdegree 1, and degree 4. The node vK is reachable from vP , via the path ef , ed , ec . The edge eb is separating in G, because removing it results in a graph with two connected components.
24
a
J
K
c
L
M
N
d e
f
P The diagram above shows the graph G′ resulting from the removal of the separating edge eb from G′ . This graph contains two connected components. The nodes vM ,vK ,vN and vP and their intersecting edges form one connected component. The nodes vL and vJ form the other. Definition 2.4 (graph morphism). A graph morphism g : G → H between two graphs G and H consists of two functions gV : VG → VH and gE : EG → EH that preserve sources, targets and labels: sH ◦ gE = gV ◦ sG , tH ◦ gE = gV ◦tG , and lH (gV (v)) = lG (v) for all v in dom(lG ), and mH (gE (e)) = mG (e) for all e ∈ EG . A morphism g is injective (surjective) if gV and gE are injective (surjective); it preserves undefinedness if lH (g(v)) = ⊥ for all v in VG − dom(lG ). Morphism g is an isomorphism if it is injective, surjective and preserves undefinedness. In this case G and H are isomorphic, which is denoted by G ∼ = H. Furthermore, g is an inclusion if g(x) = x for all nodes and edges x in G. (Note that inclusions need not preserve undefinedness.) A partial graph morphism h : G ⇀ H is a graph morphism from some subgraph of G to H. Example 2.2 (graph morphism). Suppose we have the following small graph G′′ with node set VG′′ = {vJ ′ , vM ′ , v⊥ }, edge set EG′′ = {eb′ , e⊥ }, and the labelling functions corresponding to the node and edge subscripts.
J
b
Then the functions gV = {vJ ′ 7→ vJ ′ , vM ′ 7→ vM ′ , v⊥ 7→ v⊥ }, gE = {eb′ 7→ eb′ , ee′ 7→ e⊥ } form an isomorphism (injective, surjective and preserving undefinedness) between G′′ and itself. Now consider the graph G defined in Example 2.1. The functions gV′ = ′ = {e ′ 7→ e , e {vJ ′ 7→ vJ , vM ′ 7→ vM , v⊥ 7→ vP }, gE b ⊥ 7→ ee } form an b
injective morphism between G′′ and G. The pair of functions gV′′ = {vJ ′ 7→ ′′ = {e ′ 7→ e , e ′ 7→ e } form a noninjective vJ , vM ′ 7→ vM , v⊥ 7→ vJ }, gE b b e b
morphism from G′′ to G, as gV′′ (vJ ′ ) = gV′′ (v⊥ ) (meaning vJ ′ is shared). 25
Z
g
f
Z Y
g
f
i1
Y i1
X
i2
X i P 2
P
u
j1
j2 Q
Figure 2.1: Commutative diagrams defining a pushout.
P
p2
p1
X
Q u
Y q1
f g
q2 P p Y 2 p1
Z
X
f g
Z
Figure 2.2: Commutative diagrams defining pullback. Definition 2.5 (pushout, pullback). The pushout of two graph morphisms f : Z → X and g : Z → Y with a common domain consists of a graph P and two morphisms i1 : X → P and i2 : Y → P for which the lefthand diagram in Figure 2.1 commutes. It must be true of the pushout (P, i1 , i2 ) that for any other such triple (Q, j1 , j2 ) there exists a unique morphism u : P → Q for which the righthand side diagram in Figure 2.1 commutes. A pullback is the inverse notion to a pushout. The pullback of f : G → Z and g : Y → Z consists of a graph P and two morphisms p1 : P → X and p2 : P → Y for which the lefthand diagram in Figure 2.2 commutes. It must be true of the pullback (P, p1 , p2 ) that for any other such triple (Q, q1 , q2 ) there exists a unique morphism u : Q → P for which the righthand side diagram in Figure 2.2 commutes. We call a pushout natural if it is also a pullback.
2.2
Doublepushout graph rewriting
In this section we review basic notions of the doublepushout approach to graph transformation, using a version that allows unlabelled nodes in rules [43]. Definition 2.6 (rule, direct derivation). A rule r = hL ← K → Ri consists 26
b L ← K → R g ↓ (1) ↓ d (2) ↓ k G ← D → H Figure 2.3: Two natural pushouts defining a direct derivation from graph G to graph H. of two inclusions K → L and K → R such that (1) for all v ∈ VL , lL (v) = ⊥ implies v ∈ VK and lR (v) = ⊥, and (2) for all v ∈ VR , lR (v) = ⊥ implies v ∈ VK and lL (v) = ⊥. L is the lefthand side, R the righthand side and K the interface of r. The size of a rule r, denoted by r, is equal to max(L, R). The inverse of a rule is obtained by swapping left and right hand sides together with the inclusion morphisms. Definition 2.7 (derivation). A direct derivation from a graph G to a graph H via a rule r = hL ← K → Ri, denoted by G ⇒r,g H or just G ⇒r H, consists of two natural pushouts, arranged as shown in Figure 2.3, where g : L → G is injective. A direct derivation via a set of rules R, denoted by G ⇒R H, is defined if for any rule r in R there exists a direct derivation G ⇒r H. If no direct derivation G ⇒r H exists, then G is irreducible with respect to R. A derivation G ⇒∗R H is defined if there exists a sequence of derivations ∼ I0 ⇒R I1 ⇒R . . . ⇒R In ∼ G= = H. Morphism g in Figure 2.3 is called the matching morphism while k is called the result morphism. The trackmorphism for a direct derivation G ⇒ H, denoted trG⇒H is the partial morphism between the elements of G and H that are preserved by the derivation. Lemma 2.1 (proved in [43]) gives a characterisation of the natural pushouts in a direct derivation. Lemma 2.1 (characterisation of natural pushouts [43]). Given two graph morphisms b : K → L and d : K → D such that b is injective, pushout (1) in Figure 2.3 is natural if and only if for all z ∈ K, lK (z) = ⊥ implies lL (b(z)) = ⊥ or lD (d(z)) = ⊥. Definition 2.8. dangling condition In [43] it is shown that for a given rule r and injective morphism g, there exists such a direct derivation if and only if 27
g satisfies the dangling condition: no node in g(L) − g(K) must be incident to an edge in G − g(L). If the dangling condition is satisfied, then r and g determine D and H uniquely up to isomorphism and H can be constructed (up to isomorphism) from G as follows: (1) Remove all nodes and edges in g(L)−g(K), obtaining a subgraph D ′ . (2) Add disjointly to D ′ all nodes and edges from R − K, keeping their labels. For e ∈ ER − EK , sH (e) is sR (e) if sR (e) ∈ VR − VK , otherwise gV (sR (e)). Targets are defined analogously. (3) For each node gV (v) in g(K) with lL (v) 6= lR (v), lH (gV (v)) becomes lR (v). Note that in the construction, D differs from D′ in that nodes are unlabelled if they are the images of unlabelled nodes in K that are labelled in L. We do not need D to transform G into H though. Rules with unlabelled nodes in the interface graph allow the relabelling of nodes. In addition, rules with unlabelled nodes in their left and righthand sides represent sets of totally labelled rules because unlabelled nodes in the lefthand side can act as placeholders for arbitrarily labelled nodes. Example 2.3 (rule, derivation). The following pair of morphisms define a rule r that matches a pair of nodes labelled J and L, removes the Jlabelled node and constructs a new Y labelled node with an attached alabelled edge.
J
Y a
←
1
a
→
1
1
X
Note that in all three graphs one of the nodes have associated subscript ‘1’. The morphisms map the 1subscripted node in the interface graph to the 1subscripted nodes in the left and righthand sides. As the Jlabelled node on the lefthand side does not have a corresponding node in the interface, it will be deleted by the rule. For the same reason, a new Y labelled node will be constructed by the rule. We often omit the interface graph, and define it by implication using node subscripts. The diagram given below shows the same rule written using this notation.
J
Y a
1
a
⇒
L
1
28
X
Applying this rule to the graph G′ defined in Example 2.1 gives the following direct derivation. a
J
L
K
c
M
a
N
d e
⇒r
X
Y
K
c
M e
f
N
d f
P
P
This rule cannot apply to the graph G defined in Example 2.1, even though there exists a matching morphism between the lefthand side of the rule and the graph. This is because the match would remove the Jlabelled node, leaving dangling edges and so violating the dangling condition. Definition 2.9 (graph class, Cpreserving). To keep this definition independent of any type system imposed on graphs, we introduce abstract graph classes and rules preserving such classes. A graph class over a label alphabet C is a set C of graphs over C. A rule r is Cpreserving if for every direct derivation G ⇒ H, G ∈ C implies H ∈ C.
2.3
Hyperedgereplacement graph rewriting
In this section we review the basic notions of the hyperedgereplacement approach to graph transformation. This section is based on the definitions given in [27, 38]. For this section we assume we have a fixed label alphabet C. We also assume a fixed arity function ari : C → N. Definition 2.10 (hypergraph). A hypergraph H over C and ari is a tuple H = hV, E, att, l, exti. V and E are, respectively, finite sets of vertices (or nodes) and hyperedges (often just referred to as edges). l : E → C assigns an edge label to each edges. att : E → V ∗ assigns to edges a sequence of attachment nodes, with att(e) = ari(l(e)) for all e ∈ E. The first element of att(e) is the source of hyperedge e, if it exists. We denote by att(e)[i] the ith attachment point of e. ext ∈ V ∗ defines a sequence of pairwisedistinct external nodes. A hypergraph H such that extH  = n is an nhypergraph. H the Given a hypergraph H and set X ⊆ C of labels we denote by EX
set {e ∈ EH  l(e) ∈ X} of hyperedges of H with labels in X. The class of 29
all hypergraphs over C is denoted by HC . A graph H ∈ HC is said to be a singleton if the members of VH are all members of extH , and EH  = 1. A singleton H with EH = {e} and att(e) = extH is a handle. The unique handle for a label A is denoted A• . Example 2.4 (hypergraph). The left diagram below shows a hypergraph H over labelset {A, B, C} and arity function {A 7→ 1, B 7→ 2, C 7→ 3}. 1 1
3
3
1
1
B
3
1
C
C
2
2
2
2
1
This hypergraph has three vertices and four nodes. The second attachment point of the B and Clabelled edges is the only external node, represented by the subscript 1. The source (and sole attachment point) of the Alabelled hyperedge is also the source for the B and Clabelled edges. The right diagram shows a singleton hypergraph H ′ . This hypergraph is also the handle C • , because the external nodes are the same as the list of attachment points for the single Clabelled edge. Definition 2.11 (hyperedge replacement). Let H ∈ HC be a hypergraph, B ⊆ EH be a set of hyperedges to be replaced. Let repl : B → HC be a mapping with extrepl(e)  = ari(e) for all e ∈ B. Then the replacement of B in H by repl yields the hypergraph H[repl] by removing B from EH , adding the nodes and hyperedges of repl(e) for each e ∈ B disjointly, and fusing the ith external node of repl(e) with the ith attachment node of e for each e ∈ B and i = 1, . . . , ari(e). Note that the replacement involves merging nodes in the hypergraph repl(e) if att(e) contains repeated nodes. If B = {e1 , . . . , en } and repl(ei ) = Ri for i = 1, . . . , n then we also write H[e1 /R1 , . . . , en /Rn ] instead of H[repl]. Let N ⊆ C be a set of nonterminals. A production over N is an ordered pair p = (A, R) with A ∈ N , R ∈ HC , and extA  = extR . Let H be a graph in HC and let P be a set of productions. Let e ∈ EH and (labH (e), R) ∈ P . Then H directly derives H ′ , denoted by H ⇒P H ′ if H ′ ∼ = H[e/R]. A direct derivation via a set of productions P , denoted by G ⇒P H, is defined if for any production p in P there exists a direct derivation G ⇒p H. A
30
derivation G ⇒∗P H is defined if there exists a sequence of derivations G ∼ = ∼ I0 ⇒P I1 ⇒P . . . ⇒P In = H. Example 2.5 (hyperedge replacement). Suppose we have the set of nonterminals N = {A, C}, and production p = (C, R), where R is the following graph. 1
B
2 1
1
B
2
2
B
2
Then applying p to the graph H defined in Example 2.4 gives the following derivation.
A
1
1
1 1
2
3
C
1 2
⇒p
2
1
1
1 2
2
2
1
1
Definition 2.12 (hyperedge replacement grammar). A hyperedgereplacement grammar (or HR grammar ) G over C is a tuple G = hT, N, P, Zi where T ⊆ C and N ⊆ C are sets of terminal and nonterminal symbols respectively. P is a set of productions over N . Z is the set of initial graphs. The language of the grammar, written L(G) is the set of all graphs H ∈ HT such that there exists a derivation I ⇒∗P H for some I ∈ Z. The language family L : N 7→ HT generated by G is given by L(A) = {G ∈ HT  A• ⇒∗P H}. Note that in this document we define hyperedge replacement grammars with a finite initial set of graphs. In contrast, the standard definition featured in e.g. [27, 38] (and many others) has a single initial graph. This change to the definition makes no difference to the theoretical properties of hyperedgereplacement grammars, because each definition can be simulated by the other. A set of initial graphs {G1 , . . . , Gn } can be trivially simulated in the singlegraph definition by replacing the initial set with a graph containing a single Ilabelled hyperedge of arity 0 (where I does not occur in the given grammar), and adding n productions (I, Gi ) for 1 ≤ i ≤ n. 31
Example 2.6 (hyperedge replacement grammar). Suppose we have a set of terminal labels TCL = {L, E}, arity function ariCL = {L 7→ 2, E 7→ 2}, and set of nonterminal symbols NCL = {L}. Then we can define a hyperedge replacement grammar hTCL , NCL , PCL , ZCL i which generates the language of cyclic Elabelled lists. This grammar has a single graph in its initial set. The initial graph, shown below, consists of a single Llabelled edge with both attachment points attached to the same node. 1
L 2
The grammar has two productions (L, R1 ) and (L, R2 ). The production based on graph R1 , shown on the left below, produces an Llabelled edge and an Elabelled terminal edge. The production based on R2 , shown on the right, terminates the derivation by producing only a single terminal edge. 1
1
E
2
1
L
2
1
1
2
E
2
2
The language of graphs for this grammar is the class of all Elabelled cyclic lists. For example, the following hypergraph is a terminal member of the language containing five edges. 2
1
2 1
1 2
2 1
2.4
2
1
Graph signatures
All of the notation given in previous sections is standard in the literature on graph transformation. We now define the notion of a graph signature, which is not standard. A graph signature defines a class of conformant graphs that restrict the number of edges with a particular label that can be attached to a node with a particular label. Signatures are used in later chapters to conceptually separate local patterns of node and edge attachment from more complex language membership questions.
32
Definition 2.13 (graph signature). A graph signature Σ = hL, in, outi consists of a label alphabet L = hLV , LE i and a pair of partial functions in, out : LV × LE → N. A graph G over C is a Σgraph (or conforms to Σ) if for every node v and edge label k, (1) if out(lG (v), k) is defined, then there are at most out(lG (v), k) edges with source v and label k, and (2) if in(lG (v), k) is defined, then there are at most in(lG (v), k) edges with target v and label k. A graph is Σtotal if for every node v and edge label k, (1) if out(lG (v), k) is defined, then there are exactly out(lG (v), k) edges with source v and label k, and (2) if in(lG (v), k) is defined, then there are exactly in(lG (v), k) edges with target v and label k. Note that no restrictions are placed on the number of incoming and outgoing edges with a particular label if in or out are equal to ⊥ (undefined) for the node and edge label. Example 2.7 (graph signature). Let Σ be a signature hL, in, outi such that L = h{a}, {b}i, in(a, b) = 1 and out(a, b) = ⊥.
The lefthand graph above conforms to the signature, because the indegree of every alabelled node does not exceed 1. The outdegree is unbounded, as out(a, b) = ⊥. However, the righthand graph above does not conform to the signature, because the indegree of the lowest node is 2, which is greater than in(a, b). The notion of a graph signature defined here is similar to the widelyused notion of a multiplicity label. This notion is used in type graphs [73, 68], and in UML class diagrams [62]. A signature Σ where out(l) = n corresponds to a multiplicity of 0 . . . n on an outgoing edge from an llabelled node. If out(l) = ⊥, then the corresponding multiplicity would be ∗. The same is true for in. However, our notion of a signature is designed to be an entirely local, syntactic property. In contrast, type graphs and class diagrams use multiplicity annotations as part of much more expressive systems for specifying 33
largerscale graph properties. Type graphs and class diagrams specify the permitted relationships between nodes. In contrast, our notion of a signature only specifies the permitted multiplicities of node and edge combinations, and places no other restriction on the structure of a graph. Proposition 2.2 (signature conformance complexity). Checking whether a graph G is a Σgraph requires at worst time O(G). Proof. Each node v must be checked for signature conformance. The node label lV (v) and the set of labels l of edges attached to v are first checked to confirm that they are in L. Under our general assumption about the time complexity of accessing information about a graph (Assumption 3.1 in §3.1) given a particular edge label l and node v, the number of outgoing edges from v with label l can be retrieved in time O(1). This value can then be compared with the signaturevalue out(lv (v), l) to check the signature holds for this particular pair. The same O(1) bound holds for incoming edges with label l. Our graphs have fixed, finite labelsets, so for any node v the signature can be checked for all edge labels in time O(1). To check signature conformance for the whole graph requires that each node is checked in this way, so the overall time complexity is O(G). We now define a sufficient syntactic condition on doublepushout graph transformation rules ensuring that they preserve conformance to a signature. Definition 2.14 (Σrule). A rule r = hL ← K → Ri is a Σrule if L, K and R are Σgraphs and for each node v in K and node label l ∈ CV ,1 (1a) lL (v) = ⊥ = lR (v) implies outlabL (v, l) ≥ outlabR (v, l). (1b)
lL (v) = ⊥ = lR (v) implies inlabL (v, l) ≥ inlabR (v, l).
(2a)
lL (v) 6= ⊥ implies out(lL (v), l) − outlabL (v, l) + outlabR (v, l) ≤ out(lR (v), l).
(2b)
lL (v) 6= ⊥ implies in(lL (v), l) − inlabL (v, l) + inlabR (v, l) ≤ in(lR (v), l).
These two conditions ensure that Σgraphs are preserved by Σrules. Conditions (1a) and (1b) ensure that number of outgoing and incoming edges with a particular label cannot increase if the node’s label is not fixed by the 1 To simplify notation, we let the node v stand for its image in the left and righthand side graphs in some cases. Which is meant will be obvious from the domains of the functions.
34
rule. Conditions (2a) and (2b) ensure that the number of edges added by the righthand side combined with the number already in the graph cannot exceed the maximum number mandated by the signature. Example 2.8 (Σrule). Let Σ be a signature hL, in, outi such that L = h{a}, {b}i, in(a, b) = 1 and out(a, b) = 3.
1
2
1
1
⇒ 2
2
1
⇒
2
The lefthand side rule given above is a Σrule, as the new edges respect the signature for the labelled nodes, and do not intersect with the unlabelled node. The righthand rule is not a Σrule, because a new edge is created intersecting an unlabelled node, and because the edges intersecting with the labelled node may violate the signature. Proposition 2.3 (Σrules preserve Σgraphs). Let G ⇒r H be a derivation such that G is a Σgraph and r a Σrule. Then H is a Σgraph. Proof. Consider a node v in VH and label l ∈ LE . If v was not matched by r then the number of llabelled nodes incoming and outgoing from v are unchanged from G and by assumption the node must conform to the signature. If v was matched by an unlabelled node in r, then by conditions (1a) and (1b) the derivation can only decrease the number of incoming and outgoing edges labelled l, so signature conformance is preserved from G. If v was matched by a labelled node, then conditions (2a) and (2b) mean arithmetically that the increase in the number of attached incoming and outgoing llabelled edges cannot exceed the maximum number of edges mandated by the signature.
35
Part II
Fast graph transformation and recognition
36
Chapter 3
Fast graph transformation Graph transformation rules under the doublepushout approach are a powerful mechanism for formulating graph rewriting systems. However, a major obstacle to using these rules in practical computation systems is the time required for rule application. Finding a match for a rule r = hL ← K → Ri in a graph G requires at worst time O(GL ). This is too expensive for some applications, even if r is fixed (meaning that L is constant). For example, in Part IV we propose the language CGRS that extends the C programming language with graphtransformation constructs to enable the safe manipulation of pointers. We argue in §9.2.1 that to make such a language acceptable for programmers, individual rule constructs must be applicable in constant time. Other applications would also benefit from faster graph transformation. Languages based on graphtransformation, such as the GP language [65, 57], and the GROOVE tool [66, 67], are based on the sequential application of graphtransformation rules. For programs written in such languages to execute in a reasonable amount of time, individual rules must apply quickly. Constanttime rule application is achieved in CGRS by using a restricted form of graph transformation characterised by (1) a requirement that all nodes in the hostgraph have distinctlylabelled outedges, and (2) the presence of uniquelylabelled root nodes in rules and host graphs. These roots serve as entry points for the matching algorithm and ensure, under further assumptions on lefthand sides and host graphs, that all matches of a rule can be found in time independent of the size of the host graph. In this chapter we take the CGRS approach and present a more general
37
theory of fast graph transformation in the setting of the doublepushout approach, encompassing both lineartime and constanttime rewriting. The rooted graph transformation used in CGRS is included in this general theory as a special case. Our approach is based on syntactic restriction of rules and graphs that result in an improved worstcase application time. The structure of this chapter is as follows. Section 3.1 defines the basic concepts used in the rest of the chapter, and defines the fundamental problems of graph matching and graph transformation that are solved by our algorithms. Section 3.2 presents a characterisation of socalled leftconnected graph transformation rules, and gives an algorithm for applying these rules. The semantic conditions under which these rules apply in linear time are characterised, and syntactic conditions ensuring these properties hold are given. Section 3.3 presents socalled rooted graph transformation rules, and defines the semantic conditions under which such rules can be applied in constant time. Once again we give syntactic conditions that guarantee that these semantic conditions hold. Finally, Section 3.4 defines the problem of multistep graph transformation, that is, the problem of applying a set of rules in a sequence of rewrites. This section presents conditions on graph transformation systems ensuring that multistep ruleapplications of bounded length terminate in linear time.
3.1
The problems of graph transformation
This section defines formally the basic problems of graph transformation. Here we look at the problem of applying a single rule once only; in §3.4 we extend this to look at applying sets of rules and applying rules in sequence. Assumption 3.1. We assume in the rest of this thesis that graphs are stored in a format such that the time complexities of various problems are as given in the table below.
38
Input
Output
Time
label l
Set Z of nodes with label l.
O(Z)
node v
Values deg(v), indeg(v), outdeg(v).
O(1)
node v, label l
No. nodes with source v and label l.
O(1)
node v, label l
No. nodes with target v and label l.
O(1)
node v, label l
Set Z of nodes with source v and label l.
O(Z)
node v, label l
Set Z of nodes with target v and label l.
O(Z)
graph G
VG  and EG 
O(1)
These assumptions are satisfied by a graph structure where each node stores a set of references to incoming and outgoing edges for each edge label, and also records the size of each of these sets. Constructing a matching morphism for a rule r = hL ← K → Ri into a graph G requires at worst time O(GL ). This time bound is achieved by even the simple algorithm that constructs a partial matching morphism of increasing size by adding nodes and edges in arbitrary order, then backtracks when no extension is possible. As there are at most GL distinct total morphisms from L to G, the size of the searchtree of partial morphisms is similarly bounded. In general, we consider the rule for which we are constructing a derivation fixed, rather than as an input to the problem. This fixedrule viewpoint fits well with the application of this work to graph recognition and programming by graph transformation. In both of these applications we have a ruleset of fixed size from which rules are applied to variablesize graphs, which can be arbitrarily large. This viewpoint also fits with the standard view of computational complexity for computer programs: programs are considered fixed, and datastructures are of variable size. A consequence of the fixedrule viewpoint is that the size of the lefthand side is also fixed, and so the time complexity O(GL ) is a polynomial.1 The exponent may however be very large, depending on the size of the matched rule. The time complexity of applying a rule hL ← K → Ri to a graph G is dominated by the cost of finding the matching graph morphisms L → G. 1
If we consider both the lefthand side L and hostgraph G as part of the input, the problem of constructing a matching morphism becomes the subgraph isomorphism problem. This is the problem to decide whether there exists an injection from L to G. This problem is known to be NPcomplete [35]; In the worst case, if there is no subgraph isomorphism, solving it is as expensive as finding all solutions.
39
This is because for each of these morphisms, checking the dangling condition and transforming G into H can be done in time independent of the size of G (under the assumptions about graph representation given in Assumption 3.1). We isolate the cost of matching by examining first the rule application problem. Rule Application Problem (RAP). Given: A graph class C and a Cpreserving rule r = hL ← K → Ri. Input:
A graph G ∈ C and an injective morphism g : L → G.
Output:
Either a graph H such that G ⇒r,g H, or if no such graph exists, fail.
We have factored out the dangling condition from the rule application problem, meaning that the dangling condition must be checked for a complete morphism after matching. An alternative algorithm would be to check the dangling condition for each node incrementally at the point it was added to the matching morphism. Such an incremental algorithm would result in an improved application time in many cases by ruling out candidate matches earlier. This improvement makes no difference to the worstcase application time, however. We have chosen to factor out the dangling condition despite this potential inefficiency in order to clearly separate concerns between matching and all other aspects of rule application. By focusing on matching exclusively we can more clearly explain our solutions to the high cost of graph transformation. In addition, the algorithms we give below can be simply modified to include incremental checking of the dangling condition without disturbing our improved time complexity results. Finally, an incremental algorithm may give a worse application time than the staged algorithm in cases with many candidate but few complete matches. To solve the rule application problem we define the pair of auxiliary algorithms Dangle and Apply. These algorithms are used in the definition of several of the later algorithms of this chapter. Algorithm 3.1 (Dangle). The algorithm Dangle(G, r, h) takes as its argument a graph G, a rule r = hL ← K → Ri and a candidate morphism g : L → G. It returns pass if g satisfies the dangling condition, or fail otherwise. Dangle is implemented by degree comparison. The dangling condition holds if and only if for all nodes v in VL − VK , degL (v) = degG (h(v)). 40
The dangling condition can therefore be checked by comparing degL (v) with degG (h(v)) for all nodes v in VL − VK . Algorithm 3.2 (Apply). The algorithm Apply(G, r, h) takes as its arguments a graph G ∈ C, Cpreserving rule r = hL ← K → Ri and injection g : L → G satisfying the dangling condition. The algorithm constructs the unique (up to isomorphism) double pushout required for application of r, as given by the following diagram. It returns the uniquelydetermined resulting graph H. L
K
R
D
H
g
G
The algorithm constructs the double pushout for the derivation G ⇒g,r H by first constructing the intermediate graph D, by deleting nodes matched to L − K in the morphism, and removing the labels of nodes that are the images of nodes unlabelled in K. Edges not present in K are also removed. The algorithm then constructs the result graph H. New nodes are constructed, labels are inserted, and new edges are added, so that the elements added to D correspond to the graph R − K. Proposition 3.1 (complexity of RAP). The rule application problem for a rule r = hL ← K → Ri can be solved in time O(r). Proof. The rule application problem can be solved using the algorithms Dangle and Apply. Given an injection h : L → G, checking the dangling condition requires O(VL ) comparisons of node degrees. We have assumed a graph representation such that the degree of any node can be retrieved in constant time, so all of the degree comparisons required by a single run of Dangle can be checked in time O(VL ). Apply must terminate in time O(L − K + R − K + VK ). Given a morphism g, nodes and edges can be removed in time O(L − K). New nodes and edges can be inserted in time O(R − K). Relabelling nodes requires time O(VK ) at most. Thus we obtain the time bound for the RAP of O(VL  + L − K + R − K + VK ). As VL , L, R, K and VK  are all bounded by r, the problem complexity can be estimated as O(r).
41
Having solved the rule application problem, this now leads us to the core problem: the construction of matching morphisms. Graph Matching Problem (GMP). Given: A graph class C and a Cpreserving rule r = hL ← K → Ri. Input:
A graph G in C.
Output:
The set {g : L → G  g is injective}.
As discussed above, on page 38, solving the graph matching problem requires time O(GL ) in the worst case – better algorithms are not known. In the following sections we examine syntactic restrictions on graph transformation that improve this worstcase performance.
3.2
Fast leftconnected graph transformation
This section introduces conditions sufficient to ensure that rules can be executed in linear time (with fixed rules, as under the graph matching problem). Intuitively, for each node in the target graph, these conditions place a constant bound on the number of potential matches which include this node. Because the number of nodes in the graph is bounded by the size of the graph, this results in a search space that grows linearly. Definition 3.1 (leftconnected rule). A graph transformation rule r = hL ← K → Ri is leftconnected if the lefthand side graph L is connected. Assumption 3.2. For the rest of this section, let C be a graph class and r = hL ← K → Ri a fixed Cpreserving leftconnected rule. Let n always refer to the number of edges in L. Our matching algorithm for leftconnected rules is based on the auxiliary algorithm Search, which takes a node s in the lefthand side of a rule and a node v in the target graph and constructs all matching morphisms that map s to v. The algorithm starts with the set A0 consisting of all partial injections that associate only s to v. Each iteration of the algorithm then extends the set of injections in the previous working set with a single edge and its target node, or a single edge and its source node, or a single edge only. Injections that cannot be extended are removed. The algorithm terminates when all of the remaining injections in the set are total, or the set of injections is 42
empty. When an iteration of the algorithm adds some lefthand side node or edge to the domain of an injection, we speak of the node or edge being matched. A preprocessed enumeration of the edges of the lefthand side ensures the algorithm matches edges in order, so that when an edge is matched, its source or target must have been matched in some previous iteration. Definition 3.2 (edge enumeration). An edge sequence e1 , . . . , en is an edge enumeration of L if EL = {e1 , . . . , en } and for i = 2, . . . , n, ei is incident with the source or target of some edge in e1 , . . . , ei−1 . The ith member of enumeration EL is referred to by E[i]. A node is an initial node of the enumeration if it is the source or target of edge e1 . Example 3.1 (edge enumeration). Suppose we have the following graph G. In this graph labels are used to uniquely identify graph edges. 1
a
c
2
3
d e
f 4
The sequence [a, b, c, e, d, f ] is a valid edge enumeration for this graph, with initial nodes 1 and 2. The sequence [f, d, c, b, a, e] is also valid, with initial nodes 3 and 4. However the sequence [a, c, b, e, d, f ] is not valid, as edge c is included before one of its incident nodes has been incident with an earlier edge. The sequence [a, b, d] is also not valid, because it omits some of the graph’s edges. Every connected graph L possesses at least one edge enumeration. An enumeration can be constructed by picking an arbitrary start edge, and then picking edges nondeterministically from the set of edges adjacent to those already in the enumeration. The connectedness of L ensures that all edges must be either attached to an initial node or adjacent to an edge earlier in the enumeration. Definition 3.3 (morphism extension). Given partial morphisms g, g′ : G ⇀ H and edge e ∈ EG , we write g >e g′ (pronounced ‘g extends g′ by e’), if e∈ / dom(h′E ) and dom(hE ) = dom(h′E ) ∪ {e} and dom(hV ) = dom(hV ) ∪ 43
{sG (e), tG (e)}, and for all v ∈ dom(h′V ), h(v) = h′ (v), and for all e ∈ dom(h′E ), h(e) = h′ (e). Example 3.2 (morphism extension). Consider the graph G in Example 3.1. Let v1 and v2 be the nodes with tags 1 and 2, and let ea be the alabelled edge. Suppose we have a partial morphism h : G → G such that hV = {v1 7→ v1 } and hE = ∅. Then the morphism h′ extends h by ea if h′V = {v1 7→ v1 , v2 7→ v2 } and h′E = {ea 7→ ea }. Algorithm 3.3 (Search). The algorithm Search(j, v, G, E) assumes the fixed rule r and it takes as its inputs a lefthand side start node j ∈ VL , and a target graph start node v ∈ VG , a graph G ∈ C, and edge enumeration E = e1 , . . . , en with j as an initial node. It returns as its output a (possibly empty) set of all injections h : L → G such that hV (j) = v. Search(j, v, G, E) 1
A0 ← {g : L ⇀ G  gV (j) = vG ∧ dom(gV ) = {j} ∧ dom(gE ) = ∅}
2
for i ← 1 to n
3 4
do Ai ← {g : L ⇀ G  g is injective ∧ ∃g′ ∈ Ai−1 . g >E[i] g′ } return An
Proposition 3.2 (correctness of Search). Let G ∈ C be a graph. Let E = e1 , . . . , en be an edge enumeration of L and let j ∈ VL be an initial node of E. Let v ∈ VG be a node in G. Then Search(j, v, G, E) returns the set of all injections h : L → G such that hV (j) = v. Proof. Termination is guaranteed by the fact that a given morphism can only be extended to a finite number of distinct new morphisms, bounded by the number of nodes in the target graph. Soundness. The sets Ai for 0 ≤ i ≤ n, must by the definition of Search consist only of partial injections from L to G, so the same holds for the returned set An . For all injections h ∈ A0 it holds that hV (j) = v, so by the definition of morphism extension, this must also hold for all morphism in all other sets Ai for i ≤ n. All that remains then is to show that the morphisms in An are total. It follows by induction on the definition of morphism extension that all morphisms in set Ai are partial injective morphisms with edgedomain e1 , . . . , ei 44
and nodedomain of the sources and targets of e1 , . . . , ei . By the requirement for lefthand side connectedness, all nodes in the graph must be the source or target of some edge ei . Therefore, the returned set An contains only injective morphisms with edgedomain EL and nodedomain VL , i.e. total injective morphisms. Completeness. For i = 1, . . . , n, let Li be the subgraph of L consisting of the edges e1 , . . . , ei and their incident nodes. Also, let L0 be the subgraph consisting of just the start node i. A straightforward induction on i shows that for i = 0, . . . , n, {g : L ⇀ G  gV (j) = v and h is injective and dom(h) = Li } ⊆ Ai . Since Ln = L by the structure of L, it follows that An contains all total injections from L to G. We now define the algorithm FastMatch (Algorithm 3.4), which solves the graph matching problem for leftconnected graph transformation rules.2 This algorithm solves the graph matching problem by searching, for each node in the host graph, for all matching morphisms that include the identified node in their range. Algorithm 3.4 (Matching algorithm FastMatch). The algorithm works for the fixed rule r and an input graph G ∈ C, as stated in the graph matching problem. It assumes an edge enumeration E = e1 , . . . , en of L, and a start node j ∈ VL such that j is an initial node for E. FastMatch(j, G, E) 1
A←∅
2
for each v in VG
3 4 5
do H ← Search(j, v, G, E) A← A∪H return A
Proposition 3.3 (correctness). Algorithm FastMatch solves the graph matching problem. 2 It would be easy to merge the algorithm Search into FastMatch. The algorithms are given separately rather than merged because Search is reused in several later algorithms.
45
Proof. We have shown in Proposition 3.2 that the auxiliary algorithm Search constructs the set of all morphisms h : L → G that matches the lefthand side start node j ∈ VL to a particular host graph start node v ∈ VG . FastMatch applies Search in turn to every node v ∈ VG . Soundness therefore follows immediately from the correctness of Search. Completeness follows from the fact that each morphism h must include some node as the value of hV (j); applying Search to this as the start node will construct the corresponding set of morphisms. We now define the branching factor for an edge enumeration of r’s lefthand side over a target graph. The branching factor determines the worstcase performance of algorithm Search, and so determines the conditions under which it terminates in constant time. Definition 3.4 (branching factor). Let G ∈ C be a graph. Let E = e1 , . . . , en be an edge enumeration of L. Let h : L ⇀ G be a partial injection such that dom(hE ) = {e1 , . . . , ek } for some k such that 1 ≤ k < n, and for any node v, v ∈ dom(hV ) if and only if v is the source or target of an edge in dom(hE ). The outbranching factor of hk is the number of edges e ∈ EG with label l(ek+1 ) and source hV (s(ek+1 )). The inbranching factor is the number of edges e ∈ EG with label l(ek+1 ) and target hV (t(ek+1 )). The local branching factor for hk is the minimum of the out and inbranching factors, if they are defined. The branching factor of E over graph G is the maximum local branching factor for any hk with 1 ≤ k < n. Example 3.3 (branching factor). Left: a lefthand side graph L. Right: a graph G ∈ C. We can use the edge labels of L to stand for its edges as they are distinct for distinct edges. We assume the edge enumeration [a, b] and a partial injection h such that dom(hE ) = {a}. The injection hV maps node l1 to t1 and node l2 to t2. b l1
l2
l3
b
b
t1
t2
b
The outbranching factor for h is 3, as the next edge in the enumeration is labelled b, and there are three edges in the host graph with label b outgoing 46
from node t2. The inbranching factor is undefined, as l3 is not in the domain of the morphism. The local branching factor is therefore 3. This is the maximum of any of the partial morphisms from L to G so the overall branching factor is also 3. Note that, if there is a maximal node degree d in C, every edge enumeration of rule r has a branching factor b ≤ d over all G ∈ C. Proposition 3.4 (complexity of Search). If edge enumeration e1 , . . . , en of the lefthand side graph L has branching factor b over graph G ∈ C then P algorithm Search terminates in time at most O( ni=0 bn ). The maximum size for the resulting set of morphisms is bn . Proof. The initialisation of the algorithm constructs the set A0 , which contains at most one partial morphism, and which (under Assumption 3.1) can be constructed in constant time. A single run of the algorithm involves n iterations of the main loop, each of which attempts to extend all morphisms h in Ai−1 by edge ei . By the definition of an edge enumeration, h must be defined for either sL (ei ), or tL (ei ), or both. By the definition of branching factor there can be at most b distinct edges with the same label as ei attached to either hV (sL (ei )), or hV (tL (ei )), meaning that there can be at most b distinct new injections h′ constructed from h. It follows Ai  ≤ bAi−1 . Under Assumption 3.1, the set of extensions to each h can be identified time O(b), so the time required to update Ai−1 to Ai is O(bAi−1 ). Thus we obtain the following upper bound for the overall running time: O(1 + bA0  + bA1  + . . . + bAn−1 ). By recursively expanding each term Ai  to its maximal size, we arrive at the expression O(1 + b + b2 + . . . + bn ) = P O( ni=0 bi ). Because the final term of the expansion also gives the maximal size of the resulting set, this also shows that maximal size of An is bn .
Theorem 3.5 (complexity of FastMatch). If edge enumeration E = e1 , . . . , en of the lefthand side graph L has branching factor b over graph P G ∈ C, then FastMatch requires time O(VG  ni=0 bi ) at most. The maximal size for the resulting set A is VG bn .
Proof. We have shown in Proposition 3.4 that the time bound for algorithm P Search given a branching factor of b is O( ni=0 bn ), and that it constructs at
most bn distinct morphisms. The algorithm FastMatch applies Search 47
to each node v in VG in turn. We can therefore conclude that the algorithm P requires time O(VG  ni=0 bi ) at most, and that the maximal size for the resulting set of morphisms An is VG bn . Note that n is constant according to the graph matching problem. Hence, if b is bounded then the time bound given in Theorem 3.5 is linear. The time complexity results of Proposition 3.4 and Theorem 3.5 require the existence of an edge enumeration with a bounded branching factor over all members of C. This is a semantic condition, meaning that a complex proof is required to show that the condition holds for a given graph class. We now present several simple syntactic conditions on rule r and class of graphs C that ensure that an edge enumeration with bounded branching factor over all graphs in C can be constructed. These conditions have the main advantage that it is simple to check graph rules and graph classes for conformance to the conditions. Condition 1 (Bounded degree). There exists an integer d ≥ 0 such that for every graph G in C, the degree of every node v ∈ VG is bounded by d. Lemma 3.6 (Condition 1 branching factor). If Condition 1 is satisfied, all edge enumerations of r have branching factor of at most d over all graphs in C. Proof. The degreebound ensures that for any node in a graph G ∈ C and any edge label, there can be no more than d attached edges with this label. Therefore, because the branching factor is defined by the number of attached edges with a particular label, the branching factor b for any edge enumeration over any graph in C must be less than or equal to d. In the next condition we restrict only the outdegree of nodes. This has the interesting consequence that the indegree of the node can be unbounded, but matching still only requires linear time. Condition 2 (Bounded outdegree). (1) There exists a node s in L such that every node in L is reachable from s. (2)
There exists an integer d ≥ 0 such that for every graph G in C, the outdegree of every node v ∈ VG is bounded by d. 48
Lemma 3.7 (Condition 2 branching factor). If Condition 2 is satisfied, there exists an edge enumeration E = e1 , . . . , en with branching factor at most d over all graphs in C. Proof. The reachability property ensures that an edge enumeration E = e1 , . . . , en can be constructed such that for i = 2, . . . , n, the source of ei is the source or target of any of the edges of e1 , . . . , ei−1 . As a result of the outdegree bound, the local branching factor for any morphism h and edge ei with the source of ei already matched in h cannot be larger than d. The structure of the edge enumeration ensures that any partial morphism h constructed from a partial edge enumeration e1 , . . . , ei must include the source node for ei+1 . Therefore, the branching factor b over any G ∈ C of such an enumeration must be less than or equal to d. Proposition 3.8. If either Condition 1 or Condition 2 is satisfied, the graph matching problem for rule r and graph G ∈ C can be solved in time at most P O(VG  ni=0 di ), or linear time. The resulting set contains at most VG dn injections.
Proof. Immediate consequence of Theorem 3.5 and Lemmas 3.6 and 3.7. The next condition requires that outedges are distinctly labelled. This results in an improved time complexity compared to the results proved in Proposition 3.8 for Conditions 1 and 2. Condition 3 (Distinctlylabelled outedges). (1) There exists a node s in L such that every node in L is reachable from s. (2)
For every graph G in C, distinct edges outgoing from the same node have distinct labels.
This condition is satisfied by the graphs and graph transformation rules we use in Chapters 9 and 10 to model pointer structure rewriting. This is because datastructures can include only a single outgoing field of each type (corresponding to a single outedge with each label), but can be the target of an unbounded number of other datastructure fields (corresponding to an unbounded indegree). As a result, the time complexity bound that we prove for this condition holds in our model of pointer rewriting. Note that Condition 3 implies Condition 2, which can be seen by choosing bound d as the size of CE . The converse does not hold in general. Condition 49
1 is incomparable with Conditions 2 and 3, because while its restriction on the degree of nodes is stronger, its restriction on the lefthand sides of rules is weaker. Lemma 3.9 (Condition 3 branching factor). If Condition 3 is satisfied, there exists an edge enumeration E = e1 , . . . , en with branching factor 1 over all graphs in C. Proof. As with Condition 2, the reachability ensures that an enumeration e1 , . . . , en can be constructed such that for i = 2, . . . , n, the source of ei is the source or target of one of the edges of e1 , . . . , ei−1 . Due to the distinctness of outgoing edge labels the local branching factor for any morphism h and edge ei with the source of ei already matched in h cannot be larger than 1. As a result, such an edge enumeration must have an overall branching factor of 1. Proposition 3.10. If Condition 3 is satisfied, the graph matching problem can be solved in time O(nVG  + VG ), or linear time. The resulting set contains at most VG  injections. Proof. Immediate consequence of Theorem 3.5 and Lemma 3.9. Propositions 3.8 and 3.10 show that the graph matching problem can be solved in linear time under Conditions 1, 2, and 3. This is a substantial improvement on the polynomial time required to solve the graph matching problem in general. Conditions 1, 2 and 3 do not alone guarantee that an application of rule r preserves the constraints on C expressed in the conditions. To preserve Condition 1, it suffices that for each node v in K, degR (v) ≤ degL (v). For properties (1) and (2) of Condition 2, it suffices to require that for each node v in K, outdegR (v) ≤ outdegL (v). To ensure the preservation of properties (1) and (2) of Condition 3 it suffices to require that for each node v in K and label l there exists an llabelled node in R with source v only if there exists such an edge in L.
3.3
Fast rooted graph transformation
We now present conditions on graph transformation systems which ensure that rules can be executed in constant time. Intuitively, these systems con50
form to the conditions which ensure that leftconnected rules apply in linear time, and the new requirement that each rule has a root node which can be matched to only a single node in the target graph. Under these conditions, the overall space of possible matches is of constant size, which means that constant time matching can be achieved. Constant time rule application through the use of roots is of interest to us for two reasons, aside from the intrinsic merit of improving the time complexity of rule application. Firstly, rooted rules useful for modelling of pointer rewriting, as we do in Part IV of this thesis. Second, in Chapter 4 rooted rules are used as the basis for efficient language recognition systems. In these rooted recognition systems, the root is explicitly used to control the order of application of reduction rules. Definition 3.5 (root label, root node, rooted rule). A node label ̺ in graph G is a root label if G contains exactly one ̺labelled node. A node v in a graph G is a root if it has label ̺. A graph G is ̺rooted if ̺ is a root label for G. A rule r = hL ← K → Ri is a ̺rooted rule if (1) r is a leftconnected rule, and (2) L is ̺rooted. Assumption 3.3. For the rest of this section, let ̺ be a node label and C a graph class such that ̺ is a root label for every graph in C. Let r = hL ← K → Ri be a fixed Cpreserving ̺rooted rule. Any matching morphism between the target graph G ∈ C and the rule r graph must match the ̺labelled nodes in L and G together. This can cut down substantially the space of possible matches. As the two ̺ labelled nodes must be matched together, they can be used as the start nodes in a single call to Search (Algorithm 3.3), which removes the need for the algorithm FastMatch. We now prove that a single call to Search suffices to solve the graphmatching problem. We also show that the existence of an edge enumeration with a the ̺labelled node as an initial node and bounded branching factor suffices to ensure constant time termination for Search. (See Proposition 3.3 and Theorem 3.5 for the analogous results for leftconnected graph transformation rules). Proposition 3.11 (correctness under rootedness). Let E = e1 , . . . , en be an edge enumeration such that the ̺labelled node in L is an initial node for 51
E
n
E
n
1
⇒ E
E
2
1
n
E 2
Figure 3.1: Rooted rule removing an element from a linked list. E. Let j be the ̺labelled node in L and v be the ̺labelled node in G. Then the call Search(j, v, G, E) solves the graph matching problem for rule r. Proof. By the definition of a root label, there exists exactly one node j in L and exactly one node v in G such that l(j) = l(v) = ̺. For this reason, for any morphism h : L → G it must be true that hV (j) = v. We have shown in Proposition 3.2 that the call Search(j, v, G, E) returns the set of all total injective morphisms h : L → G such that hV (i) = v. Thus the Search solves the graph matching problem. Proposition 3.12 (complexity under rootedness). If the enumeration E = e1 , . . . , en has a branching factor of b over graph G, and the root node labelled ̺ in L is an initial node for E, then the graph matching problem can be solved P by algorithm Search in time O( ni=0 bi ). The maximal size of the resulting set of matching morphisms is bn .
Proof. Immediate consequence of Proposition 3.11 and Proposition 3.4. Note once again that n is constant according to the graph matching problem. Hence, if b is bounded then the time bound of Prop. 3.12 is constant – albeit a possibly large one. Example 3.4 (rooted rule). The root node in a rooted graph transformation rule can be seen as a handle into the graph, used to select an application area for the rule. For example, the rule shown in Figure 3.1 removes a single element from a singlylinked list. The root node, shown as a small grey node, identifies the preceding node from the list element which will be removed. We now give sufficient syntactic conditions on rule r and class of graphs C to ensure that an edge enumeration with bounded branching factor over all graphs in C can be constructed. These conditions are based on Conditions 1, 2, and 3 given in §3.2. We begin by modifying Condition 1 to give Condition R1. 52
Condition R1 (Bounded degree with roots). There exists an integer d ≥ 0 such that for every graph G in C, the degree of every node v ∈ VG is bounded by d. Lemma 3.13 (Condition R1 branching factor). If Condition R1 is satisfied, there exists an edge enumeration with the root of L as an initial node and branching factor of at most d over all G ∈ C Proof. By Lemma 3.6 we know under Condition 1 all edge enumerations have branching factor less than d over graphs in C. The general assumption that rule r is leftconnected ensures that an edge enumeration exists with a rootlabelled node as its initial node. Condition R1 implies Condition 1, so this enumeration must have a bounded branching factor over all G ∈ C. Conditions 2 and 3 are modified in a similar way to give conditions R2 and R3. While Conditions 2 and 3 require that every node in L is reachable from some arbitrary node, Conditions R2 and R3 require that every node is reachable from the rootlabelled node in L. Condition R2 (Bounded outdegree with roots). (1) There exists a node s in L with label ̺ such that every node in L is reachable from s. (2)
There exists an integer d ≥ 0 such that for every graph G in C, the outdegree of every node v ∈ VG is bounded by d.
Lemma 3.14 (Condition R2 branching factor). If Condition R2 is satisfied, then there exists an edge enumeration with branching factor at most d over all graphs in C. Proof. The reachability property ensures that an edge enumeration E = e1 , . . . , en exists such that the rootlabelled node is an initial node, and for i = 2, . . . , n, the source of ei is the source or target of any of the edges of e1 , . . . , ei−1 . By the same argument as used in Lemma 3.7, this enumeration must have a branching factor less than or equal to d over all G ∈ C. Proposition 3.15. If either Condition R1 or Condition R2 is satisfied, the P graph matching problem can be solved in time at most O( ni=0 di ). The resulting set contains at most dn injections.
Proof. Immediate consequence of Proposition 3.12 and Lemmas 3.13 and 3.14. 53
Condition R3 (Distinct outlabels with roots). (1) There exists a node s in L with label ̺ such that every node in L is reachable from s. (2)
For every graph G in C, distinct edges outgoing from the same node have distinct labels.
Lemma 3.16 (Condition R3 branching factor). If Condition R3 is satisfied, there exists an edge enumeration E = e1 , . . . , en with branching factor 1 over all graphs in C. Proof. The reachability property ensures that an edge enumeration E = e1 , . . . , en exists such that the rootlabelled node is an initial node, and for i = 2, . . . , n, the source of ei is the source or target of any of the edges of e1 , . . . , ei−1 . By the same argument used in Lemma 3.9, this enumeration must have branching factor 1 over all graphs in C. Proposition 3.17. If Condition R3 is satisfied, the graph matching problem can be solved in time O(n). The resulting set contains at most one injection. Proof. Immediate consequence of Proposition 3.12 and Lemma 3.16. Propositions 3.15 and 3.16 demonstrate that Conditions R1, R2 and R3 are sufficient to ensure termination in constant time. Note that this is a considerable improvement on the polynomial time complexity of unrestricted graph transformation, and also an improvement on the linear termination time possible under Conditions 1, 2, and 3. Preservation of Conditions R1, R2 and R3 is largely similar to the requirement for preservation given at the end of chapter 3 for the corresponding unrooted conditions. To preserve the ̺labelled node as a root node, it suffices to require that exactly one ̺labelled node is present in both L and R.
3.4
Multistep graph transformation
In many applications of graph transformation rules are not applied individually and in isolation. Rather they are applied in sets and in sequence: the graph resulting from one rule application becomes the input graph for the next rule application. So far in this chapter we have discussed conditions 54
that improve the application time of individual graph transformation rules. This section discusses the efficient sequential application of sets of graphtransformation rules. We examine two approaches to efficient multistep graph transformation, one rooted and one unrooted, both of which ensure linear time termination. We first define formally the multistep application problem. MultiStep Graph Transformation Problem (MGTP). Given: A Graph class C and a set of leftconnected, Cpreserving rules R. Input:
A Graph G ∈ C and a length l ≥ 0.
Output:
A Graph Hi ∈ C which is the result of derivation sequence G ⇒R H1 ⇒R H2 . . . ⇒R Hi where either i = l, or i < l and H is irreducible w.r.t R.
Intuitively, in this problem we are given a set of rules, an initial graph, and a sequence length bound. We must construct an arbitrary sequence of rule applications of up to the given length, starting from the given graph, using the rules in the given set. Any derivation shorter than the given length is also acceptable if it results in an irreducible graph. This avoids the need for backtracking by allowing ‘stuck’ configurations to be valid results. Backtracking must be avoided if we want to achieve lineartime termination, because even quite heavily restricted graph transformation systems with backtracking have an exponential time complexity. For example, if each iteration of the derivation sequence produces two distinct possible results, then there are at most 2l distinct derivations of length l. If all these fail on the final step of the derivation sequence, the search for a valid derivation sequence of length l requires the construction of O(2l ) graphs, even without the cost of matching. In this section we consider both rooted and unrooted multistep graph transformation rules. The rooted case is a simple application of the results given in §3.3, while the unrooted case requires a new application algorithm. For this reason we consider the simpler rooted case first. We have shown in Proposition 3.12 that a rooted rule can be applied to a graph in constant time given a rooted edge enumeration with a bounded branching factor over the graph. As any derivation sequence consists of a sequence of single rule applications, lineartime termination can be achieved by applying this result directly to the multistep graph transformation prob55
lem. This still holds when we construct the sequence by choosing nondeterministically from a set of rules, as the size of the ruleset is fixed under the multistep graph transformation problem. Assumption 3.4. For the rest of this section, let ̺ be a node label and C̺ a graph class such that ̺ is a root label for every graph in C̺ . Let R̺ be a fixed set of Cpreserving ̺rooted rules. In the following lmax̺ refers to the number of nodes in the largest lefthand side of the rules in R, and rulemax̺ refers to sum of the number of nodes in the left and righthand sides of the largest rule in R. Proposition 3.18 (complexity of the MGTP with rooted rules). If there exists for each rule r = hL ← K → Ri in R̺ an edge enumeration Er with the ̺labelled root of L as an initial node and branching factor less than b over all graphs G in C̺ , then the MGTP can be solved in time Plmax O(l(R̺  i=0 ̺ bi + rulemax̺ R̺ blmax̺ )). Proof. By applying Proposition 3.11 we know that we can search for a match
for any rule r ∈ R̺ or show that no such match exists in time at most Plmax O( i=0 ̺ bi ). In the worst case we have to do this for every rule, for a Plmax time complexity of O(R̺  i=0 ̺ bi ). Once the set of potential matches has
been constructed, by Proposition 3.1 we can attempt to apply each match
in time O(lmax̺ ). As each rule can result in at most blmax̺ morphisms, this requires time O(lmax̺ R̺ blmax̺ ) in total. A derivation sequence of length Plmax l requires at most l such searches. The time bound O(l(R̺  i=0 ̺ bi + lmax̺ R̺ blmax̺ )) follows immediately.
If b is a constant, under the multistep graph transformation problem, Plmax C = R̺  i=0 ̺ bi + rulemax̺ R̺ blmax̺ is a constant. Substituting in C
gives time bound O(C.l), or linear time.
In §3.3 we gave Conditions R1, R2 and R3 which ensure the existence of an enumeration with a bounded branching factor for a single rooted rule and graph class. These conditions can also be applied in the context of multistep graph transformation. We say that a set of rules satisfies one of the conditions if every rule in the set satisfies the condition. Proposition 3.19. If R̺ and C̺ satisfy Condition R1, R2 or R3, then the MGTP can be solved in time O(l).
56
Proof. Immediate consequence of Lemmas 3.7 and 3.9, and Proposition 3.18.
3.4.1
Amortising the cost of matching
We now consider an algorithm that permits fast multistep graph transformation for leftconnected rules. Assumption 3.5. For the rest of this section, let R be a set of leftconnected, Cpreserving rules. In the following lmax refers to the number of nodes in the largest lefthand side and rmax the number of nodes in the largest righthand side of the rules in R. rulemax refers to sum of the number of nodes in the left and righthand sides of the largest rule in R. We have shown in §3.2 that leftconnected rules can be applied in linear time given the existence of an edge enumeration with bounded branching factor. Intuitively this would seem to imply that solving the multistep graph transformation problem must require polynomial time, as each of the l steps must require at worst linear time. However, if for every node in the lefthand side of every rule in R there exists an edge enumeration with a bounded branching factor and the lefthand side node as its initial node, then the MGTP can in fact be solved in linear time. Condition 1, described in §3.3, ensures the existence of such an enumeration bound, and so ensures linear time termination of our new algorithm MultistepApply . The advantage of this algorithm lies in its time complexity, which is considerably improved over a na¨ıve algorithm that applies each rule in isolation and shares no information between steps in the derivation sequence. For comparison, we now define an example of such a na¨ıve algorithm, making use of the algorithm FastMatch (Algorithm 3.4). Algorithm 3.5 (na¨ıve multistep application algorithm). This algorithm takes arguments in the same format as MultistepApply. MultistepNa¨ıve(H0 , R, l, E) 1 2 3 4
for i ← 1 to l do for r = hL ← K → Ri in R do pick s from L M ← M ∪ FastMatch(s, Hi−1 , E[r, s]) 57
if M = ∅
5 6 7
then return Hi−1 else pick (h, r) from M Hi ← Apply(Hi−1 , r, h)
8 9
return Hl
The termination time of MultistepNa¨ıve depends on the maximum size of the intermediate graphs constructed during the derivation. The maximum possible size of any graph constructed on a derivation of length l from the graph H0 ∈ C is given by the function maxsize(H0 , l) = H0  + l.rmax. This function is correct because any application of a rule in R can add at most rmax nodes to the existing graph. Even assuming an array of edge enumerations with branching factor bounded by b over all graphs in C, MultistepNa¨ıve has worstcase terP i mination time of O(l.R.maxsize(H0 , l). lmax i=0 b ) (consequence of Theorem 3.5). Under Condition 1, this gives polynomial time termination. Our more sophisticated algorithm MultistepApply improves on this because it permits linear time termination (assuming an enumeration branching factor bound). This result holds even when each individual rule application requires at worst linear time for application. It is achieved by sharing information between iterations of the derivation by maintaining a set of unsearched nodes. This amortises the high cost matching over the whole derivation. Sharing this information between steps requires new application algorithm MultistepApply, which is based on the algorithm of [1]. MultistepApply maintains a search set consisting of a set of unsearched host graph nodes. In each iteration of the algorithm an unsearched node in the host graph is first selected. Then for each rule in R the set of matches that include the selected node are constructed. If a match is discovered then the particular rule is applied, and the algorithm begins again on the new graph. If no matches that include the selected node exist for any rule in R, then the node is removed from the search set and a new node is selected. When the graph is updated, any match between a rule in R and the new graph not already present in the previous graph must include some of the nodes accessed by the update, so these are added to the searchset. 58
The algorithm is guaranteed to terminated in a linearlybounded number of iterations, even without a bound on the enumeration branching factor. This is because each rewrite can add only add a bounded number of new nodes to the graph, and so the searchset can only increase by a bounded amount in each iteration. However a constant bound on the branching factor is required to ensure that the algorithm Search, called by MultistepApply, terminates in constant time, which ensures overall linear time termination. We first define the auxiliary algorithm Apply*, which extends the rule application algorithm Apply. Apply* differs from in Apply in that that it returns two values rather than one. In addition to the derived graph, Apply* also constructs the set of nodes which constitute the image of the rule’s righthand side in the new graph. This set is used in the algorithm MultistepApply to locate the nodes accessed by each iteration of the algorithm. Algorithm 3.6 (algorithm Apply*). The algorithm Apply*(G, r, h) extends the algorithm Apply (Alg. 3.2). As with Apply, the algorithm takes as its arguments a graph G ∈ C, Cpreserving rule r = hL ← K → Ri and injection g : L → G satisfying the dangling condition. The algorithm constructs the unique (up to isomorphism) doublepushout required for application of rule r, as given by the following diagram. It is implemented in exactly the same way as described for Apply. L
K
g
R k
G
D
H
Apply* returns a pair of values: (1) the uniquelydetermined resulting graph H, and (2) the set ran(kV ) of nodes in H matched to R by the result morphism k. We now give algorithm MultistepApply, which solves the multistep graph transformation problem for leftconnected rules. This algorithm uses the general approach of the algorithms given in the work on socalled special recognition systems in [1] and [11], but applies it to our domain of general rewrite systems based on leftconnected rules, rather than just the domain of graphlanguage recognition. The differences between our algorithm and the algorithm of [1] are described in detail in §5.3. 59
Algorithm 3.7 (MultistepApply). This algorithm takes as its input a nonempty graph H0 ∈ C, a set of leftconnected, Cpreserving rules R, a derivationsequence length l ≥ 0, and an array of edge enumerations E[r, s], where index values r and s consist of a rule r ∈ R and a start node s in the lefthand side of r. MultistepApply(H0 , R, l, E) 1 i←1 2 U ← VH 0 3 while U 6= ∅ and i 6= l 4
do M ← ∅
5
pick v from U
6
for r = hL ← K → Ri in R
7
do for s in VL do S ← Search(s, v, Hi−1 , E[r, s])
8
M ← M ∪ {(h, r)  h ∈ S ∧ Dangle(Hi−1 , r, h)}
9 10
if M = ∅
11
then U ← U \ {v}
12
else i ← i + 1
13
pick (h, r) from M
14
U ← U \ dom(hV )
15
(Hi , P ) ← Apply*(Hi−1 , r, h)
16 17
U ←U ∪P return Hi
The definition of MultistepApply we give here first picks a node in the set of candidate nodes, and then constructs all possible matches for all rules in R that can be discovered from this location. Only then does the algorithm check the dangling condition for any of the matches. This algorithm is less efficient in many cases than the alternative version that checks the dangling condition for each match as it is constructed. This backtracking version of MultistepApply eliminates candidate matches earlier, and thus often finds a successful match more quickly. In addition, the version of MultistepApply given here often finds matches more than once while searching from different starting points. The backtracking version of MultistepApply avoids some of this duplication, 60
although failed partial matches can still appear several times. However, the backtracking version of MultistepApply is considerably harder to understand intuitively, and the version of MultistepApply given here is in any case a large and complex algorithm. The worstcase time complexity result given below in Theorem 3.23 holds for both versions of the algorithm. To make our explanation simpler we have therefore given the comparatively inefficient version of the algorithm. Our aim in giving the algorithm is to communicate the approach embodied in it, rather than present the most highly optimised version possible. The time complexity results given below depend on the assumption that insertion and removal of nodes from the working set U requires only constant time. To achieve this, we assume that the nodes of the graph include in their representation both ‘next’ and ‘previous’ fields, in addition to the representation of graph edges. The working set of graphnodes U can then be stored as a circular doublylinked list embedded in the graph itself, with each graphnode storing pointers to two adjacent nodes. Such a list permits both constant time insertion and constanttime removal of any node. Such a node representation can be constructed in time OH0  if the graph is not already in this format, so this assumption does not affect linear time termination results given Theorem 3.23. Intuitively, the following lemma expresses the fact that, given a derivation G ⇒ H, any morphisms between the lefthand side of some rule r ′ and H which weren’t present between r and G must have been generated by the derivation. Direct derivations under the doublepushout approach are local, in the sense that only the portion of the graph matched by the lefthand side is modified. As a result, any ‘new’ morphism must include in its range a node matched to the righthand side graph of the rule in the derivation. Lemma 3.20 (new morphisms). Let r = hL ← K → Ri and r ′ = hL′ ← K ′ → R′ i be rules. Let G ⇒r H be a derivation for r, and let k : R → H be the result morphism for the derivation. Let trG⇒H be the corresponding trackmorphism. Let h′ : L′ → H be a morphism such that h′ 6= trG⇒H ◦ h for all h : L′ → G. Then then there must exist a v in ran(h′V ) such that v is also in ran(kV ). Proof. Suppose no node exists in ran(h′V ) that is also in ran(kV ), so all nodes in ran(h′V ) are also in ran(trG⇒H ). As the relationships between nodes are 61
preserved outside of the domain of the righthand side, we can construct an h such that h′ = trG⇒H ◦ h by simply applying the inverse of trG⇒H to h′ , which contradicts our assumption that h′ 6= trG⇒H ◦h for all h : L′ → G. Theorem 3.21 (correctness). The set of possible results for the nondeterministic algorithm MultistepApply is the set of possible results for the multistep graph transformation problem.3 Proof. Algorithm soundness follows trivially from the soundness of algorithms Search (proved in Proposition 3.2) and Dangle and Apply*. To prove completeness we must show that if a graph is a solution to the multistep graph transformation problem, then it is a possible result for the algorithm. We prove this by showing that the set U contains enough nodes to ensure that any matching morphism can be discovered by applying Search to some node in U . We prove this by showing that the algorithm maintains the following invariant I. Here i ≥ 1 is the current iteration in MultistepApply, where the working graph Hi is the result of derivation sequence H0 ⇒R H1 ⇒R . . . ⇒R H i . I:
For every rule r = hL ← K → Ri in R and every morphism h : L → Hi−1 there must exist a node in U that is a member of ran(hV ).
We now show that I is preserved by the main loop of MultistepApply. When the algorithm starts, i = 1 and H0 is the input graph and U = VH0 . Therefore by construction the property holds. Now consider the inductive case. An iteration of the main loop begins by selecting some node v ∈ U . It then executes the algorithms Search and Dangle on this node for all rules and lefthand side initial nodes, and stores the results in set M . There are two possible results for the search: 1. M = ∅. By the completeness of Search and Dangle, no pair (h, r) of a rule r ∈ R and a morphism h exists such that v ∈ ran(h) and h is a matching morphism for r in Hi which satisfies the dangling condition. 3
Note that this is a slightly stronger result than simple deterministic correctness. As well as knowing that MultistepApply will produce some correct solution to the MGTP, we want to know that any of the possible correct results for the problem can be reached nondeterministically by the algorithm.
62
The node v is removed from U , which preserves the invariant because no morphism exists starting from the selected node. 2. M 6= ∅. By the correctness of Search and Dangle, the set M consists of all pairs (h, r) of a rule r ∈ R and a morphism h such that v ∈ ran(h) and h is a matching morphism for r in Hi which satisfies the dangling condition. The algorithm selects an arbitrary member (h, r) ∈ M and constructs derivation Hi ⇒r,h Hi+1 using algorithm Apply*. Apply* also constructs the result morphism k for the derivation and adds the nodes ran(kV ) to U . By Lemma 3.20, any matching morphism for rule r ′ ∈ R in Hi+1 not present between r ′ and Hi must include at least one node in ran(kV ). Therefore, adding these nodes to U preserves the invariant. As a consequence of invariant I and the correctness of Search, at any iteration of the algorithm, any morphism between rule r and the current graph Hi can be discovered by nondeterministically applying Search to some node in U . Any node in U can be selected nondeterministically, so by induction on the length of a derivation sequence any derivation up to length l can be constructed. This means that any solution to the MGTP can be constructed, and so completes the proof. Theorem 3.22 (complexity). Let H0 be a graph in C, and let l be a fixed sequence length. If the algorithm MultistepApply is called with an array of edge enumerations E such that each edge enumeration has a branching factor less than b over every G ∈ C, then it terminates in time O((VH0  + P i l.rmax + l)(R.lmax. lmax i=0 b ) + l(rulemax + rmax)). Proof. Each iteration of the main loop either removes a node from U or
increments the counter i and adds at most rmax nodes to U . As i must be less than l, over the algorithm execution at most l.rmax nodes can added to U . Hence there can be at most VH0  + l.rmax + l iterations of the main loop. Each algorithm iteration applies algorithm Search to graph Hi−1 for some node v ∈ U and for every lefthand size node of every rule r ∈ R. The maximal size of any lefthand side is lmax. If the edge enumerations used by Search have a branching factor of at most b, by Proposition 3.4 each P i iteration of the algorithm requires time O(R.lmax. lmax i=0 b ) to perform all the applications of Search.
63
In addition, at most l iterations of the loop use a morphism constructed by Search to construct a derivation using algorithm Apply*. By Proposition 3.1 each application of a rule r requires time O(r), for a maximum time over the whole algorithm execution of O(l.rulemax). Each of these ruleapplying iterations also insert a maximum of rmax nodes into the set U , which in the worst case requires time O(rmax). Combining all of these results together produces an overall algorithm P i time bound of O((VH0 +l.rmax+l)(R.lmax. lmax i=0 b )+l.(rulemax+rmax)). Theorem 3.23. If C and R satisfy Condition 1, then the multistep graph transformation problem can be solved in linear time. Proof. Under the specification of the multistep graph transformation probP i lem the terms C1 = (R.lmax. lmax i=0 b ) and C2 = (rulemax + rmax) are constant. As a result, the time bound O((VH0  + l.rmax + l)C1 + l.C2 ) given by Theorem 3.22 is equivalent to O(l), or linear time. By Lemma 3.6, if Condition 1 is satisfied for all rules in R and graph class C, then every edge enumeration of a rule in R has a branching factor less than the constant bound d over any graph in C. As a consequence of Theorem 3.22, Condition 1 therefore allows the construction of an array of edge enumerations which ensures that algorithm MultistepApply terminates in linear time.
64
Chapter 4
Efficient graph recognition One application of graph transformation rules is in the definition of graph languages by socalled reduction systems. These recognise languages by reduction; language members are reduced to a final accepting graph by repeated application of a set of reduction rules. Reduction systems are the converse notion to conventional graph grammars, in that a grammar constructs a language by derivation from an initial graph. For any reduction system an equivalent graph grammar can be constructed, and vice versa. Reduction systems are used in Part IV of this thesis to specify the properties of pointer structures. One advantage of reduction systems over grammars is that a reduction system defines an implied algorithm that checks language membership. Membership can be checked for a graph by repeatedly applying reduction rules, although backtracking may be required. However, this membership checking algorithm is expensive even for recognition systems that avoid the need for backtracking, because (as discussed in the previous chapter) each rule application requires at worst polynomial time. In the previous chapter we examined several approaches that improved the worstcase application time of graph transformation rules. In this chapter we show that these approaches can be applied to reduction systems, allowing us to define fast reduction systems that have an efficient membership checking algorithm. Two kinds of fast reduction system are described – rooted and unrooted – corresponding to the two approaches to solving the multistep graph transformation problem given in §3.4. Despite the restricted form of these fast re
65
duction systems, they are surprisingly expressive, allowing lineartime recognition of both contextfree and contextsensitive graph languages. The structure of the chapter is as follows. Section 4.1, introduces rooted graph reduction specifications (RGRSs) that define graph languages of rooted graphs. We identify a class of linear RGRSs that permit a linear membership test. Section 4.2 defines several examples of linear RGRSs recognising noncontextfree languages. Section 4.3 then introduces leftconnected graph reduction specifications (LGRSs), and identifies a class of linear LGRSs permitting a linear membership test. Section 4.4 compares the expressive power of LGRSs and RGRSs. Section 4.5 discusses the process of developing and validating GRSs.
4.1
Recognition by rooted reduction
This section defines a class of rooted graph reduction systems which consist of rooted rules, as described in Chapter 3. We give syntactic conditions which ensure that the graph languages defined by such reduction systems have a lineartime membership test. This is in sharp contrast to the situation for general graph grammars, where even contextfree languages can have an NPcomplete membership test [27]. Graph reduction systems are defined by adapting the approach of [4] to the setting of fast graph transformation. The notion of a graph signature was defined in Chapter 2 as a means for defining basic constraints on the labelling and shape of graphs. Assumption 4.1. For the rest of this section Σ = hL, in, outi is an arbitrary but fixed graph signature. We now define a ‘rooted’ version of the graph reduction specifications of [4]. Recall from §3.3 that a node is described as a root if its label ̺ appears exactly once in the graph and that A graph is described as ̺rooted if it contains a single ̺labelled root. We now define a general notion of a ̺rooted Σrule. This definition syntactically restricts rules and graphs in a similar way to Conditions R1, R2 and R3 in §3.3. However, our objective with Conditions R1, R2 and R3 was to define simple, easily understood restrictions ensuring fast graph transformation. In contrast, the definition ̺rooted Σrule is defined in 66
terms of the individual edgelabels restricted by the signature Σ, and so is considerably more complex. Checking the definition is also in general more difficult than checking Conditions R1, R2 and R3, as checking requires the construction of an edge enumeration conforming to its restrictions. We have define this more complex condition to provide a common framework for defining other syntactic conditions. The definition of a ̺rooted Σrule is more general than the syntactic Conditions R1, R2, and R3 given in §3.3 (proved in Lemma 4.1), and all of our examples conform to one of the simpler conditions. The common syntactic condition also allows us to reason generally about the expressive power of RGRSs (see §4.1.1). Definition 4.1 (̺rooted Σrule). We call a Σrule r = hL ← K → Ri ̺rooted if (1) L and R each include a single ̺labelled node, (2) r is leftconnected, and (3) there exists an edge enumeration e1 , . . . , en of L with the ̺labelled node v as an initial node such that every edge ei either (A) sL (ei ) is either v or the source or target of some edge ej where j < i, and (lL (sL (ei )), mL (ei )) ∈ dom(out), or (B) tL (ei ) is either v or the source or target of some edge ej where j < i, and (lL (tL (ei )), mL (ei )) ∈ dom(in). Example 4.1 (̺rooted Σrule). Let Σ be a signature hL, in, outi such that L = h{a, ̺}, {b, p}i, out(̺, p) = 1, out(̺, b) = 0, out(a, p) = 0 and out(a, b) = 2. We define in( , ) = ⊥ for all labels.
1
b 2
b
⇒
1
1
b
b 2
2
b
⇒
1
b 2
The lefthand side rule given above is a ̺rooted Σrule. Here the ̺labelled root shown as a small grey node, and the plabelled root edge is unlabelled. An edge enumeration for this rule must begin with the root edge, and then include the left and righthand side blabelled edges in arbitrary order. The righthand rule is not a ̺rooted Σrule, because no valid edge enumeration exists conforming to Def. 4.1. This is because no enumeration exists that has the root as an initial node where the source of the blabelled 67
edge eb is the target of any edge earlier in the numeration than eb . Every enumeration must include the eb , but in(b, a) = ⊥, meaning any enumeration violates Def. 4.1. The definition of a ̺rooted Σrule is similar to the definition of a Vstructure avoiding rule under D¨orr’s framework [26]. In §5.1 we discuss the separating properties between this approach and ours. Lemma 4.1. If a rule r and class of Σgraphs CΣ conform to Condition R1, R2 or R3, then r is a rooted Σrule. Proposition 4.2 (̺rooted Σrule time complexity). A ̺rooted Σrule can be applied in constant time to a ̺rooted Σgraph. Proof. The definition of a ̺rooted Σ requires the existence of an edge enumeration restricted by the signature. This edge enumeration has a bounded branching factor over all Σgraphs, because either the inbranching factor or outbranching factor for each edge in the enumeration is bounded by the signature. By appeal to Definition 3.4, the branching factor must therefore be bounded by some fixed bound d. As a consequence of this and the existence of a uniquelylabelled root, we can apply Proposition 3.12 to show that the rule can be applied in constant time. We now define the notion of a rooted graph reduction specification. This consists of a signature Σ, a root label used to ensure constanttime graph rewriting, a set of reduction rules, and an accepting graph. The language defined by it is the class of Σgraphs that can be reduced to the accepting graph by repeated application of the reduction rules. Our notion of a graph reduction specification is derived from [4], where graph reduction specifications are used for defining classes of pointer structures. Our notion of a reduction system is in one way more general, because our notion of a signature is much less restrictive than the one used in [4]. Their notion of a signature only permits graphs with uniquelylabelled out edges. However, their notion of a reduction system does not require that rules are leftconnected, or that they include a root label, and does not require the existence of an edge enumeration with the rootlabelled node as an initial node. Definition 4.2 (rooted graph reduction specification). A rooted graph reduction specification S = hΣ, ̺, CN , R, Acci consists of a signature Σ = 68
hC, in, outi, a root label ̺ ∈ CV , a set CN ⊆ CV of nonterminal labels, a finite set R of ̺rooted Σrules and an Rirreducible ̺rooted Σgraph Acc, the accepting graph. The graph language specified by S is L(S) = {G  G is a Σgraph and G ⇒∗R Acc and lG (VG ) ∩ CN = ∅}. We often abbreviate ‘rooted graph reduction specification’ by RGRS. The recognition problem (or membership problem) for RGRS languages is defined as follows: RGRS Recognition Problem. Given: An RGRS S = hΣ, ̺, CN , R, Acci. Input:
A ̺rooted Σgraph G.
Output:
Yes if G belongs to L(S), and No otherwise.
Note that in this formulation of the recognition problem the signature Σ is fixed, and the input graph G is assumed to conform to Σ. Our later claim of lineartime graph recognition therefore ignores the cost of checking the conformance of G to Σ. Proposition 2.2 shows that conformance to any given signature can be checked in time O(G), by simply examining each graph node in turn. For this reason, if the complexity of membership checking is linear or worse, it makes no difference to the order of the complexity result whether the input graph is a Σgraph or an unrestricted graph. We use a signature because some restriction on the class of input graphs is a necessary precondition for the existence of a static bound on the branching factor of an edge enumeration. As discussed in §3.2 and §3.3 the existence of such a bounded enumeration improves rule application times. Definition 4.1 gives sufficient conditions on rules and signatures to ensure improved application time. We could achieve similar results without a signature by including a depth bound as a parameter to the matching algorithm, and searching only a bounded space of possible matches. However this approach would require a more complex correctness proof to show that all matches exist within the depth bound. Several problems are also simplified by the assumption that input graphs conform to a signature. The reduction rules that define an RGRS are generally simpler under this assumption. This is because rules can assume more about the form of the graph. Without a signature, many more rule cases 69
E 1 n E n
Reduce:
Acc: E
n
E
Finish:
E n
E 1 n
⇒
E
2
2
1
n
⇒ 1
E
E
n
Figure 4.1: RGRS CL for rooted cyclic lists. need to be enumerated to eliminate malformed graphs. Proofs of correctness and linear termination for RGRSs also need to consider fewer cases under the assumption of a signature. Without a signature, even malformed graphs need to be considered in these proofs. The following simple example of an RGRS specifies the language of rooted cyclic lists. In addition to the normal nodes and edges forming a cyclic list, language members also include a root node. Example 4.2 (rooted cyclic list). The RGRS CL = hΣCL , ̺, ∅, RCL , Acc CL i has the signature ΣCL = hh{̺, E}, {p, n}i, inCL , outCL i, where inCL (l, l′ ) = ⊥ for all l ∈ {̺, E} and l′ ∈ {p, n}. 1 if l = ̺ and l′ = p, or l = E and l′ = n. outCL (l, l′ ) = 0 otherwise.
The accepting graph Acc CL and the rules RCL are shown in Figure 4.1.
The unique ̺labelled node is drawn as a small greyfilled node and the label p of its outgoing edge is omitted. Proposition 4.3 (CL correctness). The language L(CL) is the set of all rooted cyclic lists. Proof. We have to show soundness (every graph in L(CL) is a cyclic list) and completeness (every cyclic list is in L(CL)). 70
Soundness follows from the fact that for every inverse r −1 of a rule r in RCL , and every cyclic list G, G ⇒r−1 H implies that H is a cyclic list. Every reduction G ⇒∗ Acc via RCL gives rise to a corresponding derivation Acc ⇒∗ G via R−1 CL . Acc is a cyclic list, and therefore by induction G must be a cyclic list. Completeness is shown by induction on the number of Elabelled nodes in cyclic lists. The cyclic list with one Elabelled node is Acc CL , which belongs to L(CL). If G is a cyclic list with at least two Elabelled nodes, then there is a unique injective morphism from the lefthand side of either Reduce (if G has more than two Elabelled nodes) or Finish (if G has exactly two Elabelled nodes) to G. Hence there is a step G ⇒RCL H, and it is easily seen that the resulting graph H is a cyclic list that is smaller than G. Hence, by induction, there is a derivation H ⇒∗RCL Acc and thus G ∈ L(CL). We now define a class of RGRSs whose languages can be recognised in linear time. Definition 4.3 (linear RGRS). An RGRS hΣ, ̺, CN , R, Acci is linearly terminating if there is a natural number c such that for every derivation G ⇒R G1 ⇒R . . . ⇒R Gn on ̺rooted Σgraphs, n ≤ cG. It is closed if for every step G ⇒R H on ̺rooted Σgraphs, G ⇒∗R Acc implies H ⇒∗R Acc. A linearly terminating and closed RGRS is a linear RGRS. Example 4.3 (linear RGRS). The RGRS CL given in Example 4.2 is a linear RGRS. Reduction sequences must terminate in at most VG  steps, because all of the reduction rules reduce the number of nodes in the graph. Consequently the termination measure is 1. Reduction is also closed (in fact it is deterministic), as ̺rooted graphs must contain a unique root and the lefthand sides of the two rules cannot match overlapping areas of any graph in ΣCL . The closedness of an RGRS is a semantic property of the reduction system; that is, it cannot be checked from the simple syntactic structure of the rules. A sufficient condition for closedness is confluence. Definition 4.4 (confluence). An RGRS hΣ, ̺, CN , R, Acci is confluent if for any ̺rooted Σgraph G and pair of derivations G ⇒∗R H, G ⇒∗R H ′ , 71
there exists some graph G′ such that H ⇒∗R G′ and H ′ ⇒∗R G′ . Confluence implies closedness, although the converse does not hold. Example 4.4 (confluence). The following pair of rules form a confluent RGRS, as the lefthand sides of the rules cannot overlap. 1
J a
2
L
1
⇒
1
c
b
2
⇒
L c
2
However, adding the following extra rule makes the GRS nonconfluent. 1
J
1
a
⇒
N d
2
2
This is because the lefthand sides of the first and third rules overlap, meaning that any graph isomorphic to the rule’s lefthand side can be derived into two possible graphs, neither of which are reducible. Confluence is also a semantic property, and furthermore is known to be undecidable, even for terminating graph rewriting systems [64]. However, the approach of [63] gives a sufficient syntactic condition that ensures that the rewriting system is confluent through the identification of socalled critical pairs. Critical pair analysis is sufficient to show that all of the examples given below are are confluent. Theorem 4.4 (linear recognition). The recognition problem is decidable in linear time for a linear RGRS. Proof. Let S = hΣ, ̺, CN , R, Acci be a linear RGRSs and G a ̺rooted Σgraph. Membership of a Σgraph G in L(S) can be tested as follows: (1) Check that G contains no node labels from CN . (2) Apply the rules of R in sequence (nondeterministically) until a graph is reached that is irreducible with respect to R. (3) Check whether the resulting graph is isomorphic to Acc. Correctness of this testing procedure follows immediately from the fact that S is closed. If the testing procedure reaches an irreducible graph, closedness ensures that the resulting graph is isomorphic to Acc if and only 72
if the input graph can be reduced to Acc. Because S is linearly terminating, the testing procedure must eventually reach an irreducible graph. The time complexity of the problem can be broken down into the three phases. Phase (1) requires time O(G) time to examine each node for nonterminal symbols. Phase (2) of this procedure requires at most cG reduction steps, as S is linearly terminating. As a consequence of Proposition 4.2, each step can be performed in constant time. So the time needed for phases (1) and (2) is O(G). Lemma 4.5 shows that checking the existence of an isomorphism between a Σgraph and a fixed Acc requires only constant time. Therefore the whole testing procedure requires only linear time. Lemma 4.5 (checking isomorphism to Acc). Let Acc be a fixed accepting graph. For any Σgraph G we can test whether an isomorphism h exists between G and Acc in constant time. Proof. Under the general assumptions about graph representation (given in Assumption 3.1) the number of nodes and edges in a graph can be determined in constant time, meaning that graphs G with different numbers of nodes and edges to Acc can be eliminated in constant time. Only graphs of the same size need to be checked. The complexity of checking that two graphs size S are isomorphic is O(S S ) (application of the standard subgraph isomorphism time complexity [35]). Under the RGRS recognition problem, Acc is fixed, which means S = Acc is constant. The cost of checking isomorphism with Acc is therefore also constant.
4.1.1
RGRS expressive power
Proposition 4.6 (rgrs languages have a bounded number of connected components). Let S be an RGRS. The number of connected components in any graph G in L(S) must be at most b, the number of connected components in Acc. Proof. If G is a member of L(S) there must exist a reduction sequence G ⇒∗ Acc via R, and a corresponding inverse derivation Acc ⇒∗ G via R−1 . Each rule r in R is leftconnected, so each inverse rule r −1 in R−1 has a connected righthand side. Each rule in r has a ̺labelled node in its righthand side, so each r −1 must have a nonempty lefthand side. Given inverse derivation H ⇒r−1 H ′ , graph H ′ must therefore have no more connected 73
components than H. The accepting graph has a fixed number of connected components, so any graph produced by an inverse derivation Acc ⇒∗R−1 G must have the same number or fewer. Therefore the number of components in any graph in L(S) must be at most b, the number of connected components in Acc. We now use this result to prove a stronger result about languages definable by RGRSs modulo the removal of the root node and incident edges. Definition 4.5 (descendant node, descendant edge). Let G ⇒ H be a derivation and trG⇒H the track morphism for the derivation. For any node v in dom(trG⇒H ), the node v ′ = trG⇒H (v) is the descendant node of v. Similarly for any edge e in dom(trG⇒H ), the node e′ = trG⇒H (e) is the descendant edge of e. Example 4.5 (descendant node, descendant edge). Assume the following simple rule r over elabelled nodes.
⇒
1
b
The following graphs form a derivation sequence.
e b
b
⇒r
e
⇒r b
e The nodes identified by the dotted area form a sequence of descendant nodes, reading from left to right. The blabelled edges attached to these nodes also form a sequence of descendant edges. Lemma 4.7 (preservation of separating edges). Let R be a set of leftconnected rules and let G, H be graphs such that G ⇒R H. Let e be an edge in EG such that e is separating and that there exists a descendant edge e′ in EH . If e is not matched by the derivation then edge e′ is still separating in H.
74
Proof. The result follows from the leftconnectedness of the rules in R. To make e′ nonseparating, the derivation must add an extra edge between the two connected components resulting from the removal of e. But because all rules are connected, to match both components, a leftconnected rule must also match e. Proposition 4.8. Let S be an RGRS and G a graph in L(S). Let G′ be G with the ̺labelled root node and all incident edges removed. There must exist a bound b such that any G′ contains less than b connected components. Proof. From Proposition 4.6 we know that there must exist a bound b′ such that any G has less than b′ connected components. Therefore it suffices to show that the number of separating edges attached to the rootlabelled node in G must be bounded. Suppose the converse holds; there exists an RGRS S such that for any b, we can pick a graph G ∈ L(S) with more than b separating edges attached to the root. As the labelset is finite, there must exist a label l such we can pick a graph with more than b llabelled separating edges attached to the root. As there can be an unbounded number of these edges, some of them must be removed by the reduction rules. By Lemma 4.7 these edges must remain separating until they are matched by some rule. Removing any of the b separating edges therefore requires a rule with one or more separating llabelled edge attached to the root in its lefthand side. However, by the definition of the language the signature Σ cannot bound the number of llabelled edges attached to the root, so such a rule violates the condition on edge enumerations in the definition of a rooted Σrule (Definition 4.1). Therefore no such language exists, which proves the result. This means that a RGRS cannot recognise quite simple languages such as the language of forests, or even the language of graphs consisting just of unconnected nodes. Other than this quite elementary restriction, we have not been able to clarify the limits of the expressive power of rooted graph reduction systems. For example, we do not know whether RGRSs are of equivalently expressive to unrooted GRSs over languages of connected graphs.
75
B r
l B l L
B r
l
L
L
r L
Figure 4.2: Balanced binary tree.
4.2
Noncontextfree RGRS languages
To demonstrate the expressive power of linear RGRSs, in this section we present several more complex examples of graph reduction systems. All three of the examples presented here are noncontextfree languages that are definable by a rooted RGRS.1 It is interesting that we are able to recognise such languages in linear time, as there exist contextfree graph languages with an NPcomplete membership problem, even among languages of bounded node degree [51]. In §4.5 below we discuss the construction and validation of such large graph reduction systems.
4.2.1
Balanced binary trees
The first example in this section is the noncontextfree graph language of balanced binary trees, which can be specified by a linear RGRS. Definition 4.6 (balanced binary tree). A graph is a balanced binary tree, BBT for short, if it consists of a binary tree built up from nodes labelled B, U and L such that all paths from the tree root to leaves have the same length. Nodes labelled with B have two children pointed to by edges labelled l and r; nodes labelled with U have one child pointed to by a clabelled edge; nodes labelled with L have no children. Example 4.6 (balanced binary tree). Fig. 4.2 shows a balanced binary tree of depth 2. 1
The graph languages are noncontextfree in the sense of both hyperedge replacement grammars [27] and node replacement grammars [29].
76
In addition to the normal tree nodes, rooted BBTs include a uniquelylabelled root node (not to be confused with the tree root) with a single outgoing edge called the root pointer that can point to any treenode. As in Example 4.2, we draw the root of a BBT as a small grey node, and omit the label of its unique plabelled outgoing edge. The RGRS for rooted balanced binary trees is RBB = hΣRBB , {B ′ , U ′ }, ̺, RRBB , Acc RBB i. The reduction rules and accepting graph are is shown in Figure 4.3. The signature is ΣRBB = h{̺, B, B ′ , U, U ′ , L}, {p, l, r, c}, outRBB , inRBB i, where inRBB (l, l′ ) = 1 for all l, l′ , and 1 if l = ̺ and l′ = p 1 if l ∈ {B, B ′ } and l′ ∈ {l, r} ′ outRBB (l, l ) = 1 if l ∈ {U, U ′ } and l′ = c 0 otherwise.
The rules in RRBB satisfy the syntactic conditions given in §4.1 ensuring that rules they are ΣRBB preserving. The rules in RRBB and class of ΣRBB graphs conform to our syntactic Condition R1 given in §3.3, meaning by Lemma 4.1 the rules are ΣRBB rules. As consequence of Proposition 4.2 rule application can be therefore be completed in constant time. This rooted GRS RBB is a rooted modification of the unrooted reduction system for balanced binary trees given in [4] (the unrooted version is used later in this chapter, in §4.3, as an example for unrooted fast reduction). Intuitively, the root in RBB behaves as a proxy for a lefthand side match in the unrooted version. Rather than rely on the lefthand side matching process to locate the areas for reduction, the root is explicitly moved by the rules of the RGRS to possible application areas, and then rooted versions of the original reduction rules are applied. Note that this RGRS omits the size 1 graph containing a single leaf node from its language. This is to remove the need for a special case rule, and so reduce the number of rules. We do the same in the rooted binary DAG and unrooted balanced binary tree examples (§4.2.3 and Example 4.11 respectively). The rooted BBT RGRS has a larger set of reduction rules than the unrooted version, and this increase is mostly a result of the extra cases required to allow traversal of the graph by the root. This is because the
77
root controls the application for the rules. In the unrooted version, the automatic process of matching takes the place of explicit root moving. The size of the RGRS is also increased by the number of nonterminal symbols. These are used to ensure termination when the RGRS is applied to malformed BBTs. Nodes are marked when they are visited by the root. Without node marking, the RGRS would not terminate when applied to (for example) a circular list of U nodes. The RGRS operates by collapsing subtrees together using the R2 and R3 rules to form a new subtree of the same height consisting entirely of U labelled nodes. The root is moved in a depthfirst manner, down to the lowest point at which one of these rules can be applied. The root then alternately collapses subtrees, and searches up the tree for the next subtree that can be collapsed. Figure 4.5 shows a sequence of reductions for a member of L(RBB). We now show that L(RBB) is the set of all rooted balanced binary trees, by proving RBB sound (Proposition 4.9) and complete (Proposition 4.13). we call a graph an NBBT (for nonterminal BBT) if it can be obtained from a rooted BBT by relabelling any number of Bnodes into B ′ nodes and any number of U nodes into U ′ nodes. We describe the single edge with a ̺labelled node as its source as the root pointer. Proposition 4.9 (RBB soundness). Let G be a ΣRBB graph. If G is a member of L(RBB), then G is a rooted balanced binary tree. Proof. We will show that every ΣRBB graph reducible by RRBB to Acc RBB is an NBBT, implying that every graph in L(RBB) is a BBT. Clearly, Acc RBB is an NBBT. By the same argument as used in Proposition 4.3, it suffices to show that the inverses of the rules in RRBB preserve membership in the class of NBBTs. The inverses of the rules Up, D1 and D2 are clearly NBBTpreserving, as they only relabel nonterminal into terminal nodes and redirect the root pointer to some other node in the tree. The dangling condition ensures that the inverse of rules R1U and R1B can only be applied at the tree root, because the rule application deletes either a U labelled or Blabelled node without an ingoing tree edge. Hence the inverted rule just inserts a new U or U ′ labelled tree root on top of the old one, which preserves balance and so NBBT membership. The inverses of rules R2L and R2R are also NBBT78
U c
Acc :
L
U
Up :
c
y ∈ {L, U, U ′ }
y D1 :
l
y ∈ {B, B ′ } z ∈ {U, U ′ }
z
1
⇒ y
y
r B
3
1
y
2
2
1
2
U′ c
⇒
r
l z
1
B′
2
l
3
l 4
4
x B D2 :
l
B′
1
⇒
2
c
1
R1U :
l
y
x, y ∈ {U, U ′ }
2
U
⇒ c
c 1
1
x c
R1B : x ∈ {U, U ′ } y ∈ {B, B ′ }
y l 1
B
⇒ r
l 2
1
r 2
Figure 4.3: Accepting graph and rules for the rooted GRS RBB.
79
1
1
x
U
R2L : L
c
⇒
r
l
x ∈ {B, B ′ }
L
L
1
1
x R3L : x ∈ {B, B ′ } y, z ∈ {U, U ′ }
l
U c
r
y
⇒
z c
c 2
3
B l 2
r 3
R2R: as R2L, but with labels l and r swapped R3R: as R3L, but with the lefthand root pointing to the znode Figure 4.4: Accepting graph and rules for the rooted GRS RBB (cont). preserving: replacing a U node pointing to a leaf with a Bnode pointing to two leaves preserves balance. Similarly, the inverses of R3L and R3R preserve balance and the other NBBT properties. Definition 4.7 (rootpointerpredecessor). Given an NBBT in which the root pointer does not point to the tree root, we call a node a rootpointerpredecessor if it is on the unique path from the tree root to the parent of the root pointer’s target. Example 4.7 (rootpointerpredecessor). In the graph shown in Fig. 4.6, the two B ′ labelled nodes and the Llabelled target of the root edge are rootpointerpredecessors. We call a graph an IBBT (for invariant BBT) if it is an NBBT satisfying the following conditions: (1) rootpointerpredecessors are not labelled U ′ and (2) all nodes labelled B ′ are rootpointerpredecessors. Note the class of terminal IBBTs is the class of BBTs. Proposition 4.10. RGRS RBB is linear. Proof. Closure can easily be checked for RBB by the critical pair approach of [63]. 80
B′
B r
l B
2×D2
B r
l L
L
L
r
r
l
B′
⇒
r
l
l l
L
L
B L
r
L
L
B′ l
B′ l R2−L
r
U
⇒
L
L
L
R2−L
⇒
U c L
R3−R
l
L
D2
B
⇒
c
c
L
B′
⇒ r
l
L
L
r L
U c
⇒
L
U c
U R2−L
L
U r U
r
l
L
B′ l
c
D1
⇒ r
l
B′
U
B c
r
c Up
U
U′ c
⇒ c
L
L
R1−U
⇒
U c L
Figure 4.5: Example reduction sequence for a member of L(RBB).
81
B′ l
r B′
U c L
l L
r L
Figure 4.6: BBT including three rootpointerpredecessors. To show that RBB is linearly terminating we define a termination measure T . For every ΣRBB graph G the termination measure T (G) = 2G + −1 ({B, U, L}). We show by simple examination of the rules in RRBB that lG
for any derivation G ⇒RRBB H on ΣRBB graph G, T (G) > T (H). Rules Up, D1 and D2 preserve the size of graphs but decrease the number of terminally labelled nodes, hence they decrease T ’s value. Rules R1U and R1B increase the number of terminals by at most one, but also decrease the graph size by two, so T decreases. Rules R2L and R2R decrease size without increasing the number of terminal node labels, so they decrease T ’s value too. Rules R3L and R3R decrease the graph size by two and increase the number of terminal node labels by at most two, so they also decrease T ’s value. The length of any derivation G ⇒∗RRBB H given a ΣRBB graph G must therefore be less than the value of the termination measure. But T (G) is at most 3G so RBB is linearly terminating. Lemma 4.11 (nonAcc IBBTs are reducible). For every IBBT G, either G∼ = Acc RBB , or G is reducible by some rule in RRBB . Proof. Clearly Acc RBB is irreducible by RRBB . We show that every nonAccRBB IBBT is reducible by RRBB by enumerating possible cases. Case 1: The root pointer points to a U or U ′ node. Then we know from ΣRBB that it must have an outgoing edge labelled c. If it has no incoming edges, it is the treeroot and we can reduce it using either rule R1B or R1U depending on the target of the clabelled edge. If this node has an incoming 82
edge, it must be labelled c, l or r. If c, from the signature and the definition of an IBBT we know that the source must be a U node, and so rule Up applies. If the incoming edge is labelled l or r, its source must be a B or B ′ node and it must have a sibling r or l edge. From the balance property of an IBBT, the other edge must point to another node with an outgoing edge – either a U , U ′ , B, or B ′ . B ′ is excluded by the definition of an IBBT. If U or U ′ , rule R3L or R3R applies. If a B, by ΣRBB it must have outgoing l and r edges, and so rule D1 applies. Case 2: The root pointer points to a Bnode. Then by ΣRBB it must have an outgoing edge labelled l, so the D2 rule applies. By the definition of an IBBT, the root pointer cannot point to a B ′ node. Case 3: The root pointer points to an Lnode. Then there must exist a single incoming edge labelled l, r, or c. If c, by ΣRBB and definition of IBBT the source must be labelled U and the graph can be reduced by the Up rule. If the incoming edge is labelled l or r, its source must be labelled B and it must have a sibling edge labelled r or l. We know by the balance property that the target of this edge must be another Lnode, so either R2L, or R2R applies. This completes the proof that every IBBT apart from Acc RBB can be reduced. Lemma 4.12 (RRBB preserves IBBTs). For every derivation G ⇒RRBB H, if G is an IBBT then H is an IBBT. Proof. We show that all rules preserve IBBT membership by examination of each rule in turn. Rule Up moves the root pointer from its current position to the node’s parent and relabels this parent from U to U ′ . As the rest of the graph is preserved, this preserves IBBT condition (1). Rules D1 and D2 move the root pointer and relabel a single Bnode to B ′ . In both cases the B ′ node is a rootpointerpredecessor, so IBBT condition (2) is satisfied. No U ′ nodes are added, so IBBT condition (1) is satisfied. Rules Up, D1 and D2 only relabel nodes and move the root pointer, so balance is preserved. Rules R1U and R1B delete a U or U ′ node and move the root pointer to the child of the current position. We know from the IBBT conditions that this child cannot be labelled B ′ , and so the IBBT conditions are satisfied. Rule R1U and R1B can only delete the treeroot, as this is the only nonLnode that can be safely deleted under the dangling condition, and so it preserves balance. 83
Rules R2L and R2R replace a B or B ′ node with a U node and move the root pointer. Replacing a Bnode and two leaves with a U node and one leaf preserves balance. The IBBT conditions are satisfied, as the new target of the root pointer is a U node and the rootpointerpredecessors are otherwise preserved. Rules R3L and R3R replace two U or U ′ nodes with a B node. The rule preserves balance because the distance from the ‘top’ of the rule to the ‘bottom’ is preserved. These rules replace a single B or B ′ node on the path to the treeroot with a U node, and the rootpointerpredecessors are otherwise unaltered. No B ′ nodes are added, so both IBBT conditions are satisfied. This completes the proof that all rules in RRBB are IBBTpreserving. Proposition 4.13 (RBB completeness). Let G be a ΣRBB graph. If G is a rooted balanced binary tree, then G is a member of L(RBB). Proof. We show that every IBBT is reducible to Acc RBB by RRBB , implying that every BBT is in L(RBB). In Proposition 4.10 we show that every RRBB derivation sequence terminates, so it suffices to show in Lemma 4.11 that AccRBB is the only RRBB irreducible IBBT and in Lemma 4.12 that applying any rule in RRBB to an IBBT results in another IBBT.
4.2.2
Rooted grids
We now give an RGRS for the language of rooted grids, based on the grid graph GRS given in [4]. Definition 4.8 (rooted grid). A graph is a rooted grid if it is constructed from nodes labelled B and L, where nodes labelled B have two outgoing edges labelled d and r (intuitively these edges are ‘down’ and ‘right’), and nodes labelled L have no outgoing edges. A grid graph consists of a k × l grid of Blabelled nodes – k rightward and l downward – with the downward and rightward edges of the grid terminated by Llabelled nodes. A rooted grid includes an extra root node that points to the single Blabelled node without any incoming edges (the ‘upper left corner’ of the grid). Example 4.8 (rooted grid). Fig. 4.7 shows a rooted grid of size 3 × 2. The RGRS for rooted grids is GG = hΣGG , {C}, ̺, RGG , Acc GG i. The reduction rules and accepting graph for GG are shown in Figure 4.8. The 84
r
B d B
r
B d B
d L
r r
d B
d L
r
B
r
L L
d L
Figure 4.7: Rooted grid. signature is ΣGG = h{̺, B, C, L}, {l, r, p}, outGG , inGG i, where inGG (v, e) = ⊥ for all v, e, and 1 if v = ̺ and e = p, or v ∈ {B, C} and e ∈ {d, r} outGG (v, e) = 0 otherwise.
GG operates by incrementally reducing the height of the grid. The Del rule removes Blabelled nodes, while checking that each rowelement can be paired up with an element in the next row, until the whole row is deleted. The grid structure of the graph is ensured by the fact that when each row is removed, the rules ensure that the following row is of the same width. Following the deletion of a row, the rules Next, Next’ and Next’’ select the next row. Once all the rows but one have been removed, the bottom row is deleted by the DelBot rule. The rules and class of all ΣGG graphs conform to Condition R2 given in §3.3, meaning by Lemma 4.1 that the rules are ΣRBB rules. Proposition 4.14 (GG correctness). The language L(GG) is the set of all rooted grids. Proof. The same approach can be used here as used in proving Propositions 4.9 and 4.13. Completeness is proved by showing that all members of the class of partiallyreduced grids can be reduced. Soundness is proved by showing that inverse rules preserve membership of the class of partiallyreduced grids. In both cases, the proof works by constructing possible cases for the root node to point to, and then showing that the two properties hold in all cases. Proposition 4.15. RGRS GG is linear. 85
Acc :
r
B
L
d
d
L
2
r
C Del :
r
B
d 1
d
B
2
B
r
r
C d Next :
B
r
B B
B
4
r d
B
r
r d
B
r
r d
Next’’ :
B
r
2
B
4
B
r
B
1
d B
3
B
4
d B
L L
r
B
⇒
B 2
d
r
L
d
1
3
L L
⇒
r
B
B
r
L
2
d
1
B
B
1
r
r d
3
B
B
2
C
2
d
3
d
1
C
d
3
r
B
d B
r
C
⇒
1
⇒
r
d
B
r
B
4
d
B
2
d
Next’ :
r
B
Init :
1
L
r
B L
L
d
⇒
d
1 1
r
B d
DelBot : L
r
B d
B 1
d
⇒
L
r 1
L
Figure 4.8: Accepting graph and reduction rules for the rooted grid RGRS GG. 86
3
l
l
l
Figure 4.9: Rooted binary DAG. Proof. Closure can easily be checked for GG by the critical pair approach of [63]. To show GG is linearly terminating, we define for all ΣGG graphs −1 −1 (B) is the number of (B), where lG the termination measure T (G) = lG
Blabelled nodes in G. All rules of RGG reduce the number of Blabelled nodes. Therefore, for any derivation G ⇒RGG H where G is a ΣGG graph, T (G) decreases. As a consequence, no derivation from G can be longer than −1 (B) so GG is linearly terminating. G, as G > lG
4.2.3
Rooted binary DAGs
Our final example is a linear RGRS recognising rooted binary DAGs, a class of structures with nodes of unbounded indegree. This is in contrast to our previous examples, which have all defined languages of graphs with a bounded overall degree. Definition 4.9 (rooted binary DAG). A rooted DAG is a directed and acyclic graph where there exists some node from which all other nodes are reachable (we will call this node the DAG root, to distinguish it from our ̺labelled graph root). A rooted binary DAG is a rooted DAG constructed from nodes labelled B and L, where nodes labelled B have two outgoing edges labelled l and r, and nodes labelled L have no children. Example 4.9 (rooted binary DAG). Fig. 4.9 shows a rooted binary DAG. The language of rooted binary DAGs is recognised by the RGRS RBD = hΣRBD , {B ′ , B ′′ , L′ , s, b, n}, RRBD , Acc RBD i. The signature is ΣRBD = h{̺, B, B ′ , B ′′ , L, L′ }, {l, r, s, t, n}, ̺, outRBD , inRBD i, where inRBD (v, e) = ⊥ for all v,e and outRBD is defined as 87
1 1 outRBD (v, e) = 1 1 0
if v = ̺ and e ∈ {s, t} if v ∈ {B, B ′ , B ′′ } and e ∈ {l, r} if v = L′ and e = n if e = b otherwise
The rules and accepting graph of the RGRS are shown in Figure 4.10. Dashed edges stand for the edges labelled b that are used by the reduction system to record the path taken through the graph. As in previous examples, we draw the root of the rooted binary DAG as a small grey node and back pointers as dashed arrows, omitting labels in both cases. The RGRS RBD works by moving the tlabelled rootedge through the graph in a depthfirst manner, converting Blabelled nodes into Llabelled nodes. The resulting Llabelled nodes are then deleted. As rules in the reduction system are all rooted, any disconnected graph elements cannot be deleted. The RGRS avoids the possibility of garbage nodes by attaching nodes to a stack as they are encountered. This stack is constructed by pushing an Lnode onto the stack when one of its parent Bnodes is relabelled to an Lnode. The top of the stack is pointed to by the slabelled (‘stack’) rootedge. This slabelled root edge is a nonterminal edge that is added to the graph by the stack initialisation rule StartStack. As the graph is not a tree, the t rootedge may encounter the same Lnode several times during the depthfirst traversal. The reduction system prevents the same node appearing several times on the stack by relabelling Lnodes to the nonterminal label L′ once they are placed on the stack. Nodes are only deleted from the stack when they have no nonstack incident edges, as ensured by the dangling condition. The rules in RRBD and the class of ΣRBD graphs conform to Condition 3, and so by lemma 4.1 we can conclude that the rules of the RGRS are Σrules. We describe a graph G as an IRBD (for invariant RBD) if it is a ΣRBD graph satisfying the following conditions: (1) Graph G is acyclic. (2) There exists a none̺labelled top node from which all nonL′ nodes are reachable, if any exist. (3) There exists a path of l and redges from the top node to the target of the root tedge where all nodes on the path are B ′ or B ′′ labelled and have a blabelled edge pointing to their immediate parent on the path. 88
x
1
s
LRDel :
L′
x∈
L′
2
{B ′ , B ′′ }
t
r
l
t
Acc :
0
⇒ 0
LDel :
0
1
t
0
r
l
t
L′
L′
2
3
1
L′
2
t
n
B ′′
L
⇒
L
1
L′
L′
2
3
Stack : s
t
t
L′
Free :
⇒ s
n L′
t L′
L
1
L′
t L′
⇒
s
1
s
1
1
n 2
L′
2
Down:
StackStart :
x ∈ {B, L} s t 1
t
⇒ L
L′
t
0
1
x B′
LAc : 0
t
B′
1
⇒
l
n
x ∈ {B, L}
B
l L′ 2
2
1
1
⇒
r
0
l
t
x
B ′′ l
r
x
L′
x
3
2
3
t
0
RDel : as LDel, but with labels l and r swapped RAc : as LAc, but with labels l and r swapped Figure 4.10: Linear RGRS recognising rooted binary DAGs.
89
1
2
(4) Nodes not on the path are not B ′  or B ′′ labelled. (5) Each B ′′ node must have at least one L′ labelled child. (6) Each L′ node must have either one L′ node parent or through the s rootedge a ̺labelled parent. (7) An L′ node’s outgoing nedge must either be a selfloop or point to another L′ node. (8) If no nonL′ nodes exist, then rootedge t points to the target of rootedge s. Proposition 4.16. RGRS RBD is linear. Proof. Closure can be checked by the critical pair method. The termination −1 (L) + function for every ΣRBD graph G is defined as T (G) = G + 2lG −1 −1 (B ′ ). It can easily be checked by inspecting the rules of (B ′′ ) + lG 2lG
RRBD that for every step G ⇒∗RRBD H on ΣRBD graphs, T (G) > T (H). This result implies that the length of any derivation G ⇒∗RRBD H on ΣRBD −1 −1 −1 (B ′ ) ≤ (B ′′ ) + lG (L) + 2lG graphs can be at most G + 2VG , since 2lG 2VG . Proposition 4.17 (RBD soundness). Let G be a ΣRBD graph. If G is a member of L(RBD), then G is a rooted binary DAG. Proof. We show that every ΣRBD graph reducible to Acc RBD is an IRBD. As the set of rooted binary DAGs is exactly the set of terminal IRBDs this is sufficient to prove soundness. As in Proposition 4.9 we show by caseanalysis that the inverses of rules in RRBD preserve membership in the class of IRBDs. The inverse of rules Down, LAc and RAc clearly preserve IRBDs because they only move the path from the root and relabel a node. Inverse rules StackStart and Stack reorganise the stack in a way compatible with IRBDs. The inverse of rule Free just increases the stack length by one. The inverses of the deletion rules LRDel, LDel, RDel convert Lnodes to B ′′ or B ′ nodes, preserving the pathlabelling requirement. Proposition 4.18 (RBD completeness). Let G be a ΣRBD graph. If G is a rooted binary DAG, then G is a member of L(RBD). Proof. We show that every IRBD can be reduced to Acc RBD . As the set of rooted binary DAGs is exactly the set of terminal IRBDs this is sufficient to prove completeness. As in Proposition 4.13 we show that Acc RBD is the only
90
RRBD irreducible IRBD and that applying any rule in RRBD to an IRBD results in another IRBD. The reducibility of any IRBD is shown by case analysis. If t points to a Blabelled node then Down must apply. If it points to an Llabelled node either Stack or StartStack applies. If an L′ labelled node then the node may be the target of a B ′ or B ′′ node, in which case one of the Ac or Del rules must apply. Finally, if the L′ node is not the target of any other edge, then the node must be the top of the stack, and so the rule Free applies. IRBD preservation is demonstrated by case analysis of possible graph configurations matching the rule applications. Most of these cases are clear. The difficult cases are the deletion rules LRDel, LDel, RDel. In these rules the existence of the stack of L′ nodes ensures that the graph stays connected.
4.3
Recognition by leftconnected reduction
The previous sections describes an approach to lineartime recognition which depends on the presence of uniquelylabelled roots. In this section we describe socalled leftconnected graph reduction systems that achieve lineartime reduction without the need for roots. Application of a leftconnected graph reduction system uses the MultistepApply algorithm described in §3.4. This algorithm permits the derivation of an application of a set of rules R to a graph G ∈ C in linear time if an edge enumeration with bounded branching factor can be constructed for each node in the lefthand side of each rule. The basic definitions used in defining leftconnected graph reduction systems are quite similar to those used in Section 4.1 to define RGRSs. Definition 4.10 (fast Σrule). Let Σ be a graph signature. We call a Σrule r = hL ← K → Ri fast if for every node v in the lefthand side L there exists an edge enumeration e1 , . . . , en with initial node v such that every edge ei either (A) sL (ei ) is either v or the source or target of some edge ej where j < i, and (lL (sL (ei )), mL (ei )) ∈ dom(out), or (B) tL (ei ) is either v or the source or target of some edge ej where j < i, and (lL (tL (ei )), mL (ei )) ∈ dom(in). Example 4.10 (fast Σrule). The definition of a fat Σrule is quite similar to the definition of a ̺rooted Σrule – the major difference is the lack of 91
a root label. Consequently the rules given in Example 4.1 also serve as illustration for fast Σrules. The lefthand rule given in this example is a fast Σrule, while the righthand rule is not, for the same reasons given in that example. Lemma 4.19. If a rule r and class of Σgraphs CΣ conform to Condition 1 (given in §3.2), then r is a fast Σrule. Lemma 4.20. For every node v in the lefthand side of a fast Σrule there exists an edge enumeration with v as an initial node and bounded branching factor over all Σgraphs. Proof. Simple generalisation of the proof given for Proposition 4.2. Definition 4.11 (leftconnected graph reduction specification). A leftconnected graph reduction specification S = hΣ, CN , R, c, Acci consists of a signature Σ = hC, in, outi, a set CN ⊆ CV of nonterminal labels, a finite set R of fast Σrules, a termination factor c, and an Rirreducible Σgraph Acc, the accepting graph. L(S) = {G ∈ G(Σ)  G
⇒∗R
The graph language specified by S is
Acc and lG (VG ) ∩ CN = ∅}.
Note that the termination factor c is included as part of the definition of an LGRS to conform to MultistepApply, which requires a derivation length as one of its arguments. We often abbreviate ‘leftconnected graph reduction specifications’ by LGRS. The recognition problem for LGRS languages is defined as follows: LGRS Recognition Problem. Given: An LGRS S = hΣ, CN , R, Acci. Input:
A Σgraph G.
Output:
Does G belong to L(S)?
Definition 4.12 (linear LGRS). An LGRS hΣ, CN , R, c, Acci is linearly terminating if for every derivation G ⇒R G1 ⇒R . . . ⇒R Gn on Σgraphs, n ≤ cG. It is closed if for every step G ⇒R H on Σgraphs, G ⇒∗R Acc implies H ⇒∗R Acc. A linearly terminating and closed LGRS is a linear LGRS. Theorem 4.21 (linear LGRS time complexity). The recognition problem is decidable in linear time for a linear LGRS. 92
Proof. By Theorem 3.21, if we have a initial graph H0 , set of leftconnected rules R, depth bound l, and array of edge enumerations for the lefthand sides of R, then the algorithm MultistepApply defined in §3.4 will return some graph H such that either (1) H is irreducible with respect to set of rules R, and H is the product of a derivation of length k ≤ l, or (2) H is the product of a derivation of length l. Let S = hΣ, CN , R, c, Acci be a linear LGRS, and G a Σgraph. By the definition of a linear LGRS, any derivation must be shorter than cG. Due to the fact that a linear LGRS is closed, any irreducible graph derivable from G will be isomorphic to Acc if and only if G ∈ L(S). We can therefore solve the recognition problem for linear LGRSs by calling the algorithm MultistepApply, with the graph G as the initial graph, the set of LGRS rules RS as the reduction rules, and cG as the depth bound. The algorithm will return graph Acc if and only if the graph G is in L(S), ensuring correctness of the algorithm. By Theorem 3.22, if MultistepApply is called with Σpreserving rules and an array of enumerations such that each enumeration has a bounded branching factor over all Σgraphs, then the algorithm terminates in linear time. All Σrules preserve membership of the class of Σgraphs. By Lemma 4.20, the definition of a fast Σrule ensures the existence for each lefthand side node of an edge enumeration with the node as its initial node, and a bounded branching factor. Therefore, MultistepApply solves the recognition problem for linear LGRSs in linear time. The following example of an LGRS specifies the set of unrooted balanced binary trees. This example is one of the GRSs appearing in [4] (with minor alterations to fix an error discovered in the original version). Example 4.11 (unrooted balanced binary tree GRS). The LGRS BB = hΣBB , ∅, RBB , 1, Acc BB i defines the language of unrooted balanced binary trees. The signature is ΣBB = h{B, U, L}, {l, r, c}, outBB , inBB i, where inBB (v, e) = 1 for all v, e, and 1 outBB (v, e) = 0
if v = B and e ∈ {l, r}, or v = U and e = c otherwise
The ruleset RBB and accepting graph Acc BB are shown in Figure 4.11. The accepting graph has been slightly changed from the original version where 93
U c
U c
Acc :
U
U
FellU :
c
L
c
⇒ 1
1
U c B
FellB : l 1
B 1
1
B
l
1
r
L
1
B
U
⇒
c
L
l
L
1
r
U
U c
U c
2
2
2
PickLeaf :
PushBranch :
⇒
r
r
l
B
⇒ l
c 3
2
r 3
Figure 4.11: Rules and accepting graph for LGRS BB. it consisted of a single Lnode; The original could be restored by adding a special case rule that reduced only the current accepting graph. Lemma 4.22. The LGRS BB is linear. Proof. Given in [4]. It is interesting to note that in the source paper [4] the original balanced binary tree GRS was presented simply as an example of a GRS requiring a linear number of reductions to terminate. It is only now, after we have 94
defining the conditions ensuring linear termination for leftconnected reduction systems, that it has become clear that the membership problem for this LGRS can be solved in linear time.
4.4
Comparison between LGRSs and RGRSs
We now compare the LGRS and RGRS approaches to reduction. Note that, while our major results up to this point have been about linear LGRSs and RGRSs, in this section we compare LGRSs and RGRSs in general for expressive power. This is because we have not been able to characterise the classes of linear LGRS and RGRS languages precisely enough to compare their expressive powers. Definition 4.13 (LR , LL , L′R ). LR stands for the class of languages for which an RGRS exists and LL for the class of languages for which an LGRS exists. L′R stands for the class of languages such that for each L′ ∈ L′R there exists an L ∈ LR where L′ is the language of graphs in L with rootlabelled node and attached edges removed. Clearly the languages in LL and LR differ trivially in that members of a language in LR must include a uniquelylabelled root node. Proposition 4.23. There exists a language L in LL − L′R Proof. As shown in Proposition 4.6, for any RGRSrecognisable language there exists a bound b such that no graph in the language contains more than b connected components. By Proposition 4.8, this result holds even if the single rootlabelled node and attached edges are removed. LGRS languages in contrast can contain of an arbitrarily large number of connected components. For example, the language of single nodes (that is, an unbounded number of nodes with no incoming or outgoing edges) can be recognised by an LGRS with a single reduction rule and an empty graph as the accepting graph. The distinction between the two kinds of GRSs is a result of two major differences between LGRSs and RGRSs: (1) LGRS rules do not have to be applied at the location of a uniquelylabelled root node, and (2) LGRS rules do not have a root node that must be preserved. As a result, even though
95
the accepting graph and the lefthand sides of rules must be connected in an LGRS, connected components can be deleted by reduction. The converse result also holds: there exist languages recognisable by some rooted GRS that cannot be recognised by any leftconnected GRS. This is true because in an RGRS only the root needs to be the initial node of an enumeration satisfying the signature condition. In an LGRS each lefthand side node must be the source of such an enumeration. Intuitively, the existence of a root in the rules of an RGRS restricts the order of search, and so prevents some searches that require an unbounded amount of time. Proposition 4.24. There exists a language L in LR − LL . Proof. Our counterexample is the language of rooted binary DAGs, originally defined in §4.2.3. As shown in that section, this language can be expressed using a linear rooted GRS. It therefore suffices to show that no LGRS exists recognising the language of RBDs. Let us assume the converse; we have an LGRS RBDL recognising the language of rooted binary DAGs, as defined in §4.2.3. We call an llabelled edge that points to an Llabelled node with indegree one an lsingleton. An lsingleton is separating for the singleton’s source and target in an RBD. As an Llabelled node in an RBD can have an unbounded number of incoming llabelled edges, in the signature ΣRBDL it must hold that in(L, l) = ⊥. As a consequence of Lemma 4.7, an lsingleton can only be removed or modified by a rule that contains an lsingleton. We can pick a RBD with an unboundedly large number of lsingletons, meaning some must be removed to reach the fixedsize accepting graph. Consequently RBDL must include a reduction rule that includes an lsingleton on its lefthand side. Any enumeration over this rule with the Llabelled node as an initial node must include the lsingleton edge. However, as in(L, l) = ⊥ in the signature, this violates the condition on edge enumerations in the definition of a fast Σrule (Definition 4.10). Consequently, no such LGRS RBDL exists, which proves our result.
4.5
Developing and validating GRSs
In this chapter we have given several RGRSs and LGRSs, some of which are quite complex. In this section we sketch the process we have used for designing and validating new GRSs. 96
In most cases, the GRSs presented in this thesis were developed from other preexisting GRSs. The only GRS designed entirely from scratch was the most complex, the rooted binary DAG RGRS given in §4.2.3. Designing a GRS entirely from scratch was essentially the process of designing an algorithm for reduction. We first sketched a number of potential strategies for reduction. We then tried to implement the strategies as GRSs. Inevitably some of the approaches turned out to be unworkable, or very difficult to understand, or to require a very large GRSs. The most successful strategy was debugged and developed further. Our major design objectives for a GRS were as follows, roughly in order of importance from most to least. • Minimise the complexity of the reduction approach. • Minimise the number of reduction rules. • Minimise the number of reduction steps to termination. The construction of a nonterminal class of intermediate graphs goes hand in hand with the development of a GRS. A set of reduction rules define a nonterminal class, so the nonterminal class can be seen as capturing the approach to reduction in the RGRS. In several the of the GRSs presented in this thesis, we designed the class of nonterminal graphs before defining the appropriate rules to reduce the class. For many LGRSs, a corresponding RGRS can be constructed by augmenting the reduction rules with a root node, and adding extra ‘search’ rules to move the root to application areas. The RGRSs GG and RBB from §4.2, and CL from Example 4.2 are rooted versions of unrooted GRSs appearing in [4]. In all of these case adding roots resulted in a much larger number of reduction rules. The major general design difference between the two kinds of GRS is the level of operational control of the reduction process given by the rules. RGRSs are rooted, and the root is the only valid application area. Consequently RGRSs have to explicitly move the root between application areas. This means that the reduction system gives quite a specific ordering on the sequence of reductions applied to the graph. In contrast, in LGRSs the matching algorithm finds an application area automatically, and this often means that the choice of application area is much more nondeterministic. 97
The strategy for moving the root can be quite complex (see for example RBD, §4.2.3), as the direction of search can be controlled to a fine degree using labels. The main challenge with adding explicit movement of the root is ensuring termination. Just adding arbitrary rootmoving operations will often result in nontermination in illformed graphs with cycles. In RBB and several other examples termination is achieved by marking visited nodes. We found it difficult in general to judge informally whether a GRS was correct, as correctness often depended on subtle rule interactions. We have made use of a general methodology for validating RGRS and LGRS against a given graph language. This consists of defining the class of nonterminal intermediate graphs and then showing correctness and linearity as follows: • Show soundness by showing that the class is closed under the inverses of the reduction rules. • Show completeness by showing that every member of the nonterminal class can be reduced by some reduction rule. • Show linear termination by showing that some termination measure exists that is reduced by every reduction rule. Termination generally was simple to check, as our termination measures depend on simple structural properties of the rules. Validation of soundness involves considering all possible cases for a rule match to show that the nonterminal class is closed under the rule. Similarly completeness involves breaking down the nonterminal class by case to check that every member can be reduced. In the case of soundness and correctness checking rules is quite tedious, as a large number of cases have to be checked. This suggests that it may be possible to automate this step of the validation process.
98
Chapter 5
Other approaches to fast graph transformation In this chapter we compare our work on fast graph transformation and reduction systems to other related approaches. The approach to fast graph transformation we present in Chapter 3 and Chapter 4 improves the worstcase application time of rules or systems of rules. For this reason, we focus on approaches that improve the worstcase application time, and mention heuristicbased approaches only when they relate closely to our work. The chapter is structured as follows. §5.1 discusses the approaches that have been developed for improving the time complexity of single derivations. Section 5.2 discusses the approaches developed for efficient multistep derivations. In Section 5.3 we describe other approaches to the problem of efficient graph language recognition, and specifically discuss the relationship of our fast recognition systems to the special reduction systems of [11, 1].
5.1
Efficient direct derivations
Many graph rewriting systems are based on local search algorithms. These begin matching from a single node and then extend the matching morphism to neighbouring nodes and edges. For example, the matching algorithms of GrGen [36, 59] and GP [57] work in this way. Both of these approaches use heuristics to select appropriate start nodes. In the case that the heuristics make a good selection of start node and search order, these approaches achieve the timebounds given for leftconnected and rooted graph transfor99
mation. However, these approaches do not ensure lineartime or constanttime application. The major result of our work on fast graph transformation, given in Chapter 3, has been to take the local search approach and define the restrictions on graphs and rules necessary to ensure linear and constant worstcase time bounds. The heuristic approaches often attempts to identify uniquelylabelled nodes to use as start nodes. For example, GrGen [36, 59] attempts to approximate the frequency of node labels, and use less frequent labels as start nodes. In this way, these approaches make use of root nodes. Other works have considered using explicitlyidentified root nodes to enable fast rule application. For example, [33, 34] consider classes of hostgraphs representing infinite trees constructed by ‘unrolling’ from some root. This approach permits lineartime matching with rules as inputs. However, host graphs are severely restricted by the requirement that they are treestructures. The approach closest to ours is the work of D¨orr [25, 26]. This is a rooted approach based on constructing edge enumerations that avoid socalled strong Vstructures. Definition 5.1 (strong Vstructure). A strong Vstructure is defined as by a triple of graph labels hla , le , lv i. A strong Vstructure corresponding to hla , le , lv i is two edges e1 , e2 and three nodes v0 , v1 , v2 where (1) e1 and e2 have label le , (2) v1 and v2 have label lv , (3) v0 has label la , and (4) either v0 is the source of e1 , e2 and v1 , v2 are their targets, or the reverse. Example 5.1 (strong Vstructure). A strong Vstructure corresponding to hla , le , lv i consists of a subgraph isomorphic one of the following two cases. la le lv
lv le
lv
le
lv
le la
Suppose we have a set T of label triples such that all Vstructures in the host graph correspond to members of T . Then an edge enumeration is bypassing with respect to T if the source label, target label, and edge label of an edge in the enumeration matches no triple in T . 100
D¨orr’s application algorithm is a simple enumerationbased matching algorithm, very similar to the algorithm FastMatch given in §3.3. If the input enumeration bypasses all strong Vstructures then the application algorithm terminates in linear time. Our notion of an enumeration with a bounded branching factor can be seen as a generalisation of D¨orr’s notion of bypassing enumeration. A bypassing enumeration has a branching factor of 1 under our approach. D¨orr also requires unique root nodes. For this reason, rules with a bypassing enumerations can be applied in constant time using the rooted application algorithm given in §3.3. Our notion of branching is more general because rules under the Vstructure approach are deterministic, while ours may be nondeterministic. Furthermore, all hostgraphs under D¨orr’s approach are assumed to belong to a graph grammar which can be analysed for possible Vstructures. We don’t require any generation mechanism for host graphs, and instead have developed simple syntactic conditions that ensure the existence of an edgeenumeration bound. We have extended the bounded enumeration approach in ways that D¨orr does not consider. D¨orr’s requirement for constant time application means that he considers only rooted systems, while we have extended the notion of enumerations with a bounded branching factor to unrooted leftconnected systems. By modifying the algorithm of [11], we have also shown that sequences of such unrooted rules can be applied in linear time, even if individual rule applications also require at worst linear time. Finally, D¨orr differs from our work in the uses he makes of his rules with improved application times. He applies rooted rules to general computation systems based on graph transformation (his example in [26] is the implementation of a functional programming language), while we have applied our work on fast graph transformation to the recognition problem for graph transformation systems.
5.2
Efficient multistep derivation
Our algorithm MultistepApply, which solves the multistep graph transformation problem for unrooted rules, is derived from the multistep algo
101
rithm given by Bodlaender and de Fluiter in [11].1 Bodlaender and de Fluiter’s version of the algorithm maintains a list of search locations in the same way that ours does. However, it ensures that each individual search terminates in bounded time by bounded adjacency search. The graph is represented by an adjacency list representation, and the algorithm searches only within a bounded distance from previouslymatched edges to find more matching edges. This means that the correctness of the algorithm depends on the assumption that a bounded search is guaranteed to find a match. This assumption is discharged by applying the algorithm only to socalled special reduction systems. However, their definition of such reduction systems is semantic. It cannot be checked by examining the syntactic structure of the rewrite rules. This is the main difference between our version of the algorithm and the version of [11]. We have defined simple sufficient syntactic conditions ensuring that our algorithm terminates in linear time. Bodlaender and de Fluiter’s algorithm is in turn based on the algorithm given in [1]. This version of the algorithm searches only from interfacegraph nodes. The conditions on rules which ensure lineartime termination are syntactic, but they require the absence of edges between interfacegraph nodes – a severe restriction. In contrast, our algorithm requires only the existence of sufficient edge enumerations with bounded branching factor, as ensured by our syntactic conditions. Our conditions on labels and node degree also seem more natural than prohibiting all edges between interface nodes. Our algorithm MultistepApply shares information between rewriting steps. As such, it is similar to the approach of [77]. Under this approach, all potential matchings between a rule and the current graph are recorded in a database. After rewriting, the matches in the database must be updated with the new structure of the graph. Our algorithm is also similar to the RETE algorithm [30], an algorithm for matching patterns to objects which can be used for graph matching. As with [77], RETE maintains a structure during a derivation represents all partial matches. Like our algorithm, both of these approaches use an intermediate struc1
This algorithm and the algorithm of [1] are used in approaches to graph recognition. In the next section we compare these approach to our work on recognition systems.
102
ture to amortise the cost of matching over a derivation. The intermediate datastructures recorded by these two approaches are quite complex, meaning that the cost of maintaining the structures may be very high. In contrast, our algorithm maintains only a list of potential locations, and updating is therefore guaranteed to be much cheaper. Unlike our algorithm, these approaches apply to unrestricted graph transformation systems, meaning that they only aim to improve the average performance of graph matching. The results given in [77] are experimental; in contrast, we ensure an improved worstcase time complexity.
5.3
Efficient recognition and special reduction systems
This section compares our fast leftconnected and rooted graph reduction systems to other approaches that have been developed for efficient recognition by reduction. Our main comparison is with the approach to language recognition described by Bodlaender et al. in [11] and Arnborg et al in [1]. This work is based on Courcelle’s earlier work on monadic secondorder logic [17], and the prior work on the treewidth of graphs [10]. The formulations in [11, 1] are slightly different, but both give the following: (1) An algorithm that solves the language recognition problem. (2) A class of socalled special reduction systems which ensure that the recognition algorithm terminates in linear time. (3) A condition on graph languages ensuring the existence of a corresponding special reduction system. Suppose we have a language of graphs that can be defined as the intersection of (1) the set of graphs of treewidth up to some bound, and (2) a language that can be defined by some formula in monadic secondorder logic. Then [11] and [1] show that a special reduction system can be automatically constructed, and consequently that membership of these languages is decidable in linear time. Our approach can recognise in linear time languages which violate both of these sufficient conditions. The language of balanced binary trees is defined under the RGRS approach in §4.2.1 and LGRS approach in §4.3, but we show in Appendix A that this language is not expressible in monadic secondorder logic2 . The language of gridgraphs is defined using the RGRS 2
It is not clear in [1] whether their sufficient condition requires monadic secondorder
103
approach in §4.2.2 and can be expressed in a similar way using an LGRS, but it has an unbounded treewidth. This is because an n × n grid graph has treewidth n [18, p324]. The formulations of a special reduction system given in these two papers are slightly different. Our aim in our work has been to construct syntactic conditions on rules and graphs ensuring improved termination times. For this reason, we focus on the syntactic SRS formulation of [1], rather than the semantic formulation of [11]. On the one hand, special reduction systems as given in both [11] and [1] are of incomparable power to both LGRSs and RGRSs. This is because the notion of a reduction system given in these papers lacks nonterminal labels, which places quite a severe restriction on their expressive power. To prove this, we give an example of a language inexpressible without nonterminals. Definition 5.2 (complete binary tree). We describe a graph as a complete binary tree (CBT) if it is a balanced binary tree where all of the branch nodes are binary. The language of all complete binary trees can expressed by both LGRSs and RGRSs. The rooted BBT example given in §4.2 and the unrooted BBT example given in §4.3 can both be adapted to recognise CBTs by simply turning the terminal U label into a nonterminal label. However, the language of complete binary trees cannot be expressed in a special reduction system, or indeed any reduction system under the formulation in [11] and [1]. We prove this result by a simple size argument. This proof is a restatement of the proof of Theorem 5 in [4]. Proposition 5.1. No reduction system as defined in [1] exists which can recognise the language of complete binary trees. Proof. By the definition of a reduction system in [1], a reduction system R R
for property P must be ‘safe’, meaning whenever G − → G′ , then P (G) ⇔ P (G′ ). Assume we have Rc , a reduction system for CBTs. Let s be the maximum of the sizes of the lefthand sides of rules in Rc . Let G be a CBT of depth k chosen such that 2(k−1) > s. Each reduction by any rule in Rc can remove at most s nodes from the graph G. However, every CBT smaller logic with or without an incidence predicate. In Appendix A we prove that balanced binary trees are inexpressible in both versions of MSOL.
104
R Acc:
H
s
Reduce: H
⇒
H
1
1
R R
s
n
s s R
H
s R s
s
R
R Figure 5.1: A special reduction system which recognises the language of star, and an example star. than G is at least 2(k−1) nodes smaller; every larger CBT is at least 2k nodes larger. Therefore Rc does not exist. On the other hand, the special reduction systems of [1] can also express languages which are inexpressible under both linear and rooted GRSs. We call a graph a star if it consists of a single Hlabelled ‘hub’ node, and an unbounded number of Rlabelled ‘rim’ nodes, and for each rim node there exists a single slabelled ‘spoke’ edge with the rim node as its source and the unique hub node as its target. A special reduction system exists which recognises the language of star. An example of a star, and the corresponding special reduction system is shown in Figure 5.1. For clarity we use our normal notation for a rule rather than the notation used in [1], but the two notations are equally expressive. This language of stars is not in L′R . This means that it cannot be recognised by an RGRS, no matter where the uniquelylabelled graph root is added to the graph. To prove this result we need the following lemmas. Lemma 5.2 (moving the root). Let G be a rooted graph with a separating edge l in the same component as the root. Let v be the node attached to l that is in the same component as the root in G \ l. Let G ⇒r H be a derivation such that l is not matched, and let l′ be the descendant of l in H. Then l′ is separating in H, and the descendant of v is in the same component as the 105
root in H \ l′ . Lemma 5.3 (roots and separating edges). Let r = hL ← K → Ri be a rooted rule with matching enumeration e1 , . . . , en . Let G be a rooted graph, and let G ⇒r H be a direct derivation where edge e matched by l ∈ EL is separating in G. Let vu be the node attached to l not in the same component as the root in G \ l. Let l be the ith element of the edge enumeration. Then there can be no lj in the enumeration such that j < i and lj is incident to vu . Proposition 5.4. No RGRS exists which recognises the language of stars, even with the addition of an arbitrarilylocated root node. Proof. Suppose we have an RGRS which recognises the language of stars with an arbitrarilyattached ̺labelled root node. All rules in the RGRS must satisfy the conditions on rooted Σrules given in Def. 4.1. Now we can pick an arbitrarilylarge star H with the root attached to some node. To reach the finitesize accepting graph from graph H, we must therefore be able to delete some of the rim nodes and spoke edges. To delete or modify an edge it must be matched to the lefthand side of some rule during the reduction. However, any such rule is not a rooted Σrule. There are two cases. Case 1: The root is attached to the hub node. All the spoke edges are separating for the hub node and the corresponding rim node. By Lemma 5.2 and Lemma 5.3, to match any of the spoke edges, the spoke edge must be matched in some lefthand side by an edge enumeration which matches from the hub towards the rim. Because the number of edges outgoing from the hub is unbounded, such an edge enumeration must violate the condition on rooted Σrules. Case 2: The root is attached to a rim node. By Lemma 5.2 and Lemma 5.3, to match and remove more than one spoke edge must again require an edge enumeration which extends from the hub to the rim. Once again, this violates definition 4.1. Therefore no such RGRS exists. The proof that no LGRS exists for star graphs is easier, because any rule can be applied nondeterministically anywhere in the graph, rather than just at the root. 106
Proposition 5.5. No LGRS exists which recognises the language of stars. Proof. Suppose we have an LGRS which recognises the language of stars. Then all rules in the LGRS must conform to Def. 4.10 for fast Σrules. Let us pick a star G which is larger than the accepting graph, with hub degree d > b. To recognise the accepting graph we must therefore remove some of the spoke edges in the graph. By Lemma 4.7 to remove any spoke edge there must exist a rule which includes a separating slabelled spoke edge. But, by the same argument as used in Proposition 4.24 to argue that the language of RBDs is not LGRSexpressible, any such rule must violate Def. 4.10. Therefore no such LGRS exists. The restriction on special reduction systems imposed by the absence of nonterminal symbols can be lifted by modifying the definition of an SRS to permit the use of nonterminals. We denote by LS the class of graph languages definable by special reduction systems extended with nonterminal symbols. Conjecture. We believe that neither of the classes LL and LR are subsets of the class LS of languages definable by special reduction systems extended with nonterminal symbols. This conjecture seems plausible because special reduction systems prohibit edges between interface nodes, which severely restricts the structure of rewrite rules. Intuitively, it seems that this must restrict the formal power of the system. However, at present we have not been able to prove this.
107
Part III
Graph grammars and separation logic
108
Chapter 6
Semantics of formulas and grammars Hyperedgereplacement grammars, which are defined formally in §2.3, are a natural extension of contextfree string grammars to the world of hypergraphs. Hyperedge replacement productions are contextfree, meaning that they only replace a single nonterminal hyperedge, rather than a subgraph of the target graph. The language defined by such a grammar is the class of graphs derivable from some initial graph. Separation logic is a recentlydeveloped logic for specifying the properties of heaps. It makes use of a separating conjunction to enable local reasoning. Loosely speaking, this separating conjunction allows a logical formula to specify the spatial relationships between assertions in the heap. Formulas can specify properties that are true in disjoint portions of the heap. A separationlogic formula is normally seen as defining a class of satisfying states, each of which consists of a heap, recording the relation between memory locations, and a stack, recording program variables. States are graphlike structures, with heap locations as ‘nodes’ and pointers as ‘edges’. So both hyperedge replacement grammars and separation logic formulas define classes of graphlike structures. Separation logic formulas are frequently used with recursivelydefined predicates. These formulas with recursive predicates resemble hyperedge replacement grammars quite closely, in that a predicate stands for a class of satisfying heaps attached to the larger separationlogic state through its arguments. Furthermore, the separating property enforced by the separating 109
conjunction resembles the contextfree property of hyperedgereplacement grammars, in that separated predicate calls can only share a fixed number of locations. That is to say, predicate calls can’t ‘overlap’. In this part of the thesis, we show that this intuition is correct: restricted hyperedge replacement grammars and formulas in a restricted fragment of separation logic are indeed related. We do this by defining mappings in both directions between these restricted grammars and formulas in the fragment. We show that the two mappings g and s are correct in the sense that they are semanticspreserving with respect to a bijective mapping α between separationlogic states and graphs. The preservation of semantics that we are looking for in the mappings can be expressed by the requirement that the following pair of diagrams commute: language
=
g
language
Graphs
Grammar
Formula
Graphs
Grammar
α
s
States
=
States
Formula
satisfied by
α−1
satisfied by
Intuitively, if this correctness property holds, then any class of structures that can be expressed in one domain can also be expressed in the other. This means that our fragment of separation logic is of equivalent expressive power to our restricted form of hyperedgereplacement. This result has the interesting consequence that some of the theoretical results for hyperedge replacement languages, such as the inexpressibility results, can be imported wholesale into our fragment of separation logic. For example, the languages of redblack trees, balanced binary trees and grid graphs are all known to be inexpressible by any hyperedgereplacement grammar. Therefore no formula exists in our fragment specifying any of these structures. The chapter is structured as follows. In Section 6.1 we define the fragment of separation logic we use and formally define its semantics. In Section 6.2 we show that all formulas in this fragment can be flattened into a simpler form without nested recursive definitions. In Section 6.3 we define a class of heapgraphs that correspond to separationlogic heaps, and define a mapping between heapgraphs and heaps. Finally, in Section 6.4 we define a class of graphs corresponding to heaps, and show that any grammar can be normalised to one producing only heapgraphs. 110
6.1
Separation logic syntax and semantics
Separation logic [46, 69] extends firstorder logic with socalled spatial assertions that describe the state of the heap. Spatial assertions in separation logic are local, meaning that they describe properties that hold in disjoint areas of the heap, and that they restrict the sharing between regions of the heap. Spatial assertions permit local reasoning, meaning that assertions can be safely combined without explicit restrictions on sharing. This section specifies the syntax and semantics of a fragment of separation logic. The semantics for separation logic given in this section are based on those appearing in [74, 52], with modifications that we describe. The definition of satisfaction given in Figure 6.2 is based on [74], while the semantics of the recursive let is based on [52].
6.1.1
Separation logic model
Separation logic formulas primarily express properties of heaps. We use a consmodel for the heap, where each location in the heap points to a pair of elements. We have chosen this model because it is simple enough to easily define our translation, but rich enough to define interesting structures such as trees and lists. Other models can be used as the domain for separation logic. In §8.2 we consider an extended heapmodel based on tuples. More complex models are also available, such as the RAM model, which records locations as integers and so permits pointer arithmetic [69]. The RAM model is too complex to map to hyperedge replacement in a semanticspreserving manner, because the relationship between adjacent cells is impossible to capture using edges. In order to model it would need to be able to create edges between arbitrary members of the heap, to model locations that were accidentally adjacent. Doing this would require that we maintain nonterminal edges between all heap locations, which would break the locality of hyperedge replacement. Other heap models such as the one used in [74] allows integer values in the heap, but prohibits pointer arithmetic; Here we abstract away to deal only with the structure of the heap. In this, the model we use is similar to the model used for symbolic heaps that form the basis of recent separationlogicbased program analysis [6, 20]. In §8.3.2 we look at the relationship 111
between our fragment of separation logic and the symbolic heaps fragment. Assumption 6.1. Let Loc be a countably infinite set of locations. The set of elements, Elem = Loc ∪ {nil}, consists of locations and a special atomic value nil. We also assume an atomic unknown value .
Definition 6.1 (heap). A heap h : Loc ⇀ (Elem × Elem) ∪ is a finite
partial function mapping heap locations to pairs of elements or . We use
HE to stand for the set of all possible heaps.
The image of a heap h, written img(h), is the set of locations held in pairs
in the heap, that is img(h) = {v ∈ Loc  ∃v ′ . h(v ′ ) = (v, ) ∨ h(v ′ ) = ( , v)}. We describe a location l in a heap h as dangling if l ∈ img(h) and l ∈ / dom(h). Note that in general img(h) \ dom(h) may be nonempty, meaning elements in h may refer to locations outside the heap. The inclusion of the unknown value is a departure from the standard
model given in [69]. In the semantics of formulas, we want to avoid locations
that are in the image of a heap but not in the heap domain. Locations that
could dangle in this way are avoided by mapping representative locations to the value . See p. 117 for more on this.
Example 6.1 (heap). The heap h shown below associates three locations, l1 , l2 and l3 with pairs of values. Other locations are undefined. l2 :
nil
h = { l1 7→ hl2 , nili, l1 :
l2 7→ hnil, l3 i,
l3 : nil
l3 7→ hl2 , l1 i }
We represent this heap by the diagram shown on the righthand side. Undefined locations are not shown. We consider heaps as distinct only up to isomorphism. In practice, this means that the underlying set of locations can be substituted for each other without changing the meaning of the heap. This relies on the implicit assumption that only the structure of the heap is important, as encoded by the relation between heap locations. This is sensible when we are interested in reasoning about the abstract properties of separation logic. It may break 112
down however if we are dealing with more concrete heaps with distinguishable locations.
6.1.2
Separation logic fragment
Our mapping between separationlogic formulas and hyperedge replacement grammars operates over a fragment of the full separation logic as given in [69]1 . This includes separating conjunction (∗), the recursive let (let Γ in P ), ‘pointsto’ (7→), disjunction, existential quantification, elements from the set Elem, predicate names σ from a set Pred, and variable names x, x1 , . , xn from a set Var. The class SL of all separationlogic formulas in this fragment is defined by the following abstract syntax, with initial symbol F . Given a set of variables K we write FK for a formula that contains only free variables in K – see the discussion on p. 121. F
::= emp  x 7→ V, V  F ∗ F  ∃x. F  F ∨ F  σ(V, . . . , V )  let Γ in F
Γ ::= σ(x1 , . . . , xn ) = Fx1 ,...,xn  Γ, Γ V
::= x  nil
The precedence order for nested operators, given from strongest to weakest, is: ∗, ∨, ∃, let Γ in F . The simplest assertion is emp, which asserts that the heap is empty. A heap is empty if the heap’s address function is undefined for all addresses. Next, the pointsto assertion, written a 7→ e1 , e2 , asserts that the heapfunction defines the single address a as pointing to the tuple (e1 , e2 ). Separation logic’s most important extension to firstorder logic is the separating conjunction, written ‘∗’. A heap h satisfies P0 ∗ P1 if there exist disjoint subheaps satisfying P0 and P1 , such that h is the composition of the two subheaps. Separation logic extends firstorder logic with several constructs permitting reasoning about heaps. The most basic of these is the pointsto assertion 1
We generally describe our logic as a fragment of full separation logic. However it is not a true fragment, as our recursive let is more general than the notion of recursion in most formulations separation logics. The exception to this are the separation logics given in [52, 74], both of which include a general operator for recursion.
113
i′
i h1 :
i′′
i′
i′′
h2
i
i′′
Figure 6.1: Heap h1 satisfies the formula ∃xyz. ((x 7→ z, y) ∗ (y 7→ z, x)) with x instantiated with i, y with i′ and z with i′′ . Heap h2 does not satisfy the formula, due to sharing. a 7→ e1 , e2 , which asserts that the heap has a singleton domain {a} and that it maps a to (e1 , e2 ). Separation logic also introduces the separating conjunction P0 ∗P1 , which asserts that P0 and P1 hold in disjoint sections of the heap. This prohibits sharing. For example, the formula ∃xyz. ((x 7→ z, y) ∗ (y 7→ z, x)) asserts that the heap associates location x with the pair (z, y), and location y with the pair (z, x), and that x and y are distinct. This is satisfied by heap h1 in Figure 6.1, but not by heap h2 due to sharing. Most of the constructs in this fragment are either conventional firstorder logical operators, or are conventional separationlogic operators as introduced in [69]. The only unusual construct is the letconstruct, which is used to define recursive predicates. A letexpression let Γ in F takes as its arguments a list of predicate definitions Γ and a separation logic formula F , over which the definitions scope. Predicates definitions have the form σ(x1 , . . . , xn ) = R. Here σ is a predicate name, x1 , . . . , xn are variable arguments, and R is the predicate body. Intuitively, a formula let σ(x1 , . . . , xn ) = R in F states that any (recursive) invocation of the predicate σ in F is satisfied by every heap satisfying R. Predicates defined in Γ scope over the definitions of Γ, so letstatements can define mutuallyrecursive predicates. We call a formula letfree if it does not contain a letexpression. Example 6.2 (binary tree formula). The separationlogic formula for the class of binary trees is given below. It is satisfied by any heap containing a binary tree with nil leaves. The predicate bt(x) defines the class of heaps containing a binary tree with nilvalued leaves and root at x. We abstract away from a particular root node by existentially quantifying x. ∃x.let bt(x1 ) = (x1 7→ nil, nil) ∨ (∃x2 , x3 . (x1 7→ x2 , x3 ) ∗ bt(x2 ) ∗ bt(x3 )) in bt(x) 114
In this predicate each node of the tree holds a pair of values. The leaves of the tree consist of pairs of nilvalues. bt(x) is satisfied if either x points to a location holding a pair of nilvalues, or if x points to a pair of locations, both of which also satisfy bt. The separating conjunction ∗ between the branch and the two subtrees differs from conventional conjunction in that it prevents its conjuncts from overlapping in the heap. This enforces the tree property by preventing sharing between the subtrees. Our fragment of separationlogic has been selected to remove features that are inexpressible in hyperedgereplacement grammars. Notably the conjunction, negation and separating implication (−∗) operators are omitted. Conjunction and negation are omitted because the class of HRexpressible languages is not closed under either language intersection or negation [39]. Negation is also omitted in order to ensure the existence of a least fixedpoint in our semantics for letstatements. Separating implication is omitted because a formula with separating implication can be used to ‘match’ and ‘remove’ a portion of the heap; operations that are fundamentally context sensitive, and so outside the domain of hyperedge replacement grammars. Section 8.1 discusses in more detail our reasons for omitting these constructs, and proves that some of the omitted constructs are inexpressible under hyperedgereplacement.
6.1.3
Fragment semantics
To define the semantics of a formula in our fragment, we define the satisfaction relation ‘=’. Satisfaction is defined for a heap h, a variable interpretation i and a predicate interpretation η. Assumption 6.2. In the following, let Var be a countably infinite set of logical variable names and let Pred be a countably infinite set of predicate names. Definition 6.2 (variable interpretation). A variable interpretation defines the meaning of variables in a separation logic state. A variable interpretation i : Var → Elem is a partial function mapping variables to heap elements. A variable interpretation i defines an interpretation function [[−]]i : Var ∪ {nil} → Elem, where [[nil]]i = nil and [[v]]i = i(v) for all v ∈ Var.
115
Example 6.3 (variable interpretation). Given variables x, y and z and locations l1 and l2 , we can define the following variable interpretation. i = {x 7→ l1 , y 7→ nil, z 7→ l2 } Definition 6.3 (predicate interpretation). A predicate interpretation defines the semantics of a recursively defined predicates as a class of satisfying heaps with attachment points. A predicate interpretation η : Pred ⇀ P ow(Loc∗ × H) is a partial function mapping predicate names to (possibly infinite) sets of pairs, consisting of a heap and a sequence of locations in the heap. Example 6.4 (predicate interpretation). Let P1 and P2 be names in Pred. Then we can define the following (finite) predicate environment η. η = { P1 7→ {h[l1 ], {l1 7→ hl2 , nili, l2 7→ hl1 , nili}i}, P2 7→ {h[l1 , l3 ], {l1 7→ hl2 , nili, l2 7→ hnil, l2 i, l3 7→ hl2 , l1 i}i} } η associates a single pair with P1 , where the list of locations contains a single locations. It also associates and a single pair with P2 , where the list of locations contains two locations. The variable interpretation i0 and predicate interpretation η0 are empty interpretations, meaning that dom(i0 ) = dom(η0 ) = ∅. We say that a heap h satisfies a formula F iff h, i0 , η0 = F . The definition of = is given in Figure 6.2. We make use of several extra concepts in this definition. Square brackets are used to denote the function update operator. For example, f ′ = f [x 7→ y] is defined so f ′ (a) = f (a) when a 6= x and f ′ (a) = y otherwise. If ~a = a1 , . . . , an and ~b = b1 , . . . , bn are vectors, we write f [~a 7→ ~b] as shorthand for f [a1 7→ b1 ] . . . [an 7→ bn ]. Finally, we allow [~a 7→ ~b] to stand for f⊥ [~a 7→ ~b], where f⊥ is the empty function that has dom(f⊥ ) = ∅. In defining satisfaction for the recursive let formula let Γ in P , we need to access the individual predicate definitions in Γ. We use dom(Γ) to stand for a set containing the names of the predicates defined in Γ. We write ‘bind σ(~x) = R to Γ(σ) in F ’ to associate ~x and R with the variables and righthand side of the syntactic definition of σ given in Γ. In defining the fixedpoint function for recursive let formulas, we use a λ notation to define simple functions. The definition λx. Y stands for the 116
h, i, η = emp
iff dom(h) = ∅
h, i, η = (x 7→ V1 , V2 )
iff dom(h) = {[[x]]i, [[V1 ]]i, [[V2 ]]i} and h([[x]]i) = ([[V1 ]]i, [[V2 ]]i) and h([[V1 ]]i) = h([[V2 ]]i) =
h, i, η = P ∗ Q
iff there exist h0 , h1 such that h0 · h1 = h and h0 , i, η = P and h1 , i, η = Q
h, i, η = P ∨ Q
iff h, i, η = P or h, i, η = Q
h, i, η = ∃x. P
iff exists v ∈ Loc such that h, i[x → v], η = P
h, i, η = σ(V1 , . . . , Vn ) iff (([[V1 ]]i, . . . , [[Vn ]]i), h) ∈ η(σ) h, i, η = let Γ in P iff h, i, η [β 7→ k(β)]β∈dom(Γ) = P where k = fix λk0 .λσ ∈ dom(Γ). bind σ(~x) = Q to Γ(σ) in {(~l, h)  h, ~x 7→ ~l , η β 7→ k0 (β) β∈dom(Γ) = Q} Figure 6.2: Definition of satisfaction for separation logic. function taking a single input value and substituting all instances of x in Y with the input value. Definition 6.4 (heap fusion). The heap h0 · h1 denotes the fusion of the two heaps h0 and h1 . This is defined only if the set of defined, non locations for the heaps are disjoint. That is, no location l exists such that both h0 (l) ∈ (Elem × Elem) and h1 (l) ∈ (Elem × Elem).2 The fusion overwrites values with known values when fusing the two heaps. Fusion
is defined as follows, if the two heaps’ nonunknown domains of definition are disjoint.
h0 · h1 (l) =
h (l) if hi (l) ∈ (Elem × Elem) ∧ i ∈ {0, 1} i ⊥
if hi (l) = ∧ hj (l) ∈ {, ⊥} ∧ i, j ∈ {0, 1} ∧ i 6= j otherwise
2 The semantics presented in [74, 52] define this fusion as simple disjoint union, which our definition reduces to in heaps without valued addresses. Our introduction of the unknown value makes the definition heap fusion a little more complex however.
117
Example 6.5 (heap fusion). Let heaps h1 , h2 and h3 be defined as follows. h1 = {l1 7→ hnil, l3 i, l3 7→ hnil, l2 i} h2 = {l2 7→ hl3 , nili, l3 7→ }
h3 = {l1 7→ hnil, l2 i}
Then the fusion of h1 and h2 is defined as follows. h1 · h2 = {l1 7→ hnil, l3 i, l2 7→ hl3 , nili, l3 7→ hnil, l2 i} Note that the unknown value for l3 in h2 is overwritten by the definite value for l3 in h1 . The fusion of h1 and h3 is undefined, as location l1 has a non value in both heaps. The fixedpoint definition for recursive predicates is defined with respect to the following ordering on predicate interpretations. Definition 6.5 (≤p ). We define the relation ≤p over predicate interpretations with a given set of predicate names by simple setinclusion for each symbol. So η1 ≤p η2 if for every predicate symbol σ in the domain of η1 , it holds that η1 (σ) ⊆ η2 (σ). Example 6.6 (≤p ). Suppose h1 and h2 are heaps, σ1 and σ2 are predicate names, and l1 , l2 are locations. We define the predicate interpretations η1 , η2 and η3 as follows. η1 = {σ1 7→ {h[l1 , l2 ], h1 i}, σ2 7→ ∅} η2 = {σ1 7→ {h[l1 , l2 ], h1 i, h[l1 , l2 ], h2 i}, σ2 7→ {h[l1 , l2 ], h1 i}} η3 = {σ1 7→ {h[l1 , l2 ], h1 i}, σ2 7→ {h[l1 , l2 ], h1 i, h[l1 ], h2 i}} Then η1 ≤p η2 holds, but η2 and η3 are incomparable.
6.1.4
Differences from standard separation logic
The semantics of most of the nonrecursive operators in our fragment are close enough to the intuitive behaviour described in §6.1.2 that we will not explain them on a casebycase basis. The exceptions are in the handling of dangling values, the semantics of predicate definitions, and the scope of variables, all of which differ substantially from the conventional semantics presented in [74, 52].
118
Handling of dangling values Normally in separation logic, dangling values (for example, y and z in the formula ∃xyz. x 7→ y, z) are satisfied by any arbitrary value assignment. By contrast, under our semantics a dangling heap locations must contain the special unknown heap element , rather than an arbitrary heap element.
This means that a heap that satisfies the formula x1 7→ x2 , x3 should define a value for three locations, two of which consist of .
The unknown value is included in our semantics to avoid the problem
of modelling dangling pointers in a hyperedge replacement grammar. Without the unknown value, a grammar would have to construct every possible instantiation of dangling values by attaching them to arbitrary heap nodes. The addition of does not seem too radical a modification. The se
mantics is still sufficiently close to the standard semantics to be of general interest. If a formula contains a dangling value, instead of an arbitrary value the value in a satisfying heap must be . In formulas without dangling vari
ables, the semantics are identical.
Semantics of predicate declarations Our semantics for recursive predicates differs from the semantics given in [52] (on which it is based) and we will therefore examine it in detail. A predicate interpretation (defined in Def. 6.3) records the meaning of predicates, by associating each predicate name in Pred with a sets of pairs. Each of these pairs consists of a heap, which is a satisfying instance of the predicate, and a sequence of locations, which form the points to which the arguments of the predicate are bound. The satisfaction relation for an instance of a predicate σ in the predicate interpretation η is defined as follows. h, i, η = σ(V1 , . . . , Vn ) iff (([[V1 ]]i, . . . , [[Vn ]]i), h) ∈ η(σ) A predicate σ(x1 , . . . , xn ) is satisfied by a heap h and interpretations i, η if there exists a pair ((l1 , . . . , ln ), h′ ) ∈ η(σ) such that (1) h = h′ and (2) the sequence of locations l1 , . . . , ln correspond to locations [[x1 ]]i, . . . , [[xn ]]. New predicates are defined using a recursive letstatement. A formula let Γ in P is satisfied by a heap h in variable interpretation i and predicate
119
interpretation η, if h satisfies P in a new predicate interpretation η[β 7→ k(β)] recording the semantics of the predicates defined in Γ. The predicate interpretation η[β 7→ k(β)] is defined over all of the predicate names defined in Γ as the value of newlyconstructed predicate interpretation k. This k is defined as the least fixedpoint with respect to ≤p of the following function, which we call fη,Γ . fη,Γ = λk0 . λσ ∈ dom(Γ). bind (σ(~x) = Q) to Γ(σ) in {(~l, h)  h, ~x 7→ ~l , η β 7→ k0 (β) β∈dom(Γ) = Q}
The satisfaction relation for let Γ in P is then as follows. h, i, η = let Γ in P
iff h, i, η [β 7→ k(β)]β∈dom(Γ) = P where k = fix fη,Γ
The function fη,Γ takes as its argument a predicate interpretation k0 and returns a predicate interpretation fη,Γ (k0 ). Intuitively fη,Γ applies a single iteration of the recursive definitions given in Γ. Let σ(~x) = Q be a definition in Γ. The function fη,Γ is defined so that fη,Γ (k0 , σ) is the set of pairs that satisfy Q in the predicate interpretation k0 . The least fixedpoint of fη,Γ is the least predicate interpretation such that another iteration of the recursion does not alter the predicate interpretation. This fits with our intuitive understanding of a recursive predicate definition. It is important to show that our modified semantics for separation logic gives a semantics to all formulas in the fragment. This is trivially true for letfree formulas, but we must prove that a fixedpoint exists for let formulas. The existence of a least fixedpoint has been proved for other versions of the separationlogic semantics with letstatements [52]. However, the semantics given in this chapter have been modified from the ones appearing in these papers, which means that we must reprove the result. Lemma 6.1 (existence of fixedpoint3 ). Let fη,Γ be the function constructed by the satisfaction relation to define the semantics of a letexpression. A least fixedpoint for fη,Γ is guaranteed to exist. Proof. The existence of a least fixedpoint for the function fη,Γ is guaranteed by Tarski’s fixedpoint theorem [75]. Let (L, ≤) be some complete lattice, 3
This proof was suggested by Hongseok Yang in personal communication.
120
meaning that any subset of L has both a least upper bound and greatest lower bound. Let f be a monotone increasing function over L (that is, for l1 , l2 ∈ L, if l1 ≤ l2 , then f (l1 ) ≤ f (l2 )). Then Tarski’s theorem states that the set of fixedpoints of f is also a complete lattice with respect to ≤. This implies there must exist a least fixedpoint of f . It is clear from the definition of ≤p that every set of predicate interpretations has both a least upper bound and a greatest lower bound, constructed respectively by set union and intersection over each symbol. Therefore the class of predicate interpretations over a given set of predicate names forms a complete lattice. By Tarski’s result, to show that a least fixedpoint exists for the function fη,Γ , we need only show that it is monotonic increasing with respect to ≤p . In other words, given the function fη,Γ defined by the semantics of satisfaction from a particular separation logic formula, we want to show that η1 ≤p η2 implies fη,Γ (η1 ) ≤p fη,Γ (η2 ). As our fragment of separation logic is free of negation, it is simple to show by structural induction that for any symbol σ, any pair present in fη,Γ (η1 ) must also be in fη,Γ (η2 ). This completes the proof. Handling of variable scope Our semantics for recursive predicates differs from the semantics of [52] in its handling of logical variables. In the original semantics, variables scope over predicate definitions, while in our semantics they do not. This means that free variables cannot be meaningfully used in predicate definitions. All variables in the body of a predicate must be bound, either by an existential quantification, or by the argument names for the predicate. We restrict variable scope in this way to reduce the complexity of mapping predicates to nonterminal hyperedges. Passing variables by scope is difficult to simulate correctly in a hyperedge replacement grammar. To do it successfully we must analyse the formulas to see which variables are used by each predicate’s righthand side and then add extra attachment points to hyperedges to pass nodes corresponding to these variables. Restricting scope makes the mapping considerably easier. We justify this change to the semantics by observing, that our semantics is identical to the semantics of [52] for formulas without free variables in predicate definitions. We also observe that for any formula defined under 121
the semantics of [52], an equivalent formula can be constructed without free variables in predicate definitions. This is because the set of variable names referred to in a formula is always finite, so variables can always be passed as arguments to a predicate definition, rather than passed implicitly by scope. For example, consider the following formula, which passes the variable x implicitly by scope. let f (z) = z 7→ x, nil in f (y) This can be converted to the following formula by converting the variable x into a predicate argument. let f (z1 , z2 ) = z1 7→ z2 , nil in f (y, x) As a result, restricting variable scope in our semantics results in no alteration in expressive power.
6.2
Flattening separation logic formulas
Mapping directly from our fragment of separation logic to graph grammars is difficult, because the potential nesting of letconstructs makes the definition of productions complex. For this reason, our mapping from formulas to grammars operates over the restricted domain of flat formulas. Definition 6.6 (flat formula). A formula let Γ in P ∈ SL is a flat formula if Γ and P are letfree. SLF is the class of all flat formulas in SL. Example 6.7 (flat formula). The following formula is flat, as it contains only a single outermost let. let P (x) = x 7→ nil, nil in ∃y, z. P (y) ∗ P (z) However, the following formula is not flat, as it contains more than one let, and in any case neither of the lets is outermost. (let P (x) = x 7→ nil, nil in ∃y. P (y)) ∨ (let Q(x) = emp in ∃y. Q(y)) In this section we show that every formula in our fragment can be rewritten as an equivalent flat formula. This means that restricting our mapping to flat formulas does not reduce its power. 122
Any letfree formula F can be converted to the corresponding flat formula by embedding it in a letstatement with no predicate definitions. Nonletfree formulas must be explicitly flattened. Before flattening, we must first ensure that predicate names are used unambiguously in formulas. The letconstruct in our fragment operates in the same way as variable quantification, in that predicate instances can be either bound or free. A predicate instance is bound if it exists inside a letexpression that defines a meaning for the predicate. Other predicate instances are free. A formula in our fragment is conflictfree if each predicate name is used in the scope of at most one letexpression. This means that different let expressions must define predicates with different names, and also that the names of free predicate instances must not be used in any letexpression’s definitions. Any formula in our fragment can be rewritten as an equivalent conflictfree formula. Lemma 6.2. Let F be a formula in SL. Then there exists a corresponding conflictfree formula F ′ ∈ SLF such that for any heap h, variable interpretation i and predicate interpretation η, h, i, η = F ⇐⇒ h, i, η = F ′ . Proof. In the following, let F [a/b] stand for formula F with all free instances of the predicate name a replaced by b. We prove our result by showing that freevariable rewriting alters only the names of the variables in the satisfying predicate interpretation. For any predicate name σ and unused predicate name ς, if h, i, η = F , then h, i, η[σ 7→ ⊥][ς 7→ η(σ)] = F [σ/ς]. This result follows trivially from the semantics of satisfaction. We then apply this result to show that for any letexpression F = let Γ in P , h, i, η = F if and only if (s, h), η = let Γ[σ/ς] in P [σ/ς]. This follows from the fact that replacing Γ with Γ[σ/ς] result in a predicate interpretation k[σ 7→ ⊥][ς 7→ k(σ)], where k is the predicate interpretation defined by Γ. Once we have this result for letformulas, it follows by structural induction that we can replace any bound predicate name in a formula with an arbitrary unused predicate name without altering the semantics of the formula. Therefore, any conflicting predicates can be renamed. We now define a function that takes as its input an arbitrary conflict123
def
flat (P ∗ Q) = lift(flat (P ) ∗ flat (Q)) def flat (P ∨ Q) = lift(flat (P ) ∨ flat (Q)) def flat (∃x. P ) = lift(∃x. flat (P )) def flat(let Γ in P ) = lift(let flat(Γ) in flat (P )) def flat(σ1 (x~1 ) = R1 . . . σn (x~n ) = Rn ) = σ1 (x~n ) = flat(R1 ) . . . σn (x~n ) = flat(Rn ) Figure 6.3: Flattening function flat . free formula in our fragment and produces a sematicallyequivalent flattened formula. A formula is flattened by incrementally ‘promoting’ letexpressions outwards, merging together predicate definitions, until only the outermost let remains. The flattening function flat is defined by a bottomup transformation of the structure of a formula. The flattening itself is performed by the function lift, which takes as its argument a formula F with flat subexpressions and produces a flattened formula lift(F ). Function flat first applies flat to the subexpressions of the formula, and then applies the rewriting function lift to the resulting formula. Definition 6.7 (flat ,lift). flat is defined in Figure 6.3. All of the unintroduced cases for flat are the identity function on formulas. The rewriting function lift is defined in Figure 6.4. Here, the variables P and Q always stand for letfree formulas. Once again, all unintroduced cases are defined as the identity. Example 6.8 (flattening function). Consider the following nonflat formula: let (σ1 = (let σ1 = emp in σ1 ())) in (let (σ1 = emp) in σ1 ()) We first convert the formula into an equivalent nonconflicting formula by rewriting the predicate names. let (σ1 = (let σ2 = emp in σ2 ())) in (let (σ3 = emp) in σ3 ()) Applying flat gives the following derivation.
124
def
lift(∃x. let Γ in P ) = let Γ in ∃x. P def
lift((let Γ in P ) ∗ Q) = let Γ in (P ∗ Q) def lift(P ∗ (let Γ in Q)) = let Γ in (P ∗ Q) def lift((let Γ1 in P ) ∗ (let Γ2 in Q)) = let Γ1 , Γ2 in (P ∗ Q) def
lift((let Γ in P ) ∨ Q) = let Γ in (P ∨ Q) def lift(P ∨ (let Γ in Q)) = let Γ in (P ∨ Q) def lift((let Γ1 in P ) ∨ (let Γ2 in Q)) = let Γ1 , Γ2 in (P ∨ Q) def
lift(let Γ1 in (let Γ2 in P )) = let lift(Γ1 ), Γ2 in P def lift(let Γ in P ) = let lift(Γ) in P def
lift(Γ1 , Γ2 ) = lift(Γ1 ), lift(Γ2 ) def lift(σ(~x) = let Γ in P ) = Γ, σ(~x) = P Figure 6.4: Rewriting function lift. flat ( let (σ1 = (let σ2 = emp in σ2 ())) in (let (σ3 = emp) in σ3 ()) ) ⇒ lift(let flat(σ1 = (let σ2 = emp in σ2 ())) in flat (let (σ3 = emp) in σ3 ())) ⇒∗ lift(let (σ1 = lift(let (σ2 = emp) in σ2 ())) in lift(let (σ3 = emp) in σ3 ())) ⇒∗ lift(let (σ1 = σ2 (), σ2 = emp) in (let (σ3 = emp) in σ3 ())) ⇒∗ let (σ1 = σ2 (), σ2 = emp, σ3 = emp) in σ3 () The result is a flat formula. Termination of lift is ensured because the only recursion in lift occurs over definitions inside a letexpression. By assumption, the righthand sides of these predicate definitions are flat, so the recursion only needs at most a single iteration on each definition. Termination of the flattening function flat is guaranteed (assuming termination of lift) by the fact that flat simply traverses a formula. It is simple to see from the structure of lift that each application results in a flat formula, under the assumption that all the input formula’s subexpressions are flat. The formulas constructed by flat are guaranteed to be 125
flat by the fact that lift produces flat formulas. It remains for us to prove that flat is sound. Lemma 6.3. Let F be a separation logic formula, h a heap, i a variable interpretation and η a predicate interpretation such that h, i, η = F . Let σ be a predicate name that is unused in F . Then for any value R ∈ P ow(Loc∗ × H) defining a new predicate interpretation η ′ = η[σ 7→ R], it holds that h, i, η ′ = F . Proof. This follows directly from the definition of satisfaction. Extending a predicate interpretation by unused predicate symbols leaves the semantics of the existing symbols unchanged. We now show that the flattening function flat is correct, in the sense that the result of applying flat to a formula P is a semantically equivalent formula flat (P ). To do this, we first show that lift is semanticspreserving. Proposition 6.4. Let F be a conflictfree formula, h a heap, i a variable interpretation and η a predicate interpretation. Then h, i, η = F if and only if h, i, η = lift(F ). Proof. First consider the case where we have letexpression let Γ1 in (let Γ2 in P ) and the set of predicates defined in Γ1 is disjoint from the set defined in Γ2 . This formula is equivalent to let Γ1 , Γ2 in P because the predicate interpretation k2 defined by Γ2 is unaltered by the addition of the predicate interpretation k1 defined from Γ1 . We can use this to show the correctness of most of the other cases. Consider the function lift applied to a separating conjunction, disjunction, or existential quantification. By the predicate interpretation extension result given in Lemma 6.3, we know that the predicate interpretation can be extended without altering the semantics. Because the function is conflictfree, we can promote a single let expression from a subexpression to the surrounding expression without altering the semantics. All of the definitions of lift over these constructs can be composed from letfusion and a single promotion, so all of them are safe. Finally, we need to show that the promotion of rules from the righthand side of a predicate definition to the surrounding set of definitions is safe. This 126
follows from the assumption that formulas are conflictfree, which ensures that the new fixedpoint derived by promoting a definition is the union of the two old fixedpoints. Note that this result applies only to formulas that are conflictfree. Without this assumption, lift can alter the semantics of a formula in two ways. First, a letexpression could be promoted to surround a free variable that is included in the letexpression’s ruleset. Second, two letexpressions that define the same predicate name could be merged, resulting in a new definition for both. We can now prove that any formula can be converted into an equivalent flat formula. Corollary 6.5 (correctness of flat ). Let F be a formula in SL. Then there exists a flat formula F ′ in SLf such that for any heap h, variable interpretation i and predicate interpretation η, h, i, η = F if and only if h, i, η = F ′ . Proof. Any letfree formula F can be converted to the corresponding flat formula let in F , that is by embedding it in a letstatement with no predicate definitions. Nonletfree formulas must be flattened using flat. All formula rewrites performed by flat are applications of lift, so the fact that flat is semanticspreserving for conflictfree formulas follows as a direct consequence of Proposition 6.4. Lemma 6.2 states that for any formula, a corresponding conflictfree formula exists, so flat can always be safely applied, resulting in a flat formula corresponding to F .
6.3
Heapgraphs and mapping between domains
Separationlogic formulas and hyperedgereplacement grammars both define sets of graphlike structures. However, the two approaches operate over different domains. For this reason, to define semanticspreserving mappings between formulas and grammars, we must first define a mapping between the domains of the two approaches. Because our mapping should be semanticspreserving, the mapping between domains must be bijective. To begin with, we identify the two domains. A separation logic formula defines the class of heaps that satisfy the formula. The content of this class is defined by the satisfaction relation given in §6.1.3.
127
A hyperedge replacement grammar defines a class of graphs. We are interested, however, only in those grammars that define classes of socalled heapgraphs. That is, graphs for which there exists a corresponding heap. Intuitively, we model defined locations by nodes, pointsto assertions by Elabelled edges of arity 3, and nil by a unique edge of arity 1 pointing to a node modelling nil. Definition 6.8 (wellsourced). We say that a graph is wellsourced if each node is the source for at most one hyperedge. Definition 6.9 (heapgraph). We say that a hypergraph H is a heapgraph if (1) The label set is {E, nil}, where E has arity 3, and nil arity 1. (2) There exists exactly one nillabelled edge. (3) The graph is wellsourced. We use HG to stand for the set of all heapgraphs. Example 6.9 (heapgraph). The righthand side of Figure 6.5 shows a heapgraph containing four nodes and four edges. We now define α, a bijective mapping from heaps to heapgraphs. As α is bijective, this also defines by implication a bijective inverse α−1 from heapgraphs to heaps. The function α constructs a node for each heap location with a defined value and the atomic value nil. The single nilvalue results in a single node with an attached nillabelled hyperedge. Pointers between locations result in Elabelled hyperedges. Locations that have the value result in nodes which are not the source of any hyperedge.
For example, the following diagram shows a fragment of a heap consisting
of a single location l in a heap h, where h(l) is defined as a pair of locations (a1 , a2 ). It also shows the corresponding heapgraph fragment, consisting of a node with an attached Elabelled hyperedge. α
−→ ...
1 E
α−1
...
←−
2
...
3
...
Definition 6.10 (α). The function α : HE → HG maps from heaps to heapgraphs. Given a heap h, the heapgraph α(h) consists of: (1) A vertex vk for each heap element k ∈ Elem where either k ∈ dom(h), or k = nil. (2) A 128
nil
b 1
nil nil
−→
b 1
2 2
α−1
←−
a
E
3
α
1
E a
c
3
3
2
E
c
1
Figure 6.5: Left: A heap h with three locations. Right: The corresponding heapgraph α(h). hyperedge ek for each heap location k ∈ dom(h) where h(k) = (k1 , k2 ), such that l(ek ) = E, and att(ek ) = vk , vk1 , vk2 . (3) A hyperedge enil such that l(enil ) = nil and att(enil ) = vnil . The mapping α is a bijection (with heaps and graphs considered unique up to isomorphism) because each element of the heap results in sufficient nodes and edges to record its relationship to other elements in the resulting heapgraph. As a result, the heapgraph constructed for a particular heap h is distinct from the heapgraph resulting from any other heap. As α is bijective, the it implicitly defines the inverse bijective mapping α−1 : H → G from graphs to heaps. Example 6.10 (α). The domain of the heap on the lefthand side of Figure 6.5 contains three locations. The resulting heapgraph, shown on the right of Figure 6.5, contains three Elabelled hyperedges, corresponding to locations defined by the heap function, and four nodes. The heap locations are labelled a, b, c, and the corresponding nodes and edges in the graph are identified by dashed regions. The extra node corresponds to the nilelement of the heap.
6.4
Heapgraph grammars and source normalisation
Our correspondence operates between formulas in SL and hyperedge replacement grammars producing only heapgraphs.
129
f
Z
=
1 2
1 2
3
3
1
f
f
⇒
1 nil
2
1 E 2 3
1 E 2 3
2
3
1
3
Figure 6.6: Grammar producing nonheap graphs. Definition 6.11 (heapgraph grammar). We call a grammar G a heapgraph grammars if all the graphs in L(G) are heapgraphs. The set HGG refers to the class of all possible heapgraph grammars. When constructing the grammar that corresponds to a separationlogic formula, we must be careful that we construct a heapgraph grammar. Using the intuitive approach described in §7.1, it is easy to inadvertently construct a grammar with a language containing nonheapgraphs. This is because the simple approach does not restrict the sources of edges. The following example illustrates the problem. Example 6.11 (na¨ıve grammar construction). Consider the following recursive definition. let f (z1 , z2 ) = ((z1 7→ z2 , nil) ∨ (z2 7→ z1 , nil)) in ∃x, y. f (x, y) ∗ f (x, y) A na¨ıve mapping from formulas to grammars translates the single predicate definition into a pair of productions over the symbol f , and the insubexpression to an initial graph. This gives the grammar shown in Fig. 6.6. The language of this grammar includes heapgraphs, such as graph A below, but it also includes nonheapgraphs, such as graph B. A:
B: E
1 2
2 1
3
3
E
E
1 nil
1 2
1 2
3
3 1 nil
130
E
The problem with graph B is that the first attachment points of both Elabelled edges are the same. Clause (3) of the definition of a heapgraph (Def. 6.9) forbids this, because such graphs do not correspond to heaps. The simplest solution (if it worked) would be to define syntactic conditions on rules which ensure that grammars are heapgraph grammars. It is easy to ensure syntactically that the first two clauses of Def. 6.9 are respected by a grammar. The first is just a restriction on the grammar’s terminal labelset, while the second requires the existence of a single nillabelled edge. The third clause, that the graph is wellsourced (see Def. 6.8), is more tricky to ensure. A sequence of derivations violating this requirement may involve many rules, and removing any of the rules may make the grammar a heapgraph grammar. Conformance to this requirement is a property of the whole grammar, rather than a simple syntactic property that can be checked on a rulebyrule basis. We could simply ignore nonheapgraphs in the languages of grammars constructed by the mapping. This seems inelegant however. Instead we show that for any grammar we can construct a corresponding grammar with all nonheapgraphs removed from its language. Definition 6.12 (source normalisation). A grammar H ′ is a source normalisation of grammar H if L(H ′ ) is the set of all wellsourced graphs in L(H). To make use of source normalisation in our mappings, we define a source normalisation operator ⌊−⌋. This takes an input grammar H and constructs a grammar ⌊H⌋ such that ⌊H⌋ is a source normalisation of H. To show that we can implement this operator, we need to show that a source normalisation can be constructed for each input grammar. Given a hyperedgereplacement grammar and an arbitrary property, we cannot necessarily construct a new grammar with a language consisting of the intersection between the two. In [27] it is proved that for any hyperedge replacement grammar H and predicate P expressing a compatible property in the sense of [27], definition 2.6.1, a HR grammar HP can be constructed so that L(HP ) = {g ∈ L(H)  P (g)}.
131
Intuitively, a compatible property is a property that can be decomposed and checked piecewise. To check such a property for a large hypergraph it suffices to examine the smaller component graphs constructed during the derivation of the large graph. That is, suppose we have a nonterminal hypergraph G and a derivation to a terminal graph H. We can first check a compatible property for the component graphs that replaced G’s nonterminal edges, then compose these properties in the initial graph G to check the compatible property for H. Restriction to a compatible property works because any such property can be checked purely by examination of the constituent graphs formed from nonterminal nodes. Consequently the property can be ensured statically by embedding property information into nonterminal symbols. To show that for any grammar H, some source normalisation ⌊H⌋ can be constructed, it suffices to show that membership of the class of wellsourced graphs is a compatible property. We do this by constructing a compatible predicate W S0 such that W S0 (H) holds for graph H if H is wellsourced. We now state formally the definition of a compatible predicate. This definition is taken verbatim from [38], with minor updates to conform to our notation. Definition 6.13 (projection). If we have graphs a derivation R ⇒∗ H and edge e ∈ ER , we use H(e) to stand for the subgraph of graph H resulting from edge e in the derivation. We call this graph the projection of e in H. Definition 6.14 (compatible predicate). Let C be a class of HR grammars, I a finite set, called the index set, P ROP a decidable predicate defined on pairs (H, i), with H ∈ HC , and i ∈ I, and P ROP ′ a decidable predicate on triples (R, assign, i) with R ∈ HC , a mapping assign : ER → I, and i ∈ I. Then P ROP is called (C, P ROP ′ )compatible if for all HRG = hN, T, P, Zi ∈ C and all derivations A• ⇒ R ⇒∗ Hi with A ∈ N ∪ T and H ∈ HT , and for all i ∈ I, P ROP (H, i) holds if and only if there is a mapping assign : ER → I such that P ROP ′ (R, assign, i) holds and P ROP (H(e), assign(e)) holds for all e ∈ ER . A predicate P ROP0 on HC is called Ccompatible if there exist predicates P ROP and P ROP ′ such that P ROP is (C, P ROP ′ )compatible and P ROP0 holds for a graph H if and only if P ROP (H, i0 ) holds for some index i0 . 132
This definition requires the existence of three predicates P ROP , P ROP ′ and P ROP0 . Property information is held using index values. P ROP is the property defined without any auxiliary information recorded for nonterminal nodes. P ROP ′ records auxiliary information for nonterminal edges using the assignment function assign. P ROP0 eliminates the auxiliary information from P ROP by just requiring that P ROP holds for some index. Intuitively, P ROP is called (C, P ROP ′ )compatible if for every derivation R ⇒∗ H, if P ROP holds for H then there exists an assign such that P ROP ′ holds for R, and the information in assign holds for the projections of the edges in H. Example 6.12 (compatible predicate). We show that the property ‘graph contains at most one Xlabelled edge’ is compatible. We make the index set {0, 1}. The predicate CX(H, 1) holds if H contains a single Xlabelled edge, and CX(H, 0) holds if it contains no such edge. For any nonterminal graph R, CX ′ (R, assign, 1) holds if either (1) R contains an Xlabelled edge and assign assigns 0 to all nonterminal edges, or (2) assign assigns 1 to a single nonterminal edge and R contains no Xlabelled edges. CX ′ (R, assign, 0) holds if R contains no Xlabelled edges and assign assigns 0 to all nonterminal edges. CX is CX ′ compatible because for any derivation R ⇒∗ H, if CX(H, 1) holds then the single Xlabelled node must either appear in R, or appear in the projection of one of the nonterminal edges in R. Consequently there must exist an assignment assign such that CX ′ (R, assign, 1) holds. The same holds for CX(H, 0). Let HRG be the class of all hyperedge replacement grammars. We now define predicates W S ′ , W S and W S0 , such that W S0 is HRGcompatible according to Definition 6.14 and W S0 holds if a graph is wellsourced. Definition 6.15 (W S ′ , W S, W S0 ). Let W S ′ be a predicate defined on triples (R, assign, I), with R ∈ HC a hypergraph over labelset C, assign : N → P ow(N) a mapping from nonterminal hyperedges to sets of natural ER
numbers, and I ⊆ N a finite set of natural numbers. The set ass records the configuration of terminal edges which can replace the nonterminal edges, while I records the edges attached to the external nodes of R. W S ′ (R, assign, I) holds if and only if: (1) For each node v ∈ VR there exists at most one edge e ∈ ER such that either l(e) is terminal and 133
att(e)[1] = v, or l(e) is nonterminal and there exists an n ∈ assign(e) such that att(e)[n] = v. (2) For each i ∈ I there exists exactly one edge e such that either l(e) is terminal and s(e) = extR (i), or l(e) is nonterminal and there exists an n ∈ assign(e) such that att(e)[n] = extR (i). The binary predicate W S is defined as W S(H, I) = W S ′ (H, emp, I). The predicate W S0 (H) holds if W S(H, I) holds for some I ⊆ N. Intuitively the predicate W S(H, I) holds when the graph H is wellsourced and terminal, and I records the external nodes that are the sources of edges. W S0 (H) records that the graph is wellsourced, by quantifying over I. Example 6.13 (W S ′ ). Consider the following graph R with terminal edge label E and nonterminal edge label X. Edges have subscripts on their bottom left corner to uniquely identify them as the edges e1 , e2 and e3 . 2 2
1 1 1E 2
E
3
3 1
1 3
2
3
X
3
2
Then W S ′ (R, assign, I) holds, with ass = {e3 7→ {1, 3}} and I = {1, 3}. W S ′ (R, assign, I) holds because every node is either the source of at most one terminal edge, or is attachment point 1 or 3 of the nonterminal node e3 . The assign function records that these attachment points will reduce to the sources of terminal nodes. I records the sources of the external nodes of the graph, including those that will be generated from e3 . Lemma 6.6. Predicate W S0 holds for a graph H if and only if H is wellsourced. Proof. The if direction follows directly from the definition of predicate W S ′ . If graph H is wellsourced, then the first requirement for W S ′ must be satisfied by definition, because every edge is terminal and every node has at most a single Elabelled edge with the node as its first attachment point. The second requirement is automatically satisfied for the same reason; as every node has either zero or one edge with the node as its source, any appropriate I can always be constructed. 134
Now we prove that if H satisfies W S0 , then H must be wellsourced. The condition on terminal edges ensures that the graph satisfies the restriction on Elabelled edge sources. The definition of a heapgraph (Definition 6.9) places no restriction on the external nodes of the graph, meaning that any I for the external nodes will result in a wellsourced graph. Lemma 6.7. The predicate W S0 is a compatible predicate. Proof. According to the Definition 6.14, to show that W S0 is compatible we must show that for any derivation R ⇒∗ H, W S(H, I) holds if and only N → P ow(N) such that W S ′ (R, assign, I) if there is a mapping assign : ER N. and W S(H(e), assign(e)) holds for all e ∈ ER N → The ‘if’ direction is simple. Assume that the mapping assign : ER
P ow(N) exists. If we consider a node n ∈ NH , then there must be at most one attached edge with n as a source. This is because the function assign records the attached edges for the external nodes of the projected graph H(e), so if we replace the nonterminals with the terminal graphs, W S(H, I) must hold. The ‘onlyif’ is slightly more difficult. Assume that W S(H, I) holds. Then there must exist an assignment assign such that W S ′ (R, assign, I) N . Assignment assign can be and W S(H(e), assign(e)) holds for all e ∈ ER
constructed by removing the subgraphs H(e), constructing a set I such that W S(H(e), I) holds, and assigning to assign(e) the set I. the values of I here can be defined by simple examination of the projections H(e) with respect to the function W S. By construction the predicates must then satisfy our requirements. Proposition 6.8. For any hyperedgereplacement grammar H we can construct a grammar ⌊H⌋ that is a sourcenormalisation of H. Proof. In [27] it is proved that given a grammar H ∈ C and a Ccompatible predicate P ROP0 , we can construct a new grammar H ′ such that a graph g is in L(H ′ ) if and only if g ∈ L(H) and P ROP0 (g). We use this result to prove that we can define the sourcenormalisation operator. Lemma 6.7 shows that the predicate W S0 is a compatible predicate, and Lemma 6.6 shows that W S0 (H) holds if and only if H is wellsourced. Therefore, by application of the language restriction result of [27] we can
135
construct a grammar ⌊H⌋ such that a graph g is a member of L(⌊H⌋) if and only if g ∈ L(H) and g is wellsourced. The source normalisation operator works by recording in the nonterminal symbols which external node (or nodes) will receive the source of a terminal edge. Example 6.14 (source normalisation). We apply the source normalisation operator to the nonheapgraph grammar shown in Fig. 6.6. The operator first modifies the nonterminal symbol f , giving cases f1 and f2 . The symbol f1 points the first attachment point of the terminal E to the first external node, while f2 points it at the second external node. The operator then factors the initial graph into two graphs by replacing the two instance of f with f1 and f2 . The fact that the symbol name records the location of the source means that we can ensure statically that the constructed graphs result in wellsourced terminal graphs. The two resulting graphs are isomorphic, so the new grammar has only a single initial graph. The resulting sourcenormalised grammar is as follows.
f1
Z
=
1 2
1 2 f2
3
3
1
f1
⇒
1 nil
1 E 2 3 3
1 2
f2
⇒
2 E 1 3
2
3
This example is quite simple – all of the productions return terminal graphs. However, the same casesplitting and propagation approach can be applied to an arbitrarily complex grammar, resulting in a source normalisation of the grammar.
136
Chapter 7
Mapping between formulas and grammars This chapter defines formally the mappings between formulas and grammars that prove the existence of a correspondence. We show that our mappings are semanticspreserving. The chapter is structured as follows. Section 7.1 describes intuitively the correspondence between formulas and grammars. Section 7.2 defines the mapping g from separation logic formulas to hyperedge replacement grammars. Then in Section 7.3 we prove that g is semanticspreserving. Section 7.4 defines the mapping s from hyperedgereplacement grammars to separation logic formulas. Finally, in Section 7.5 we prove that s is also semanticspreserving.
7.1
Intuitive relationship
A correspondence exists between separation logic formulas and hyperedge replacement grammars because (1) the recursive definitions commonly used in separation logic closely resemble hyperedge replacement productions, and (2) the separating property enforced by separating conjunction corresponds to the contextfree replacement of nonterminal hyperedges. The following example illustrates this correspondence. Example 7.1 (binary tree with shared nilleaf). The following formula defining the class of all binary trees with nillabelled leaves was presented in Example 6.2.
137
let bt(x1 ) = (x1 7→ nil, nil) ∨ (∃x2 , x3 . (x1 7→ x2 , x3 ) ∗ bt(x2 ) ∗ bt(x3 )) in ∃x. bt(x) This formula corresponds to the hyperedge replacement grammar BT = hT, N, Z, P i. This defines the language of binary tree graphs with a shared nillabelled leaf.1 The sets of terminal and nonterminal edge labels are respectively T = {E, nil} and N = {B}. The initial graph Z and set of productions P are:
Z
=
1 B 2
1
B
⇒
1 nil
1 1 E
2
1 2 E 3 3
2
1 B 2
2
1 2 B
The upper node of the initial graph Z corresponds to the address x with which the predicate bt is called. The lower node of Z models the unique value nil by a single node with an attached nillabelled edge. The individual cases of the production defined for label B in the grammar correspond to the two disjuncts defining the predicate bt. The first disjunct corresponds to a terminal branch, and the second a branch and a pair of child trees. Intuitively the elements of hyperedge replacement grammars and separation logic formulas are related as follows: • Productions over a single nonterminal symbol in the grammar correspond to the definition of a single recursivelydefined predicate in separation logic. • Terminal hyperedges in the grammar’s initial graph and in the righthand sides of productions correspond to separation logic’s pointsto assertion (7→). • The atomic value nil is modelled as a single node with an attached nillabelled hyperedge of arity one. 1
The nilleaves are shared because our mapping from graphs to heaps models nil by a single node – see §6.3 for justification.
138
• Nonterminal hyperedges in the grammar correspond to instances of recursivelydefined predicates in separation logic. The attachment nodes of the edges correspond to the arguments passed to the predicate. We now prove that this correspondence exists by defining a pair of mappings and proving that the mappings are semanticspreserving.
7.2
Mapping from formulas to grammars
In this section we define the function g : SLF → HGG that maps from flat separation logic formulas to hyperedgereplacement grammars. This mapping implements formally the intuitive mapping between domains that we described in §7.1.
7.2.1
Basic notions used in the mapping
We first define some extra notions used by the definition. Graphs in the mapping g are constructed by ‘gluing together’ small graphs into larger graphs. These graphs are then used as either the righthand sides of productions or as the members of the initial graphset. To define g we therefore need a definition for this gluing process. In order to define gluing operations, we need to identify the nodes that should be merged. We do this using tags attached to the nodes of a graph. Definition 7.1 (tagged graph). Let F be a countably infinite set of tags. A tagged graph T = hG, ti consists of a hypergraph G and a partial tagging function t : VG → P ow(F ) that associates tags sets to nodes. The tagset for a node v is t(v). The function tag(T ) gives the union of all tagsets in T . A tagged graph is welltagged if the tagsets of the nodes are pairwise disjoint. The tagged graph T \ x is the graph T with the tag x removed from all tagsets. Example 7.2 (tagged graph). We show tags visually as labelsets attached to graph nodes. {x, y, z}
1 X 2
{j, k}
{x, y, z}
139
1 X 2
{x, k}
In the left twonode graph, the first attachment point of the Xlabelled edge has the tagset {x, y, z}, and the second has tagset {j, k}. The lefthand graph is well tagged. The righthand graph is not welltagged, as both nodes are tagged with x. Tags are used by the unify labels and graph join operators to merge graphs. Definition 7.2 (unify tags). Let T = hG, ti, be a tagged graph. A unification step on T is constructed by fusing any pair of nodes v1 and v2 where t(v1 ) ∩ t(v2 ) 6= ∅. The resulting node is tagged with t(v1 ) ∪ t(v2 ). The tag unification of denoted by ⇓ T , is the welltagged graph constructed by the reflexive transitive closure of unification steps. Example 7.3 (unify tags). We begin by giving a tagged graph T that is not welltagged. 1
T =
1
2
1
2
2
a,x}
{c,x}
{b,x}
Applying a single tag unification to this graph gives two possible resulting graphs. 1
2
1
2
{a,b,x}
1
1
2
2
{c,x}
{a,x}
1
1
2
2
{b,c,x}
The tag unification ⇓ T is the graph given below. Here all nodes containing x in their label sets have been merged.
⇓T =
1
1
1
2
2
2
{a,b,c,x}
140
At least one tag unification of a graph is guaranteed to exist because (1) each unification step decreases the size of the graph, and (2) a unification step can be applied to any nonwelltagged graph. The tag unification for a graph is unique because unification steps are confluent. Definition 7.3 (join). The join of two tagged graphs T0 and T1 , denoted by T0 ⊲⊳ T1 , is constructed by combining T0 and T1 by disjoint union, and then constructing the tag unification of the resulting graph. Example 7.4 (join). The following diagram show the result of ⊲⊳ when applied to a pair of graphs with overlapping tag sets. {a,e}
{d,f}
1
1
⊲⊳
{a,e,g} 1 2
2
3
{b,d}
{c}
{a,g}
2
Y
= 2
1
3
{c}
{b,d,f}
The tagging function for a welltagged heapgraph is similar to a variable interpretation for a heap. We define αt as a function mapping a heap h and variable interpretation i to a tagged heap graph hG, ti. Definition 7.4 (αt ). Let h be a heap and i a variable interpretation. Then αt (h, i) is the tagged graph hG, ti. Here G is α(h), and the tagging function t is defined so that for any node v ∈ α(h) and any variable x ∈ Var, x ∈ t(v) if i(x) = l and l is the unique heap location in h mapped to v by α. The function αt is bijective to welltagged graphs, so it defines by implication α−1 t , the inverse function from welltagged graphs to a heap and variable interpretation. Example 7.5 (αt ). We apply αt to the following heap and variable interpretation. h = {l1 7→ hl3 , nili, l2 7→ hl4 , nili, l3 7→ , l4 7→ } i = {x 7→ l1 , y 7→ l2 , z 7→ nil}
The resulting tagged heapgraph αt (h, i) is as follows.
141
{x}
{y}
1
2
3
2
2
3
{z} 1
nil
Tags are used by the expose operator to attach external nodes to a graph. Definition 7.5 (expose). Let T = hH, ti be a welltagged graph, and ~x = x1 , . . . , xn be a sequence of tags such that each xi belongs to tag(T ). Then expose(H, ~x) is identical to H, except (1) it has n external nodes, and (2) the ith external node is the node tagged with xi . Example 7.6 (expose). Left: a welltagged graph T . Right: the untagged graph produced by expose(T, [a, b, c]). {b,d,f}
{a,e,g} 2
7.2.2
1
2
{c}
Y
2 2
1
1
1
3 2
Y
1
Mapping from formulas to grammars
Function g : SLF → HGG maps a single flat separationlogic formula F from the restricted fragment we have defined, to a set of initial graphs Z and a set of productions P . A minimal set N of nonterminal labels and T of terminal labels are inferred from the labels used in the productions and the initial graphs. This suffices to define a complete hyperedge replacement grammar H = hT, N, P, Zi. A corresponding heapgraph grammar can then be constructed by applying the source normalisation operator to this grammar (see §6.4). Figure 7.1 defines g recursively over the structure of a separationlogic formula. g takes as its argument a single letexpression and constructs from it a grammar. Much of the work of the mapping is done by two subsidiary functions: h, which constructs a set of graphs from a formula, and r, which constructs productions. The function h takes as its argument a letfree formula and constructs a set of tagged graphs. It does this by recursing over the structure of the formula. The terminal Elabelled hyperedges are constructed from pointsto assertions, with attached nodes labelled according to the assertion’s variable 142
g[[let Γ in F ]] = hZ, P i where {nil} 1 nil H = h[[F ]] ⊲⊳
g[[F ]] = hZ, ∅i if F is letfree where {nil} 1 nil H = h[[F ]] ⊲⊳
Z = expose(H, {nil})
Z = expose(H, {nil})
P = r[[Γ]]
h[[σ(V1 , . . . , Vn )]] = ⇓H where ... {V1 } {Vn }
h[[∃x. P ]] = {H ′  H ∈ h[[P ]] ∧ H ′ ∼ = H \ x}
n σ n+1
1
H=
h[[emp]] = h empty graph i
{nil} h[[P ∨ Q]] = h[[P ]] ∪ h[[Q]]
h[[P ∗ Q]] = h[[P ]] ⊲⊳ h[[Q]]
h[[x 7→ V1 , V2 ]] = ⇓H where {x} H=
1 E
2 3
{V1 } {V2 }
r[[Γ1 , Γ2 ]] = r[[Γ1 ]] ∪ r[[Γ2 ]] r[[ σ(x1 , . . . , xn ) = P ]] = {(σ, H ′ )  H ∈ h[[P ]] ∧ tag(H) = {x1 , . . . , xn , nil} ∧ H ′ = expose(H, (x1 , . . . , xn , nil)) } Figure 7.1: Mapping g from separation logic formula to hyperedgereplacement grammar.
143
arguments. Nonterminal hyperedges are constructed from calls to predicates. The attachment points of such a nonterminal edge correspond to the variable arguments, with the addition of a niltagged node. This attachment point to nil is needed to keep track of a common nilnode for the whole graph. In both cases, for terminal and nonterminal hyperedges, after generating the edge the tag unification operator ⇓ is applied, merging all nodes that have the same label. This ensures that for any variable name x, only a single node exists with the tag x in its variableset. The disjunction operator ∨ merges by union the graphsets constructed for each subexpression of the formula. Intuitively, a heap satisfies the disjunction if it satisfies either the lefthand subexpression or righthand subexpression, so setunion models the intuitive semantics. The separating conjunction ∗ constructs a new set of graphs by applying the graphjoin operator ⊲⊳ pairwise to the members of the two sets constructed from its subexpressions. Because the tagset for newly constructed tagged graph corresponds to sets of variables, this merge fuses any nodes that are pointed to by the same variable, which results in a correctlyjoined set of graphs. The function r is called by g to handle recursive predicate definitions. The righthand sides of the productions are constructed by applying the h function to the righthand sides of the predicate definitions, and then applying the expose operator to attach the appropriate external nodes. Tags are removed from graphs by the expose operator. The natural way to model a let formula is as a set of rules defined from predicate definitions applied to a set of graphs defined by the let body. We noted in Chapter 2, p. 31 that we have modified the standard definition of a hyperedgereplacement grammar (as given in for example [27]) to use an initial set of graphs, rather than the more conventional single initial graph. The reason for this redefinition was to simplify the definition of s. As noted in Chapter 2, we could preserve the standard definition by introducing extra rules to allow a grammar with a single initial graph. We have decided however that it is simpler to alter the basic grammar definition. Example 7.7. We can demonstrate the application of g by taking the following small formula for a linked list: Consider the following small formula, which asserts that the heap con
144
tains a linked list. let ll(x1 ) = (x1 7→ nil, nil) ∨ (∃x2 . (x1 7→ nil, x2 ) ∗ ll(x2 )) in ∃x. ll(x) Applying g to the two subexpressions (x1 7→ nil, x2 ) and ll(x2 ) gives the following pair of singleelement graph sets. ( {x1 }
h[[x1 7→ nil, x2 ]] =
h[[ll(x2 )]] =
n
{x2 }
)
{nil}
1 E 2 3
{x2 }
{nil}
1 ll 2
o
The fusion of these two graphs gives the following single graph. Note that the label x2 has been removed as per the mapping of existentially quantified variables. h[[∃x2 . (x1 7→ nil, x2 ) ∗ ll(x2 )]] =
{x1 }
1 E 3
2
1 ll 2
{nil}
When combined with the other disjunct in the predicate definition, this results in the following production definitions. 1 1
3
ll ⇒ 2
1 E
1 E 2
1 ll 2
2 3
3 2
When combined with an initial graph constructed from ∃x. ll(x), these productions give a grammar corresponding to the formula. Example 7.8. Figure 7.2 and Figure 7.3 show a complete derivation of a graph grammar from the binary tree predicate given in Example 6.2. This derivation shows the intermediate subgraphs constructed by the function while building the grammar. ZF is used to refer to sets of initial graphs for the constructed grammar, and PΓ to refer to sets of constructed productions.
7.3
Proving the correctness of mapping g
We now prove that the mapping g from formulas in SLF to heapgraph grammars is correct with respect to the semantics of separation logic and hyperedgereplacement, and of α, the mapping from heaps to heapgraphs. 145
146
g[[ ∃x. bt(x) ]] ⊲⊳
{nil}
n , {nil} = 1 nil
1
1 bt
2
bt
2
1 nil
1 nil
o
, bt ⇒ 1
bt 2 2
1 2 bt
1 2 E 3
1 1
2
2 3
bt
1
G2 = g
s
n {x1 }
∃x2 , x3 . (x1 7→ x2 , x3 ) ∗ bt(x2 ) ∗ bt(x3 )
G1 = g[[(x1 7→ nil, nil)]] =
{
2 3
{nil}
o
= G3 \ {x2 , x3 } =
1 bt
} (x1 7→ nil, nil) ∨ G = g v (∃x2 , x3 . (x1 7→ x2 , x3 ) ~ = G1 ∪ G2 ∗ bt(x2 ) ∗ bt(x3 ))
u
1 2 bt
{nil}
bt 2
1
1 2 E 3
{x1 }
} bt(x1 ) = (x1 7→ nil, nil) ∨ PΓ = r v (∃x2 , x3 . (x1 7→ x2 , x3 ) ~ = {(σ, g′ )  g ∈ G ∧ labv (g) = {x, nil} ∧ g′ = expose(g, (x, nil))} ∗ bt(x2 ) ∗ bt(x3 ))
u
ZF = expose
where:
let bt(x1 ) = (x1 7→ nil, nil) ∨ (∃x2 , x3 . (x1 7→ x2 , x3 ) ∗ bt(x2 ) ∗ bt(x3 )) ~ = hZF , PΓ i = in ∃x. bt(x)
}
Figure 7.2: Transforming the binary tree predicate from Example 6.2 into the corresponding hyperedge replacement grammar.
gv
u
G3 = g[[(x1 7→ x2 , x3 ) ∗ bt(x2 ) ∗ bt(x3 )]] = G4 ⊲⊳ G5 G4 = g[[(x1 7→ x2 , x3 )]] =
{x1 }
1 E
G5 = g[[bt(x2 ) ∗ bt(x3 )]] = G6 ⊲⊳ G7 =
G6 = g[[bt(x2 )]] =
n
G7 = g[[bt(x3 )]] =
n
{x2 }
2 3
{x3 }
{x2 }
1
{x2 }
1 bt
2
{nil}
{x3 }
1 bt
2
{nil}
bt 2
o
{x3 } 1 2 bt
{nil}
o
Figure 7.3: Transforming the binary tree predicate from Example 6.2 into the corresponding hyperedge replacement grammar (cont). We first define our requirement for correctness. For the mapping to be correct, if we map a separation logic formula to its corresponding grammar using g and construct the language for the grammar, we get the same set of graphs as if we construct the satisfying set of heaps, and mapped the set of heaps to graphs using α. In other words, the mapping g must be semanticspreserving. Formally we say that the mapping g between separation logic formulas and graph grammars is correct if for all formulas F ∈ SLF and heaps h, h, i0 , η0 = F if and only if α(h) ∈ L(g[[F ]]). This correctness requirement can be visualised as the requirement that the following square commutes. HGG
L(−)
=
g[[−]] SLF
=
P ow(HG) α(−) P ow(HE)
Our strategy for proving correctness is as follows: 1. We define the notion of a graph environment and define a correspondence between graph environments and predicate interpretations (Definition 7.7 and Definition 7.8).
147
2. We show that a letfree formula F evaluated in a given predicate interpretation corresponds to the set of graphs h[[F ]] evaluated in the corresponding graph environment (Proposition 7.3 and Theorem 7.4). 3. We show that the predicate interpretation defined by a flat formula F corresponds to the graph environment defined by the productions in g[[F ]] (Lemma 7.5). 4. We apply (2) and (3) to prove that g is correct (Theorem 7.6). We have found in practice that plain sets productions (as defined in §2.3) are unwieldy to reason about, especially when proving results about fixedpoints and letformulas. Instead we define the notion of a graph environment that records the set of terminal graphs for each nonterminal edge. We also define a notion of evaluation in a graph environment, which substitutes all nonterminal edges with some corresponding terminal graph. Definition 7.6 (graph environment, graph evaluation2 ). Let N be a set of nonterminal symbols. A graph environment L : N → P ow(HC ) over N maps each nonterminal symbol to a (possibly infinite) set of terminal graphs with the same number of external nodes. A graph H is evaluated in a graph environment L, denoted κ(L, H), by replacing all of the nonterminal edges in H labelled σ ∈ dom(L) with some terminal graph r ∈ L(σ). This gives the set of graphs N → H ]  repl(e) ∈ L(l (e)) for e ∈ E N . κ(L, H) = H[repl : EH T E H
Example 7.9 (graph environment, graph evaluation). The graph environment L maps the single nonterminal symbol X to two possible terminal heapgraphs.
L(X) =
2
1
1 1
1
2
, 2
2
3
2
Let H be the following nonterminal graph.
3
1
3
2 This definition of a graph environment is modified from the discussion of hyperedge replacement and fixedpoints in [27].
148
1
2
2
1
X 3
Evaluating H in environment L (written κ(L, H)) results in the following pair of distinct terminal graphs.
3
1
2
2
1
2 3
3
3
1
1
2
2
1
3
We also define an alternative formulation for the productions of a graph grammar, more suitable for use in fixedpoint calculations. This definition is taken from [38]. Definition 7.7 (equation system). Let H = hT, N, P, Zi be a hyperedge replacement grammar. The equation system EQP associated with H is a function EQP : N → P ow(HN ∪T ), defined as EQP (A) = {R  (A, R) ∈ P }. We say that a graph environment L is the fixedpoint of an equation system if L(A) = κ(L, EQP (A)) for all symbols in A ∈ dom(EQP ). If an environment L is such a least fixedpoint for a set of productions P , we say that L is the environment defined by P . Example 7.10 (equation system, least fixedpoint). The following equation system EQ assigns a pair of graphs to the nonterminal symbol X. EQ(X) = {
1
2 3
1
X
,
1
2 3
1
}
The fixedpoint L for this equation system maps X to the infinite family of graphs of the following form (as well as the small graphs of size 1, 2 etc.). 1
2 3
1
2 3
...
1
2 3
In [27], it is proved that the least fixedpoint of the equation system associated with H is the language family defined by H. That is, suppose we have a grammar H = hT, N, P, Zi. The language family defined for H is LH , and the language of H can be constructed by applying the language family to the set of initial graphs, κ(LH , Z). Let EQP be the equation system defined by P , and LP the least fixedpoint of the equation system. Then 149
it must be true that LP = LH , and consequently κ(LP , Z) also defines the language of H. The consequence of this result is that we can use the fixedpoint for a grammar’s equation system to reason about the language defined by the grammar. The semantics of a separation logic letstatement is defined in terms of a least fixedpoint, and we later show that formulas and grammars correspond by showing that the fixedpoint of a letstatement corresponds to the fixedpoint of a equation system. Graph environments and predicate interpretations play the same role in their respective domains: that of defining for a ‘symbol’ (either a nonterminal or predicate) a class of structures for which the symbol can stand. Both also define attachment points that are used to replace the nonterminal in the structure with a corresponding terminal structure. It therefore makes sense to define a notion of correspondence between them that holds when the sets of structures for each symbol are the same, modulo α. Definition 7.8 (correspondence). Let graph H be a heapgraph with n external nodes. Let h be a heap and ~x = x1 , . . . , xn be a sequence of locations. We say that H corresponds to the pair (~x, h) if applying the mapping function α(h) results in H and the heaplocation xi is mapped to the ith external node for i = 1 . . . n. A predicate interpretation η corresponds to a graph environment L if: (1) dom(η) = dom(L). (2) For pair (~x, h) ∈ η(s) there exists a corresponding graph H ∈ L(s). (3) For every graph H ∈ L(s) there exists a corresponding pair (~x, h) ∈ η(s). Example 7.11 (correspondence). The following graph environment L just maps the symbol X to a single terminal graph with two external nodes. L = {X 7→ { 1
2 3
1
1
2 3
2 }}
The following predicate interpretation η corresponds to L. η = {X 7→ {h[l1 , l3 ], {l1 7→ hl2 , l2 i, l2 7→ hl3 , l3 i}i}} In this correspondence location l1 corresponds to external node 1, and location l3 corresponds to external node 2. We now prove some subsidiary results needed for the main proof of correctness. 150
Lemma 7.1. Let L be a graph environment and G1 , G2 and G3 be tagged graphs. Then G3 ∈ κ(L, G1 ⊲⊳ G2 ) iff G3 ∈ κ(L, G1 ) ⊲⊳ κ(L, G2 ). Proof. The tagged graphs G1 and G2 are joined by ⊲⊳ by merging nodes, meaning that the new graph G1 ⊲⊳ G2 contains the same set of nonterminal edges as the two original graphs. If we consider an individual node or edge in G1 or G2 we can see that the same connections between nodes must be constructable by either merging first and then applying the environment, or the other way round. Lemma 7.2 (decomposing heapgraphs). Let L be a graph environment, and let G1 and G2 be sets of tagged heapgraphs. Let h be a heap and i a variable interpretation. Heapgraph αt (h, i) is a member of set κ(L, G1 ) ⊲⊳ κ(L, G2 ) if and only if there exist heaps h0 ,h1 and interpretations i0 ,i1 such that h = h0 · h1 , and i = i0 ∪ i1 , and αt (h0 , i0 ) is a member of κ(L, G1 ) and αt (h1 , i1 ) is a member of κ(L, G2 ). Proof. Assume that αt (h, i) is a member of κ(L, G1 ) ⊲⊳ κ(L, G2 ). By the definition of ⊲⊳ we can pick graphs g0 ∈ G1 and g1 ∈ G2 such that αt (h, i) = κ(L, g0 ) ⊲⊳ κ(L, g1 ). Each defined location in the heap must be mapped to a node in either κ(L, g0 ) or κ(L, g1 ), or both. We must be therefore able to construct heaps h0 ,h1 and interpretations i0 ,i1 by splitting h and i, such that such that h = h0 · h1 , and i = i0 ∪ i1 , and αt (h0 , i0 ) is a member of κ(L, G1 ) and αt (h1 , i1 ) is a member of κ(L, G2 ). The converse result follows immediately from the definition of ⊲⊳ and α. We now state a correspondence result for the mapping h over letfree formulas. This result forms the basis of our correctness claim for letfree formulas. Proposition 7.3 (κ and letfree formulas). Let formula F ∈ SL be a letfree separationlogic formula, and s a heap, i a variable interpretation and η a predicate interpretation. Let L be a graph environment corresponding to η. Then s, i, η = F if and only if αt (s, i) ∈ κ(L, h[[F ]]). Proof. The proof proceeds by structural induction on the structure of formula F . Note that the use of the mapping αt restricts these results to the domain of heapgraphs. Later in the chapter we give a result for the language defined by a grammar using the normalisation operator. 151
emp Formula emp is satisfied by the empty heap, and h[[emp]] results in the empty graph. Therefore trivially s, i, η = emp if and only if αt (s, i) ∈ κ(L, h[[emp]]). x 7→ V1 , V2 We can see that s, i, η = x 7→ V1 , V2 if and only if αt (s, i) ∈ κ(L, h[[x 7→ V1 , V2 ]]). After applying αt to s and i, we must have a graph with a single terminal hyperedge and three nodes. The label unification ⇓ ensures that vertices which are assigned the same logical variable are correctly merged. Simple enumeration of all possible instances then shows that all cases are correct. σ(V1 , . . . , Vn) If: Assume s, i, η = σ(V1 , . . . , Vn ). Therefore it must be true that the pair (([[V1 ]]s, . . . , [[Vn ]]s), h) is in η(σ). Then in the heap s and interpretation i, V1 must point to the first value in [[V1 ]]i, and so on for V2 . . . Vn . By the definition of h the singleton set h[[σ(V1 , . . . , Vn )]] must consist of a graph with only a single σlabelled hyperedge. Let L be a graph environment that corresponds to η. Then by the correspondence of η and L, it must be true that αt (s, i) ∈ κ(L, h[[σ(V1 , . . . , Vn )]]). Only if: Assume αt (s, i) ∈ κ(L, h[[σ(V1 , . . . , Vn )]]). By the same argument used in the ‘if’ case, it must be true that s, i, η = σ(V1 , . . . , Vn ). P ∨ Q Consequence of the semantic equivalence between disjunction and union. P ∗Q If: Assume s, i, η = P ∗ Q. Therefore there must exist interpretations i0 , i1 and heaps s0 , s1 such that s0 · s1 = s and s0 , i0 , η = P and s1 , i1 , η = Q. By the inductive assumption αt (s0 , i0 ) ∈ κ(L, h[[P ]]). The same holds for h[[Q]]. Applying h[[P ∗ Q]] gives h[[P ]] ⊲⊳ h[[Q]]. From Lemma 7.2 we know that if αt (s0 , i0 ) ∈ κ(L, h[[P ]]) and αt (s1 , i1 ) ∈ κ(L, h[[Q]]) and s0 · s1 is defined, then αt (s0 · s1 , i0 ∪ i1 ) ∈ κ(L, h[[P ]]) ⊲⊳ κ(L, h[[Q]]). By Lemma 7.1, αt (s0 · s1 , i0 ∪ i1 ) ∈ κ(L, h[[P ]] ⊲⊳ h[[Q]]). Only if: Assume αt (s, i) ∈ κ(L, h[[P ]] ⊲⊳ h[[Q]]) and αt (s, i) is a heapgraph. From Lemma 7.1 and Lemma 7.2 we conclude there exist variable interpretations i0 , i1 and heaps s0 , s1 such that αt (s, i) = αt (s0 · s1 , i0 ∪ i1 ) and 152
αt (s0 , i0 ) ∈ κ(L, h[[P ]]) and αt (s1 , i1 ) ∈ κ(L, h[[Q]]). By the inductive assumption, we have s0 , i0 , η = P and similarly s1 , i1 , η = Q. We therefore conclude that s0 · s1 , i0 ∪ i1 , η = P ∗ Q. ∃x. P Consequence of the semantic correspondence between variables and tags in αt . The normalisation operator ⌊−⌋ is applied to the result of g to ensure that the class of graphs only includes heapgraphs. The result of Prop. 7.3 applies only to g applied in the presence of the evaluation function κ, but it can be used to prove the correctness of g over letfree formulas. Theorem 7.4 (correctness of g over letfree formulas). Let formula F ∈ SL be a letfree separation logic formula, and let h be a heap. Then h, i0 , η0 = F if and only if α(h) ∈ L(⌊g[[F ]]⌋). Also for graph H with label alphabet {E, nil}, if H ∈ L(⌊g[[F ]]⌋) then H is a heapgraph. Proof. All graphs in the language L(⌊g[[F ]]⌋) must be heapgraphs by the definition of the normalisation operator. Let hZ, P i = ⌊g[[F ]]⌋, and let L be the graph environment defined by P . By the definition of a graph environment, we know that L(⌊g[[F ]]⌋) = κ(L, Z). As F is letfree, by the definition of g it must be true that P = ∅. Therefore L must be the empty graph environment, where for all nonterminal symbols σ, L(σ) = ∅. This means that L corresponds to the empty predicate interpretation η0 . Therefore, by the result of Proposition 7.3 and the correctness of normalisation, h, i, η0 = F if and only if αt (h, i) ∈ κ(L, Z), which holds if and only if αt (h, i) ∈ L(⌊g[[F ]]⌋). We can extend this result further to prove the correctness of g for formulas of the form let Γ in P , where both Γ and P are letfree. We begin by proving a lemma showing that the predicate interpretation defined by a let corresponds to the environment defined by the corresponding set of productions constructed by g. Recall that the semantics of let given in §6.1.3 makes use of the following if and only if expression: h, i, η = let Γ in F
iff h, i, η [β 7→ k(β)]β∈dom(Γ) = F where k = fix fη,Γ 153
As discussed previously, the new predicate interpretation η[β 7→ k(β)] is defined over the symbols in dom(Γ) by the predicate interpretation k, which is the leastfixedpoint of the following function. fη,Γ = λk0 . λσ ∈ dom(Γ). bind (σ(~x) = Q) to Γ(σ) in {(~l, h)  h, ~x 7→ ~l , η β 7→ k0 (β) β∈dom(Γ) = Q}
We use this definition to state our lemma about the correspondence of graph environments and predicate interpretations. Lemma 7.5 (environmental correspondence for let). Let let Γ in F be a flat formula. The least fixedpoint of fη0 ,Γ corresponds to the graph environment L defined by the set of rules r[[Γ]]. Proof. Let k be the least fixedpoint of fη0 ,Γ . That is, k the least predicate interpretation such that k = fη0 ,Γ (k, σ) for all symbols σ ∈ dom(Γ). As stated earlier in the section, a graph environment L is defined by a set of rules P if L is the least environment satisfying L(σ) = κ(L, EQΓ (σ)) for any symbol σ ∈ dom(EQΓ ), where EQΓ is the equation system defined by P . Let ki be a predicate interpretation, and Li be a corresponding graph environment. Now let kj (σ) = fη0 ,Γ (ki , σ), and let Lj (σ) = κ(Li , EQΓ (σ)) for all σ ∈ dom(Γ). To show our result we need to show that kj corresponds to Lj . Let us look at the definitions of these two functions for a particular symbol σ. First consider kj , which is defined by instantiating the definition of the fixedpoint function. In the following equation, the expression η β 7→ ki (β) β∈dom(Γ) is replaced by ki because the surrounding predicate interpretation is η0 .
kj (σ) = bind (σ(~x) = Q) to Γ(σ) in {~v , h)  (h, [~x 7→ ~v ], ki = Q} The graph environment Lj is defined by the following application of κ(Li , −), taken from the definition of a graph environment. Lj (σ) = κ(L, EQΓ (σ)) From the result of Proposition 7.3, we know that a heap s, variable interpretation i and predicate interpretation η satisfy the formula Q if and 154
only if the graph αt (s, i) is a member of the class of graphs κ(L, h[[Q]]), where L is a graph environment corresponding to η. We now have to show that a pair (~v , h) is a result of the function if and only if the corresponding graph G is a member of the set of graphs constructed from g[[Q]]. A pair (~v , h) is a member of the set if ([~x 7→ ~v ], h), ki = Q. By Prop 7.3, this set of pairs corresponds to the set of graphs g′ such that there exists a g ∈ κ(Li , Q) where var(g) = ~x, nil and g is exposed to g′ with {~x, nil}. This set of graphs is equal to the set of graphs defined by applying κ(L, −) to the righthand sides in EQΓ (σ). Consequently the set of pairs in kj (σ) corresponds to the set of graphs in Lj (σ). As a result, any predicate interpretation k that is a fixed point of the function fη0 ,Γ must correspond to an environment L that is a fixedpoint of κ applied to the equations in EQΓ . The correspondence between predicate interpretation and graph environments is monotone (that is, if k1 ≤ k2 and k1 corresponds to L1 and k2 corresponds to L2 then L1 ≤ L2 , and vice versa), so the least fixedpoint k0 corresponds to the least fixedpoint L0 . This proves our result. We now use this lemma to prove that g is correct when applied to a letformula with letfree subarguments. Theorem 7.6. Let formula F ′ = let Γ in F be a flat separation logic formula. Let h be a heap. Then h, i0 , η0 = F ′ if and only if α(h) ∈ L(g[[F ′ ]]). Proof. A heap h, variable interpretation i and predicate interpretation η satisfies the formula if the heap satisfies formula F ′ in predicate interpretation ηk , formed by redefining η(σ) to k(σ) for all σ ∈ dom(k). We have shown in lemma 7.5 that the k defined by the forcing relation from Γ in environment η0 must correspond to the graph environment L defined by the set of rules r[[Γ]]. In the case of formula F ′ we have that η = η0 , so ηk = k. Because F is letfree, we know that in the pair hZF , PΓ i = g[[F ′ ]], PΓ = ∅. Because F is letfree, we know that in the pair hZ ′ , PF i = g[[F ]], PF = ∅ and Z ′ = ZF . So a graph is in L(g[[F ]]) if and only if it is in κ(L, ZF ). We have shown in Proposition 7.3 that for any letfree formula K and pair hZ, P i = g[[K]], and corresponding predicate interpretation η and graph environment L, h, i, η = K if and only if αt (h, i) ∈ κ(L, Z). Applying these results gives us the following logical expression that holds for any heap h. 155
h, i0 , η0 = F ′
⇐⇒
h, i0 , k = P
⇐⇒
α(h) ∈ κ(L, ZF ′ ) (equivalence of k and L)
⇐⇒
α(h) ∈ L(g[[F ′ ]])
(semantics of let) (P is letfree)
This completes the proof. We have only proved this result for the class of flat formulas in our fragment of separation logic. However, we have shown in §6.2 that any formula in our fragment can be transformed into a corresponding flat formula, so by composing the flattening function flat with g we can construct the mapping g ◦ flat from any formula in the fragment to a semantically equivalent graph grammar.
7.4
Mapping from grammars to formulas
We now define the function s : HRH → SLF, which maps a pair hZ, P i consisting of a set of initial graphs Z and set of productions P to a recursive letstatement let ΓP in FZ . The definition of s is given in Figure 7.4. The definition of s uses the subsidiary functions sq , sG , sg and se , which operate over productions, sets of graphs, graphs and edges respectively. s calls sq to construct the predicate definitions ΓP and sG to construct the letbody FZ . The mapping needs to associate each node with a particular variable name (or nil). To do this, we assume that every initial graph and production righthand side G in an input grammar has been replaced with a corresponding welltagged graph hG, tG i. For each graph G, we assume that tG is an arbitrary but fixed injective tagging function satisfying the following formula. Here n is the number of external nodes in G. if v ∈ extG and posG (v) = i. {xi } t(v) = {nil} if the niledge is attached to v. {r} s.t. r ∈ V ar \ {x . . . x , nil} otherwise. 1 n
The tagging function associates internal nodes with arbitrary variable
names, while the ith external node of a graph is given the fixed tag ‘xi ’. The nil node is tagged with nil.
156
def
s[[hZ, P i]] = let sq [[EQP ]] in sG [[Z]]
def
sG [[{g1 , . . . , gm }]] = sg [[g1 ]] ∨ . . . ∨ sg [[gm ]]
def
sg [[hg, ti]] = ∃y1 . . . ∃ym . se [[e1 , t]] ∗ . . . ∗ se [[en , t]] ∗ emp where {e1 , . . . , en } = {e ∈ Eg  l(e) 6= nil} {y1 , . . . , ym } = {t(v)  v ∈ Vg ∧ v ∈ / extg ∧ t(v) 6= nil}
def
se [[e, t]] =
t(v) 7→ t(v0 ), t(v1 ) where att(e) = (v, v0 , v1 )
σ(t(v1 ), . . . , t(vn )) where σ = lE (e) att(e) = (v1 , . . . , vn )
if lE (e) = E
if lE (e) ∈ N
def
sq [[EQP ]] = σ(x1 , . . . , xn , nil) = sG [[EQP (σ)]], sq [[EQP \ σ]] where n = ari(σ) Figure 7.4: Mapping from heapgraph grammars to separation logic formulas. The function sG maps a set of tagged heapgraphs to a disjunction of formulas, each of which correspond to a single graph. These single graphs are constructed by the function sg , which takes as its input a tagged graph and constructs a separating conjunction, with each conjunct corresponding to a single graph edge. The conjunction for a graph is wrapped in existential quantifications that quantify out variables corresponding to internal nodes. Individual conjuncts for a graph are constructed by the function se . Terminally labelled Eedges result in pointsto assertions, while nonterminal edges result in calls to recursivelydefined predicates. The arguments to pointto assertions and predicate calls are recovered from the tagging function. The function sq takes the equation system EQP corresponding to a set
157
of productions P (see Def. 7.7). It constructs the predicate definitions for a letstatement. An equation system is used rather than the original set of productions because sq needs to recover all the righthand sides for a single nonterminal symbol, something that is easier to specify in an equation system. sq constructs a single recursive definition for each nonterminal symbol in P . The set of righthand side graphs for a single symbol are mapped to a righthand side formula, using the function sG for sets of graphs. The arguments to the predicate are given x1 , . . . , xn , which by the definition of the tagging function form the free variables of the righthand side formula. Figure 7.5 shows the derivation of a separationlogic formula from the binary tree predicate given in Example 7.1. We omit the tagging function, as the mappings from nodes to variables should be obvious. As with Figure 7.2, to fit the example on the page, arrows are used to indicate the values of some intermediate variables. This example shows the derivation of individual elements in the formula. sg constructs the letstatement body ∃x. bt(x, nil) from the single initial graph. For the righthand side of the definition of bt, sq constructs the two disjuncts ∃y1 , y2 . x1 7→ y1 , y2 ∗bt(y1 , x2 )∗bt(y2 , x2 ) and (x1 7→ x2 , x2 ) from the two production righthand sides. The derivation shows these elements being glued together to form the final formula, which is identical to the one in example 6.2 (modulo bound variable renaming).
7.5
Proving the correctness of mapping s
In this section we prove that the mapping s, as defined in the previous section, is correct. For s to be correct, it must be true that for all hyperedgereplacement grammars H = hT, N, P, Zi and heapgraph G, G ∈ L(H) if and only if α−1 (G), i0 , η0 = s[[hZ, P i]]. This can be visualised as a the requirement that the following square commutes: L(−) P ow(HG) HGG s[[−]] SL
= =
158
α−1 (−) P ow(HE)
159
1
1 nil
u
r
,
u
r
w w F2 = sg w v
F1 = sg
w w Γ = sq w v
F = sg
where:
2
bt
2
2
1 2 bt
1 2E 3
1
2 3
1 bt 2 2
z
z
w w = bt(x , x ) = s 1 2 Gw v ~
}
= (x1 7→ x2 , x2 )
1 bt 2 3 2
1
u 1 bt 2 2
1 2 bt
1 2E 3
1
,
1 bt 2 3 2
1
= F1 ∨ F2 ~
}
let bt(x1 ) = (x1 7→ nil, nil) ∨ (∃x2 , x3 . (x1 7→ x2 , x3 ) ∗ bt(x2 ) ∗ bt(x3 )) = let Γ in F = ~ in ∃x. bt(x)
}
= ∃x. bt(x, nil)
1 bt 2 3 2
1
= ∃y1 , y2 . x1 7→ y1 , y2 ∗ bt(y1 , x2 ) ∗ bt(y2 , x2 ) ~
}
2
1 2 bt
1 2E 3
1
1 nil
2
1 2 bt
1 2 E 3 1 bt 2
1 E
1 bt 2
1
bt ⇒
1 bt
bt ⇒
1
Figure 7.5: Transforming the binary tree grammar from Example 7.1 into a corresponding separation logic formula.
w w s w v
u
We prove our correctness result incrementally beginning with single graphs, moving to sets of graphs, and then finally proving the result for full grammars. Because the nonterminal symbols and predicates must be modelled in the proof of correctness, our results are proved with the addition of graph environments and corresponding predicate interpretations (see Definition 7.8). We first prove that s is correct for single graphs (Lemma 7.7), then sets of graphs (Lemma 7.8). We then apply this result to prove that the letformula defined by s from a grammar H defines a predicate interpretation corresponding to the graph environment defined by H (Lemma 7.9). Finally we apply the previous results to prove the overall correctness of g (Theorem 7.10). In the following we use the function α−1 t (Def. 6.10) to map from a tagged heapgraph g to a pair (h, i) consisting of a heap h and a variable interpretation i. To shorten proofs involving α−1 t , we sometimes write (h, i), η = F to stand for h, i, η = F . Lemma 7.7. Let G and H be tagged heapgraphs with n external nodes. Let L be a graph environment and η a predicate interpretation corresponding to L. Then H ∈ κ(L, G) if and only if h, i′ , η = sg [[G]], where (h, i) = α−1 t (H) ′ and i is i restricted to the domain {x1 , . . . , xn }. Proof. We first show for graphs G and H where all nodes are external that H ∈ κ(L, G) if and only if α−1 t (H), η = sg [[G]]. The proof works by induction on the number of hyperedges in the graph G. In the base case there is only a single hyperedge in the graph. There are two possible cases for a heapgraph G1 containing a single hyperedge: either the hyperedge can be an Elabelled terminal hyperedge or a nonterminal hyperedge with some label σ. If the graph G1 contains a terminal edge, then κ(L, −) performs no substitution and so H ∈ κ(L, G) if H = G. The resulting expression sg [[G1 ]] −1 consists of x1 7→ x2 , x3 . By the semantics of α−1 t , αt (H), η = sg [[G]].
If the graph consists of a nonterminal edge labelled σ, then the resulting expression sg [[G1 ]] will consist of some predicate σ(x1 , . . . , xn , nil). By assumption, the environments η and L correspond. Therefore κ(L, −) must replace the σlabelled hyperedge with a heap subgraph corresponding to one of the heaps in η(σ). As a result, the heap α−1 t (H), η = sg [[G]]. 160
Now consider an arbitrary heapgraph Gn with n hyperedges. Gn can be decomposed into two smaller tagged graphs G′ and G′′ such that Gn = G′ ⊲⊳ ′ G′′ . By the inductive hypothesis, H ′ ∈ κ(L, G′ ) iff α−1 t (H), η = sg [[G ]], and the same for G′′ . We have shown in Lemma 7.1 that κ distributes over ⊲⊳, so κ(L, Gn ) = κ(L, G′ ⊲⊳ G′′ ) = κ(L, G′ ) ⊲⊳ κ(L, G′′ ). Because the two graphs are fullylabelled, H ′ and H ′′ must result in pairs of heaps and variable interpretations −1 ′′ ′ α−1 t (H ) and αt (H ) where the heaps have disjoint domains. separating
conjunction is both associative and commutative so the formula sg [[G]] can be rewritten in the form sg [[G′ ]] ∗ sg [[G′′ ]]. By the semantics of conjunction, a state satisfies P ∗Q if it can be decomposed into two disjoint states satisfying P and Q. By the inductive assumption, this holds, so α−1 t (H) ∈ sg [[G]]. We now want to extend this result to graphs with internal nodes. We extend it by incrementally removing external nodes. Proof proceeds by induction over the number of internal nodes in the graph. By the semantics of existential quantification, removing a node from the sequence of external nodes results in the existential quantification of the corresponding variable name. The order of node removal is immaterial because existential quantification is commutative. Any graph can be constructed by removing external nodes, so this completes the proof. This lemma for single graphs is now extended to sets of graphs, which are used to form the initial element of the grammar and form the basis of production definitions. Lemma 7.8. Let G be a set of heapgraphs and H a single heapgraph. Let L be a graph environment and η a corresponding separation logic environment. Then H ∈ κ(L, G) if and only if α−1 t (H), η = s[[G]]. Proof. First assume that H ∈ κ(L, G). There must be some graph G′ ∈ G ′ such that H ∈ κ(L, G′ ). By application of Lemma 7.7, α−1 t (H) = s[[G ]]. As
s[[G′ ]] must be an argument of the disjunction s[[G]] we conclude α−1 t (H) = s[[G]]. Now assume that h, i, η = s[[G]] (as αt is bijective, we can use a pair h and i in place of the corresponding heapgraph). Formula s[[G]] is a disjunction consisting of arguments s[[G′′ ]] constructed from graphs G′′ ∈ G. Therefore h, i, η must satisfy at least one such argument, so we assume that h, i, η =
161
s[[G′′ ]]. By application of Lemma 7.7, αt (h, i) ∈ G′′ , and G′′ ∈ G, which proves our result. As with the proof of correctness for g, we need to prove that the graph environment defined by a rule set is the same as the one defined by the separationlogic semantics for let. Lemma 7.9. Let H = hT, N, P, Zi be a heapgraph grammar, and let s[[hZ, P i]] = let Γ in F be the resulting separationlogic formula. Then the graph environment defined by P corresponds to the environment k defined by the semantics of satisfaction when evaluating the formula in environment η0 . Proof. This proof uses much the same approach as the proof of Lemma 7.5 and so we give only sketch rather than the full details of the proof. We observe that both environments are defined as the fixedpoint of a function over environments, and show that, if we begin with a corresponding pair of environments, the two functions always construct a pair of environments. This suffices to show that the two least fixedpoints are the same, which proves our result. To prove that the two functions over environments produce corresponding environments when passed corresponding environments, we use the result of Lemma 7.8. As in the earlier proof, we show that the correctness result for graph sets carries through to a correspondence between resulting functions. We now apply Lemma 7.7, Lemma 7.8 and Lemma 7.9 to prove the correctness of the mapping s from grammars to formulas. Theorem 7.10. Let H = hT, N, P, Zi be a heapgraph grammar. Let G ∈ HC be a graph. Then G ∈ L(H) if and only if α−1 (G), η0 = s[[hZ, P i]]. −1 Proof. By the semantics, α−1 t (G), η0 = s[[hZ, P i]] if and only if αt (G), k =
sH [[Z]], where k is defined by a fixedpoint function based on η0 and sq [[P ]]. We have shown in Lemma 7.9 that this environment k must correspond to the graph environment defined by P . A graph G is a member of hZ, P i if and only if it is a member of κ(L, Z), where L is the graph environment defined by P . We concluded in Lemma 7.8 that, given corresponding environments k and L, then G ∈ κ(L, Z) if and only if α−1 t (G) = sH [[P ]]. We have shown that the environments 162
k and L must correspond, so we conclude that G ∈ L(H) if and only if α−1 t (G), η∅ = s[[hZ, P i]]. Note that Theorem 7.10 proves a full correctness result for s, rather than correctness over a restricted domain of flattened formulas as with our results for g. This difference is due to the fact that there is no nesting of recursive definitions in the definition of hyperedge replacement grammars, which makes a full proof considerably simpler than for separationlogic formulas.
163
Chapter 8
Consequences and limitations In this chapter we examine some of the consequences of our translation from separation logic formulas into hyperedge replacement grammars, and vice versa. We examine both the theoretical and practical reasons that our correspondence is interesting. We also look at the limitations of our approach, some of which can be overcome by further work, and some of which are theoretical limits that cannot be overcome without changing our basic approach. The chapter is structured as follows. In section 8.1 we examine the separationlogic semantics of some of the constructs not present in our fragment, and prove that formulas featuring these constructs cannot be modelled in general by a hyperedgereplacement grammar. In §8.2 we consider an extension of the heapmodel permitting tuples, rather than pairs. In §8.3 we describe the consequences of the correspondence for both separation logic and hyperedge replacement. Finally, in §8.4 we consider some of the related work on separation logic, grammars, and expressiveness.
8.1
Inexpressible separation logic operators
The fragment of separation logic we have chosen has been restricted so that it is expressible by hyperedge replacement grammars. We claim in §6.1 that the omitted constructs are fundamentally noncontextfree. In this section we justify this claim. 164
h, i, η = true
holds always
h, i, η = P ∧ Q
iff
h, i, η = P and h, i, η = Q
h, i, η = ¬P
iff
h, i, η 6= P
h, i, η = P −∗ Q
iff
∀h′ . if h′ #h and h, i, η = P then (h′ · h), i, η = Q
Figure 8.1: Definition of satisfaction for separation logic operators not present in our fragment. To do this, we extend our semantics for satisfaction to include separation logic operators not present in our initial fragment. The extended semantics is defined in Figure 8.1. As with our basic semantics for satisfaction, this definition is based heavily on [74, 52].
8.1.1
Conjunction
Conjunction is omitted from our fragment because it corresponds to language intersection, and the class of HRdefinable languages of heapgraphs is not closed under intersection. A conjunction P ∧ Q is satisfied by any heap h satisfying both P and Q. Suppose that P and Q correspond in our mappings to grammars g[[P ]] and g[[Q]]. Then the grammar g[[P ∧ Q]] should construct the set L(g[[P ]]) ∩ L(g[[Q]]). It is shown in [39] that the class of hyperedgereplacement languages is not closed under intersection. The proof of this works by defining the following pair of string languages: L1 : {an1 bn1 an2 bn2 . . . bnk−1 ank bnk  k ≥ 1 ∧ n1 . . . nk ≥ 1} L2 : {an1 bn2 an2 bn3 . . . bnk ank bnk+1  k ≥ 1 ∧ n1 . . . nk+1 ≥ 1} These definitions use the normal notation for strings, so the string an bm consists of n ‘a’ characters followed by m ‘b’ characters. L1 and L2 are used to define languages of stringgraphs. Definition 8.1 (stringgraph). The graph w• is the stringgraph corresponding to string w. A stringgraph for string w = c1 c2 . . . cn consists of n + 1 unlabelled nodes v1 , . . . , vn+1 and n labelled edges such that edge ei has source vi and target vi+1 and label ci . The graph language L• is the language of stringgraphs {w•  w ∈ L}. 165
Example 8.1 (stringgraph). For the string ‘abaab’, the corresponding stringgraph (abaab)• is as follows. a
b
a
a
b
L1 and L2 are used to define the HR languages L•1 and L•2 . The intersection between L•1 and L•2 gives the following language: L•1 ∩ L•2 = {((an bn )k )•  k ≥ 1 ∧ n ≥ 1} In [27] it is shown by application of the pumping lemma for hyperedge replacement languages that L•1 ∩L•2 is HRinexpressible. However, this proof applies to the class of unrestricted HR languages. To show that intersection is not possible in the restricted domain of heapgraph languages we must alter the proof. First we define a new encoding. Rather than using edgelabels to record character names, we use configurations of edges. Our string encoding is defined over characters ‘a’ and ‘b’ only. Definition 8.2 (string heapgraph). The graph w◦ is the string heapgraph corresponding to string w. A string heapgraph for string w = c1 c2 . . . cn consists of n + 1 unlabelled nodes v1 . . . vn+1 and n Elabelled edges e1 . . . en . If character cn is an ‘a’, then the edge E has attachment points [vn , vn+1 , vn+1 ]. If cn is a ‘b’ then the edge has attachment points [vn , vn+1 , vn ]. The graph language L◦ is the language {w◦  w ∈ L}. Example 8.2 (string heapgraph). For the string ‘abaab’, the corresponding string heapgraph (abaab)◦ is as follows.
a 1 E
b 2 3
3 E 2 1
a 1 E
a 2 3
1 E
b 2 3
3 E 2 1
1 nil
Let H1 and H2 be HR grammars defining L◦1 and L◦2 respectively. Applying our translation function s allows us to construct formulas P1 = s[[H1 ]] and P2 = s[[H2 ]]. Now suppose we include conjunction in our fragment of separation logic. The set of heaps satisfying the formula P1 ∧P2 would correspond to the class of heap graphs {((an bn )k )◦  k ≥ 1∧n ≥ 1}. But by the same argument used in the original proof, this language is not expressible by any HR grammar. Therefore grammar g[[P1 ∧ P2 ]] cannot be constructed, and so conjunction cannot be modelled using hyperedge replacement. 166
8.1.2
Negation
Negation is prohibited in our fragment because it corresponds to language complement, and the class of HR languages of heapgraphs is not closed under complement. We use this fact to prove that complement is inexpressible. We write LC to denote the complement of a language L. Proposition 8.1. The class of of HR heapgraph languages is not closed under complement. Proof. We have shown in the previous section that the class LHR of HRexpressible heapgraph languages is not closed under intersection. It is known that the class LHR is closed under union. The union of two grammars can be constructed by simply merging their respective sets of initial graphs and sets of productions by disjoint union. Suppose now that LHR is closed under complement. For languages A and B, by De Morgan’s laws, (AC ∪ B C )C = A ∩ B, so if LHR is closed under complement, it implies that it is closed under intersection, which is false. Therefore, the class is not closed under complement. This result means that we cannot express separationlogic negation in a hyperedgereplacement grammar. By Proposition 8.1 there must exist a heapgraph grammar G such that the complement of its language is not HRexpressible. We construct the corresponding separationlogic formula s[[G]]. If we apply the negation operator, to give formula ¬s[[G]], then by the semantics of negation, the grammar g[[¬s[[G]]]] must generate the complement of L(G). But L(G)C is HRinexpressible, so no grammar g[[¬s[[G]]]] can be defined.
8.1.3
The elementary formula true
In [38], Chapter IV, it is shown that any HR language of simple graphs (meaning graphs without loops or parallel edges) must have a maximum bound on the size of any clique in the language. Heapgraphs cannot contain parallel edges, so the class of all loopfree heapgraphs is such a language of simple graphs. Consequently the set of loopfree heap graphs cannot be expressed by any hyperedgereplacement grammar. The formula true is satisfied by any heap h. Suppose that we can construct a corresponding grammar G = g[[true]]. This grammar would have 167
to construct the language of all possible heapgraphs. The loopfree property is a compatible property in the sense of [27]. This is because the loopfree property for a graph can be composed from the pairwise reachability of the external nodes of the graph’s constituent subgraphs. Consequently we can construct grammar G′ that defines the language of loopfree heapgraphs. No such grammar exists. Therefore no such grammar g[[true]] can be defined.
8.1.4
Separating implication
The separating implication operator −∗ is the separating equivalent of normal implication. A heap satisfies formula P −∗ Q if joining it with a heap satisfying P results in a heap satisfying Q. Conjecture 8.2. For any DPO graph grammar G defining a language of heapgraphs, there exists a corresponding separationlogic formula F in SL+ {−∗} such that g ∈ L(G) iff α(g) = F . This conjecture rests on the observation that the ‘holes’ constructed by separating implication can be seen as corresponding to the context matched by the lefthand of a DPO rewrite rule. Suppose that we have a contextsensitive rule hL ← K → Ri. Let L, R and K be heapgraphs. We have shown in §7.4 that we can construct a formula from any terminal heapgraph by applying the function sg . Let FL be a formula corresponding to the lefthand side and FR a formula corresponding to the righthand side. We use the interface graph K to ensure that nodes shared between the left and righthand sides share free variable names in FL and FR . Suppose we have a formula FH describing the starting heapgraph H. Now we write the following formula representing the rule. (FL −∗ FH ) ∗ FR This formula is satisfied by a heap satisfied by FH which has had a subheap satisfying FL removed and a subheap satisfying FR added. Example 8.3 (simulating DPO rules). Suppose we have the following DPO rule. 1
1
2 3
2
⇒
1
168
1
2 3
1
2 3
2
Then the corresponding formula is as follows, given a formula FH that is a description of the starting graph H. ∃x, y. (((x 7→ y, y) −∗ FH ) ∗ ∃z. (x 7→ z, z ∗ z 7→ y, y)) The shared variables x and y take the place of the interface nodes between the left and righthand graphs. If true, this conjecture would also suffice to show that separating implication is not expressible by any hyperedgereplacement grammar. This is because languages are known to exist (such as the language of balanced binary trees or the set of all (hyper)graphs) that can be expressed by a doublepushoutbased grammar but that are not HRexpressible.
8.2
Extending the heap model
To simplify both the translations and our proofs of correctness, in previous chapters we have used a pairbased heap model (see §6.1). At the cost of more complexity, we can extend this model to a more expressive heap model where locations can map to tuples of arbitrary size. In this section we sketch the extended model and describe the extension it permits to our fragment of separation logic and notion of a heapgraph grammar. To control the size of the tuples in the heap, locations in the heap must be typed. This extension requires that we define syntactically new pointsto assertions for each location type. We also must extend the set of labels permitted in a heapgraph to record the new types. We assume a finite set Typ of types. Then we define a tupleheap as follows: Definition 8.3 (tupleheap). A tupleheap j = hh, ti consists of a heap function h : Loc → Elem∗ ∪ {}, a partial function with finite domain mapping each location to a tuple of elements or , and a typing function t : Loc → Typ, a partial function mapping locations to types.
Definition 8.4 (tupletype schema). A tupletype schema s : Typ → Z is
a total function mapping types to integers. We say that a tupleheap hh, ti is wellformed with respect to s if dom(s) = ran(t) and for every location l ∈ dom(h), s(t(l)) = h(l). In a wellformed tupleheap, the typeschema gives each type T an integer size n, and every tuple stored at a location with type T is of size n. 169
Example 8.4 (tupleheap, tupletype schema). Suppose we have set of types {X, Y, Z} and a tupletype schema s defined as follows. s = {X 7→ 3, Y 7→ 1, Z 7→ 2} Of the following tupleheaps, h1 is wellformed, but h2 is not wellformed because location l2 has an arity of 3 but a type of Y , where s(Y ) = 1. h1 = h{l1 7→ hl2 , nili, l2 7→ hl1 i}, {l1 7→ Z, l2 7→ Y }i h2 = h{l1 7→ hl2 , nili, l2 7→ hl1 , nil, l2 i}, {l1 7→ Z, l2 7→ Y }i For a given typeschema s we can define a fragment of separation logic specifying wellformed tupleheaps. For each type T ∈ Typ we define a T distinct pointsto assertion x 7− → y1 , . . . , yn , where n = s(T ). Such a pointsto assertions states that the tuple associated with location x is hy1 , . . . , yn i. We also define a notion of a heapgraph over s. This is defined in largely the same way as a normal heapgraph (see Definition 6.9). The main alteration is that the set of labels for such a graph is Typ ∪ {nil}, and the arity of a terminal symbol T ∈ Typ is defined as ari(T ) = s(T ) + 1. The arity of nil remains 1. A heapgraph grammar over s is defined as a grammar producing heapgraphs over s. Once we have defined such an extension of the semantics, we can redefine our mappings between domains. Our intuitive correspondence is as before, but pointsto assertions of type T now correspond to terminal edges with label T . Other elements of the mapping are unchanged. This is because the other constructs of separation logic and hyperedge replacement interact in the same way with the tuplemodel as with the consmodel. The tuplemodel of separation logic includes the model given in the previous chapter as a special case. Such a model of separation logic corresponds more closely to Clike pointer structures, where each location corresponding to a structure with a fixed number of fields. The corresponding heapgraphs also correspond more closely to a general notion of hyperedge replacement, with the restriction on terminal labels removed. It will be the subject of future work to modify the functions and proofs given in the previous chapter to match with this extended model. The proofs of correctness for such a mapping will be more complex, as the notion of a type introduces more cases to the mapping, but we do not expect that they will be conceptually more difficult. 170
8.3
Consequences of the correspondence
In this section we describe some of the consequences of the correspondence between our separationlogic fragment and heapgraph grammars. We look both at the theoretical consequences of the correspondence, in providing properties that can be imported between domains, and the practical usefulness of our fragment of separation logic. First, let us define the meaning of the correspondence we have defined between these two domains. We have defined a bijective mapping between the domains of heapgraphs and heaps, and we have shown that the mappings g[[−]] and s[[−]] are correct with respect to this mapping. This means that formulas in our fragment and heapgraph grammars can be used interchangeably as methods for defining classes of structures. Theorem 8.3 (equivalent expressive power). Any class of structures satisfying a formula in SL can be generated by some heapgraph grammar, modulo α, and vice versa. Proof. Function s is defined as a total function. The composition flat ◦ g of the flattening formula flat and g is total over formulas in SL. The result follows as a consequence of the correctness of flattening (Corollary 6.5) and of the correctness of g and s (Theorem 7.6 and Theorem 7.10). This proves that s is a semantic inverse to g. For heap h and heapgraph g, h, i0 , η0 = s[[g[[F ]]]] ⇐⇒ h, i0 , η0 = F , and g ∈ L(g[[s[[G]]]]) ⇐⇒ g ∈ L(G). In other words, s and g define a correspondence between the domains of separation logic and hyperedge replacement. This is the major result of our work on separation logic and hyperedge replacement. However, s is not syntactically the inverse of g, even if we discount variable naming and trivial formula reordering. This is because the source normalisation operator (see §6.4) removes information when normalising to a heapgraph grammar. To see this, consider the example given in §6.4. We have a formula let f (z1 , z2 ) = ((z1 7→ z2 , nil) ∨ (z2 7→ z1 , nil)) in ∃x, y. f (x, y) ∗ f (x, y). Applying g to formula results in a nonheapgraph grammar, which we then normalise to the following grammar.
171
f1
Z
=
1 2
1 2 f2
3
3
1
⇒
f1
1 nil
1 E 2 3
1 2
f2
⇒
2 E 1 3
3
2
3
But this is the same grammar that results from applying g to the following formula. let f1 (z1 , z2 ) = (z1 7→ z2 , nil), f2 (z1 , z2 ) = (z2 7→ z1 , nil) in ∃x, y. f1 (x, y) ∗ f2 (x, y). Consequently g is nonbijective, so s cannot be its inverse. However, this problem does not occur if we consider only formulas in the range of s. This is because formulas constructed by s are already sourcenormalised. We call this class of formulas SLF ′ . Aside from source normalisation and variable renaming, corresponding formulas and grammars are structurally very similar. Consequently we conjecture that g : SLF ′ → HGG, the function g restricted to the range of s, is the inverse of s.
8.3.1
Making use of theoretical results
An immediate consequence of this result is that some of the theoretical results that have been proved for hyperedgereplacement grammars hold for formulas in the fragment. The most obvious of these are the results about inexpressible languages. If a language of graphs is proved to be inexpressible by any hyperedge replacement grammar, then the corresponding class of heaps must also be undefinable in our fragment. Examples of known results include the facts that both the languages of grid graphs, balanced binary trees are inexpressible by any hyperedge replacement grammar (consequences of the pumping lemma and lineargrowth theorem of [38] respectively) as is the language of all heapgraphs (consequence of the cliquesize limit discussed in §8.1.3). The most flexible result for proving inexpressibility results is the pumping lemma for hyperedgereplacement languages (already mentioned in §8.1.1).
172
The interested reader is directed to [38] for an extended discussion of the inexpressibility results that follow from the pumping lemma. We have already made use of several expressibility results in defining our mappings. For example, the class of hyperedge replacement expressible languages is closed under intersection with socalled compatible properties. We used the fact that heapgraph conformance is a compatible property in §7.2 to show that we can define a heapgraph normalisation operator. This result holds for any formula in our fragment. So for any formula in SL, a new formula can be constructed expressing the intersection between the formula and a compatible property.
8.3.2
Relationship with symbolic heaps
Our approach is related to other work in the composition of our fragment SL. This fragment might appear at first to be quite restrictive. However, it is actually quite close to the symbolic heaps that form the basis of the Space Invader tool [5, 79] (see §11.3 for more on Space Invader). Loosely speaking, symbolic heaps are a fragment of separation logic that has been developed for symbolic execution. The exact fragment of separation logic used varies with the paper cited. However, a symbolic heap Π  Σ as defined in [20] consists of a finite set Π of equalities and a finite set Σ of heap predicates. The equalities E = F are between expressions E and F , which are variables x or primed variables x′ or nil. The elements of Σ are of the form E 7→ F , ls(E, F ), and junk. ls(x, y) stands for a recursivelydefined list segment, while junk can stand for any heap. Π is referred to as the pure part of the heap, and Σ is referred to as the spatial part of the heap. Semantically, a symbolic heap Π  Σ where Π = {P1 , P2 , . . . , Pn } and Σ = {Q1 , Q2 , . . . , Qm } expands to a formula (P1 ∧ P2 ∧ . . . ∧ Pn ) ∧ (Q1 ∗ Q2 ∗ . . . ∗ Qm ) Later papers extend the symbolic heap notion to include more expressive pure formulas [15], and more sophisticated recursive predicates, such as a ‘list of lists’ predicate [5]. Our fragment includes a general notion of recursive predicate, while symbolic heaps use specific recursive predicates defined for particular domains. 173
For example, early work on the Space Invader tool used a simple list fragment predicate ls(x, y), while more recent work has included a nested ‘list of lists’ predicate. In both cases these predicates can be defined using our let statement. While the various versions of the symbolic heap fragment are quite close to our fragment, they all include nonspatial assertions that we do not include in our fragment. All of the symbolic heap fragments include assertions that describe the arithmetic relationships between locations. In models that include pointer arithmetic these includes lessthan and greaterthan assertions. Under our model, without pointer arithmetic, such formulas are meaningless. However, we could meaningfully extend our fragment to include pure equality and inequality assertions. We believe, but have not yet shown, that equality and inequality assertions can be modelled using hyperedgereplacement grammars. In [28], grammars are examined that extend the semantics of hyperedge replacement to permit repetition in a rule’s sequence of external nodes. Operationally, such rules can ‘fuse’ the external nodes of a nonterminal hyperedge. These nodefusing rules seem like a natural approach to modelling equality and inequality statements in separation logic. It is proved in [28] that for any grammar defined using rules with repetition, a corresponding repetitionfree grammar exists that defines the same language. For this reason, we conjecture that conventional HR rules as defined in Chapter 2 should be expressive enough to model equality and inequality. The predicate junk is equivalent to true, and so by the result given in §8.1.3 cannot be expressed by any hyperedge replacement grammar. However, as the name suggests, junk is largely used to handle unconnected sections of the heap. Structure in the heap is specified in the junkfree fragment of symbolic heaps, which suggests that the structurespecifying properties of symbolic heaps may be similar to our fragment. The result of the similarity between our fragment and the symbolic heaps used in other work on separation logic is to suggest that our fragment, while restricted, is still suitable for practical use in the description of program states. This fits with our intuition about hyperedge replacement grammars; they are general enough to describe useful classes of structures.
174
8.4
Other related work
Our work is relatively novel. The idea of relating separation logic to contextfree graph grammars has not appeared in other work that we have discovered. The work of Lee et al. [52] is the closest we have found. In this, separation logic is used to give a semantics to grammars, which is then used as the abstract domain in an automatic shape analysis 1 . A semantics is given to a grammar by mapping it to a separation logic formula. However, the mapping is only oneway, meaning no correspondence result can be derived (this is not the aim of the paper). In addition, the grammars used in [52] are severely restricted compared to our heapgraph grammars. While the initial elements of the grammar can be graphs, productions over nonterminals can only construct single binary branches. Some other work has been done on the expressibility of separation logic. The class of closed separationlogic formula is known to be of equivalent expressive power to the class of formulas in firstorder logic without separating operators [56]. However, in [16] it is shown that the correspondence does not hold for separation logic formulas that include logical parameters standing for formulas, and it also does not hold between separation logic with a list segment predicate and firstorder logic with such a predicate. Our results regarding expressiveness are incomparable to these results, because the fragment we use omits several operators from full separation logic, but includes a more general notion of recursion. However, our translation gives us a more general framework for deriving expressiveness results, because we map to a class of grammars with many wellunderstood properties. The inclusion of a general notion of recursion also allows us to cope with any recursivelydefined predicate, rather than just lists.
1
It is interesting that the work on symbolic heaps is descended from this work. As mentioned in the previous section, symbolic heaps exhibit many grammarlike properties.
175
Part IV
A language for shape safety
176
Chapter 9
CGRS: A language for shape safety Pointers in imperative programming languages are indispensable for the efficient implementation of many algorithms at both applications and systems level. It is however notoriously difficult to program using pointers without introducing subtle errors. This is because the type systems of current programming languages are in general too weak to detect illshaped pointer structures. At best, a language may ensure that pointer structures are locally safe, ensuring pointers refer to allocated heapstructures of the correct type. This kind of correctness prevents the most obvious pointer errors, such as dereferencing errors and bounds errors. Java programs are pointersafe in this sense; C programs are not. However, such local typing places no restriction on the overall shape of the pointer structure. For a pointer program to behave as expected, without error, it must preserve certain properties of its pointer structures. For example, a binary search tree insertion must preserve membership of the class of binary search trees. We call these largescale properties shapes, and we describe programs that preserve required shape properties as shape safe. There exists no mechanism in current widelyused programming languages for ensuring that programs are shape safe. The Safe Pointers by Graph Transformation (SPGT) project [4, 3] is a recent approach to ensuring the safety of pointer manipulations. Under this approach, shapes are specified by graph reduction, and rewrites are modelled as graph transformation rules. The SPGT project has developed 177
a checking algorithm that allows the checking of graph transformation rules to determine whether they are shape safe. This chapter describes a new language that applies the SPGT shape specification approach to the C programming language. Our new language extends C with constructs that are the analogues of graphtransformation rules and of SPGTstyle shape specifications. This allows a direct application of the SPGT shapechecking methods to the checking of the program for shape safety. We have called our extended programming language CGRS. The chapter is structured as follows. Section 9.1 summarises how shapes are defined by graph reduction under the GRS approach and sketches the checking algorithm for shapepreservation. Section 9.2 describes the new constructs in CGRS that allow programmers to write shape specifications and operations on shapes. Section 9.3 describes an extended example of the use of a CGRS program for inserting values into an AVL tree. Finally Section 9.4 considers the size of programs written in CGRS, both before and after compilation.
9.1
Safe pointers by graph transformation
Under the Safe Pointers by Graph Transformation approach, the properties of pointer structures are specified as shape types. A shape type consists of a class of graph structures with common properties defined by a socalled graph reduction specification (GRS). The kinds of nodes that can be used in a GRS are defined by a signature that gives a set of labels for nodes and outgoing edges corresponding to these labels. Definition 9.1 (GRS typing). Each node models a tagged record of pointers where the node label, drawn from the nodelabel alphabet LV , is the tag. Each edge leaving a node corresponds to a pointer field where the edge label, drawn from the edgelabel alphabet LE , is the name of the pointer field. We use a function type : LV → ℘(LE ) to associate with each record tag its set of field names: if node v is labelled l and has an outgoing edge e, then the label of e must be in type(l) and no other edge leaving v must have this label. We can encode the type requirement in our notion of a graph signature (see Definition 2.13). The signature Σ = hL, in, outi corresponding to a type 178
function type is defined so that L = hLV , LE i, in(l, l′ ) = ⊥ for all labels l, l′ , and out(l, l′ ) = 1 if l′ ∈ type(l) and out(l, l′ ) = 0 otherwise. To operate on these Σgraphs, we use the doublepushout approach rewrite rules defined in §2.2. Σgraphs in rules need not be Σtotal, they can contain nodes with an incomplete set of outgoing edges or unlabelled nodes with no outgoing edges. We refer to [4] for conditions on unlabelled nodes and outgoing edges in rules that ensure that rule applications preserve both Σgraphs and Σtotal graphs. For a rule hL ← K → Ri we require the following. • Unlabelled nodes in L are preserved and remain unlabelled with the same outlabels. • Labelled nodes in L which are preserved with the same label have the same outlabels in L and R. • Relabelled nodes have a complete set of outlabels in L and R. Nodes may not be labelled in L and unlabelled in R, or vice versa. • Deleted nodes have a complete set of outlabels. • Allocated nodes are labelled and have a complete set of outlabels. See [4] for the formal definitions of these conditions. Rules satisfying these conditions are called Σtotal rules. The members of a GRS shape are specified by graph reduction. Each GRS has a single accepting graph and a set of reduction rules. Any graph that can be reduced to the accepting graph by some sequence of rule applications is a shapetype member. Definition 9.2 (graph reduction specification). A graph reduction specification S = hΣ, R, Acci consists of a signature Σ, a set of Σtotal rules R and a Σtotal Rirreducible accepting graph Acc. It defines the graph language L(S) = {G  G ⇒∗R Acc}. Definition 9.3 (polynomially terminating GRS). A GRS S is polynomially terminating if there is a polynomial p such that for every reduction G0 ⇒R · · · ⇒R Gn on Σtotal graphs, n ≤ p(VG  + EG ). It is closed if for all G ∈ L(S), G ⇒R H implies H ∈ L(S). A polynomial graph reduction specification, PGRS for short, is a polynomially terminating and closed GRS. 179
BranchLeaf:
Acc : top
1
L
l
aux
L
B
1
r
⇒
L
L
AuxRoot: top 2
aux
⇒ 3
top
aux
2
3
Figure 9.1: Rooted GRS defining the language of binary trees. Example 9.1 (graph reduction specification). Figure 9.1 shows a GRS for full binary trees with an extra root node labelled with R (shown in the picture as a greyshaded node) and an auxiliary pointer from the root node to any node in the tree. Its signature is LV = {R, L, B}, LE = {top, aux , l, r}, type(R) = {top, aux }, type(B) = {l, r} and type(L) = ∅. Tree nodes are either Llabelled leaves or Blabelled branch nodes with outgoing pointers l and r, and there is a unique Rlabelled node with pointers top and aux that point to the root of the tree and to an arbitrary tree node, respectively. The accepting graph, Acc, is the smallest graph of this kind. Fig. 9.1 gives two reduction rules for the GRS. The AuxRoot rule redirects the auxiliary pointer to the top of the tree (regardless of the labels of nodes 2 and 3), while the BranchLeaf rule deletes two leaves with the same parent and relabels that parent as a leaf. Any full binary tree with an auxiliary pointer can be reduced to Acc by the repeated application of these two rules, but no other graph can be reduced to Acc. To see that the rules in the GRS cannot reduce illshaped graphs to Acc, consider their inverses (which are obtained by swapping left and righthand sides). The inverse of AuxRoot can only move the aux pointer to an arbitrary location, while the inverse of BranchLeaf can only create more leaf nodes. Consequently both inverse rules preserve full binary trees with an auxiliary pointer, which implies that the specified shape cannot contain other graphs. The GRS is polynomially terminating – actually linearly terminating – because for every step G ⇒ H on Σgraphs, the number of nodes without 180
Insert: aux aux L
⇒
1
B l L
1
r L
Figure 9.2: Graph transformation rule Insert that inserts a new branch into a binary tree at the position of the aux pointer. outgoing parallel edges is reduced. The GRS is also nonoverlapping, meaning that for each pair of steps H1 ⇐ G ⇒ H2 on Σgraphs, either H1 ∼ = H2 or there is a Σgraph M such that H1 ⇒ M ⇐ H2 . This property implies closedness and hence the GRS is a PGRS. Just as pointer structures are modelled by GRSs, operations on those structures are modelled by graph transformation rules. Figure 9.2 shows an operation on the shape of Figure 9.1 that replaces a leaf destination of the auxiliary pointer with a branch node and two new leaves. This rewrite rule is an example of a shape safe rule: when applied to a graph that is a binary tree with an auxiliary pointer, it is guaranteed to produce a graph in the same shape. Unrestricted GRSs are universally powerful in that they can define every recursively enumerable shape, but their membership problem is undecidable in general. To check that pointer rewrites conform to the restriction of a GRS, they must be modelled by graphtransformation rules (which need not obey the restrictions of PGRSs). Definition 9.4 (shapesafe rule). A graphtransformation rule r is shapesafe with respect to a shape L(S) if for all G in L(S), G ⇒r H implies H ∈ L(S).1 The static checking algorithm ShapeCheck developed in the SPGT project is described in [3]. Briefly, given a graphtransformation rule r and 1
For simplicity, we assume that rules have the same input and output shape. The shapechecking method of [3] can also handle shapechanging rules.
181
a GRS S, the algorithm constructs two abstract reduction graphs (ARGs) that represent all contexts of r’s left and righthand side in members of L(S). The rule is safe if the righthand ARG includes the lefthand ARG as a subgraph. Some ARGs are infinite and hence their construction does not terminate, but in many practical cases the algorithm produces finite ARGs representing all left and righthand contexts so that inclusion can be checked. The general shapesafety problem is undecidable even for contextfree shapes [32] and hence every checking method is necessarily incomplete. The incompleteness which must exist in any shapechecking algorithm manifests itself in two ways in ShapeCheck: first, the construction of either of the ARGs may not terminate, and second, even given termination a shapesafe rule may fail the inclusion test. In practice, this algorithm can check insertions and deletions in over contextfree shapes such as cyclic list, linked list and binary search trees. However, it often nonterminates when applied to noncontextfree shapes such as balanced trees. Intuitively, such shapes require an arbitrarily large amount of context, causing nontermination of the ARG construction algorithm. See [3] for more discussion.
9.2
CGRS: a language for safe pointers
CGRS is an extension to C that implements the GRS approach to shape specification and shape checking. The main ideas (adopted from [31]) are that certain pointer structures used in the program have a declared shape that specifies the possible form of the pointer structure, and that such pointer structures are only manipulated by transformers that correspond to graph transformation rules.
9.2.1
Conforming to the C model
The main aim for CGRS is to enable the shapechecking of C programs by introducing constructs corresponding to graph transformation rules. However, the graph transformation model of computation differs considerably from the C model. Rules under the DPO approach can apply at any location in a graph where their lefthand sides match. As there may be more than one such location, they are also nondeterministic. C pointer assignments in contrast are deterministic and local, in that they can be applied exclusively at 182
locations held by pointers. The nondeterministic matching of graph transformation rules creates another mismatch between the two models. To find a match for its lefthand side, a rule must search the graph, an operation that requires polynomial time in the worst case for a rule of fixed size. C pointer assignments in contrast are guaranteed to terminate in constant time. As a result, an individual graph transformation rule is considerably more powerful than an individual C pointer assignment. To apply the GRS approach to C, the graphtransformation idiom must be fitted more closely with the conventions of C. In CGRS we use a restricted form of graph transformation more compatible with the expectations of C programmers. The pointer structures and transformer functions used in CGRS are restricted by requiring that they are rooted. Individual rewrite rules are less powerful than general rules, but they are still sufficiently powerful to replace C pointer manipulations. Definition 9.5 (Clike rooted graph). We describe a graph as a Clike rooted graph if: (1) There exists a root label ̺ that is the label for exactly one node in the graph. (2) Distinct edges with the same sourcenode have distinct edge labels. C pointer structures naturally correspond to such rooted graphs. Such structures are accessed through a finite group of distinctlylabelled stack variables, corresponding to roots. The fields of a C struct are distinct, conforming to the restriction on outedges in rooted graphs. Definition 9.6 (Clike rooted rule). A graph transformation rule is a Clike rooted rules if (1) it has a root label ̺ such that the lefthand side contains exactly one ̺labelled node, and (2) every lefthand side node is reachable from some root. Transformer functions correspond to Clike rooted rules. Example 9.2 (rooted graphs and rules). The GRS shown in Fig. 9.1 defines a class of Clike rooted graphs. The rule in Fig. 9.2 is a Clike rooted rule. Chapter 3 discusses rooted rules in detail, as part of a more general theory of fast graph transformation. The requirement for a unique root nodetype and the requirement for lefthand side reachability ensures that Clike rooted graphs and rules conform to our Condition R3. For this reason, Clike rooted rules are deterministic and rule application requires only constant time. 183
The failure behavior of graph transformation also differs from the standard C behavior. In the general definition of a graphtransformation rule, the result of a rule is undefined if no match can be constructed. This is also the case in programming languages based on graph transformation (e.g. the GP language [65]). This fits badly with the C approach, where a function only ever has an undefined value if an error has occurred. This behaviour is also quite impractical, in that the failure to find a match can be a useful result for a transformer function. For example, a match failure can be used to check whether the root node points to a node of a particular type. As a result, CGRS transformer functions return a boolean value recording whether rule application succeeded. This allows us to use transformers as conditions as well as a rewriting system. Transformers are passed pointers to shapestructures and modify these structures inplace. Inplace rewriting is safe because a transformer can only fail during the matching portion of the execution, before any rewriting has occurred. If the matching process for a transformer function fails, the input graph is left unmodified. Once the function terminates, a boolean value is returned that indicates whether the application succeeded. The handling of dangling edges in graph transformation differs greatly from C pointer rewriting. In a graph transformation rule, the dangling condition ensures that dangling edges cannot be created. Every edge has as its target a valid node. In C pointer rewriting, no such restriction exists. Any value can be written into a pointer field, and consequently pointers do not necessarily point to valid values in the heap. In this case, the graph transformation behavior is safer than the standard C behavior. Avoiding dangling edges is an important objective of shape safety. In CGRS we maintain information in the data structure so that the dangling condition can be checked for pointer structures. Consequently transformer function cannot create dangling edges. Aside: Injective and noninjective matching Given that we are modelling pointer rewrites, the choice of a formulation for contextsensitive graph transformation that uses injective matching may seem slightly surprising. Conventional pointer rewrites are noninjective, in that rewrites can ‘match’ the same locations in a pointer structure with several different names in a single rewrite. For example consider the following 184
pointer accessing rewrite. v>a>a = w>a The first and second instance of a referenced from v can refer to the same location, if the value in v>a points back to itself. In addition, v and w may point to the same location. Both possibilities correspond to noninjective matching. However, our objective in CGRS is to improve pointer program safety, rather than replicate all the features of conventional pointer rewriting. It is known that injective matching is more general than noninjective matching, in that noninjective rules can be simulated by injective rules [40]. The ability to distinguish between injective and noninjective matches gives a substantially increased expressive power rewrite rules, and so we have chosen to use it in our graph transformation formulation and so to apply it to pointer rewriting. In addition, using injective matching simplifies the overall presentation of the thesis. Injective matching provides the most general basis for Part II, where we generalise to rewriting arbitrary graphs, rather than just pointer structures. Using noninjective matching would unnecessarily restrict the scope of this work. The alternative of using different but similar approaches in different parts of the thesis seems confusing for the reader.
9.2.2
Signatures and shapes
Transformer functions operate on shape structures that are guaranteed to have the form specified by the shape declaration. Shape structures are composed of nodes, each of which has a type and an associated fixed set of fields. In normal C these types and fields are defined by declaring struct types, while in CGRS they are defined by a signature. The abstract syntax of CGRS signature declarations and shape declarations is given in Figure 9.3. Here structdeclcont stands for any normal C type declaration. A signature defines a set of nodetypes. However, nodetypes are not manipulated directly in a CGRS program – instead they are referenced through shape declarations (see page 186). Signatures consist of a signature block, containing node definitions. Nodes are defined with a node block, which strongly resembles a C struct declaration. The ‘fields’ of the declaration consist of edge names labelled 185
sigdef
::=
signature sigid{ [nodedef;]+ }
nodedef
::=
nodetype ntid { [nodecont;]+ }  root nodetype ntid { [nodecont;]+ } nt nodetype ntid { [nodecont;]+ }
nodecont
::=
edge ed [, ed]∗  structdeclcont
shapedef
::=
shape shid sigid { accept { [nodedec;]+ [nid.ed => nid;]∗ } rules { [rdid;]∗ } }
nodedec
::=
ntid nid
Figure 9.3: Syntax for signatures and shapes. with the edge keyword, and nonedge fields. Unlike normal C pointers, edgefields are defined without stating the type of the objects they are pointing to – edges in CGRS structures can point to any type of node permitted by the signature. The following code declares a nodetype for use in a binary tree signature. nodetype branchnode { edge l, r; int val; }
Node declarations can include any type of data field (including pointer data). To ensure that pointer rewrites are safely encapsulated, transformer functions cannot do anything other than read or write from these fields. In the abstract domain of graphs, the data values stored in nodes are abstracted away, leaving only node and edge labels. Some classes of graphs require nonterminal nodes for a GRS to successfully specify their properties (for example, the language of complete binary trees, see Theorem 5 in [4]). To model this in CGRS, node types in a signature can be labelled with the keyword nt. Nodes that are so declared cannot be used in a transformer. To fit with our requirement for rootedness structures in CGRS must include distinguished root nodes (see §9.2.1). These are distinctly labelled and can appear at most once in the pointer structure. In the signature exactly one nodetype must be declared with the root keyword. 186
A shape declaration in CGRS is the analog of a graph reduction specification. It defines a class of shape structures, and defines a type for the shape that can be passed to transformers for manipulation. On an abstract level, a shape declaration defines a class of conforming shape structures. The content of this class is defined by mapping shapes to graph reduction systems and shape structures to graphs (this mapping is defined in §10.1). A shape declaration consists of a block prefaced with a shape keyword, a shape name, and a signature name. In the shape block, there are two blocks: an accept block and a rules block. The accept block uses the same syntax as a transformer definition to specify the accepting graph, while the rules block consists of a list of names of reduction rules. In addition to defining a class of conforming structures, the shape declaration has two roles. First, it defines a C type for pointers to shapes, and these pointers can be passed around in the same way as pointers to normal heap datastructures. Second, the shape declaration implicitly defines a constructor for the shape. For shape safety to hold, it must be true that all of the shape structures constructed during program execution are members of the class of structures defined by the shape. To ensure this is true for when the shape is first created, we automatically generate a shapesafe constructor function. We then assume that all shape structures are created by applying shapepreserving transformers to shapesafe structures, starting with the initial graph. The constructor for a shape S is called newgraph S, so for our binary tree example it will be called newgraph tree. A constructor takes no arguments and returns a pointer of type S, the toplevel shape type defined by the shape declaration. The structure resulting from the constructor corresponds to the accepting graph for the shape. (See §10.2.2 for a definition of the constructor function generated for a shape). Example 9.3 (signature, shape declaration). In the case of our running example, the tree insertion code operates over structures composed of root nodes, branch nodes with two outgoing edges, and leaf nodes with no outgoing edges. We call such a signature with binary branches and leaf nodes ‘bin’. The left side of Figure 9.4 shows the CGRS signature declaration for bin. The node types treeroot, branchnode and leafnode correspond to the 187
shape tree bin { accept { treeroot rt; leafnode leaf; rt.top => leaf; rt.aux => leaf; } rules { branchleaf; auxroot; } }
signature bin { root nodetype treeroot { edge top, aux; } nodetype branchnode { edge l, r; int val; } nodetype leafnode {} }
Figure 9.4: Left: Signature for a structure composed of branches and leaves. Right: shape declaration corresponding to the GRS declaration in Figure 9.1. root, Blabelled branches and Llabelled leaves of the binary tree GRS. The root nodetype is called treeroot. The signature of a shape is defined separately from the shape itself so that it can be reused in several shapes. For example, the bin signature can be used to build structures other than plain binary trees. Clearly, all graph languages that are subsets of the set of binary trees (such as the language of balanced binary trees) use the same signature. We want our transformers to operate over rooted binary trees as defined by the GRS shown in Fig. 9.1. The corresponding declaration for a CGRS shape tree is shown on the righthand side of Figure 9.4. The accepting graph for the tree shape consists of a rttype node and a leafnodetype node. We specify the signature as bin by giving its name as an argument. The rules block of a signature refers by name to a set of reducers, which correspond to the reduction rules of the GRS. Reducers are declared separately from the shape declaration using the reducer keyword. See the Section 9.2.3 for a description of the syntax of reducers.
9.2.3
Transformer functions
Transformer functions (or just transformers) are the construct in CGRS used to define pointer manipulations. They are intended to correspond syntactically and semantically to graph transformation rules. CGRS uses a textual syntax that correspond closely to the graphical syntax for DPO 188
transformer
::=
transformer trid sigid ( shid id [, tid *id]+ ) { left ( [nid]+ ) { [nodedec;]+ [nid.ed => nid;]∗ } right( [nid]+ ) { [nodedec;]∗ [rightgraph;]∗ } }
reducerdef
::=
reducer rdid sigid { left ( [nid]+ ) { [nodedec;]+ [nid.ed => nid;]∗ } right( [nid]+ ) { [nodedec;]∗ [nid.ed => nid;]∗ } }
nodedec
::=
ntid nid
rightgraph
::=
nid.id => nid  nid.id = nid.id  nid.id = id  id = nid.id
Figure 9.5: Syntax for transformers and reducers. rules. Transformer functions are declared in CGRS in much the same way as normal C functions. Declarations are added to the top level of the C program (or to another file, with the use of a linker). The resulting transformer function can then be called in the same manner as a conventional function. Transformer function declarations are translated into booleantyped C function declarations with the same name, so in most ways they can be treated as if they were normal function declarations. The abstract syntax of a transformer declaration is given in Figure 9.5. The first declared argument to a transformer function must be a shapetyped pointer that gives the transformer its shape, and so also its signature. Following this argument, there can be any number of additional arguments of pointer type. Values are passed to the transformer function by reference to ensure that they can be modified inplace. The constituent nodes of the left and righthand sides of a transformer are declared in a list for each side of the transformer. Unlike graphtransformation rules, nodes in transformer functions are named, and the sharing of
189
names defines the interface nodes between the sides of the transformer function. The nodes preserved by the transformer rewrite are those appearing in both the left and righthand node lists. Nodes present on one or the other side only are either allocated or deleted. The permissible types in these nodetype declarations are defined by the signature used by the transformer (see §9.2.2 above). Nodes are assigned a nodetype (or tag in the terminology of §9.1) using a syntax similar to C variable declarations. For example, the following code declares a branchnode for branchnode n1, n2;
The edges between declared nodes are defined using an arrow syntax similar to the field access syntax for normal C structures. The following code matches an llabelled edge from node n1 to n2. n1.l => n2;
To conform to our requirement for rootedness (see §9.2.1), there must be exactly one node on both the left and righthand side with a root type. Furthermore, all nodes in the lefthand side must be reachable by traversing edges from the roottyped node. This ensures that the corresponding graphtransformation rules are Clike rooted rules (see Def. 9.6). To ensure that all nodes in the resulting shape structure have the correct complement of edges, nodes that are created or retyped must have all the edges for their nodetype declared on the righthand side. Nodes that are retyped must also have all of their associated edges matched on the lefthand side. Without this condition, a transformer could require assignment of two values to the same field. This condition on transformers corresponds to the restriction of DPO rules to Σtotal rules. The lefthand side of the transformer is matched against the shape structure to identify the section of the transformer for rewriting. Matching operates by locating a portion of the shape structure that is isomorphic to the lefthand side, starting at the uniquelylabelled root. The match must satisfy the dangling condition, meaning that applying the rule will not create dangling edges. The transformer returns the boolean value FALSE if matching fails.
190
If matching of the lefthand side succeeds, the transformer applies the rewrites expressed by the righthand side. A transformer function performs five kinds of rewrites on a shape structure: 1. Node creation occurs when a node is named in the righthand side list but not the lefthand side. 2. Node deletion occurs when a node is named in the lefthand side list but not the righthand side. The matched node is removed from the shape structure (and from memory entirely). 3. All edges in the lefthand side are deleted. 4. All edges in the righthand side are created. 5. Node relabelling occurs when the same node is declared in the righthand side with a different label than on the lefthand side. As well as edges and nodes, the righthand side of a transformer function can also include value assignments and retrievals. A value may be written from a variable passed by reference into the value fields of a node, or viceversa, out from a value field into a variable. Any C type can be used as the value field for a nodetype, so any kind of value can be stored by a transformer function in this way. Values can also be rearranged inside the shape structure by assigning from one node’s field to another. In transformers with several such rearranging assignments, it might appear that there is a danger of nondeterminism resulting from the undefined order of assignment. For example, the meaning of the following pair of assignments seems to depend on the order in which assignment takes place: n1.a = n2.a; n2.a = n1.a;
Our solution is simple: in any assignment a = b, the value assigned to a is the value of b before execution of the transformer. In other words, values are frozen on entry to the transformer, and are only altered on exit from the transformer. In the case of the code fragment immediately above, the values in n1.a and n2.a are swapped. This has the advantage of avoiding the need for intermediate variables. 191
transformer tree_goleft bin ( tree *t ) { left (rt, n1, n2) { treeroot rt; branchnode n1, n2; rt.aux => n1; n1.l => n2; } right (rt, n1, n2) { rt.aux => n2; n1.l => n2; } }
aux B
B 1
⇒ aux
l
1
l B
B
Figure 9.6: Textual syntax for transformer function for moving a root pointer down a lefthand branch, and corresponding graphtransformation rule. Example 9.4 (transformer function). Figure 9.6 shows the declaration for the transformer function tree goleft, used in the search tree insertion code to move the auxiliary pointer down the tree. As with a graph transformation rule, a transformer function declaration consists of a left and righthand side, identified by the left and right keywords. The righthand side of Figure 9.6 shows the corresponding graph transformation rule to this transformer function. We define formally the mapping from transformers to graph transformation rules in §10.1. A transformer declaration such as the one given in Example 9.4 results in a transformer function that can be used in much the same way as an ordinary C function. A transformer function must take as its first argument a pointer to a shape structure. It can then have an arbitrary number of additional arguments consisting of pointers to variables which can be read from or written to. Example 9.5 (transformer function program). We will illustrate CGRS with the simple example of insertion into a binary search tree. This code fragment inserts a value i into a binary search tree pointed to by the pointer variable b. The pointer b into the binary search tree has the shape tree, corresponding to rooted binary tree GRS given in Figure 9.1. The pointer manipulations required for performing the insertion have been encapsulated into transformer functions. insert( tree *b, int i ) {
192
int t; tree_reset(b); while ( tree_getval(b, &t) ) { if ( t == i ) return b; else if ( t > i ) tree_goleft(b); else tree_goright(b); } tree_insert(b, &i); }
The insertion into the tree works as follows: First the transformer tree reset moves the auxiliary pointer to the root of the tree. Then the tree is traversed by repeatedly comparing the integer values in branch nodes with the integer i and following either the left or the right pointer, using the transformers tree goleft and tree goright (as defined in Example 9.4). Leaves don’t hold values, so if the search ends at a leaf (identified because the value recovery transformer function tree getval fails) then tree insert inserts a branch node, and the value is written into the appropriate field. In addition to the transformer functions in a CGRS, there can be a number of reducers, which correspond to the reduction rules of the GRS. Reducers are declared using the reducer keyword. They have a similar syntax to transformer functions, including left and righthand side declarations. They differ in that reducers have no arguments and cannot be called as functions in the resulting program. Their semantics is purely abstract. Example 9.6 (reducer). The left side of Figure 9.7 shows branchleaf, a reducer for the binary tree example, and the right side shows the corresponding reduction rule BranchLeaf from the binary tree GRS in example 9.1.
9.3
Example: tree insertion and rebalancing
In this section we give a more complicated example of the use of CGRS: rebalancing operations. This example illustrates the fact that complex graph rewrites can be specified in CGRS in a way which closely conforms to their original specifications. An AVL tree is a balanced search tree that allows inexpensive rebalancing after an insertion or deletion [50, §6.2.3]. 193
reducer branchleaf bin { left (br, l1, l2) { branchnode br; leafnode l1, l2; br.left => l1; br.right => l2; } right (br) { leaf br; } }
1
l L
B
1
r
⇒
L
L
Figure 9.7: Reducer for the binary tree shape shown in Figure 9.4 and corresponding reduction rule from the GRS given in Figure 9.1. Definition 9.7 (AVL tree). A binary tree is an AVL tree if the heights of the two child subtrees of any node differ by at most one. The balance factor of a node is the height of its right subtree minus the height of its left subtree. A node with balance factor 1, 0, or 1 is considered balanced, while nodes with any other balance factor are considered unbalanced and require a rebalancing of the tree. To allow fast rebalancing, AVL tree structures record the balance factor of each node in the node itself. Lookup, insertion and deletion all require time O(log n) for an AVL tree, so AVL trees are often used as a datastructure for the efficient recording of sorted data. In [4] it is shown that the class of AVL trees can be specified using a GRS. A corresponding AVL tree shape can be constructed in CGRS by simply implementing this example as a shape. The CGRS syntax is close enough to graph transformation that this example can be translated without much difficulty. However, the GRS is quite complex, and we require in addition to the AVL shape a stack to record backpointers which makes the GRS larger still. Our objective in this section is illustrate CGRS as a programming language, rather than to just convert existing GRSs into CGRS syntax. To simplify the example, and focus on the rebalancing operations, we here define a weaker shape that includes AVL trees as a subclass of its language. The shape we define is the class of unbalanced trees with AVLlike labels. Treenodes in our shape can be labelled with +, − or •. Treenodes have two outgoing edges, labelled l and r. Our AVL trees also have an attached 194
p
t
h
+ l
r
• l
h
+ r
L
L
r
l
h
•
L
r
l
h
•
L l L
r L
Figure 9.8: Tree with stack and AVL labels. stack permitting traversal up the tree, where stack nodes are labelled h. Stack nodes have two outgoing edges. These are labelled b and c, but are shown in diagrams as finelydotted and coarselydotted edges respectively. Figure 9.8 shows an (unbalanced) member of the class. To specify a GRS for trees with AVL labels we modify the binary tree GRS given in Fig. 9.1 to specify a language of arbitrarily unbalanced AVL trees with an attached stack. Figure 9.9 shows the reduction rules and accepting graph of the GRS for the GRS. The accepting graph consists of a single leafnode, a stack node, and a root node. The reduction rules consist of BranchLeaf, which replaces tree branches with leaves, and StackDelete, which removes stack nodes. The class of validlylabelled balanced AVL trees is a subclass of the language specified by the GRS. In a valid AVL tree, nodes that are balanced are marked with the • symbol; nodes have a balance factor of 1 have the symbol −; nodes with balance factor 1 are marked with +. Figure 9.10 shows a balanced AVL tree. Figure 9.11 shows the CGRS shape avl which corresponds to this GRS. The signature avltree defines the required node types, and the reducers branchleaf and stackdelete correspond directly to the reduction rules in the GRS. In defining avl we make use of a slightly extended syntax for nodes and labels. The existence of several kinds of distinct branch node means that 195
BranchLeaf:
Acc : p
x
1
t
h
L
1
r
l L
L
⇒
L
x ∈ {−, •, +}
StackDelete: 1
x
h
y
3
1
x
⇒ h
2
y
h
3
2
x ∈ {−, •, +}, y ∈ {l, r} Figure 9.9: GRS for unbalanced trees with AVL labels.
p
t
h
+ l
r
• l L
h
+ r L
r
l
h
•
L l L
r L
Figure 9.10: Balanced AVL tree with stack.
196
signature avltree { root nodetype avlroot { edge t, p; } nodetype lbr { edge l, r; } nodetype rbr { edge l, r; } nodetype bbr { edge l, r; } nodetype stack { edge c, b; } nodetype leaf {} }
reducer stackdelete avl { left ( br, n1, s1, s2 ) { [lbr,rbr,bbr] br; stack s1,s2; br.[x] => n1; s1.c => br; s2.c => n1; s2.b => s1; } right ( br, n1, s1 ) { br.[x] => n1; s1.c => br; } }
shape avl avltree { accept { avlroot rt; leaf lf; stack st; rt.t => lf; rt.p => st; st.c => lf; st.b => rt; } rules { branchleaf; stackdelete; } }
reducer branchleaf avl { left (br, l1, l2) { [lbr,rbr,bbr] br; leaf l1, l2; br.l => l1; br.r => l2; } right (br) { leafnode br; } }
Figure 9.11: Signature and shape declarations for the avl shape. our original syntax would require separate reducer definitions covering each case. To avoid this, we define a syntax for matching sets of node labels, and for matching variable names to edge labels. In branchleaf and stackdelete we write the following to match any node br with label rbr, lbr or bbr: [rbr, lbr, bbr] br;
We also extend the syntax of edge matching to include variables. To associate the label x with any edge from node br to n1 we write the following: br.[x] => n1;
These edge variables can then be use on the righthand side, allowing us to redirect an edge with any label permitted by the signature. 197
This minor extension does not require that we alter our semantics, as reducers written using the new syntax can always be expanded to finite sets of reducers written in our original syntax. We now look at the CGRS code that rebalances an unbalanced AVL tree following an insertion. We will assume that the first stage of insertion is implemented using a modified version of the tree insertion code from §9.2. This code constructs the stack as it traverses down the tree. The rebalancing code then steps up the stack, incrementally updating the balance factors recorded in the node, until either the height of the tree is unaltered by the update, or a single a single rotation can be applied. Once the tree has been rebalanced, the rest of the stack is removed. The following code implements this process using calls to transformer functions. for ( ; ; ) { if ( rup(tree)  lup(tree) ) ; else if ( relim(tree)  rsingle(tree)  rdouble(tree)  lelim(tree)  lsingle(tree)  ldouble(tree) ) break; }
The code takes advantage of the lazy evaluation of C boolean operators. Because a transformer returns a boolean value recording whether the transformer succeeded or failed, at most a single transformer will succeed out of any of the transformers given in the disjunction, meaning that only a single rotation or treeclimbing operation will be applied at each iteration of the loop. The lup and rup operations update the balance factor of the current node and pop a single element off the stack. The rup transformer is shown in Figure 9.12, along with the corresponding graph transformation rule. In Figure 9.13 we show the transformer function rdouble that implements a righthand double rotation. This rotation rearranges three of the tree’s nodes, while preserving the descendant subtrees and parent node. To do this, the transformer function has to include the surrounding nodes in the lefthand side, with the result that this transformer is larger than the abstract specification of a rotation. rdouble also makes use of our extended syntax for variable matching. By covering more cases this substantially reduces the number of reducers 198
transformer rup avltree (avl *x){ left (rt,h1,h2,n1,n2) { avlroot rt; bbr n1; stack h1, h2; rt.p => h2; h2.b => h1; h2.c => n2; h1.c => n1; n1.r => n2; } right (rt,h1,n1,n2) { rbr n1; rt.p => h1; h1.c => n1; n1.r => n2; } }
1
• r 2
h
3
1
+ r
⇒ h
h
3
2
Figure 9.12: Transformer rup and corresponding graph transformation rule. that must be defined. At most a single instance of the labelling can match any single target shape structure, so this extension does not introduce any unwanted nondeterminism into transformers. As with reducers, this extension does not require a change to the semantics. For any transformer function written using this new syntax, we can construct a set of corresponding transformers written in our core syntax by instantiating the node and variable names with all possibilities satisfying the signature. This set of transformers can then be inserted into a C disjunction in the same way as in the code above. The tree rotation shown in Figure 9.13 illustrates an advantage of CGRS as a language for specifying complex graph algorithms. Because its syntax is close to graph transformation rules, very large rewrites can be defined in a way that is close to their abstract specification. The CGRS function rdouble corresponds syntactically to the underlying AVL tree rotation. In contrast, the corresponding C code would be quite different from the specification of the rotation, making it more difficult to specify and debug. The remaining righthand rotations relim and rsingle are shown in Fig. 9.14 and Fig. 9.15 respectively. We also require the lefthand mirrors of the rotations, lup, ldouble, lelim and lsingle. These have been omitted as they are very similar to the transformers that have already been defined. 199
transformer rdouble avltree (avl *x){ left (rt,h0,h1,h2, n1,n2,n3,n4,n5) { avlroot rt; stack h0, h1, h2; [rbr, lbr, bbr] n0; rbr n1; lbr n2; [rbr, lbr, bbr] n3; rt.p => h2; h2.c => n2; 0 x h h2.p => h1; h1.c => n1; v h1.p => h0; 1 + h h0.c => n0; n0.[v] => n1; r n1.r => n2; 2 + h n2.l => n3; n3.l => n4; l n3.r => n5; 3 y } r l right (rt,h0,h1,n1, n2,n3,n4,n5) { 4 5 bbr n1,n2; rt.p => h1; h1.c => n3; h1.p => h0; h0.c => n0; n0.[v] => n3; n3.l => n1; n3.r => n2; n1.r => n4; n2.l => n5; } }
0
x
h
y
h
v 3
⇒
l r •
•
r
l
1
4
2
5
Figure 9.13: Transformer rdouble and corresponding graph transformation rule.
200
transformer relim avltree (avl *x){ left (rt,h1,h2,n1,n2) { avlroot rt; lbr n1; stack h1, h2; rt.p => h2; h2.b => h1; h2.c => n2; h1.c => n1; n1.r => n2; } right (rt,h1,n1,n2) { bbr n1; rt.p => h1; h1.c => n1; n1.r => n2; } }
1
− r 2
h
3
1
• r
⇒ h
h
3
2
Figure 9.14: Transformer relim and corresponding graph transformation rule.
9.4
Code size in CGRS
The large AVL tree example given in §9.3 illustrates one of the problems with CGRS as a language – the specification of rewrites are typically quite large. To compare this to standard C, let us consider the transformer rup given in Fig. 9.12. We can implement this in plain C by the following function. This checks that the balance factor of the treenode is correct, then pops the stack. It returns NULL if matching fails. We assume the presence of a separatelyallocated stack. With a tree including backpointers, the call to free could be removed. stack *rup_c (stack *h2) { node *n1; stack *h1; h1 = h2>back; n1 = h1>node; if (n1.balance = 0) { n1.balance = 1; free(h2); return h1;
201
transformer rsingle avltree (avl *x){ left (rt,n0,n1,n2,n3, h0, h1, h2) { avlroot rt; [lbr,bbr,rbr] n0; rbr n1, n2; rt.p => h2; h2.c => n2; h2.p => h1; h1.c => n1; h1.p => h0; h0.c => n0; n0.[v] => n1; n1.r => n2; n2.r => n3; } right (rt,n0,n1,n2, n3,h0, h1) { bbr n1,n2; rt.p => h1; h1.c => n2; h1.p => h0; h0.c => n0; n0.[v] => n2; n2.r => n1; n1.r => n3; } }
x
0
h
0
v 1
x
h
•
h
v + r 2
r 3
+
h
2
r
⇒ h
1
• r 3
Figure 9.15: Transformer rsingle and corresponding graph transformation rule. } else return NULL; }
Clearly the code is shorter in C – 12 lines, compared to 19 for the CGRS version. The difference in size comes from two sources. First, CGRS requires that removed nodes have all edges specified in their lefthand sides, which means that the node n2 has to be explicitly referenced. Second, CGRS reproduces several pieces of information, such as the shared graph between the left and righthand sides. This increase in codelength seems to be typical when comparing CGRS transformers with similar C functions. Let us consider a second example – the transformer tree goleft defined in Example 9.4. The following code 202
implements an equivalent operation in C. node *tree_goleft (node *h2) { if (node>type = branchnode) return node>left; else return NULL; }
This function is only 6 lines, compared to 12 for tree goleft. If we assume that the input to the function is a branchnode, we can reduce this even further, down to a singleline function. Care must be taken when comparing C with CGRS, because the matching process for a CGRS transformer is semantically more restrictive than pointer rewriting. A C pointer rewrite performs no checks about the structure being rewritten before rewriting, while a CGRS transformer checks a match for injectivity and conformance to the required pattern of labels before performing an update. To see this, consider the rewrite rdouble given in Fig. 9.13. Any corresponding code written in C must check that the tree matches the whole of the lefthand side of the corresponding rule. While the transformer function is quite large, the corresponding C code must also be large. Another source of codesize increase in CGRS is the fact that a large number of similar rules are often required. For example, to encode a full rebalancing function for AVL trees, the function would require both rup and lup functions, going up right branches and left branches. The same holds for rdouble and ldouble. It is currently difficult to express similar but distinct rewrites in a compact style in CGRS. This is caused by the limited amount of parametricity currently available in CGRS. C function in contrast can express several different cases internally in a function. Signature declarations are however typically quite similar in size to the corresponding structure declarations in a C program. A shape declaration is obviously not present in a C program, but a shape declarations avoids the need for an initialisation function for a structure. We hope that these problems can be can be ameliorated in future work. One possibility is that CGRS programs could be written graphically, in a notation matching the graph transformation syntax. This would remove the problem of the verbose syntax by hiding it entirely. Further development of 203
the syntax may also give ways to parametrise more successfully, to reduce the number of explicit cases.
204
Chapter 10
Semantics of CGRS and shape safety In this chapter we give a concrete and abstract semantics to CGRS programs. The chapter is structured as follows: Section 10.1 describes how we extract graph reduction specifications and graph transformation rules from the constructs of CGRS. Section 10.2 defines the operational semantics of CGRS constructs by mapping them to fragments of standard C. Section 10.3 defines an operational semantics for a small fragment of C called µC, used in our proof of correctness. In Section 10.5 we use this semantics to give a proof of the correctness of the two mappings described in Sections 10.1 and 10.2. In Section 10.6 we use the results derived in previous sections to define the shapesafety guarantees given by CGRS. While we have defined an operational semantics for CGRS, the language has not been implemented. In Section 10.7 we discuss some of the possibilities for an implementation of CGRS.
10.1
Extraction of GRSs and rules from CGRS
Our main aim in designing CGRS is to enable static checking for shapesafety, using the checking algorithm described in [3]. To use this algorithm, we must extract from the constructs of the CGRS program corresponding GRSs and graph transformation rules. In this section we define the extraction function G[[−]] that maps from the domain of CGRS programs to the domain of graph transformation. 205
G[[−]] operates over the domain of CGRS declarations – signature declarations, shape declarations and transformer function declarations. Signature declarations are mapped to graph signatures, shape declarations to graph reduction systems and transformer function declarations to graph transformation rules. CGRS constructs have been designed to be syntactically extremely close to graph transformation rules, graph signatures, and GRSs, so G can be defined quite informally without the risk of ambiguity. To reason about CGRS, we abstractly represent CGRS shape declarations. Definition 10.1 (abstract declaration). In order to refer unambiguously to elements of a concrete CGRS declaration, we break them down into an abstract declaration. In an abstract declaration σ = hσg , σh , σr i, σg is the shape declaration body, σh the associated signature, and σr is the set of reducers. We also assume several fixed values as part of the abstract declaration. Let G be the name of the shape and S the name of the signature. Let N1 , . . . , Nm be the names of the nodetypes defined in σg . For each node Ni , let ENi be the set of edges assigned for node Ni and VNi the set of value fields. Let R be the name of the unique root node type, and VR the set of edge fields for R. Example 10.1 (abstract declaration). For the concrete shape declaration shown in Fig. 9.4, we can construct the abstract declaration σ = hσg , σh , σr i. Here σg refers to the concrete definition of the shape tree given in Figure 9.4, σh refers to the signature bin given in the same figure, and σr refers to the definitions of branchleaf and auxroot given in Fig. 9.7. Of the associated given values, G is tree and S is bin. The set N1 , . . . , Nm of node names is {treeroot, branchnode, leafnode}. R is treeroot. The set of edges can be determined by examining the definitions in bin, so for branchnode Ebranchnode is {l, r}. Definition 10.2 (corresponding graph signature). Given an abstract declaration σ we define a corresponding graph signature Σσ = hCσ , inσ , outσ i, with set of vertex labels CV defined as the set of nodetype labels defined in σ, and set of graph edge labels CE defined as the set of all CGRS edgefield labels used in any of the nodetypes. The outgoing edge function for signature Σσ is defined for node label Ni and edge label l so that, outσ (Ni , l) = 1 206
if l ∈ ENi , and outσ (Ni , l) = 0 otherwise. The incoming edge function is defined so that inσ (Ni , l) = ⊥ for all Ni and l. The graph signature Σσ abstracts away from values held in the nodes. This is because our shape checking approach verifies only the shape properties of structures – values are ignored. For this reason, nonedge fields in a CGRS nodetype declaration have no counterparts in the resulting graph signatures. Example 10.2 (corresponding graph signature). Given the abstract declaration σ defined in Example 10.1, we can define a corresponding graph signature Σσ = hhCσ,V , Cσ,E i, inσ , outσ i. Here Cσ,E = {top, aux, l, r} and Cσ,V = {branchnode, leafnode, treeroot}. The inbound edge function in(−, −) is ⊥ for all values. The outbound edge function out(lv , le ) is 1 if (lv , le ) ∈ { (branchnode, l), (branchnode, r), (treeroot, top), (treeroot, aux)} and 0 otherwise. Graph transformation rules are constructed by G from the reducers in σr . The left and righthand side graphs of the rule are first constructed from the left and right blocks of the transformer by mapping each nodename to a node in the corresponding graph. The interface graph for the rule is then constructed from the nodes named in both of the transformer’s node lists. Example 10.3 (rule abstraction). Figure 9.6 in §9.2 shows the graph transformation rule produced by applying G to the transformer tree goleft. The ‘edges’ and ‘nodes’ of the transformer function are abstracted to corresponding graph edges and graph nodes in the rule, and the interface is constructed from the shared node names. (We use the letter B to stand for branchnode and L for leafnode). Because G abstracts away value fields, the graph transformation rules it constructs only model structural rewrites and ignore value field rewrites. This means that the graph transformation rules produced by G abstractly model the concrete transformer functions. 207
transformer bin tree_insert ( tree *t, int *inval ) { left (rt, n1) { treeroot rt; leafnode n1; rt.aux => n1; } right (rt, n1, l1, l2) { branchnode n1; leafnode l1, l2; rt.aux => n1; n1.l => l1; n1.r => l2; n1.val = *inval; } }
aux aux
G
−→
L
B
⇒
l
1
L
1
r L
Figure 10.1: CGRS transformer function tree insert and corresponding graph transformation rule produced by applying G. For example, Figure 10.1 shows the binary tree insertion transformer tree insert and the graph transformation rule constructed from it by G (this rule was originally shown at the start of Chapter 9 in Figure 9.2). In this rule, G ‘forgets’ the integer value field val in node n1. Shape declarations are mapped by G to graph reduction specifications. The accepting graph for the new GRS is constructed from the accept block in the same way as the left and righthand blocks of a transformer function. Reducers are syntactically almost identical to transformer functions, and they are mapped to graph transformation rules in the exactly the way we have described above.
10.2
Translating CGRS to C
In this section we define an operational semantics for CGRS constructs by translating them to C code. Only the extra constructs defined in CGRS (transformers, signatures, and shape declarations) result in modified C code, while the pure C portions of a CGRS program are left unmodified by the translation. After CGRS constructs have been translated, the resulting C code can be compiled and executed in the normal way. As a result, programmers can mostly treat CGRS constructs as if they were normal C
208
functions and types. The function C[[−]] takes as its input CGRS constructs and produces blocks of C code. The translation of shapes, signatures and transformer functions performed by C is discussed below, but note that reducers are not executable and so are not translated to C code. The translation function C[[−]] implements the CGRS constructs in µC, a fragment of C. This fragment, which is defined in Section 10.3, includes restricted conditional statements and assignment to heap structures, but omits almost all control flow, including function calls. The aim of translating CGRS to a restricted language such as µC is to allow a simple proof of correctness. µC has an operational semantics defined in Section 10.3, which we use in Section 10.5 to prove the correctness of the function G with respect to C. Figure 10.2.1 shows the definition of C[[−]] for signatures, Figure 10.2.2 shows it for shape declarations and 10.2.3 shows it for transformers. The definitions are annotated with comments identifying different parts of the definitions written in the C99 ‘//’ style [45]. To allow substitution, the code includes logical variables. Given code fragments C and C’, C[x \ C′ ] stands for C with variable x replaced by C’. The notation [C]x∈vals stands for the concatenation of C[x \ C1 ], C[x \ C2 ] . . . , where vals = {C1 , C2 , . . .}. If vals is an sequence of values rather than a set, then the code fragments are concatenated in sequenceorder. Otherwise, the ordering is arbitrary.
10.2.1
Signature translation
Figure 10.2 shows the definition of the translation function C[[−]] over CGRS signature declaration. We use the bin signature and tree shape given in Figure 9.1 as the running example in this section and the next. The complete code produced by C[[−]] when applied to bin and tree is given in Figure 10.3. From a signatures declaration in CGRS, C[[−]] constructs a set of C struct types that form the basic building blocks of shape structures. These datastructures are of two kinds. First, C[[−]] constructs a set of types corresponding directly to individual nodetypes. Second, C[[−]] constructs a pair of ‘wrapper’ datatypes that can be used polymorphically to stand for any node type. These wrapper types record extra node data and simplify node 209
// Node structures
[ N J N K ]N∈{N1 ...Nn } // Wrapper structure
t
signature S C { N1 ;. . . Nn ; }

=
typedef struct S struct { int type; int indeg; S union *node; } S struct; // Wrapper union
typedef union S union { [ struct n n; ]n∈N odes } S union; where: N odes = nodetypes defined in {N1 . . . Nn }
N J nodetype N { F } K T J edge E1 . . . En K
= =
struct N { T J F K }
[ S struct *e; ]e∈{E1 ...En }
Figure 10.2: Translation from CGRS signatures and data types to C type declarations. retyping. The datatypes defined from a signature declaration are held in a container structure. Container structures are then passed to transformer functions to manipulate the shape structure. Container structures are defined by C[[−]] from shape declarations (see §10.2.2 below). Individual declarations for terminal node types are translated into C struct declarations. Edge fields are translated into pointers of type *S struct, where ‘S’ is the name of the signature. This S struct type is the wrapper type used to stand for any nodetype in signature S, so edge fields in a node type can point to any type of node in the signature. Nonedge fields in a nodetype declaration are included directly as fields in the resulting struct. In the signature bin for our running binary tree example, branch nodes are declared as the nodetype branchnode. The declaration is translated by C[[−]] into the struct declaration given below.
210
struct treeroot { struct bin_struct *top; struct bin_struct *aux; };
tree *newgraph_tree () { tree *new; bin_struct *rt_s; bin_struct *leaf_s; bin_union *rt_u; bin_union *leaf_u;
struct branchnode { struct bin_struct *l; struct bin_struct *r; int val; };
rt_s = malloc(sizeof(bin_struct)); rt_u = malloc(sizeof(bin_union)); rt_s>node = rt_u; rt_s>type = TREEROOT; rt_s>indeg = 0;
struct leafnode { };
typedef union bin_union { struct treeroot treeroot; struct branchnode branchnode; struct leafnode leafnode; } bin_union;
leaf_s = malloc(sizeof(bin_struct)); leaf_u = malloc(sizeof(bin_union)); leaf_s>node = leaf_u; leaf_s>type = LEAFNODE; leaf_s>indeg = 0;
typedef struct bin_struct { int type; int indeg; bin_union *node; } bin_struct;
rt_u>treeroot.top = leaf_s; rt_s>indeg = rt_s>indeg + 1; rt_u>treeroot.aux = leaf_s; rt_s>indeg = rt_s>indeg + 1; new = malloc(sizeof( tree )); new>root = rt_s; return new;
typedef struct tree { bin_struct *root; } tree;
}
Figure 10.3: Source code produced from the shape declarations bin and tree. node branchnode { edge l, r; int val; }
C
−→
struct branchnode { struct bin_struct *l; struct bin_struct *r; int val; };
For each signature declaration, the translation function C[[−]] also defines a pair of wrapper datatypes S struct and S union that together form a generic nodetype for nodes in the S signature. In other words, these datatypes can simulate any of the nodetype structs we have defined, such as 211
branchnode. They also provide a common interface to auxiliary data stored in the structure. The wrapper structures are used in pairs. An instance of the S struct datatype records auxiliary data and points to an instance of the S union datatype, which records the outgoing edges for the node. Such an instance in the heap is called a nodepair. Auxiliary data is required in shape structures because simple pointer structures composed of structdeclared datastructures and pointers do not include enough information to support our general model of graph transformation. The wrapper structures record two pieces of auxiliary data for each nodepair: (1) the nodetype currently simulated, and (2) the number of pointers in the rest of the shape structure that point to the current nodepair. Access to the type of a node is necessary to allow untyped edges. In simple C pointerstructures, the type of a pointer specifies the type of its target in the heap. This is necessary because the type of a heap element is not recorded at runtime. Unlike C pointers, edges in GRS graphs do not record the type of the object pointed to – an edgefield can point to a node of any type defined in the signature. To implement this in C, edgefields are all typed *S struct for signature S, and individual node types are simulated by the wrapper structure. The type simulated is recorded in the type field. For simplicity, the types of nodes are recorded as integer typecodes rather than as strings. The translation assumes a globallyconsistent injective function typenum(−) from nodetype names to integers. When deleting nodes, we must prevent dangling pointers. In the graphtransformation framework this is enforced by the dangling condition, which prohibits node deletion that result in dangling edges (see §2.2). To check the dangling condition given a matching morphism, it suffices to know the number of incoming and outgoing edges to the matched node. To implement this in C, it is sufficient to know the number of incoming pointers from other structures in the shape structure, as outgoing pointers are limited by the number of fields. The use of wrapper structures enables a third feature of graph transformation that is not present in simple C pointer structures. In C it is not in general possible to alter the type of a heap object inplace. This is because 212
different heap datastructures are of different sizes, and so retyping objects may violate memory integrity. In graph transformation rules however, we want to be able to relabel nodes inplace, which corresponds to node retyping in the C domain. This is implemented by wrapping all of the nodetypes declared by a signature into a single union. Note that the auxiliary data stored in the wrapper structures must be initialised when the node is created, so for safety reasons the creation of nodes should only occur through the application of transformer functions. The wrapper structure S struct records the typecode of the node in the integer field type and the number of incoming edges in field indeg. The node field points to the wrapper union type S union. For the binary tree signature bin, the following wrapper structure bin struct is constructed. typedef struct bin_struct { int type; int indeg; bin_union *node; } bin_struct;
The wrapper S union is constructed so both the name and type of each field corresponds to a declared nodetype, as defined above on page 210. As a result, such a union can simulate any of the node types in the signature. For the binary tree example, there are three node types, treeroot, branchnode and leafnode, which results in the following union declaration. typedef union bin_union { struct treeroot treeroot; struct branchnode branchnode; struct leafnode leafnode; } bin_union;
The elements of this union are accessed through the standard C syntax for unions. So to access the val field of a branchnode structure through a variable x pointing to a bin uniontyped heap object, the syntax will be x>branchnode.val.
10.2.2
Shape translation
Figure 10.4 shows the definition of the translation function C[[−]] over CGRS shape declarations. 213
// Container type for shape structures
typedef struct S { C struct *root; } S; // Shape structure constructor
} u shape S C { w accept w w { A1 ;. . . An ; } w C w rules w w { P1 ;. . . Pm ; } ~ v }
=
S *newgraph S () { S *new; [ C struct *v s ; ]v∈N odes [ C union *v u ; ]v∈N odes [ MJ T V K ](T V)∈{A1 ...An } [ IJ s.e=>i K ](s.e=>i)∈{A1 ...An } new = malloc(sizeof( S )); S>root = R s; return new; } where: R is the roottyped node name N odes is the set of node names in {A1 . . . An }
MJ T V K
=
IJ V.E => T K
=
V V V V V
s = malloc(sizeof(T struct)); u = malloc(sizeof(T union)); s>node = V u; s>type = typenum(T); s>indegree = 0;
V u>t.E = T s; T s>indeg = T s>indeg + 1; where t is the nodetype of source node V
Figure 10.4: Translation to C for CGRS shapes. A CGRS shape declaration is mapped by C[[−]] to a definition of a toplevel container type for shape structures and a constructor function for the shape. The container type is the main handle for passing shape structures to transformer functions. The constructor function initialises a structure that corresponds to the shape’s accepting graph. The container type has the same name as the shape declaration, so in our running example of a binary tree signature, the container type tree is declared. The only field in the container type is the root field, pointing to the nodetype corresponding to the graph root. Note that this node must 214
be of the signature’s root type. In the binary tree example, the following shape type is constructed. typedef struct tree { bin_struct *root; } tree;
The advantage of defining a container for a shapestructure is that the C typesystem can be used to keep track of the shape structure’s type. From a shape declaration S, the function C[[−]] defines the constructor function newgraph S. For example, for the tree shape tree C[[−]] builds a constructor function called newgraph tree. The constructor function for a shape constructs the shapestructure corresponding to the accepting graph. This requires the construction of three kinds of heap element: (1) the toplevel shape container type, (2) instances of the appropriate nodestypes defined from the signature and (3) edges to give the correct pointers between the new nodes. The equivalent in the heap of a graph node is a nodepair, consisting of a node structure and a node union. For each node in the accepting graph, the constructor allocates a nodepair using malloc, then points the node field of the structure at the union and initialises the fields of the structure. For example, for the leafnode leaf in the binary tree example, this results in the following code (here LEAFNODE stands for the integer typenum(leafnode)): leaf s = malloc(sizeof(bin struct)); leaf u = malloc(sizeof(bin union)); leaf s>node = leaf u; leaf s>type = LEAFNODE ; leaf s>indegree = 0; The edges of the graph are instantiated and the indegree fields of the nodepairs updated. Finally the constructor allocates the shape container type using malloc, and the root field of the container is pointed at the newlyallocated roottyped node pair. The location of the new toplevel structure is returned by the constructor, so that it can be recorded in a variable by the programmer.
215
10.2.3
Transformer translation
We now define the transformation function C[[−]] from CGRS transformer functions to C. The formal definition of this function is given in Figure 10.5. We use the tree goleft transformer function given in Figure 9.6 as the running example in this section. The complete code produced by C[[−]] is given in Figure 10.6. From a transformer function declaration, C[[−]] constructs a C function with the same name. The function takes as its first argument a shape container structure holding the shape structure to be manipulated. Transformer functions resemble graph transformation rules quite closely. As with the a graphtransformation rule, there are three phases in a transformer application: 1. Variables are constructed corresponding to a matching morphism between the transformer’s lefthand side and the nodes in the shape structure. 2. The dangling condition is checked for the constructed variables. 3. The image of the lefthand side is transformed into the image of the righthand side by creating and deleting nodes and modifying the content of preserved nodes. The code in Figure 10.5 includes comments identifying these three phases. The function C assumes an edge enumeration e1 . . . en of the edges in the transformer’s lefthand side. This enumeration must be ordered so that for all 1 ≤ i ≤ n, either the source of ei is the root node, or ∃j. 1 ≤ j < i∧s(ei ) = t(ej ). Any edge enumeration satisfying the property can be chosen. Due to the reachability and rootedness sideconditions on transformer functions (given in section 9.2.3) at least one such edge enumeration must exist. (See Chapter 3 and especially §3.2 for more on graph transformation algorithms which make use of edge enumerations, and on conditions ensuring the existence of edge enumerations.) The translation to C also assumes the existence of two typing functions tl and tr . These take a node name and return the nodetype of a node in, the left and righthand sides of the transformer function respectively, as given by the nodetype declarations.
216
bool F (S *G, A) { // Matching
[ C struct *v n = NULL; ]v∈Nl ∪Nr [ C union *v u = NULL; ]v∈Nl ∪Nr K s = G>root; K u = K s>node; [ LJLK ](s.e=>i)∈{L1 ...Ln } [ if ( x s == y s ) return FALSE; ](x,y)∈P airs // Dangling Condition
u
transformer w F C (S *G, A) { w w left(Nl ) w w { L1 ;. . . Ln ; } Cw w right(Nr ) w w { R1 ;. . . Rm ; } v }
} ~
[ if ( d s>indeg != CL (d) ) return FALSE; ]d∈Nl /Nr // Rewriting
free( *d u ); free( *d s ); ]d∈Nl /Nr p>type = typecode(tr (p)); ]p∈Retype MJtr (a), aK ]a∈Nr /Nl RJRK ]R∈{R1 ...Rm } n>indeg = n>indeg + (CR (n) − CL (n)); ]n∈Nr return TRUE;
[ [ [ [ [
=
} where: K is the roottyped node name CL (i) =  {(s, e)  (s.e=>i) ∈ {L1 . . . Ln }}  CR (i) =  {(s, e)  (s.e=>i) ∈ {R1 . . . Rm }}  Retype = {p ∈ Nl ∩ Nr  tl (p) 6= tr (p)} P airs = {(x, y) ∈ Nl × Nl  x 6= y}
LJ S.E => T K
=
T s = S u>tl (S).E; if ( T s>type != tl (T ) ) return FALSE; else if (T u == NULL) T u = T s>node ; else if ( T u != T s>node ) return FALSE;
RJ S.E => T K
=
S u>tr (S).E = T s;
= =
S u>tr (S).V = X; X = S u>tr (S).V;
RJ S.V = X K RJ X = S.V K
Figure 10.5: Translation from CGRS to C for transformers.
217
int tree_goleft( struct tree *t ) { bin_struct *rt_s = NULL; bin_struct *n1_s = NULL; bin_struct *n2_s = NULL; bin_union *rt_u = NULL; bin_union *n1_u = NULL; bin_union *n2_u = NULL; rt_s = t>root; rt_u = rt_s>node; n1_s = rt_u>treeroot.aux; if ( n1_s>type != BRANCHNODE ) return FALSE; else if (n1_s == NULL ) n1_u = n1_s>node; else if (n1_u != n1_s>node ) return FALSE; n2_s = n1_u>branchnode.l; if ( n2_s>type != BRANCHNODE ) return FALSE; else if (n2_s == NULL ) n2_u = n2_s>node; else if (n2_u != n2_s>node ) return FALSE; if (rt_s == n1_s) return FALSE; if (rt_s == n2_s) return FALSE; if (n1_s == n2_s) return FALSE; (rt_u>treeroot).aux = n2_s; (n1_u>branchnode).l = n2_s; return TRUE; }
Figure 10.6: tree goleft.
Source code produced from the transformer function
218
The function constructed by C[[−]] simulates matching of the lefthand side using a set of matching variables. For each named node n on the lefthand side, variables n s and n u are declared. The aim of the matching code is to assign to each pair of variables the location of a nodepair such that all of the lefthand side variables together correspond to a matching morphism for the lefthand side. The function first assigns NULL to all of the matching variables. It then begins matching by assigning to the root node variable pair the location pointed to by the root field of the shape structure. If the structure is a valid shape structure corresponding to a rooted graph, then the root of the structure must exist. When the root nodepair has been attached to the corresponding variable, the nonroot matching process is generated by the L constructor function Matching proceeds by following edges outgoing from previously matched nodes, starting with the root. Edges are matched in the order that they appear in the edge enumeration. By the construction of the edge enumeration no node can occur as the source of an edge before it occurs as a target of some edge. As all nodes in the lefthand side of a transformer are reachable from the root, each node will eventually be matched by this process, unless matching fails. In normal graph transformation, this kind of incremental graph matching may require backtracking. However, matching for a transformer function is deterministic for the reasons outlined in §9.2.1, so if matching fails at any edge, then no matching morphism exists and the whole process fails. Matching of an edge starts by selecting the alreadyassigned variable for the source node’s nodeunion. It then assigns to a the target variable the location held in the outgoing edge field for the source nodeunion. There are then three possible cases: 1. The target structure may be of the wrong nodetype. This is checked by comparing the expected typecode (retrieved using the typenum function) to the type field of the target. If the typecode is not correct, the match fails. 2. The matching union variable may be NULL. This shows that this lefthand side node has not been previously matched. In this case, the matching variables are assigned the value of target. 219
3. The matching union variable may have been assigned previously during the matching process. This occurs when two edges in the lefthand side point to the same node. Both edges will be followed when matching, but only the first results in an assignment. The matching process checks by comparison that the current edge points to the same nodepair as the stored value held in the variable. Otherwise the match fails. The edge n1.l => n2 in tree goleft results in the following code. This code first assigns the value held in n1 u>branchnode.l to the variable n2 s. It then checks the three possible cases using an if statement, and responds appropriately depending on the value held in the matching variable. n2_s = n1_u>branchnode.l; if ( n2_s>type != BRANCHNODE) return FALSE; else if ( n2_u == NULL ) n2_u = n2_s>node; else if ( n2_u != n2_s>node ) return FALSE;
Once all nodes of the lefthand side of a transformer have been matched to corresponding matching variables, the function checks by comparison that the values in the matching variables are pairwisedistinct. This corresponds to the requirement that a matching morphism must be injective. The dangling condition is then checked for any nodes that are removed by the rule. As discussed Section 10.2.1, given a matching morphism h the dangling condition can be checked for a lefthand side node n by comparing the number of incident edges to the n in the lefthand side graph to the number of edges incident to the hostgraph node hv (n). The same method is used to checking the dangling condition for a set of matching variables. The code used to check the dangling condition for a node n1 is as follows, if the number of incoming edges according to the lefthand side is 1. (This example is not taken from tree goleft because no nodes are deleted in tree goleft.) if (n1_s>indeg != 1) return FALSE;
Once a set of matching variables satisfying the dangling condition have been constructed, the nodepairs identified by the matching variables are 220
modified to match the righthand side of the transformer function. Five kinds of update are performed: (1) node deletion, (2) node retyping, (3) node creation, (4) edge construction, and (5) updating auxiliary data. The order is important: nodes must be constructed before their edges can be constructed. Node deletion uses the free operator in C. Nodes can be safely deleted because the dangling condition ensures that no dangling edges will be created in the shapestructure. Nodepairs are retyped (the equivalent of relabelling nodes in a graph transformation rule) by updating the type field of their node structure. The sideconditions on transformer functions described in Section 9.2.3 ensure that when a node is retyped, all the edgefields required by the signature will also be added. New nodes are constructed using the C allocation function malloc, and their auxiliary data is then updated. The code for node allocation and initialisation used here is the same as that used in the definition of a constructor function in §10.2.2. Both use the codegeneration function M[[−]]. Finally, all edges in the righthand side are created (or possibly recreated if they already existed on the lefthand side) by assigning new values to the pointer fields in the righthand side nodepairs. The indeg fields of all righthand side nodes are updated based on edges added and deleted by the transformer. In the tree goleft example, the n2 node has its incoming degree increased by one, to reflect the new incoming root pointer. n2_s.indeg = n2_s.indeg + 1;
The return value of the transformer depends on the success or otherwise of the matching process. If matching fails or the dangling condition is not satisfied, the transformer returns FALSE and the shape structure passed to the function is left unmodified. Otherwise the shape structure is updated and the function returns TRUE to indicate that it has succeeded.
10.3
Syntax and semantics of µC
We want to show that the extraction function G produces rules that correctly model the operational semantics of transformer functions produced by C. To prove this correctness property, we need a formal operational semantics for 221
prog
::=
statement∗
statement
::=
if ( condition ) operation else operation  operation
operation
::=
location = term;  var.field = val;  return val ;  free( * var );
condition
::=
location == val  location != var  val != int
term
::=
location  malloc(sizeof( cons ))
location
::=
var  var>field  var>field.field
val
::=
var.field + int  int
Figure 10.7: Abstract syntax of µC. transformer functions. The operational semantics of a CGRS transformer function is defined by mapping it to a corresponding C function. Unfortunately, C’s standard operational semantics is given informally. There is no widelyaccepted formallydefined operational semantics for C.1 Our solution is to define a microsemantics for C, based on [48]. For reasons of simplicity this semantics covers only a very small fragment of C that we call µC. We define an operational semantics for enough of C to implement transformer functions, while avoiding the complexity of a complete C semantics. Any full semantics of C should conform to our semantics over the domain of µC. Consequently any correctness results derived from the semantics for µC will carry over to the full semantics for C. The concrete syntax of µC is given in Figure 10.7. The sets of µC identifiers var, const and field are defined as members of the language of strings Σ+ . The controlflow operators available in C are mostly omitted from µC. The fragment includes an ifelse construct, but omits loops, case statements and procedure calls. Programs in µC are assumed to model blocks of C that are inside function definitions (or inside the standard main() function). For this reason, µC programs can return values. The fragment includes a return keyword that terminates execution and gives the whole program a return value. The only kind of primitive value that µC permits is the integer type. As 1
Full formal semantics have been proposed, such as [37] and [61]. These semantics for C are quite complex, and so too difficult to use in our proof of correctness.
222
with all the restrictions on µC, this is intended to reduce the complexity of the semantics. The proof results given in Section 10.5 should still hold even if the semantics was extended to permit more primitive types. µC’s memory model consists of a set of variables, and a heap. The heap consists of locations of two kinds: structs and unions. Structs are defined by a typename and a set of field names. Unions associate fields with structure types. Unlike general C, unions in µC are assumed to only have structtyped fields. Heap locations are integers, so integer values can be used as references to heap locations. µC restricts C’s syntax for accessing heap data so that it is possible to distinguish syntactically between dereferencing a field of a struct not held in a union and dereferencing one held in a union. The first case is written var>field, while the second is written var>field.field. In full C, these cases are distinguished semantically, at run time. That is, the same syntax can have a different effect depending on the type of the pointer. However, the heapaccess cases permitted in µC should have the same effect in correctlytyped C. µC omits variable and type declarations. The semantics assumes a fixed set of defined types and defined variables. This reduces the complexity of the semantics. C’s variable and type declaration mechanism is quite simple however, so µC could easily be extended to cover such declarations. We assume that we have a preprocessor that can replace predefined strings with other strings. Such macro values are written in all capitals, following the normal C convention. We assume that we have defined the standard values TRUE as 1 and FALSE as 0. The µC semantics is defined in the structural operational style [60]. This style simplifies some of the proofs in Section 10.5 by exposing the operational details of execution. The semantics of µC constructs manipulate states, which are defined over a type schema. A type schema defines the fieldnames for µC structs, and associates union fields with struct types. Type schemas are defined using the following sets of primitive elements: A set Var of variable names. A set Uni of union type names. A set Str of struct names. A set Loc ⊆ Int of locations. A set of field names Fld used to index the fields of unions and structures. The set of types Type is defined as Str ∪ Uni . Definition 10.3 (µC type schema). A type schema τ = hτu , τs i is composed 223
of two functions. The partial function τu : Uni × Fld ⇀ Str associates with a pair (u, f ) of union and field name a corresponding struct name. The total function τs : Str → P(Fld ) associates with a struct name a set of field names. Given a type schema τ we can define a µC state. Each state records the contents of both program variables and the heap, and also keeps track of running values used during execution of a program. Definition 10.4 (µC state). A µC state s = hrets , ress , vars , typs , f lds i consists of the following: rets : Bool, a value recording whether the program should return; ress : Int, a value used to record the result of evaluating a construct; vars : V ar ⇀ Int, a partial function mapping variable names to values; typs : Loc ⇀ Type, a partial function recording the type of memory locations, assuming they are defined; f lds : Str × F ld × Loc ⇀ Int, a partial function recording the fields for the structure held at a particular location. Given a state s, we write s[r : n] to replace element r of the state with n. If r is a function, we write s[r : v 7→ n] to make r(v) = n in the state. A type schema restricts the permitted states. States that conform to a type schema are said to be welltyped. Definition 10.5 (welltyped state). Let τ = hτu , τs i be a µC type schema. A memory state s is welltyped with respect to τ if for all locations l ∈ Loc, either 1. typs (l) ∈ Str and f lds (t, f, l) is defined for all f ∈ τs (typs (l)) and undefined otherwise, or 2. typs (l) ∈ Uni and for some n ∈ Fld , f lds (t, f, l) is defined for t = τu (typs (l), n) and all f ∈ τs (τu (typs (l), n)), and is undefined otherwise, or 3. typs (l) = ⊥ and f lds (t, f, l) = ⊥ for all t, f . Example 10.4 (µC type schema, state, welltyped state). Let S1 and S2 be struct names, F1 , F2 , F3 and F4 field names, and U1 a union name. Now we define a type schema τ as follows. τ = h{hU1 , F1 i 7→ S1 , hU1 , F2 i 7→ S2 }, {S1 7→ {F4 }, S2 7→ {F3 }}i
224
The following µC state s is welltyped with respect to τ . Note that f lds assigns a structure value to l3 , even though it is in fact a union. S3 is the type that l3 is currently simulating. rets = ff
ress = 0
vars = {x 7→ l1 , y 7→ 1, z 7→ l3 }
typs = {l1 7→ S1 , l2 7→ S2 , l3 7→ U1 } f lds = {hS1 , l1 , F4 i 7→ l3 , hS2 , l2 , F3 i 7→ 0, hS2 , l3 , F3 i 7→ l1 } If we replace f lds with the following alternatives, the state is no longer welltyped. f lds = { hS1 , l1 , F4 i 7→ l3 , hS1 , l1 , F2 i 7→ 1, hS2 , l2 , F3 i 7→ 0, hS2 , l3 , F3 i 7→ l1 } This definition violates τ by assigning a value to field F4 of location l1 , which is typed S1 . This field does not appear in the type schema for the struct S1 . The following state is also not welltyped. f lds = { hS1 , l1 , F4 i 7→ l3 , hS2 , l2 , F3 i 7→ 0, hS2 , l3 , F3 i 7→ l1 , hS1 , l3 , F4 i 7→ l2 } This definition violates τ by giving location l3 an inconsistent value, by giving it a value as a field of S2 and of S1 . Unions must only have a single assigned type, even though they can simulate different types at different times. The rules of the µC semantics are defined in Figure 10.8, Figure 10.9 and Figure 10.10. The semantics operates on pairs of values hc, si, where c is a program and s a state. Except where explicitly stated, reading an undefined value from any element in the state results in an undefined value for the rule application. Figure 10.8 shows the rules of the semantics that deal with control flow constructs. The rules handling composition and ifelse statements are standard for an SOS semantics, and are based on the rules of the whilelanguage in [60]. We assume a simple boolean function B that can test equality and inequality for integers and locations. Most of the control flow rules are standard, apart from the ret rule. Programs in µC can use return to break program execution at any time. Rather than explicitly jump to the end of computation, return writes tt into the boolean statevalue ret. If rets = tt, then the rule comp immediately applies, which ends the program. 225
[comp1 ]
hc, si ⇒ hc′ , s′ i hc cs, si ⇒ hc′ cs, s′ i
[comp2 ]
hc, si ⇒ s′ hc cs, si ⇒ hcs, s′ i hc cs, si ⇒ s
[compr ]
if rets = ff
if rets = ff
if rets = tt
[ifT ]
hif( t ) c1 else c2 ;, si ⇒ hc1 , si
if B(t, s) = tt
[ifF ]
hif( t ) c1 else c2 ;, si ⇒ hc2 , si
if B(t, s) = ff
[ret]
ht, si ⇒ s′ hreturn t, si ⇒ s′ [ rets′ : tt ]
Figure 10.8: SOS semantics of µC control flow. Example 10.5 (µC control flow). Suppose we have a µC state s, typed according to the type schema given in Example 10.4. The state is defined as follows. rets = ff
ress = 0
vars = {x 7→ l1 , y 7→ 1, z 7→ l3 }
typs = {l1 7→ S1 , l2 7→ S2 , l3 7→ U1 } f lds = {hS1 , l1 , F4 i 7→ l3 , hS2 , l2 , F3 i 7→ 0, hS2 , l3 , F3 i 7→ l1 } Now we apply the semantics to the following program. if ( x = 1 ) return 1; else return 2; return 3;
The resulting derivation is as follows. hif(x = 1) return 1; else return 2; return 3;, si [comp1 ], [ifT ] [ret] [compr ]
⇒ hreturn 1; return 3;, si ⇒ hreturn 3;, s[ret : tt][res : 1]i ⇒ s[ret : tt][res : 1]
The ifstatement is replaced by its first argument, as x = 1 holds. Then the statement return 1 is evaluated using the [ret] rule. This immediately ends the program with the return value 1, skipping the remaining return statements. 226
[refv ]
hv, si ⇒ s [ res : vars (v) ]
[refs ]
hv>f, si ⇒ s [ res : f lds (typs (vars (v)), f, vars (v)) ]
[refu ]
hv>u.f, si ⇒ s [ ress : f lds (τu (typs (vars (v)), u), f, vars (v)) ]
[assnv ]
ht, si ⇒ s′ hv=t;, si ⇒ s′ [ vars′ : v 7→ ress′ ]
[assns ]
ht, si ⇒ s′ hv>f =t;, si ⇒ s′ [ f lds′ : (typs′ (v), f, vars′ (v)) 7→ ress′ ]
[assnu ]
ht, si ⇒ s′ hv>u.f =t;, si ⇒ s′ [ f lds′ : (k, g, l) 7→ ⊥ ]g∈F ld, k∈Str/{h} [ f lds′ : (h, f, l) 7→ ress′ ] where h = τu (typs′ (vars′ (v)), u) l = vars′ (v)
Figure 10.9: SOS semantics of µC assignment and dereferencing. Figure 10.9 shows the rules of the semantics dealing with assignment and dereferencing. Rather than splitting cases semantically, µC distinguishes syntactically between different kinds of dereferencing of heap locations. The three different syntactic cases are (1) accessing a variable value, (2) accessing a struct field referred to by a variable, and (3) accessing a field for a structure inside a union, referred to by a variable. The semantics provides a [ref ] rule for each of these cases. Variable access is handled by simple dereferencing of the var statefunction. The other two cases are slightly more complicated. Access to a struct field uses the typ functions to retrieve the type of the target memory location and f ld to retrieve the contents of the field. Access to a struct in a union must refer to the typeschema function τu to associate a union field with a particular structure type. Dereferencing of the structure then works in much the same way as the struct case. The semantics also defines an assignment rule for each of these three
227
cases. Three distinct assignment rules are required, rather than a single rule exploiting the [ref ] rules, because of assignment to unions. Unions in µC consist of fields associated with a particular structure type. The programmer can write to any of the fields of any of the structures inside a union, but writing to the field of one structure deletes the fields of any other structures inside the union. To implement this, the rule [assnu ] for assignment to a structure inside a union makes other structures undefined (written ⊥). Example 10.6 (µC assignment). Let s be the same state considered in Example 10.5. Evaluating a variable assignment gives a simple derivation. hx = y, si [refv ], [assnv ] ⇒
s[vars : x 7→ 1]
Assigning to the field of a structure gives a slightly more complex result. Here the variable x holds a reference to location l1 , which is of structure type S1 . hx>F4 = 0, si [assnv ] ⇒
s[f lds : (S1 , l1 , F4 ) 7→ 0]
Here the variable z holds a reference to location l3 , which is of union type U1 . hz>F2 .F3 = 1, si [assnv ]
⇒ s[f lds : (S1 , l3 , F4 ) 7→ ⊥, (S2 , l3 , F3 ) 7→ 1]
In this derivation the union type is currently simulating a structure of type S1 , but we assign to it instead through field F2 , which is of type S2 . This overwrites f lds (S1 , l3 , F4 ) with ⊥, as we have assigned to the union through a different field, forcing it to alter the structure it is presently simulating. We write (S2 , l3 , F3 ) to 1, and so simulate an S2 structure. The rules of the semantics dealing with the allocation and deallocation of heap resources are shown in Figure 10.10. These rules are quite simple because µC’s syntax allows only very restricted forms of memory handling. Allocation of heap objects in µC can take place only through the application of malloc and sizeof to an identifier in Str or U ni. This means, for example, that the allocation of memory objects of arbitrary size cannot occur. The rule [alloc] for allocation fetches an unused memory location n, and writes the type of the constructor into the typ function. It then returns the freshlyallocated memory location n. 228
[alloc]
[free]
hmalloc(sizeof( c )), si ⇒ s [ typs : n 7→ c ] [ ress : n ] if typs (n) 7→ ⊥ hfree(*v), si ⇒ s [ typs : vars (v) 7→ ⊥ ] [ f lds : (vars (v), t, f ) 7→ ⊥ ]t∈T ype,f ∈F ld Figure 10.10: SOS semantics of µC memory handling.
Similarly, deallocation is severely restricted by the syntax of µC. Deallocation can only take place by applying the free keyword to dereferenced variables. The deallocation rule [free] simply writes ⊥ into the state functions typ and f ld. Example 10.7 (µC memory management). Let s be the same state considered in Example 10.5. Evaluating malloc(sizeof(S2)) assigns the type S2 to an arbitrary untyped location and writes it into the res value. hy = malloc(sizeof(S2)), si [alloc], [assnv ]
⇒
s[vars : y 7→ l4 ][typs : S2 ][ress : l4 ]
Evaluating free x overwrites the memory location l1 with ⊥, and overwrites the associated entry in f ld with ⊥. hy = free(*x), si [free] ⇒
10.4
s[typs : x 7→ ⊥][f lds : (S1 , l1 , F4 ) 7→ ⊥]
Translating from memory states to graphs
We have defined the semantics of transformer functions in both the operational C domain and the abstract graph transformation domain. However, the two semantics are defined over different domains. The µC implementation operates over µC states, while graph transformation rules operate over graphs. In this section we define a translation from states to graphs. We first define formally what is meant by a ‘shape structure’ in a state, and then define a function β that translates from a shape structure to a corresponding graph2 2 The function β is sonamed due to the similarity between it and the function α from separation logic heaps to graphs defined in Chapter 6, Def. 6.10. Both function map from classes of pointerstructures to classes of graphs, although their details are quite different.
229
We begin by defining precisely the states and graphs that we are interested in. Shapes in CGRS do not correspond to unrestricted pointer structures; Rather they define classes of shape structures. These are portions of the heap, reachable from a single container structure, that have a particular form corresponding to a rooted graph. Assumption 10.1. In the following section, let σ be an abstract declaration (Def. 10.1). Let S be the name of the declared shape, and let G be the name of the signature. Let N1 , . . . , Nm be the names of the nodetypes defined in σg . For each node Ni , let ENi be the set of edges assigned for node Ni and VNi the set of value fields. Let R be the name of the root node type, and VR the set of edge fields for R. Definition 10.6 (σschema). A type schema τ is a σschema if: (1) dom(τs ) = {N1 , . , Nm } ∪ {G, S struct}. (2) For each struct Ni ∈ {N1 , . . . Nm }, τs (Ni ) = LNi ∪VNi . (3) For each struct Ni ∈ {N1 , . . . Nm }, τu (S union, Ni ) = Ni . (4) τs (G) = { root}. (5) τs (S struct) = {indeg, type}, node}. Example 10.8 (σschema). Let σ be the abstract declaration we defined in Example 10.1. This gives struct names tree, bin struct, treeroot, branchnode and leafnode, the single union name bin union, and field names root, top, aux, l, r, treeroot, branchnode, leafnode, indeg, type, node and val. We define the corresponding σschema τσ = hτ(σ,u) , τ(σ,s) i as follows. τ(σ,u) = { hbin union, branchnodei 7→ branchnode, hbin union, leafnodei 7→ leafnode, hbin union, treerooti 7→ treeroot, } τ(σ,s) = { tree 7→ {root}, bin struct 7→ {indeg, type, node}, treeroot 7→ {top, aux}, branchnode 7→ {l, r, val} leafnode 7→ { } } Definition 10.7 (σ nodepair). Let sσ be a welltyped state with respect to a σschema τσ . We call any pair of heaplocations consisting of an S structtyped struct and an S uniontyped union a σ nodepair if (1) the node field of the structure points to the union, and (2) the type of the struct 230
recorded in the union corresponds to the typecode recorded in typefield of the S structtyped struct. A nodepair is a total nodepair if all of the edgefields of the struct inside the union are defined and point to nodepairs. The set of child nodepairs from a particular nodepair is the set of nodepairs pointed to by the edgefields of the nodepair. The reachable structure from a nodepair is the transitive closure of the child relation on the nodepair. Example 10.9 (σ nodepair). The following definitions of typ and f ld for some state s conform to the σschema τσ defined in Example 10.8. typ = { l1 7→ bin struct, l2 7→ bin union} f ld = { hbin struct, l1 , indegi 7→ 0, hbin struct, l1 , nodei 7→ l2 , hbin struct, l1 , typei 7→ typecode(branchnode), hbranchnode, l2 , li 7→ l4 , hbranchnode, l2 , ri 7→ l5 , hbranchnode, l2 , vali 7→ 0 } The locations l1 and l2 in the state contain a nodepair encoding a branchnode. Other locations are undefined. Definition 10.8 (σstructure). A heap location in sσ is a σstructure if (1) the location holds a struct of type G, (2) the reachable structure for this struct’s root pointer consists entirely of total σ nodepairs, and (3) the indeg field for any nodepair in the reachable structure is equal to the number of edgefields in reachable nodepairs that point to this nodepair. Example 10.10 (σstructure). We represent unions and structs graphically as follows. This example represents a branchnode nodepair.
S
indeg: 0
U S
type: branchnode
val: 0
bin_struct
node
bin_union branchnode
Here structs are represented by Slabelled boxes, and unions with Ulabelled boxes with Sannotations showing which struct the union is simulating. Nonedge fields are shown explicitly. Edge fields are shown as edges between nodes, as with node shown above. Using this notation, Figure 10.11 shows a larger example of a structure at location k. This structure is defined using the σschema defined in Example 10.8. In this structure, the indeg fields of the nodes are correct and all 231
k: S
tree root
l 1: S
root bin_struct
indeg: 0 type: treeroot node
U S
bin_union treeroot
top
l 2: S
aux bin_struct
indeg: 2 type: branchnode node
U S
branchnode
val: 0
l
S
bin_union
r
S
bin_struct
indeg: 1
indeg: 1
type: branchnode
type: leafnode
node
U S l
S
node
U S
bin_union branchnode
val: 0
bin_union leafnode
r
S
bin_struct
bin_struct
indeg: 1
indeg: 1
type: leafnode
type: leafnode
node
U S
bin_struct
node
U S
bin_union leafnode
bin_union leafnode
Figure 10.11: Large σstructure conforming to the σschema τσ defined in Example 10.8.
232
reachable locations are nodepairs, which means this structure is a valid σstructure. In Section 10.1 we defined the graph signature Σσ resulting from a CGRS shape declaration σ. A graph is called a σgraph if (1) it is a Σσ graph and (2) there exists exactly one node with root label R. We have now defined for a shape declaration σ both a class of σstructures that exist in C states, and a class of corresponding σgraphs. These σstructures form the domain for transformer functions. Our mapping between the domains of states and graphs therefore operates only on these two heavilyrestricted domains. The relationship between the two is defined by the mapping function βσ , derived from the CGRS signature declaration σ. This function takes as its input a state s over σschema τσ and a σstructure l in s, and constructs a corresponding σgraph βσ (s, l). Intuitively, the βσ function maps heap locations to graph nodes, nodepairs to nodes, and edge fields to edges. Definition 10.9 (βσ ). Let s be a state over the σschema τσ . Let l ∈ Loc s be the heaplocation of some σstructure, and let r ∈ Loc s be the location pointed to by the struct’s root field. Then graph βσ (s, l) is constructed as follows: 1. Construct a node v for every heap location n holding nodepairs in the reachable structure from the root field of r. v is labelled with the type recorded in the type field of the nodepair. This is determined using the inverse of the typecode function typenum(−) described in §10.2.1. In other words the label function lV (b(n)) has the value typenum−1 (f ld(n, Ss , type)). 2. Let v1 ,v2 be nodes corresponding to locations n1 and n2 . If the field f of the nodepair at n1 is defined and points to n2 , then an edge e labelled f exists from v1 to v2 . Example 10.11 (βσ ). Applying βσ to location k in the σstructure described in Example 10.10 gives the following Σσ graph.
233
1
top
aux
B
l
B
2
L
r
L
As usual branchnode and leafnode are abbreviated to B and L, and the root node is shown as a small grey node. The nodes tagged with 1 and 2 correspond to nodes l1 and l2 in the σstructure.
10.5
Correctness of translations
In this section we prove two main correspondence results for C[[−]] and G[[−]]. First in §10.5.1 we show that the constructor functions C[[−]] produced from a shape declaration σ constructs a shapestructure that corresponds to the accepting graph defined in σ. Then in §10.5.2 we show that the graph transformation rule constructed by applying G[[−]] to a transformer declaration correctly models the transformer implementation produced by G[[−]].
10.5.1
Correctness of constructor functions
The correctness requirement for constructors is as follows. Let s be a state and σ a shape declaration with name S. Let s′ be the result of applying the constructor function in state s, and let l be the return value of the function in s′ . For the constructor to be correct, it must be true that the graph βσ (s′ , l) is isomorphic to Accσ , the accepting graph defined from σ, and that no other shape structures are altered or created by the constructor function. Theorem 10.1. Let σ be a CGRS shape declaration. The corresponding constructor newgraph S defined by C[[σ]] always returns a shapestructure l in state s such that βσ (s, l) is isomorphic to Acc σ . Proof. Let σ be a CGRS shape declaration with shape name S in program P , and let newgraph S be the constructor defined from σ in the resulting C program C[[P ]]. Let Acc σ be the accepting graph constructed from σ by G[[σ]]. Let s be a µC state. Let s′ be the state resulting when newgraph S is executed in state s, and l the return value of the function. Let Gβ = βσ (s′ , l). 234
The graph Gβ is constructed from all the nodepairs reachable from the root location. So to show that Gβ is isomorphic to Acc σ we show that for every node a nodepair is constructed, and show that the constructed nodes constitute the reachable structure from the root. Suppose in the accept block there are defined node names V, W, . . . , Z. Then in the resulting constructor, C[[−]] declares corresponding variables V s, W s, . . . , Z s and V u, W u, . . . , Z u. Each declared node V has the following associated code, constructed by the semantic function M[[−]]. V s = malloc(sizeof(S struct)); V u = malloc(sizeof(S union)); V s>node = V u; V s>type = typenum(T); V s>indegree = 0; By appeal to the [assnv ] and [alloc] rules, the two malloc assignments results in a nodepair. So, at the end of these assignments, for each declared node V there exists a variable V s pointing to the struct of a nodepair and variable V u pointing to the union. By appeal to the [assnv ] rule, we now can see that the type field of each nodepair corresponds to the type of the node in Acc σ . We now show the edges of the accepting graph exist. Each edge statement results in the following code for node called V: V u>tr (V ).E = T s; T s>indeg = T s>indeg + 1; In the resulting state, by appeal to the [refv ] and [assnu ] rules, the E field of nodepair V must point to nodepair T. As each edge statement also results in an edge in graph Acc σ between nodes V and T, the bijection b satisfies the third condition, in that for each field in the corresponding graph there will exist an edge between the two corresponding nodes with label E. No matter what the structure of the accept block, for a shape called S, the constructor function ends execution with the following code. new = malloc(sizeof( S )); S>root = R s; return new; 235
G
G[[r]]
G′
βσ
=
βσ
s, l
C[[r]]
s′ , l
Σσ total graphs
µC state
Figure 10.12: Correctness requirement for C with respect to G and βσ . By the definition of the [assnv ] and [alloc] rules, in the resulting state the variable new points to a fresh Styped memory location. The constructor assigns the root field the location of the roottyped nodepair. Therefore βσ (s′ , l) is isomorphic to Acc σ .
10.5.2
Correctness of transformer functions
The correctness requirement for transformers is as follows. Let r be a transformer function. Then C[[r]] is the fragment of µC code that defines by implementation the transformer function’s operational semantics. G[[r]] is the corresponding graph transformation rule extracted from the transformer function. We write application of the rule r to graph H as r(H), and application of transformer function t to shapestructure l in state s as t(s, l). The mapping function C[[−]] is correct with respect to G[[−]] and βσ if for function r, state s and σstructure l in s, the application of the code fragment C[[r]] to state s has the same structural effect, modulo βσ (−, −), as applying the corresponding graph transformation rule to graph βσ (s, l). Formally, we require that G[[r]](βσ (s, l)) = βσ (C[[r]](s, l), l). Equivalently, the diagram in Figure 10.12 must commute. We prove this correctness result in three stages. The first part of the proof shows that the ‘matching’ portion of C[[r]] constructs a set of matching variables that correspond to the matching morphism constructed by the lefthand side of the graph transformation rule G[[r]]. The second part shows that code checking the dangling condition fails if and only if the dangling condition does not hold. The third part proves that the ‘rewriting’ portion of C[[r]] performs the same structural graph rewrite as rule G[[r]]. We ignore rewrites applied to value fields in the transformer as they have no effect on the correctness of the extraction function. 236
The first stage of the proof consists of proving that the portion of the code in Figure 10.5 labelled ‘Matching’ constructs matching variables that correspond to a matching morphism. This requires a notion of correspondence between a variable set and a morphism. Intuitively, a set of variables correspond to a morphism if each variable corresponds to a node in the morphism domain, and the value held in the variable points to a the associated location in the range. Definition 10.10 (morphism correspondence). Let s be a state and l the location of a σstructure in s. Let G = βσ (s, l) be the resulting Σσ graph, and b : Locs → VG the bijection between locations and graph nodes that defines βσ (s, l). Let v1 , . . . , vn be a set of variables in s. Let r = hL ← K → Ri be a graph transformation rule with n nodes on its lefthand side. Let t : V ar → VL be a partial bijection associating variables with lefthand side nodes. Let h : L → G be an injective morphism between L and G. Then we say that variables v1 , . . . , vn correspond to morphism h in state s, if for all i such that 1 ≤ i ≤ n, b(vars (Vi )) = hV (t(Vi )). Parallel edges are forbidden by the definition of a Σσ graph, as outgoing edges must be distinctly labelled. As a result, a set of variables in state s that correspond to a morphism h : L → G suffices to uniquely define the morphism. Example 10.12 (morphism correspondence). Let s be a state containing the σstructure given in Example 10.10. This structure contains locations l1 and l2 . Applying βσ to this σstructure gives the graph G shown in Example 10.11, with nodes v1 and v2 the nodes with tags 1 and 2. We also have the following lefthand side graph L. Nodes v1′ and v2′ are respectively the nodes with tags 1 and 2. 1
top
B
2
Suppose we have variables x1 and x2, and mapping function t = {x1 → v1 , x2 → v2 }. Let h : L → G be a partial morphism defined as hV = {v1′ 7→ v1 , v2′ 7→ v2 } and hE = ∅. If s(x1) = l1 and s(x2) = l2 , then x1, x2 correspond to h in the state s.
237
Proposition 10.2. Let T be a transformer function. If there exists a matching morphism between the G[[T ]] and βσ (s, l) then applying the matching code from C[[T ]] to a σstructure l in s results in a set of matching variables corresponding to the morphism. Otherwise the code exists with returnvalue FALSE. Proof. Let N1 , . . . , Nm be the names of the nodes declared in T . Each node n declared in the lefthand side of transformer function r results in (1) a pair of variables n u and n s declared in function C[[T ]], and (2) a node in the lefthand side of rule G[[T ]]. This relationship implicitly defines a bijection b between variables and lefthand side nodes. Let s be a state and l the location of a σstructure in s. Let graph Gβ be the graph constructed by βσ (s, l). Let L be the lefthand side graph of the rule C[[T ]]. We prove that the correspondence holds by induction on the size of the partiallyconstructed matching morphism hi . In the base case only a single root node exists. Let R be the name of the root node, and G the shapename passed to the transformer. The matching code begins with the following assignment. R s = G>root; R u = R s>node; By the definition of a σstructure, the root field must point to a roottyped nodepair. By appeal to the [assn] rules, after this code has executed the single variable R s points to the roottyped nodepair. This variable alone must therefore correspond to a partial morphism h0 that has a singleton domain consisting of the rootlabelled lefthand side node r, with h0 (r) pointing to the rootlabelled node in the graph Gβ . For each declared nodename in the lefthand side, the function C[[−]] constructs code to incrementally extend the matching morphism by an edgefield at a time. Let e1 , . . . , en be the sequence of lefthand side edge declarations defined by the edge enumeration. Let us assume that edges e1 , . . . , ei have been matched. Let V1 , . . . , Vm be the source and target nodes of these edge declarations. Assume variables V1 s, . . . , Vm s correspond to partial morphism hi that has as its domain the nodes of the lefthand side graph related to V1 , . . . , Vm by bijection b.
238
Let S.E => T be the next edge declaration ei+1 in the enumeration. This results in the following codefragment. T s = S u>tl (S).E; if ( T s>type != typenum(tl (T )) ) return FALSE; else if (T u == NULL) T u = T s>node ; else if ( T u != T s>node ) return FALSE; Let V1 , . . . , Vp be the sources and targets of the edge declarations e1 , . . . , ei . We now show that at the end of this code fragment either (1) variables V1 s, . . . , Vp s correspond to partial morphism hi+1 between the corresponding nodes and edges of the lefthand side graph and the target graph, or (2) no such extended partial morphism exists and the code returns FALSE. Because graph Gβ is a Σσ graph, there must exist exactly one edge with source hi (b(S)) and label E. There are therefore two possible failurecases in extending the domain of hi by the edge corresponding to edgedeclaration ei+1 . (1) The target node of the edge in Gβ is of the wrong type. (2) The target of the edge is already in the domain of h0 , but the edge in Gβ points to the wrong node. Otherwise the match must succeed. We show that matching fails and the code returns FALSE if cases (1) or (2) hold, and succeeds otherwise. In case (1), the code fails if the type field of the target nodepair is of the wrong type. By the definition of the typenum(−) function, this shows that extending hi along this edge reaches a wronglylabelled node in graph Gβ . In case (2) both the edge source and target have been matched in hi . The code will fail if the edgefield of the nodepair in S u does not point to the nodepair in T s. This occurs exactly if no edge exists between the two nodes in the resulting graph Gβ . By appeal to the fact that the source of the new edge has already been matched, the code will return FALSE if no field exists labelled E between the heap locations held by S s and T s. Otherwise the code writes the value into the target variable for the nodedeclaration. By the definition of βσ , there must exist a corresponding node in graph Gβ and edge labelled E between the source and target, so the new variable set V1 s, . . . , Vp s corresponds to morphism hi+1 . 239
This suffices to show that at the end of the matching code we have a total morphism h : L → G. The final section of the matching code ensures that the morphism is injective, by comparing the values held in all of the variables. The result is that the code fails if any of the variables point to the same nodepair, which occurs if and only if the corresponding morphism is noninjective. The second portion of the code in Figure 10.5 (marked by the label Dangling Condition) checks that the dangling condition is respected by the constructed morphism. Proposition 10.3 (checking the dangling condition). Let s be a state resulting from the matching code in C[[T ]] with σstructure at location l, and let v1 , . . . , vn be variables in s corresponding to morphism h for graph βσ (s, l). Then C[[T ]] returns FALSE in s if and only if G[[T ]] violates the dangling condition in βσ (s, l) with matching morphism h. Proof. Let s be a σstate and v a σstructure. By the definition of a σstructure, the indeg field of a nodepair in the σstructure is equal to the indegree of the corresponding node in the graph βσ (s, v). The following code is generated for node V declared in the lefthand side graph if the corresponding node n ∈ L in rule r = hL ← K → Ri is (1) deleted by the rule, and (2) has indegree c in L. if ( V s>indeg != c ) return FALSE; This code fragment will return FALSE if the indegree of the node h−1 (n) in graph βσ (s, v) differs in degree from the node n in L. This holds if and only if the dangling condition fails for the corresponding node. As the code is generated for all deleted nodes, the result is that the dangling condition is checked for the whole rule. We can now prove that the extraction function G produces from a transformer function T a graph transformation rule that corresponds to the operational semantics of the function defined by the implementation function C. Theorem 10.4 (correctness of translation). Let s be a σstate that includes a σstructure l. Let T be transformer function. Applying C[[T ]] to l in s using 240
the semantics given in Section 10.3 results in state s′ such that βσ (s, l) ⇒G[[r]] βσ (s′ , l), or will return false if no such derivation exists. Proof. Let Gβ be the graph βσ (s, l). We have shown in Proposition 10.2 and Proposition 10.3 that the values written into variables by the matching and dangling portions of C correspond to an injective matching morphism h : L → Gβ that satisfies the dangling condition. To show that the code C[[T ]] has the same structural effect as G[[T ]] therefore requires only to prove the section of code labelled ‘Rewriting’ performs rewrites on the state that correspond to the graph rewrites performed by graph transformation rule G[[T ]]. The rewriting code of C first generates code to delete nodes. The code consists of a pair of calls to the free function. By the [free] rule, in the resulting state the corresponding nodepair will be deleted, with the result that the same nodes will be deleted in the graph. Note that incoming edges are deleted by the edge rewriting code, meaning that reachability is preserved. The code then retypes nodepairs by writing into the type field. This has the result that the node labels for the corresponding nodes in Gβ are modified to conform to the new type. The rewrite rule has the same effect when relabelling nodes. The code then constructs new nodes. This code is generated using the function M[[−]] used in defining the constructor function, so by the same argument used in Theorem 10.1 this code successfully generates a nodepair of the required type with undefined outgoing edges. The rewriting code of C then reassigns all of the fields present in the righthand side of the transformer function. In the domain of graph transformation, if an edge f exists between nodes l1 and l2 in the lefthand side, then in the resulting graph G′ there must be an edge between the corresponding nodes matched by the morphism. The code generated is of the form: S u>tr (S).E = T s; By Proposition 10.2, the source and target variables correspond to the source and target in the graph Gβ . By the rules of the µC semantics, the above code will result in an edgefield between nodepairs matched by the morphism, as required by the lefthand side. Therefore, in the corresponding 241
graph, there will exist edge between the corresponding nodes matched by the morphism. Finally, the code updates the indeg fields of all of the righthand side nodes to reflect the updated indegree counts. By the definition of C[[−]] the indegree is altered by the number of edges deleted and generated by the graph update, so the σgraph property is maintained. This auxiliary data is not present in the graph Gβ . We have shown that the code will exit with FALSE if it fails to construct a variable set corresponding to a matching morphism. By the structure of the constructed code, it is not possible for it to exit while executing the rewriting portion of the function until the final return TRUE statement. This completes the proof.
10.6
Shape safety guarantees in CGRS µ
C
Let us assume that = ⇒ is a semantics of full C, and let = ⇒ be the µC semantics defined in 10.3. Given a C memory state s, we extract a µC state sµ by preserving any structs and unions that include only integer and pointer fields, and turning all other memory locations to undefined values. We assume that the semantics of µC corresponds to full C, in the sense that for C
µ
any valid piece of µC syntax P , hP, si = ⇒ s′ if and only if hP, sµ i = ⇒ s′µ . Definition 10.11 (shape safe location). Let σ be a shape declaration in P . Let s be a state in full C and let sµ be the corresponding µC state. Let l be a location in s that is the location of a σstructure in sµ . Then we say that l is shapesafe in s if the graph βσ (sµ , l) is a member of L(G[[σ]]), the language of graphs defined by the graph reduction system G[[σ]]. Definition 10.12 (shape safety preservation). Let P be a C program, and let σ be a shape declaration, and let l be a location in s that is shapesafe with respect to σ. We say that P preserves shape safety if every state s′ C∗
such that a derivation hP, si = ⇒ s′ exists is shapesafe with respect the σ. In this section we assume a sound shapechecking algorithm that determines whether a graphtransformation rule is shapepreserving with respect to a GRS. That is, given a GRS G and a graph transformation rule r, if the algorithm returns tt, then L(G) is closed under r. The problem of shape checking is known to be undecidable even for contextfree shapes – see [31] 242
for a reduction of the inclusion problem for contextfree graph languages to shape safety checking. Consequently no general sound and complete shapechecking algorithm exists. An example of a shapechecking algorithm is ShapeCheck, described in [3]. See 9.1 for a of this algorithm.Given a shapechecking algorithm, we can make the following guarantees for CGRS programs. Theorem 10.5. Shape structures constructed by a CGRS constructor are shapesafe by construction. Proof. Let σ be a shape declaration and let P be the constructor defined by C[[σ]]. As a consequence of Theorem 10.1, applying P in any state results in a state s and location l such that βσ (sµ , l) is isomorphic to the accepting graph for G[[σ]]. As a consequence, l is shapesafe in s with respect to σ. Theorem 10.6. Given a sound shapechecking algorithm, there exists a sound procedure for checking statically whether the transformer functions in a CGRS program preserve shapesafety. No general procedure that is sound and complete exists however. Proof. Let σ be a shape declaration, and let l be a location in s that is shapesafe with respect to σ. Let T be a transformer function. As a consequence of Theorem 10.4, l is shapesafe in the state s′ resulting from applying C[[T ]] to l if and only if G[[T ]](βσ (s, l)) ∈ L(G[[σ]]). As a consequence of this, if the shape checking algorithm verifies that the rule C[[T ]] preserves membership of the language L(G[[σ]]), then function C[[T ]] is guaranteed to preserve shapesafety for shape σ. Conversely, if the algorithm shows that C[[T ]] is not shape preserving, then there exists a state s and location l that is shapesafe with respect to σ such that l is not shapesafe in the state s′ resulting from applying C[[T ]] to l. As a result of the undecidability of shapechecking, no sound and complete procedure exists for checking statically whether transformer functions preserve shapesafety. Pointer manipulations that are not modelled as transformer functions cannot be checked by the algorithm, and can potentially violate the shape membership guarantees. CGRS can therefore only make guarantees of shape safety in programs that manipulate shape structures only by transformer functions. 243
10.7
Implementing and optimising CGRS
We have defined the concrete semantics of CGRS by a mapping function to C. However, our focus in this work has been abstract correctness rather than concrete implementation, and we have not implemented CGRS in practice. It should be the subject of future work to develop an implementation, consisting of a compiler and suite of small test programs. An implementation will require of a parser for CGRS constructing a parse tree for signatures, shapes and transformers. From this parse tree the implementation will then construct C code conforming to the definition of a transformer function, and a textual representation of the corresponding graph transformation rules. Haskell may be a suitable language for this implementation. The functions produced by the mapping to C are considerably larger than the corresponding C code. In the case of tree goleft defined in Example 9.4, the resulting code is 35 lines long, with most of the body of the code consisting of matching code and checks for injectivity. The code is given in §10.2.3. This compares to 12 lines for the transformer declaration, and 6 lines for the corresponding C code given in §9.4. This level of code length increase also seems quite typical for CGRS transformer functions. For the rebalancing transformer rup from §9.3, the constructed C function is 49 lines long. The transformer is 19 lines long, and the C code given in §9.4 is 12 lines. We hope that future work will reduce the size of code produced from CGRS, by cutting down on redundant checking. It may be possible to use some of the information in the shape declaration to remove redundant checks, by eliminating checks that cannot fail in shape members. It may also be that some redundant checks are already eliminated by the compiler. A consequence of the lack of an implementation is that CGRS has only been applied to a small number of toy problems. It should be the subject of future work to apply CGRS to a wider range of example problems, and refine the language and verification approach based on this practical experience. Testing the language in this way will also allow us to compare the speed of CGRS programs to corresponding C programs.
244
Chapter 11
Other approaches to shape safety In this chapter we compare CGRS to other approaches that have been developed for ensuring the shape safety of Clike programs. We also consider approaches to specifying classes of graphs based on graph transformation.
11.1
Shape types and structured gamma
The approach used in CGRS is most similar to Fradet’s and Le M´etayer’s work on shape specification using structured gamma [31]. Structured gamma programs are based on a ‘chemical reaction’ metaphor. Operations consist of reactions, which act nondeterministically on a structured multiset of tuples. These tuples consist of a type field that restricts the tuple’s arity, and a fixed number of address fields. These multisets can be seen as representing hypergraphs, with the addresses as vertices and the tuples as labeled edges. Structured gamma reactions are know to be equivalent to contextfree hyperedge replacement rules as defined in §2.3. The structured gamma approach forms the basis of Fradet and Le M´etayer shapechecking approach. They model pointer structures as structured multisets, with the properties of structures defined by sets of reactions that generate sets of multisets. Pointer rewrites are modelled as patternmatching rewrites. This approach is implemented as an extension to C called ShapeC, which takes a similar approach to implementing the structured gamma approach 245
as we have in CGRS for the SPGT project. ShapeC introduces a special syntax for defining both pointer structure shape and pointer rewrites. Like CGRS, it is implemented by a mapping to C. There are several differences between our approach and that of ShapeC. Most fundamentally, ShapeC is restricted to shapes specified by contextfree graph grammars. The graph reduction specifications incorporated in CGRS – even when restricted to polynomial GRSs – allow programmers to specify noncontextfree data structures such as grids and various forms of balanced trees. In addition, our approach to transformer functions in the design of CGRS is quite different. While ShapeC defines a simple syntax for rewrites that appear inline in programs, in CGRS transformers are defined as functions. This has the advantage of encapsulating large rewrites away from the main body of the program. In Part II of this thesis, we give conditions under which GRSs have an efficient runtime membership test. We have shown that several of the classes of graphs defined by graph reduction systems in [4] permit lineartime membership checking (for example: balanced binary trees, §4.2.1; grid graphs 4.2.2; cyclic lists, Example 4.2). Fradet and Le M´etayer, in contrast, do not consider the efficiency of runtime shape checking. Their reactionbased approach is equivalent to contextfree graph grammars, which in general are known to have an NPcomplete membership problem.
11.2
Specifying structures using logic
Several approaches to specifying the properties of pointer structures are based on program logics. An example of this is the work on graph types [49]. These are spanning trees with additional pointers defined by path expressions; they form the basis of pointer assertion logic [58], a monadic secondorder logic for expressing properties of pointer structures in program annotations. In addition to a logic, this approach defines a Clike language for declaring the properties of pointer structures. Graph types are declared in much the same way as C structure declarations. Nodes in a graph type can have two kinds of outgoing edges: data fields and pointer fields. A data field can only point to a node that is not pointed to by any other data field of any other node. In this way, the data 246
type Node = { data next : Node; pointer prev : Node[ this^Node.next={prev} ]; }
Figure 11.1: Graph type for a doublylinked list node. fields of a pointer structure form a spanning tree. Pointer fields, in contrast, can point to any node in the structure. However, pointer fields are annotated with routing expressions, written in pointer assertion logic, which express a set of acceptable target nodes. Pointer fields are used to implement the nontree properties of a graph structure. For example, Figure 11.1 shows the graph type for a doublylinked list. The treeshaped backbone of the list is formed by the data field next. The prev pointer field is marked with the following routing expression: this^Node.next={prev} This states that the set of nodes that point to the current node this through the data field next must consist of the node pointed to by prev. For verification, routing expressions are mapped to a fragment of monadic secondorder logic: the weak monadic secondorder theory of 2 successors. Hoare triples based on this fragment are decidable over loopfree code using the MONA tool, described in [44]. Loops must however be explicitly annotated with invariants in the normal Hoare logic manner. This approach requires programmers to use quite a sophisticated logic, but the formalism is still too weak to express some important properties. Møller and Schwartzbach give a list of shapes that have been verified using this tool, including threaded trees and nonbalanced redblack search trees. However, balanced structures such as balanced binary trees and redblack trees are outside the expressive power of pointer assertion logic. In contrast the GRS approach allows us to specify balanced structures and other noncontextfree shapes. Another logic that has been the basis of a large amount of work is separation logic [46, 69]. This is a logic for shared mutable datastructures that adds a socalled separating conjunction to the normal operators of firstorder logic. Separating conjunction divides the heap into disjoint regions for which different logical formulae hold, making it possible to reason locally. The syn247
tax and semantics of separation logic are described in detail in §6.1 , so here we will describe only the approaches to verification that have been developed based on it. The main use for separation logic is as the basis of an extension of Hoare logic that allows local reasoning about programs. Separation logic can reason about quite complex pointermanipulating algorithms in a way that would be difficult in normal Hoare logic. The earliest work on verification based on separation logic consists of byhand proofs of small programs, with explicit annotations of Hoare triples. The SchorrWaite graph marking algorithm was verified in [78], a copying garbage collector in [9]. Such byhand proofs suffer from the same problems of complexity as Hoare logic. Even small programs can require very large proofs, which can take a considerable amount of time to write. The SchorrWaite algorithm requires a twentyeight page paper to detail the proof, even though the program itself is only thirty lines long. More recent work on separation logic has resulted in the SmallFoot tool [7], which automatically verifies invariants written in a decidable fragment of separation logic. The decision procedure used by SmallFoot makes use of symbolic execution. Recursive predicates other than simple lists must be added by hand to the tool’s logic, and the inference rules in the tool must be updated to reflect the semantics of the new predicate. In comparison with CGRS, Smallfoot has the advantage that it works over plain C, rather than an augmented extension of C. Smallfoot’s fragment of separation logic however is less expressive than CGRS’s reduction systems, in that it operates over a fragment of separation logic similar to the symbolic heaps used for the Space Invader tool (see below, p. 251. In addition, even with automatic checking, the correct invariant for verifying a program can be difficult to discover. Verification also depends strongly on the selection of recursive predicates. Smallfoot copes well with invariants defined using list segments, but for more complex structures such as trees, which CGRS handles quite naturally, the proof system must be extended by hand to reason about them successfully. The construction of a correct set of inference rules for a given predicate can present a considerable challenge. Other logics have been used to specify the properties of pointer structures. Alias Logic [14] abstracts away from the presence of a garbage col248
lector to reason purely about aliasing. Navigation Temporal Logic [19] uses temporal reasoning to define all of the possible evolutions of the heap. However, both of these logics are presented in largely theoretical terms, rather than as part of a system for verifying pointer programs.
11.3
Shape analysis
Other approaches to shape safety are based on the automatic inference of the shape properties of pointer structures. Shape Analysis is the major approach in analysisbased shape safety checking. A shape analysis constructs by symbolic execution a overapproximated representation of all possible pointer structures at each point in a program. These structures can then be analysed for required shape properties. The initial work on shape analysis uses threevalued logic as an abstract domain [54]. Concrete structures in this work are represented by logical structures in twovalued boolean logic with transitive closure. A twovalued structures are defined from a universe of elements and a set of predicates that give an interpretation over these elements. Core predicates record the pointers between elements, while instrumentation predicates record largerscale properties, such as reachability. Threevalued logic extends twovalued boolean logic with a new ‘indefinite’ value. A threevalued structure embeds a twovalue structure if there exists a mapping from the nodes of the twovalue structure such that predicates either maintain their value or have value unknown. Intuitively, embedding ‘blurs’ the embedded twovalued structure by mapping from definite to indefinite values. A single threevalued structure represents the (possibly infinite) class of twovalued structures embedded in it. Shape analysis as presented in [54] constructs for each program point a set of threevalued structures. These structures embed all the possible twovalued structures that can occur at the program point. The set of structures is constructed by symbolic execution over the set of structures. First the structures are focussed, breaking them into cases with definite values for a socalled focus formula. Then the structures are updated according to a program semantics. Finally the resulting set of formulas is coerced to remove contradictory structures. Threevalued shape analysis has been implemented in a tool called TVLA 249
[55, 53]. A considerable amount of effort has been focussed on improving the speed of TVLA’s analysis. A recent paper claimed a 50fold increase in speed over earlier versions of the tool [12]. Other work has developed interprocedural version of shape analysis for improved modularity [70, 47]. Shape analysis differs from CGRS in its general approach to verification. CGRS defines a new class of program construct which are more suitable for shape checking, and annotates the program with shape invariants consisting of GRSs. Shape analysis in contrast keeps the original syntax of C and generates invariants by symbolic execution of the program. Shape analysis can simulate the invariantchecking style by annotating invariants and checking that they are a fixedpoint of the analysis. The converse does not hold however: CGRS has no mechanism for automatically generating shapes. CGRS shapes must be annotated on the program, and the shape of a structure in CGRS is invariant throughout the program. (The SPGT approach can in general deal with shapechanging graph transformations [3], but CGRS currently does not implement this feature). Shape analysis in contrast can handle invariants that alter throughout the program. Shape analysis has the advantage of a large amount of automation. However, the precision of the structures generated by analysis varies considerably. The choice of instrumentation predicate is extremely important in determining how quickly the algorithm will derive a threevalue structure, and how precise the resulting structures will be. Consequently, while analysis is entirely automatic once the correct predicates have been chosen, selecting and correctly defining these predicates may require considerable human intervention. In contrast in CGRS annotations are of a precision determined by the user. The approach to semantics differs between CGRS and shape analysis. CGRS has a single semantics for transformers, while the semantics of constructs in shape analysis depends on the abstract domain chosen. The two can be seen as similar however, in that transformers must be defined using the node and edge labels defined by the signature. The abstract domain of shape analysis is restricted to predicates in threevalued logic with reachability. This logic is sufficient to express structures such as trees. However, it gives no means to express largescale properties such as tree balance, so we conjecture that it is less expressive than CGRS’s shapes based on graph transformation. Shape analysis is guaranteed to 250
terminate however, as the domain of structures is finite. This is not true for the CGRS shapechecking algorithm, which may diverge. Separation logic has also been used as an abstract domain for shape analysis. The Space Invader tool replaces threevalued logic with symbolic heaps. These are formulas defined in a decidable fragment of separation logic. As with threevalued logic, the analysis generates for each program point an abstract representation of possible heaps. Termination is ensured by a normalisation process based on entailment which replaces large formulas with smaller formulas. Just as the success of shape analysis using threevalued logic is sensitive to the choice of instrumentation predicate, the success of analysis in Space Invader is sensitive to the choice of abstract predicates and of normalisation procedure used. Consequently the Space Invader tool has been targeted closely on the domain of device drivers. This has the advantage that drivers are written to a standard template and the data structures they use are normally of a particular form. This has allowed the tool to be very successful, in terms both of locating bugs and of the size of programs analysed. Space invader claims verification of shapesafety for 10,000 line programs [79], and location of substantial bugs in realworld device drivers [5]. Symbolic heaps are closely related to the fragment of separation logic that we use in Part III. See §8.3.2 for a discussion of this correspondence between our fragment and symbolic heaps. As grammars based on hyperedge replacement are known to be formally less expressive than grammars based on doublepushout rewriting, this suggests that Space Invader’s abstract domain is formally less expressive than the shapes used by CGRS. A major advantage of both separation logic and threevalued logic shape analysis is that they have been applied to a large number of realworld problems, some of which are of considerable size [12, 79]. This has meant that TVLA and Space Invader tools have been finetuned in the face of experience, and that their tools have been developed to perform well when applied to large blocks of code. This contrasts our own approach, CGRS, which has only been applied to a small number of toy problems. Our objective in CGRS has been to design a sound foundation language on which further work can be based. Early shape analysis work, such as [54], similarly dealt only with simple example problems. It will be the subject 251
of future work to apply CGRS to more substantial casestudies, and so to finetune it based on practical experience. See §10.7 for a discussion of this.
11.4
Specifying structures by graph transformation
We have described above the major approaches to verifying pointer structures in Clike languages using invariants. In this section we describe several approaches that use graph transformation rules to analyse and verify of the properties of classes of graphs. Highlevel conditions [2, 41, 2] are an approach to defining the properties of graphs based entirely on graphs and morphisms. Graph properties are specified by conditions, which require the existence of certain combinations of morphisms. Conditions can be negated and nested, giving a highly expressive system for specifying properties. These conditions are used in two ways: to control the application of rules, and to specify invariants. In [41] it is shown that conditions can be used in practice to specify the pre and postconditions of a graph program, in the sense of [42]. It is shown that the weakest precondition of a graph program can be constructed. In [2] a generalisation is presented that defines socalled program conditions that relate objects between the start and end of a program. Highlevel conditions differ from CGRS’s graph reduction systems in that highlevel conditions are a purely static, existential constraint. GRSs are defined algorithmically, by reduction. A consequence of this is the weakest preconditions for a highlevel condition can be easily defined. In contrast, the problem of shapechecking for GRSs is known to be undecidable. This also suggests that GRSs are formally more expressive than highlevel conditions. The more algorithmic approach of CGRS has the advantage that every GRS has an associated membership test. We demonstrate in Chapter 4 that certain GRSs can be tested for membership in linear time using graph reduction. In addition, highlevel conditions seem difficult to understand without a deep understanding of graph transformation. Graph reduction seems to have a stronger intuitive meaning as a means of specifying classes of graphs. Another approach to specifying and verifying the properties of graph classes is the abstract graph transformation work of [68, 13]. In this work 252
classes of graphs are specified by a shape, consisting of a graph and a node and edge multiplicity function. A concrete graph G is represented by a shape S if there exists a shaping morphism s : G → S respecting the multiplicity functions. A shape S is abstractly represented by a more abstract shape S ′ if there is an abstraction morphism s′ : S → S ′ . It is shown in this work that graph transformation rules over graphs can be lifted to shapes. Consequently, for a given set of rules and initial shape a transition system between shapes can be constructed reflecting all the behaviors of the system of rules. In later work [13] a modal logic is presented that is preserved and reflected by shaping and abstraction, making it suitable for reasoning about abstract graph transformation. This work on abstract graph transformation be seen as a highlevel generalisation of shape analysis (see §11.3). In both approaches, a finite representation is defined and transition system induced from some program (in this case a set of graph transformation rules). However, unlike other shapeanalysis work, abstract graph transformation has not been applied to realworld examples. Abstract graph transformation differs from CGRS in much the same ways as shape analysis. Invariants in abstract graph transformation are generated by analysis, rather than checked. Invariant checking can be simulated however by supplying an invariant and checking whether it is a fixedpoint of the analysis. As with shape analysis, a major disadvantage is that precision depends strongly on the choice of finite domain. Unlike shapes in CGRS, abstract graphs do not come with a membership test. Checking membership seems to require the construction of a matching morphism between a concrete graph and abstract graph, which may be expensive. In [13] it is suggested that the symbolic execution in abstract graph transformation also suffers from problems with performance. Abstract graph transformation are formally less expressive than graph reduction systems. Abstract graphs as presented in [68] correspond to a fragment of firstorder logic, means that unaugmented abstract graphs are weaker than even hyperedge replacement as a mechanism for specifying properties. Like shape analysis, to express interesting largescale properties such as reachability and cyclicity, extra annotations must be added to the abstract graph. In addition, the analysis presented in [13] cannot in general check whether annotated properties such as cyclicity are preserved. 253
Unlike CGRS, neither highlevel conditions nor abstract graph transformation has been applied to pointer programming. However, both approaches could in principle be applied to the problem of pointer verification. To do this would require either (1) an extraction function from a pointer language to a graph transformation system, or (2) the addition of constructs to the pointer language corresponding to graph transformation rules. In fact, both abstract graph transformation and highlevel conditions are to some degree orthogonal to the results we present for CGRS. We have defined a semantics for graphtransformation constructs in a Clike language, and an extraction function from graph transformation rules (see §10.1). The rules produced by extraction could be checked by a system based on abstract graph transformation or highlevel conditions, rather than one based on graph reduction. See 12.2.10 for a brief discussion of this idea.
254
Part V
Conclusion
255
Chapter 12
Conclusions and further work This thesis describes three pieces of research concerned with the use of graph transformation rules for specifying and manipulating pointer structures. We have described syntactic conditions ensuring efficient derivation for graph transformation rules and applied them to the problem of graph recognition. We have defined a correspondence between hyperedge replacement grammars and separation logic, an alternative approach to pointer safety. Finally, we have defined the language CGRS that implements shape checking using graph transformation rules. In this chapter we summarise and evaluate our major contributions (§12.1) and suggest areas for further work (§12.2).
12.1
Thesis summary
12.1.1
Fast graph transformation
Our first objective was to show that graph transformation rules can be made an efficient mechanism for checking the properties of graph structures. To achieve this aim, Part II describes defined a general framework for improving the worstcase time complexity for individual rules and sequences of rules. We then use this framework to achieve our objective, by defining two kinds of fast reduction system with a lineartime membership checking algorithm. Our approach is based on restricting rules and graphs using simple syntactic conditions. We have defined conditions on rules that ensure an im256
proved worstcase execution time. We define two kinds of condition: conditions requiring uniquelylabelled root nodes (Conditions 1, 2, and 3), and conditions requiring roots (Conditions R1, R2, and R3). We have shown that Conditions 1, 2, and 3 (§3.2) ensure lineartime application of unrooted rules (Proposition 3.8 and Proposition 3.10). We have shown that Conditions R1, R2, and R3 (§3.3) ensure constanttime application of rooted rules (Proposition 3.15 and Proposition 3.17). In addition, we have shown that Condition 1 and conditions R1, R2 and R3 ensures lineartime termination for multistep derivations (Theorem 3.23, Proposition 3.18). We have applied these conditions to the recognition of graph languages by reduction to give two new classes of fast recognition systems  linear LGRSs based on unrooted rules, and linear RGRSs based on rooted rules. We have shown by example that these reduction systems can be used to define a number of interesting noncontextfree languages (§4.2). We have shown that both LGRSs and RGRSs have a lineartime membership test (Theorem 4.4, Theorem 4.21). However, LGRSs and RGRSs are of incomparable expressive power (Proposition 4.23 and Proposition 4.24). The work presented in this part of the thesis proves formally that rules under our syntactic conditions have an improved worstcase time complexity over the general case. In this sense, our rules are ‘fast’. However, we have not shown that our approach is faster than other approaches when it is applied to any particular problem domain. An improved worstcase application time does not imply that our approach will improve the application time for a particular domain. There are three reasons for this. For small rules, the cost of application may be dominated by fixed costs, crowding out the benefits of our approach. Also for particular domains, heuristic approaches may achieve better results than our approach in most cases. Finally, we have focused purely on worstcase results rather than on algorithm optimisations. Other systems may perform better than ours due to better optimisation. To show that our approach improves application times in practice (and so to more fully justify the description of our approach as ‘fast graph transformation’) we require empirical evidence. To do this, we also require an implementation. The work on fast graph transformation presented in this thesis exists entirely abstractly, while testing it will require the concrete ex257
ecution of real graphtransformation systems. We hope in future work to develop and test more concrete instantiations of fast graph transformation (see §12.2.1).
12.1.2
Hyperedge Replacement and separation logic
Our second objective was to relate approaches to shape safety based on graph transformation to other shape safety approaches. Part III examines the relationship between separation logic and heapgraph grammars based on hyperedge replacement, and shows that our fragment of separation logic is of corresponding expressiveness to heapgraph grammars. The correspondence we have discovered fulfils our objective by illuminating the properties of both hyperedge replacement and separation logic. We have defined a fragment SL of separation logic and shown that that formulas in this fragment are of equivalent power to hyperedge replacement grammars. To do this, we have defined a translation function s from grammars to formulas, and a translation function g from formulas to grammars. We have also defined a bijective function α that maps from hyperedge replacement states to heap graphs. We have proved our translation functions s and g correct with respect to α. That is g ◦ α = α ◦ g (Theorem 7.6), and s ◦ α−1 = α−1 ◦ s (Theorem 7.10). Consequently, the fragment SL is of equivalent expressive power to the class of heapgraph grammars, modulo α (Theorem 8.3) Our fragment of separation logic inherits the inexpressibility properties of hyperedge replacement, and we have described several languages that consequently cannot be defined in our fragment. We have shown that hyperedge replacement cannot be used to model full separation logic by showing that the standard firstorder logic constructs ∧, ¬ and true cannot be modelled by a heapgraph grammars (§8.1). However, we have also shown that the symbolic heaps in common use in the separation logic world are close to our fragment, which suggests that our fragment is of practical utility, rather than just a curiosity.
12.1.3
A language for shape safety
The third objective of this thesis was to show that graph transformation rules can be used to verify the safety of pointer structures in a Clike pointer lan
258
guage. Part IV of this thesis describes our language CGRS that adds graph transformation constructs to C. CGRS fulfils our objective by merging the quite different idioms of graph transformation and C into a single language. Constructs in CGRS can be modelled as graph transformation rules, permitting shapesafety checking using the SPGT approach [4, 3]. CGRS extends C with transformer functions resembling graph transformation rules, and shapespecifications resembling graph reduction systems. We have defined a syntax for CGRS and given both small and large examples of CGRS constructs. We have shown by example that large pointer rewrites can be clearly expressed using CGRS transformers (§9.3). We have defined both a concrete and abstract semantics for CGRS constructs. The abstract semantics is defined by the function G, which extracts graph transformation rules and GRSs from CGRS constructs. The concrete semantics is given by the function C, which maps constructs to blocks of C code. This concrete code defined functions corresponding to transformer declarations, and also defines constructor functions for shapes. We have shown that the CGRS concrete semantics given by C is correct with respect to the abstract semantics given by G and the abstraction function βσ (Theorem 10.4 and Theorem 10.1). This means that, for a given transformer function F and shape structure s, it is always true that (βσ ◦ C[[F ]])(s) = (G[[F ]] ◦ βσ )(s). We have define a notion of shape safety with respect to the shapes defined by a CGRS program (Section 10.6). Any shape structure constructed by a CGRS constructor function is shapesafe by construction (Theorem 10.5). Finally, we have shown that transformers can be checked statically to determine whether they preserve shapesafety using the checking algorithm described in [3] (Theorem 10.6) While we have defined a mapping to C programs, and so abstractly defined an implementation, CGRS has not been concretely implemented. Examples given in this thesis have been handcompiled to C using the translation given in Chapter 10. The resulting C programs have been compiled using a C compiler, but no largescale testing with realworld examples has been undertaken. We hope that CGRS forms a suitable basis for future research on shape safety, and hope in future work to develop a practical and efficient implementation (see §10.7 for a discussion of this). 259
12.2
Further work
12.2.1
Experimental results for fast graph transformation
As discussed at the end of §12.1.1, we require experimental results to show that our syntactic conditions on rules result in an improvement in application times in practice. However, we expect that the results of these experiments will depend strongly on (1) the choice of domain, and (2) the preexisting optimisations in the system. Experiments will therefore require careful choice of a methodology in order to make them fair and representative. As a starting point, we propose to take an existing system such as the YAM compiler for the GP system [57] and adding support for fast graph transformation. We may have to extend the language with annotations denoting the root node, signature and so on. We can then compare the system using our approach with the system without it. Applying our work to a graph transformation benchmark, such as [76], may be a good way to produce results that can be compared with other systems. Experimental work of this kind will also illuminate possible ways of optimising our approach. In this thesis we have prioritised clarity of explanation above optimisation. For example, in §3.4 we said that the given version of the algorithm MultistepApply is less efficient than an alternative (more complex) version of the algorithm. Experimenting with fast graph transformation will give us an opportunity to optimise more fully, on the basis of the sound theoretical results described in this thesis.
12.2.2
Relationship with special reduction systems
Section 5.3 examines in some detail the relationship between our approach to fast graph transformation and special reduction systems [11, 1]. However, several questions remain unanswered. In particular, we have not determined whether SRSs that include nonterminals are less powerful than either LGRSs or RGRSs. We have conjectured that this is the case for both LGRSs and RGRSs (end of §5.3). Proving or disproving this conjecture and further investigating the relationship between fast recognition systems and other approaches should be the subject of further work.
260
12.2.3
Practical application of the correspondence
At present the correspondence result between separation logic and hyperedge replacement proved at the end of Part III are of mostly theoretical interest. However, one aim in developing this correspondence is to apply results from the graph transformation domain to the work on separation logic (and vice versa). In future work we should try to develop more practical applications of our correspondence. We have shown that our fragment of separation logic is closely related to the symbolic heaps used in recent shapechecking work (see §8.3.2). The shapechecking tools based on symbolic heaps are therefore an obvious target for this work.
12.2.4
Inference between graph grammars
A large part of the recent success of separation logic for shapechecking has been that it is natural to talk about inference between logical formulas. This means rewriting of formulas can be reasoned about naturally. Graph grammars in contrast have largely been studied as fixed objects, without any notion of properties under mutation. It would be interesting to try to develop some notion of inference for graph grammars. Such work could begin by examining the proof systems developed for symbolic heaps [8] and apply them to graph grammars using our mapping to hyperedge replacement.
12.2.5
Separation logic and contextsensitive graph transformation
We have conjectured in §8.1.4 that a fragment of separation logic that includes both letstatements and the separating conjunction −∗ could simulate grammars based on contextsensitive graph transformation. This raises several interesting questions. To begin with, can we simulate full DPO grammars using this approach, or only somewhere between DPO and hyperedgereplacement grammars? Also, can we construct a mapping from separation logic formulas with separating implication to the class of DPO grammars over heapgraphs? Solving this problem would provide interesting insights into full separation logic, as doublepushout graph rewriting is very well understood formally.
261
12.2.6
Implementation and optimisation of CGRS
CGRS currently lacks an implementation. See §10.7 for a discussion of the possibilities for implementing and optimising CGRS in future work.
12.2.7
Reducing the distance from CGRS to C
In designing CGRS we have tried to conform to the C programming model (see §9.2.1). However, both the structure and semantics of transformer functions still differs substantially from normal C programs. To program in CGRS requires an understanding of contextsensitive graph transformation rules. This substantially increases the cost to programmers of using the language. It should be the subject of further work to make GRSbased checking more acceptable to C programmers, by matching more closely their expectations. The approach of assigning a graphtransformation semantics to Clike constructs (prototyped in the Pasta language [72]) may be a productive direction to pursue.
12.2.8
Improving the modularity of shape declarations
CGRS lacks modularity. Shape declarations have to be included in full in every program, and there is no mechanism for combining shapes. In future work we should make CGRS more modular. The shape of a data structure should be abstracted from the type of data stored in it. This would allow reuse of shapes between programs, and so would allow the development of shapesafe libraries. Hiding transformer declarations in libraries would also make CGRS more acceptable to programmers unfamiliar with graph transformation. A more complex form of modularity would be definition of shapes by composition of existing shapes. Currently such composed shapes (e.g. lists of trees) have to be constructed by hand. However, in principle such languages could be defined as the composition of existing graph transformation systems. To compose languages in this way would require us to develop a more composable notion of a graph reduction system. Shape composition may also provide a more compositional approach to shape verification, if each composed shape can be verified separately.
262
12.2.9
Applying fast graph transformation to CGRS
At the moment, shapesafety checking in CGRS is performed statically. As we have shown, in general it is expensive to check the properties specified by a GRS by reduction. However, linear LGRSs and linear RGRSs have a lineartime membership checking algorithm. This could be used to provide an efficient runtime shape safety checker for CGRS. This would run periodically to test whether shape safety properties have been preserved. Such a system would be natural complement to the existing static checking algorithm. When developing a program, runtime checking could be used for debugging. Once the program has been developed, the static checking algorithm (or other proof techniques, see §11.4) could then be used to verify that the program is guaranteed shapesafe.
12.2.10
Other shapechecking approaches
CGRS adds two new notions to C: transformer functions for defining rewrites, and shape specifications for defining graph properties. These two are somewhat orthogonal: transformers could be used by a program without shape restrictions. It would be interesting to apply a different approach to shape specification to CGRS. In §11.4 we discussed two other approaches for defining the properties of graph transformation rules: highlevel conditions and abstract graph transformation. To apply these approaches to CGRS, the syntax of shapes would need to be changed to reflect the different annotation methods. The semantics of shape structures might also have to change. We expect however, that a large amount of our work could be reused CGRS and the new languages based on these approaches.
263
Appendix A
Balanced binary trees are not MSexpressible The proof given in this appendix show that the language of balanced binary trees, as defined in Chapter 4, cannot be defined by any formula in monadic secondorder logic, as defined in [17]. This proof is based on Courcelle’s proof that reachability is not MSexpressible (Proposition 5.2.9 of [17]). Enough background on monadic secondorder logic is given here so that the proof should be understandable on its own, but giving complete definition for every construct would require an unreasonably large amount of space. Consequently definitions in this appendix are somewhat terser than elsewhere in the thesis, and the reader unfamiliar with monadic secondorder logic may find it useful to refer to [17]. Formulas in monadic secondorder logic define the properties of relational structures. A structure S = hDS , (RS )R∈R i defined over a set of relation symbols R consists of a domain DS and a relation RS over DS for each R ∈ R. In the results below, if u is a string, then we write k u k to stand for the string structure. The relation symbols are sucS , defining the sequence of positions, and an iyS defining the character held at each position in the string. More formally, DS sucS i ∈ labyS
= {1, . . . , n} if u has length n; = {(1, 2), (2, 3), . . . , (n − 1, n)}; iff y is the ith letter of u.
We first prove the result that balanced binary trees are not MS1 definable. MS1 is the class of monadic secondorder logic formulas without an incidence 264
predicate, that is, a predicate expressing the fact that a particular edge is incident to a particular node. The proof depends on the following result, which Courcelle attributes to B¨ uchi and Elgot [17]. Theorem A.1. If L ⊆ {a, b}∗ is the set of words u such that k u k = ̺ where ̺ is a closed MS1 formula, then L is a regular language. (B¨ uchi & Elgot) Proposition A.2. No formula exists in MS1 that defines the class of balanced binary trees. Proof. Assume we have an MS1 formula β that is satisfied by a graph iff it is a balanced binary tree. Consider the language of strings of the form an cbm . We associate each string wn,m = an bm with a graph Bn,m with vertices {−n, . . . , −1, 0, 1, . . . , m}. The lefthand ‘a’ characters associate with vertices {−n, . . . , −1}, and the righthand ‘b’ characters associate with vertices {0, 1, . . . , m}. There is an edge in Bn,m from vertex 0 to vertices 1 and 1, and a vertex from each vertex greater than zero to each succeeding vertex, and from each vertex less than zero to the preceding vertex. A graph Bn,m is a balanced binary tree iff m = n + 1. The formula η that encodes in terms of a string structure the edge relation for a balanced binary tree: η(x1 , x2 ) = ((laba (x1 ) ∧ laba (x2 ) ∧ suc(x2 , x1 )) ∨ (labb (x1 ) ∧ labb (x2 ) ∧ suc(x1 , x2 )) ∨ (labb (x1 ) ∧ laba (x2 ) ∧ suc(x2 , x1 )) The formula η encodes the edge relation for the graph Bn,m in terms of a string wn,m . We can therefore construct the formula β[η/edg] that is satisfied if and only if a string has the form an bn+1 . Bn,m = β iff m = n + 1 iff k wn,m k = β[η/edg] But this results in a contradiction. The language of strings wn,(n+1) that satisfying β[η/edg] is nonregular, contradicting Theorem A.1. Our initial assumption of the existence of β must therefore be false. Remark A.1. The above proof is for balanced binary trees constructed from unlabelled nodes and edges. However, we can extend the same proof so as 265
to prove the result for balanced binary tree graphs with labelled nodes and edges. We simply replace the η formula with separate formulas for distinct edge labels, and add nodelabelling functions. We have proved that no formula for balanced binary trees exists in MS1 , the class of MSOL formulas without an incidence predicate. Courcelle shows in [17] that MS2 , the class of monadic secondorder logic formulas with such an incidence predicate, is formally more expressive than MS1 . However, he also gives the following result, which allows us to apply Proposition A.2 to MS2 formulas. Lemma A.3 (MS1 and MS2 expressiveness). Let C be a class of: (1) planar directed simple graphs, or (2) directed simple graphs of degree at most k, or (3) finite directed simple graphs of treewidth at most k. A property of graphs in C is MS2 expressible iff it is MS1 expressible (Courcelle, [17, p.338]). Proposition A.4. No formula exists in MS2 that defines the class of balanced binary trees. Proof. The class of balanced binary trees satisfies all three cases for the applicability of lemma A.3, so as a consequence of Proposition A.2 balanced binary trees are also not MS2 expressible.
266
Bibliography [1] S. Arnborg, B. Courcelle, A. Proskurowski, and D. Seese. An algebraic theory of graph reduction. Journal of the ACM, 40(5):1134–1164, 1993. (Cited on page 19, 58, 59, 99, 102, 103, 104, 105, 260) [2] K. Azab and A. Habel. Highlevel programs and program conditions. In H. Ehrig, R. Heckel, G. Rozenberg, and G. Taentzer, editors, ICGT, volume 5214 of Lecture Notes in Computer Science, pages 211–225. Springer, 2008. (Cited on page 252) [3] A. Bakewell, D. Plump, and C. Runciman. Checking the shape safety of pointer manipulations. In 7th International Seminar on Relational Methods in Computer Science, Revised Selected Papers, volume 3051 of Lecture Notes in Computer Science, pages 48–61. Springer, 2004. (Cited on page 17, 18, 177, 181, 182, 205, 243, 250, 259) [4] A. Bakewell, D. Plump, and C. Runciman. Specifying pointer structures by graph reduction. In Int. Workshop Applications of Graph Transformations With Industrial Relevance (AGTIVE 2003), Revised Selected and Invited Papers, volume 3062 of Lecture Notes in Computer Science, pages 30–44, 2004. (Cited on page 17, 18, 66, 68, 77, 84, 93, 94, 97, 104, 177, 179, 186, 194, 246, 259) [5] J. Berdine, C. Calcagno, B. Cook, D. Distefano, P. W. O’Hearn, T. Wies, and H. Yang. Shape analysis for composite data structures. In International Conference on ComputerAided Verification, pages 178– 192, 2007. (Cited on page 173, 251) [6] J. Berdine, C. Calcagno, and P. O’Hearn. Symbolic execution with separation logic. In Asian Symposium on Programming Languages and
267
Systems, volume 3780 of Lecture Notes in Computer Science, pages 52–68, 2005. (Cited on page 111) [7] J. Berdine, C. Calcagno, and P. W. O’Hearn. Smallfoot: Modular automatic assertion checking with separation logic. In F. S. de Boer, M. M. Bonsangue, S. Graf, and W. P. de Roever, editors, FMCO, volume 4111 of Lecture Notes in Computer Science, pages 115–137. Springer, 2005. (Cited on page 248) [8] J. Berdine, C. Calcagno, and P. W. O’Hearn. Smallfoot: Modular automatic assertion checking with separation logic. In In Proceedings of FMCO’05, volume 4111 of Lecture Notes in Computer Science, pages 115–137. Springer, 2005. (Cited on page 261) [9] L. Birkedal, N. TorpSmith, and J. Reynolds. Local reasoning about a copying garbage collector. In Proceedings of the 31st ACM symposium on Principles of Programming Languages, pages 1–58, 2004. (Cited on page 248) [10] H. L. Bodlaender. A tourist guide through treewidth. Acta Cybernetica, 11:1–21, 1993. (Cited on page 103) [11] H. L. Bodlaender and B. van Antwerpende Fluiter. Reduction algorithms for graphs of small treewidth. Inf. Comput., 167(2):86–119, 2001. (Cited on page 59, 99, 101, 102, 103, 104, 260) [12] I. Bogudlov, T. LevAmi, T. W. Reps, and M. Sagiv. Revamping tvla: Making parametric shape analysis competitive. In W. Damm and H. Hermanns, editors, CAV, volume 4590 of Lecture Notes in Computer Science, pages 221–225. Springer, 2007. (Cited on page 250, 251) [13] I. B. Boneva, A. Rensink, M. E. Kurban, and J. Bauer. Graph abstraction and abstract graph transformation. Technical Report TRCTIT0750, University of Twente, Enschede, July 2007. (Cited on page 252, 253) [14] M. Bozga, R. Iosif, and Y. Lakhnech. Storeless semantics and alias logic. In PEPM, pages 55–65. ACM, 2003. (Cited on page 248) [15] C. Calcagno, D. Distefano, P. W. O’Hearn, and H. Yang. Beyond reachability: Shape abstraction in the presence of pointer arithmetic. 268
In 13th International Symposium on Static Analysis, volume 4134 of Lecture Notes in Computer Science, pages 182–203, 2006. (Cited on page 173) [16] C. Calcagno, P. Gardner, and U. Zarfaty. Context logic as modal logic: completeness and parametric inexpressivity. In Proceedings of the 34th ACM SIGPLANSIGACT Symposium on Principles of Programming Languages, pages 123–134, New York, NY, USA, 2007. ACM. (Cited on page 175) [17] B. Courcelle. The expression of graph properties and graph transformations in monadic secondorder logic. In Handbook of Graph Grammars and Computing by Graph Transformation, pages 313–400. World Scientific, 1997. (Cited on page 103, 264, 265, 266) [18] R. Diestel. Graph theory. Springer, 2000. (Cited on page 104) [19] D. Distefano, J.P. Katoen, and A. Rensink. Who is pointing when to whom? In K. Lodaya and M. Mahajan, editors, FSTTCS, volume 3328 of Lecture Notes in Computer Science, pages 250–262. Springer, 2004. (Cited on page 249) [20] D. Distefano, P. W. O’Hearn, and H. Yang. A local shape analysis based on separation logic. In 12th International Conference on Tools and Algorithms for the Construction and Analysis of Systems, volume 3920 of Lecture Notes in Computer Science, pages 287–302. Springer, 2006. (Cited on page 17, 111, 173) [21] M. Dodds. From separation logic to hyperedge replacement and back (extended abstract).
In Proc. International Conference on Graph
Transformation (ICGT 2008), volume 5214/2008 of Lecture Notes in Computer Science, pages 484–486. Springer, 2008. (Cited on page 22) [22] M. Dodds and D. Plump. Extending C for checking shape safety. In Proc. Graph Transformation for Verification and Concurrency (GTVC 2005), volume 154(2) of Electronic Notes in Theoretical Computer Science, pages 95–112. Elsevier, 2006. (Cited on page 22) [23] M. Dodds and D. Plump. Graph transformation in constant time. In Proc. International Conference on Graph Transformation (ICGT 269
2006), volume 4178 of Lecture Notes in Computer Science, pages 367– 382. Springer, 2006. (Cited on page 22) [24] M. Dodds and D. Plump. From separation logic to hyperedge replacement and back. In Proc. Doctoral Symposium at the International Conference on Graph Transformation, volume 16 of Electronic Communications of the EASST. European Association of Software Science and Technology, 2008. (Cited on page 22) [25] H. D¨orr. Bypass strong Vstructures and find an isomorphic labelled subgraph in linear time. In W. Mayr, Ernst, G. Schmidt, and G. Tinhofer, editors, GraphTheoretic Concepts in Computer Science, volume 903 of Lecture Notes in Computer Science, pages 305–318, 1995. (Cited on page 18, 100) [26] H. D¨orr. Efficient Graph Rewriting and its Implementation, volume 922 of Lecture Notes in Computer Science. Springer, 1995. (Cited on page 18, 68, 100, 101) [27] F. Drewes, A. Habel, and H.J. Kreowski. Hyperedge replacement graph grammars. In G. Rozenberg, editor, Handbook of Graph Grammars and Computing by Graph Transformation. Volume I: Foundations, chapter 2, pages 95–162. World Scientific, 1997. (Cited on page 29, 31, 66, 76, 131, 135, 144, 148, 149, 166, 168) [28] J. Engelfriet. Contextfree graph grammars. In Handbook of formal languages, vol. 3: beyond words, pages 125–213. Springer, New York, NY, USA, 1997. (Cited on page 174) [29] J. Engelfriet and G. Rozenberg. Node replacement graph grammars. In G. Rozenberg, editor, Handbook of Graph Grammars and Computing by Graph Transformation. Volume I: Foundations, chapter 1, pages 1–94. World Scientific, 1997. (Cited on page 76) [30] C. Forgy et al. RETE: A fast algorithm for the many pattern/many object pattern match problem. Artificial Intelligence, 19(1):17–37, 1982. (Cited on page 102) [31] P. Fradet and D. L. M´etayer. Shape types. In Proceedings of the 1997 ACM Symposium on Principles of Programming Languages, pages 27– 39. ACM Press, 1997. (Cited on page 20, 182, 242, 245) 270
[32] P. Fradet and D. L. M´etayer. Structured gamma. Science of Computer Programming, 31(23):263–289, 1998. (Cited on page 182) [33] J. J. Fu. Linear matchingtime algorithm for the directed graph isomorphism problem. In Proceedings of the 6th International Symposium on Algorithms, volume 1004 of Lecture Notes in Computer Science, pages 409–417. Springer, 1995. (Cited on page 100) [34] J. J. Fu. Pattern matching in directed graphs. In Proc. Combinatorial Pattern Matching, volume 937 of Lecture Notes in Computer Science, pages 64–77. Springer, 1995. (Cited on page 100) [35] M. R. Garey and D. S. Johnson. Computers and Intractability : A Guide to the Theory of NPCompleteness. W.H. Freeman and Company, January 1979. (Cited on page 39, 73) [36] R. Geis, G. V. Batz, D. Grund, S. Hack, and A. M. Szalkowski. Grgen: A fast spobased graph rewriting tool. In A. Corradini, H. Ehrig, U. Montanari, L. Ribeiro, and G. Rozenberg, editors, Proc. International Conference on Graph Transformation (ICGT 2006), Lecture Notes in Computer Science, pages 383 – 397. Springer, 2006. (Cited on page 99, 100) [37] Y. Gurevich and J. K. Huggins. The semantics of the c programming language. In Selected Papers from CSL ’92, volume 702 of Lecture Notes in Computer Science, pages 274–308. Springer, 1993. (Cited on page 222) [38] A. Habel. Hyperedge Replacement: Grammars and Languages, volume 643 of Lecture Notes in Computer Science. Springer, 1992. (Cited on page 29, 31, 132, 149, 167, 172, 173) [39] A. Habel and H.J. Kreowski. Filtering hyperedgereplacement through compatible properties. In M. Nagl, editor, WG, volume 411 of Lecture Notes in Computer Science, pages 107–120. Springer, 1989. (Cited on page 115, 165) [40] A. Habel, J. M¨ uller, and D. Plump. Doublepushout graph transformation revisited. Mathematical. Structures in Comp. Sci., 11(5):637–688, 2001. (Cited on page 185) 271
[41] A. Habel and K.H. Pennemann. Satisfiability of highlevel conditions. In A. Corradini, H. Ehrig, U. Montanari, L. Ribeiro, and G. Rozenberg, editors, ICGT, volume 4178 of Lecture Notes in Computer Science, pages 430–444. Springer, 2006. (Cited on page 252) [42] A. Habel and D. Plump. Computational completeness of programming languages based on graph transformation. In F. Honsell and M. Miculan, editors, FoSSaCS, volume 2030 of Lecture Notes in Computer Science, pages 230–245. Springer, 2001. (Cited on page 252) [43] A. Habel and D. Plump. Relabelling in graph transformation. In Proc. International Conference on Graph Transformation (ICGT 2002), volume 2505 of Lecture Notes in Computer Science, pages 135–147. Springer, 2002. (Cited on page 26, 27) [44] J. G. Henriksen, J. L. Jensen, M. E. Jørgensen, N. Klarlund, R. Paige, T. Rauhe, and A. Sandholm. MONA: Monadic secondorder logic in practice. In E. Brinksma, R. Cleaveland, K. G. Larsen, T. Margaria, and B. Steffen, editors, Tools and Algorithms for the Construction and Analysis of Systems, volume 1019 of Lecture Notes in Computer Science, pages 89–110. Springer, 1995. (Cited on page 247) [45] International Organization for Standardization. ISO C standard 1999. Technical report, 1999. ISO/IEC 9899:1999 draft. (Cited on page 209) [46] S. Ishtiaq and P. W. O’Hearn. BI as an assertion language for mutable data structures. In Proceedings of the 2001 ACM Symposium on Principles of Programming Languages, volume 36(3) of ACM SIGPLAN Notices. ACM, March 2001. (Cited on page 111, 247) [47] B. Jeannet, A. Loginov, T. W. Reps, and S. Sagiv. A relational approach to interprocedural shape analysis. In R. Giacobazzi, editor, SAS, volume 3148 of Lecture Notes in Computer Science, pages 246– 264. Springer, 2004. (Cited on page 250) [48] B. W. Kernighan and D. M. Ritchie. C Programming Language (2nd Edition). Prentice Hall, 1988. (Cited on page 222) [49] N. Klarlund and M. I. Schwartzbach. Graph types. In Proceedings of the 1993 ACM Symposium on Principles of Programming Languages, pages 196–205. ACM, 1993. (Cited on page 246) 272
[50] D. E. Knuth. The Art of Computer Programming, Volume III: Sorting and Searching. AddisonWesley, 1973. (Cited on page 193) [51] K.J. Lange and E. Welzl. String grammars with disconnecting. In Fundamentals of Computation Theory, volume 199 of Lecture Notes in Computer Science, pages 249–256. Springer, 1985. (Cited on page 76) [52] O. Lee, H. Yang, and K. Yi. Automatic verification of pointer programs using grammarbased shape analysis. In Proceedings of the 14th European Symposium on Programming, volume 3444 of Lecture Notes in Computer Science, pages 124–140. Springer, April 2005. (Cited on page 111, 113, 117, 118, 119, 120, 121, 122, 165, 175) [53] T. LevAmi, R. Manevich, and S. Sagiv. Tvla: A system for generating abstract interpreters. In R. Jacquart, editor, IFIP Congress Topical Sessions, pages 367–376. Kluwer, 2004. (Cited on page 250) [54] T. LevAmi and S. Sagiv. TVLA: A system for implementing static analyses. In SAS ’00: Proceedings of the 7th International Symposium on Static Analysis, pages 280–301. Springer, 2000. (Cited on page 249, 251) [55] T. LevAmi and S. Sagiv. Tvla: A system for implementing static analyses. In J. Palsberg, editor, SAS, volume 1824 of Lecture Notes in Computer Science, pages 280–301. Springer, 2000. (Cited on page 250) [56] E. Lozes. Separation logic preserves the expressive power of classical logic. In Proceedings of the 2st Workshop on Semantics, Program Analysis, and Computing Environments for Memory Management, 2004. Informal proceedings. (Cited on page 175) [57] G. Manning and D. Plump. The York abstract machine. In R. Bruni and D. V´arro, editors, Proc. Graph Transformation and Visual Modelling Techniques (GTVMT 2006), Vienna, Austria, April 1–2, 2006, volume 211 of Electronic Notes in Theoretical Computer Science, pages 231–240. Elsevier, 2006. (Cited on page 37, 99, 260) [58] A. Møller and M. I. Schwartzbach. The pointer assertion logic engine. In Proceedings of the 2001 ACM Conference on Programming Language
273
Design and Implementation, volume 36(5) of SIGPLAN Notices. ACM, 2001. (Cited on page 246) [59] J. M¨ uller and R. Geis. Speeding up graph transformation through automatic concatenation of rewrite rules. Technical report, Institut f¨ ur Programmstrukturen und Datenorganisation, Universit¨ at Karlsruhe, 2007. (Cited on page 99, 100) [60] H. R. Neilson and F. Nielson. Semantics with Applications: An Appetizer. Wiley Professional Computing, 2005. (Cited on page 223, 225) [61] M. Norrish. An abstract dynamic semantics for C. Technical report, Computer Laboratory, University of Cambridge, Sept. 27 1997. (Cited on page 222) [62] Object Management Group. Unified Modeling Language (UML) Specification, version 2.0, 2009. (Cited on page 33) [63] D. Plump. Hypergraph rewriting: critical pairs and undecidability of confluence. In M. R. Sleep, M. J. Plasmeijer, and M. C. J. D. van Eekelen, editors, Term graph rewriting: theory and practice, pages 201– 213. Wiley, Chichester, UK, UK, 1993. (Cited on page 72, 80, 87) [64] D. Plump. Confluence of graph transformation revisited. In A. Middeldorp, V. van Oostrom, F. van Raamsdonk, and R. C. de Vrijer, editors, Processes, Terms and Cycles, volume 3838 of Lecture Notes in Computer Science, pages 280–308. Springer, 2005. (Cited on page 72) [65] D. Plump and S. Steinert. Towards graph programs for graph algorithms. In Proc. International Conference on Graph Transformation (ICGT 2004), volume 3256 of Lecture Notes in Computer Science, pages 128–143. Springer, 2004. (Cited on page 37, 184) [66] A. Rensink.
GROOVE: A graph transformation tool set for
the simulation and analysis of graph grammars.
Available at
http://www.cs.utwente.nl/~groove, 2003. (Cited on page 37) [67] A. Rensink. The GROOVE simulator: A tool for state space generation. In J. Pfalz, M. Nagl, and B. B¨ohlen, editors, Applications of Graph Transformations with Industrial Relevance (AGTIVE), volume 3062 of 274
Lecture Notes in Computer Science, pages 479–485. SpringerVerlag, 2004. (Cited on page 37) [68] A. Rensink and D. Distefano. Abstract graph transformation. Electr. Notes Theor. Comput. Sci., 157(1):39–59, 2006. (Cited on page 33, 252, 253) [69] J. C. Reynolds. Separation logic: A logic for shared mutable data structures. In Proceedings of the Seventeenth Annual IEEE Symposium on Logic in Computer Science, 2002. (Cited on page 14, 17, 111, 112, 113, 114, 247) [70] N. Rinetzky and S. Sagiv. Interprocedural shape analysis for recursive programs. In R. Wilhelm, editor, CC, volume 2027 of Lecture Notes in Computer Science, pages 133–149. Springer, 2001. (Cited on page 250) [71] G. Rozenberg, editor. Handbook of Graph Grammars and Computing by Graph Transformation. World Scientific, 1996. (Cited on page 15) [72] C. Runciman. Pasta shell: revision and first implementation. SPGT project memo, Department of Computer Science, University of York, 2002. (Cited on page 262) [73] Agg development team. ronment:
The Agg 1.4.0 Development Envi
The User Manual.
Technische Universit¨ at Berlin.
http://tfs.cs.tuberlin.de/agg. (Cited on page 33) ´ [74] E.J. Sims. Extending separation logic with fixpoints and postponed substitution.
Theoretical Computer Science, 351(2):258–275, 2006.
(Cited on page 111, 113, 117, 118, 165) [75] A. Tarski. A latticetheoretical fixpoint theorem and its applications. Pacific Journal of Mathematics, 5:285–309, 1955. (Cited on page 120) [76] G. Varr´ o, A. Sch¨ urr, and D. Varr´ o. Benchmarking for graph transformation. In Proc. of the 2005 IEEE Symposium on Visual Languages and HumanCentric Computing, pages 79–88, Dallas, Texas, USA, September 2005. (Cited on page 260) [77] G. Varr´ o and D. Varr´ o. Graph Transformation with Incremental Updates. Electronic Notes in Theoretical Computer Science, 109:71–83, 2004. (Cited on page 102, 103) 275
[78] H. Yang. An example of local reasoning in BI pointer logic: the SchorrWaite graph marking algorithm. In Proceedings of the 1st Workshop on Semantics, Program Analysis, and Computing Environments for Memory Management, Jan 2001. Informal proceedings. (Cited on page 248) [79] H. Yang, O. Lee, J. Berdine, C. Calcagno, B. Cook, D. Distefano, and P. O’Hearn. Scalable shape analysis for systems code. In International Conference on ComputerAided Verification, volume 5123 of Lecture Notes in Computer Science, pages 385–398. Springer, 2008. (Cited on page 173, 251)
276