diff options
Diffstat (limited to 'subprojects/docs/src/learn/language/predicates')
-rw-r--r-- | subprojects/docs/src/learn/language/predicates/DerivedFeature.svg | 76 | ||||
-rw-r--r-- | subprojects/docs/src/learn/language/predicates/index.md | 284 |
2 files changed, 360 insertions, 0 deletions
diff --git a/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg b/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg new file mode 100644 index 00000000..be9465b8 --- /dev/null +++ b/subprojects/docs/src/learn/language/predicates/DerivedFeature.svg | |||
@@ -0,0 +1,76 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | <svg width="216pt" height="226pt" viewBox="-6 -6 227.8699951171875 238.39999389648438" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="refinery-9k5t5y1ScYnYvNXEZbWT4"><style>.refinery-9k5t5y1ScYnYvNXEZbWT4 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-outline{stroke:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-header{fill:rgb(53, 161, 173);}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-bg{fill:#fff;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-INDIVIDUAL .node-outline{stroke-width:2;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-shadow.node-bg{fill:#19202b;opacity:0.24;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-g .node-header{fill:#e5c07b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-h .node-header{fill:#e06c75;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-i .node-header{fill:#98c379;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-j .node-header{fill:#c678dd;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-k .node-header{fill:#80a7f4;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-l .node-header{fill:#e3d1b2;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-m .node-header{fill:#e78b8f;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-n .node-header{fill:#abcc94;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-o .node-header{fill:#dbb2e8;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-p .node-header{fill:#92c0e9;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-line{stroke:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-arrow{fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN text{fill:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-line{stroke:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-arrow{fill:none;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR text{fill:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-line{stroke:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-arrow{fill:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-TRUE{fill:#19202b;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-UNKNOWN{fill:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-ERROR{fill:#ca1243;}.refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-UNKNOWN{fill:#696c77;}.refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-ERROR{fill:#ca1243;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-outline{stroke:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-header{fill:rgb(60, 127, 135);}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node .node-bg{fill:#282c34;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-INDIVIDUAL .node-outline{stroke-width:2;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-shadow.node-bg{fill:#ebebff;opacity:0.32;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-exists-UNKNOWN .node-outline{stroke-dasharray:5 2;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-g .node-header{fill:#ae8003;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-h .node-header{fill:#a23b47;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-i .node-header{fill:#428141;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-j .node-header{fill:#854797;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-k .node-header{fill:#3982bb;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-l .node-header{fill:#827662;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-m .node-header{fill:#904f53;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-n .node-header{fill:#647e63;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-o .node-header{fill:#805f89;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .node-typeHash-p .node-header{fill:#4f7799;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge text{font-family:"Open Sans Variable","Open Sans","Roboto","Helvetica","Arial",sans-serif;fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-line{stroke:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge .edge-arrow{fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN text{fill:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-line{stroke:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-UNKNOWN .edge-arrow{fill:none;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR text{fill:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-line{stroke:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .edge-ERROR .edge-arrow{fill:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-TRUE{fill:#ebebff;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 .icon-ERROR{fill:#e06c75;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-UNKNOWN{fill:#abb2bf;}[data-theme="dark"] .refinery-9k5t5y1ScYnYvNXEZbWT4 text.label-ERROR{fill:#e06c75;}</style><defs><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE" class="icon-TRUE"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-9k5t5y1ScYnYvNXEZbWT4-icon-UNKNOWN" class="icon-UNKNOWN"><path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16zM16 17H5V7h11l3.55 5L16 17z"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" id="refinery-9k5t5y1ScYnYvNXEZbWT4-icon-ERROR" class="icon-ERROR"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10s10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17L12 13.41L8.41 17L7 15.59L10.59 12L7 8.41L8.41 7L12 10.59L15.59 7L17 8.41L13.41 12L17 15.59z"/></svg></defs> | ||
3 | <g class="graph" transform="translate(4, 222.39999389648438)"> | ||
4 | <!-- n2 --> | ||
5 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-i"> | ||
6 | |||
7 | <rect stroke="none" x="64.75" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
8 | <rect stroke="none" x="60" y="-52" width="92" height="27" clip-path="url(#refinery-9k5t5y1ScYnYvNXEZbWT4-clip-0)" class="node-header"/> | ||
9 | <text text-anchor="start" x="76.52" y="-33" font-size="12.00">transition1</text> | ||
10 | <use x="70.7485" y="-19.2" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE"/> | ||
11 | <g><text text-anchor="start" x="86.75" y="-9.6" font-size="12.00" class="label label-TRUE">Transition</text> | ||
12 | </g> | ||
13 | <polyline points="64.75,-25.4 148.97,-25.4" class="node-outline"/> | ||
14 | <rect fill="none" x="64.75" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
15 | <clipPath id="refinery-9k5t5y1ScYnYvNXEZbWT4-clip-0"><rect stroke="none" x="64.75" y="-48.8" width="84.22" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
16 | <!-- n3 --> | ||
17 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
18 | |||
19 | <rect stroke="none" x="74.84" y="-218.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-bg"/> | ||
20 | <rect stroke="none" x="70" y="-222" width="72" height="27" clip-path="url(#refinery-9k5t5y1ScYnYvNXEZbWT4-clip-1)" class="node-header"/> | ||
21 | <text text-anchor="start" x="85.98" y="-202.6" font-size="12.00">vertex1</text> | ||
22 | <use x="80.8442" y="-188.8" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE"/> | ||
23 | <g><text text-anchor="start" x="96.84" y="-179.2" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
24 | </g> | ||
25 | <polyline points="74.84,-195 138.87,-195" class="node-outline"/> | ||
26 | <rect fill="none" x="74.84" y="-218.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-outline"/> | ||
27 | <clipPath id="refinery-9k5t5y1ScYnYvNXEZbWT4-clip-1"><rect stroke="none" x="74.84" y="-218.4" width="64.03" height="48.80000000000001" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
28 | <!-- n2->n3 --> | ||
29 | <g class="edge edge-TRUE"> | ||
30 | |||
31 | <path fill="none" d="M111.71,-48.78C113.97,-77.25 114.21,-125.59 112.45,-158.38" class="edge-line"/> | ||
32 | <polygon points="108.96,-157.98 111.8,-168.19 115.95,-158.44 108.96,-157.98" class="edge-line edge-arrow"/> | ||
33 | <text text-anchor="middle" x="97.12" y="-99.47" font-size="10.50">source</text> | ||
34 | </g> | ||
35 | <!-- n4 --> | ||
36 | <g class="node node-IMPLICIT node-exists-TRUE node-equalsSelf-TRUE node-typeHash-h"> | ||
37 | |||
38 | <rect stroke="none" x="143.84" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/> | ||
39 | <rect stroke="none" x="139" y="-137" width="72" height="27" clip-path="url(#refinery-9k5t5y1ScYnYvNXEZbWT4-clip-2)" class="node-header"/> | ||
40 | <text text-anchor="start" x="154.98" y="-117.8" font-size="12.00">vertex2</text> | ||
41 | <use x="149.844" y="-104" width="12" height="12" id="" class="icon icon-TRUE" href="#refinery-9k5t5y1ScYnYvNXEZbWT4-icon-TRUE"/> | ||
42 | <g><text text-anchor="start" x="165.84" y="-94.4" font-size="12.00" class="label label-TRUE">Vertex</text> | ||
43 | </g> | ||
44 | <polyline points="143.84,-110.2 207.87,-110.2" class="node-outline"/> | ||
45 | <rect fill="none" x="143.84" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-outline"/> | ||
46 | <clipPath id="refinery-9k5t5y1ScYnYvNXEZbWT4-clip-2"><rect stroke="none" x="143.84" y="-133.6" width="64.03" height="48.8" rx="12" ry="12" class="node-bg"/></clipPath></g> | ||
47 | <!-- n2->n4 --> | ||
48 | <g class="edge edge-TRUE"> | ||
49 | |||
50 | <path fill="none" d="M132.35,-48.63C140.1,-57.19 148.46,-67.04 155.81,-76.23" class="edge-line"/> | ||
51 | <polygon points="152.92,-78.21 161.83,-83.94 158.44,-73.91 152.92,-78.21" class="edge-line edge-arrow"/> | ||
52 | <text text-anchor="middle" x="133.35" y="-57.15" font-size="10.50">target</text> | ||
53 | </g> | ||
54 | <!-- n3->n2 --> | ||
55 | <g class="edge edge-TRUE"> | ||
56 | |||
57 | <path fill="none" stroke-width="2" d="M102.01,-169.7C99.75,-141.26 99.5,-92.92 101.27,-60.1" class="edge-line"/> | ||
58 | <polygon stroke-width="2" points="104.3,-60.73 101.81,-51.8 98.18,-60.33 104.3,-60.73" class="edge-line edge-arrow"/> | ||
59 | <text text-anchor="start" x="0" y="-113.73" font-weight="bold" font-size="10.50">outgoingTransition</text> | ||
60 | </g> | ||
61 | <!-- n4->n2 --> | ||
62 | <!-- n4->n2 --> | ||
63 | <g class="edge edge-TRUE"> | ||
64 | |||
65 | <path fill="none" d="M150.52,-85.14C142.77,-76.59 134.41,-66.75 127.05,-57.54" class="edge-line"/> | ||
66 | <polygon points="129.93,-55.54 121.01,-49.82 124.41,-59.86 129.93,-55.54" class="edge-line edge-arrow"/> | ||
67 | <text text-anchor="middle" x="87.8" y="-70.33" font-size="10.50">incomingTransition</text> | ||
68 | </g><g class="edge edge-TRUE"> | ||
69 | |||
70 | <path fill="none" d="M126.08,-169.94C133.27,-161.31 141.57,-151.34 149.31,-142.06" class="edge-line"/> | ||
71 | <polygon points="151.82,-144.51 155.53,-134.59 146.44,-140.03 151.82,-144.51" class="edge-line edge-arrow"/> | ||
72 | <text text-anchor="middle" x="116.57" y="-154.94" font-size="10.50">neighbors</text> | ||
73 | </g> | ||
74 | |||
75 | </g> | ||
76 | </svg> \ No newline at end of file | ||
diff --git a/subprojects/docs/src/learn/language/predicates/index.md b/subprojects/docs/src/learn/language/predicates/index.md new file mode 100644 index 00000000..261054c1 --- /dev/null +++ b/subprojects/docs/src/learn/language/predicates/index.md | |||
@@ -0,0 +1,284 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | description: Model queries and model validation | ||
5 | sidebar_position: 2 | ||
6 | --- | ||
7 | |||
8 | # Graph predicates | ||
9 | |||
10 | Graph predicates are logic expressions that can be used to query for interesting model fragments, as well as for validating the consistency of models. They are evaluated on partial models according to [four-valued logic](../logic) semantics. | ||
11 | |||
12 | Predicates in Refinery are written in [Disjunctive Normal Form](https://en.wikipedia.org/wiki/Disjunctive_normal_form) (DNF) as an _OR_ of _ANDs_, i.e., a _disjunction_ of _clauses_ formed as a _conjunction_ of positive or negated logic _literals._ | ||
13 | This matches the syntax and semantics of logical query languages, such as [Datalog](https://en.wikipedia.org/wiki/Datalog), and logical programming languages, such as [Prolog](https://en.wikipedia.org/wiki/Prolog). | ||
14 | |||
15 | import Link from '@docusaurus/Link'; | ||
16 | |||
17 | <details> | ||
18 | <summary>Example metamodel</summary> | ||
19 | |||
20 | In the examples on this page, we will use the following metamodel as illustration: | ||
21 | |||
22 | ```refinery | ||
23 | abstract class CompositeElement { | ||
24 | contains Region[] regions | ||
25 | } | ||
26 | |||
27 | class Region { | ||
28 | contains Vertex[] vertices opposite region | ||
29 | } | ||
30 | |||
31 | abstract class Vertex { | ||
32 | container Region region opposite vertices | ||
33 | contains Transition[] outgoingTransition opposite source | ||
34 | Transition[] incomingTransition opposite target | ||
35 | } | ||
36 | |||
37 | class Transition { | ||
38 | container Vertex source opposite outgoingTransition | ||
39 | Vertex[1] target opposite incomingTransition | ||
40 | } | ||
41 | |||
42 | abstract class Pseudostate extends Vertex. | ||
43 | |||
44 | abstract class RegularState extends Vertex. | ||
45 | |||
46 | class Entry extends Pseudostate. | ||
47 | |||
48 | class Exit extends Pseudostate. | ||
49 | |||
50 | class Choice extends Pseudostate. | ||
51 | |||
52 | class FinalState extends RegularState. | ||
53 | |||
54 | class State extends RegularState, CompositeElement. | ||
55 | |||
56 | class Statechart extends CompositeElement. | ||
57 | ``` | ||
58 | |||
59 | <p> | ||
60 | <Link | ||
61 | href="https://refinery.services/#/1/KLUv_WAEAiUIAOIKIR5gadMGg1ajk9jLoipJ58vc0vAE5opt1YaDpyOCAAdaCjMohSdgl4rj1yTo8UCgpTDHCIAE-o3Jr28mGO9AEoDcR-tLGh4liE2Z3IOX50z-FksLaNWLpLXd1QiUII2vNjCMBWOVEgTzjhG0eHVMIyIyFOjoxcrBv83FkgftlmJ0K_0eVDQgEBSCrXYvD1Q2wlwGXecz2HjRADQOLMh6iIYIWBPuFBBCI2igVgiHAFH4uclAydd4TFayN-oOpjzxgd0FlTzkN6QZ8CQDXBN4EPjB5VJZCANQlJA3wDd_PVyUA5eA0gaeAcgENsm4YnCogWihMAMkA8-CoB-gm9HJC0AB" | ||
62 | className="button button--lg button--primary button--play" | ||
63 | >Try in Refinery</Link> | ||
64 | </p> | ||
65 | |||
66 | </details> | ||
67 | |||
68 | [Assertions](../logic/#assertions) about graph predicates can prescribe where the predicate should (for positive assertions) or should not (for negative assertions) hold. | ||
69 | When generating consistent models | ||
70 | |||
71 | ## Atoms | ||
72 | |||
73 | An _atom_ is formed by a _symbol_ and _argument list_ of variables. | ||
74 | Possible symbols include [classes](../classes/#classes), [references](../classes/#references), and [predicates](../predicates). | ||
75 | We may write a basic graph query as a conjunction (AND) of atoms. | ||
76 | |||
77 | The `pred` keyword defines a graph predicate. After the _predicate name_, a _parameter list_ of variables is provided. The atoms of the graph predicate are written after the `<->` operator, and a full stop `.` terminates the predicate definition. | ||
78 | |||
79 | The following predicate `entryInRegion` will match pairs of `Region` instances `r` and `Entry` instances `e` such that `e` is a vertex in `r`. | ||
80 | |||
81 | ```refinery | ||
82 | pred entryInRegion(r, e) <-> | ||
83 | Region(r), | ||
84 | vertices(r, e), | ||
85 | Entry(e). | ||
86 | ``` | ||
87 | |||
88 | We may write unary symbols that act as _parameter types_ directly in the parameter list. The following definition is equivalent to the previous one: | ||
89 | |||
90 | ```refinery | ||
91 | pred entryInRegion(Region r, Entry e) <-> | ||
92 | vertices(r, e). | ||
93 | ``` | ||
94 | |||
95 | import TableIcon from '@material-icons/svg/svg/table_chart/baseline.svg'; | ||
96 | |||
97 | :::info | ||
98 | |||
99 | You may display the result of graph predicate matching in the <TableIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Table view icon" /> _table view_ of the Refinery web UI. | ||
100 | |||
101 | ::: | ||
102 | |||
103 | ## Quantification | ||
104 | |||
105 | Variables not appearing in the parameter list are _existentially quantified._ | ||
106 | |||
107 | The following predicate matches `Region` instances with two entries: | ||
108 | |||
109 | ```refinery | ||
110 | pred multipleEntriesInRegion(Region r) <-> | ||
111 | entryInRegion(r, e1), | ||
112 | entryInRegion(r, e2), | ||
113 | e1 != e2. | ||
114 | ``` | ||
115 | |||
116 | Existentially quantified variables that appear only once in the predicate should be prefixed with `_`. This shows that the variable is intentionally used only once (as opposite to the second reference to the variable being omitted by mistake). | ||
117 | |||
118 | ```refinery | ||
119 | pred regionWithEntry(Region r) <-> | ||
120 | entryInRegion(r, _e). | ||
121 | ``` | ||
122 | |||
123 | Alternatively, you may use a single `_` whenever a variable occurring only once is desired. Different occurrences of `_` are considered distinct variables. | ||
124 | |||
125 | ```refinery | ||
126 | pred regionWithEntry(Region r) <-> | ||
127 | entryInRegion(r, _). | ||
128 | ``` | ||
129 | |||
130 | ## Negation | ||
131 | |||
132 | Negative literals are written by prefixing the corresponding atom with `!`. | ||
133 | |||
134 | Inside negative literals, quantification is _universal:_ the literal matches if there is no assignment of the variables solely appearing in it that satisfies the corresponding atom. | ||
135 | |||
136 | The following predicate matches `Region` instances that have no `Entry`: | ||
137 | |||
138 | ```refinery | ||
139 | pred regionWithoutEntry(Region r) <-> | ||
140 | !entryInRegion(r, _). | ||
141 | ``` | ||
142 | |||
143 | In a graph predicate, all parameter variables must be _positively bound,_ i.e., appear in at least one positive literal (atom). | ||
144 | Negative literals may further constrain the predicate match one it has been established by the positive literals. | ||
145 | |||
146 | ## Object equality | ||
147 | |||
148 | The operators `a == b` and `a != b` correspond to the literals `equals(a, b)` and `!equals(a, b)`, respectively. | ||
149 | See the section about [multi-objects](../logic/#multi-objects) for more information about the `equals` symbol. | ||
150 | |||
151 | ## Transitive closure | ||
152 | |||
153 | The `+` operator forms the [transitive closure](https://en.wikipedia.org/wiki/Transitive_closure) of symbols with exactly 2 parameters. | ||
154 | The transitive closure `r+(a, b)` holds if either `r(a, b)` is `true`, or there is a sequence of objects `c1`, `c2`, …, `cn` such that `r(a, c1)`, `r(c1, c2)`, `r(c2, c3)`, …, `r(cn, b)`. | ||
155 | In other words, there is a path labelled with `r` in the graph from `a` to `b`. | ||
156 | |||
157 | Transitive closure may express queries about graph reachability: | ||
158 | |||
159 | ```refinery | ||
160 | pred neighbors(Vertex v1, Vertex v2) <-> | ||
161 | Transition(t), | ||
162 | source(t, v1), | ||
163 | target(t, v2). | ||
164 | |||
165 | pred cycle(Vertex v) <-> | ||
166 | neighbors+(v, v). | ||
167 | ``` | ||
168 | |||
169 | ## Disjunction | ||
170 | |||
171 | Disjunction (OR) of _clauses_ formed by a conjunction (AND) of literals is denoted by `;`. | ||
172 | |||
173 | ```refinery | ||
174 | pred regionWithInvalidNumberOfEntries(Region r) <-> | ||
175 | !entryInRegion(r, _) | ||
176 | ; | ||
177 | entryInRegion(r, e1), | ||
178 | entryInRegion(r, e2), | ||
179 | e1 != e2. | ||
180 | ``` | ||
181 | |||
182 | Every clause of a disjunction must bind every parameter variable of the graph predicate _positively._ | ||
183 | _Type annotations_ on parameter are applied in all clauses. | ||
184 | Therefore, the previous graph pattern is equivalent to the following: | ||
185 | |||
186 | ```refinery | ||
187 | pred regionWithInvalidNumberOfEntries(r) <-> | ||
188 | Region(r), | ||
189 | !entryInRegion(r, _) | ||
190 | ; | ||
191 | Region(r), | ||
192 | entryInRegion(r, e1), | ||
193 | entryInRegion(r, e2), | ||
194 | e1 != e2. | ||
195 | ``` | ||
196 | |||
197 | ## Derived features | ||
198 | |||
199 | Graph predicates may act as _derived types_ and _references_ in metamodel. | ||
200 | |||
201 | A graph predicate with exactly 1 parameters can be use as if it was a class: you may use it as a [_parameter type_](#atoms) in other graph patterns, as a _target type_ of a (non-containment) [reference](../classes/#references), or in a [_scope constraint_](../logic#type-scopes). | ||
202 | |||
203 | _Derived references_ are graph predicates with exactly 2 parameters, which correspond the source and target node of the reference. | ||
204 | |||
205 | import TuneIcon from '@material-icons/svg/svg/tune/baseline.svg'; | ||
206 | import LabelIcon from '@material-icons/svg/svg/label/baseline.svg'; | ||
207 | import LabelOutlineIcon from '@material-icons/svg/svg/label/outline.svg'; | ||
208 | |||
209 | :::info | ||
210 | |||
211 | You may use the <TuneIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Filter panel icon" /> _filter panel_ icon in Refinery to toggle the visibility of graph predicates with 1 or 2 parameters. | ||
212 | You may either show <LabelOutlineIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="Unknown value icon" /> _both true and unknown_ values or <LabelIcon style={{ fill: 'currentColor', verticalAlign: 'text-top' }} title="True value icon" /> _just true_ values. | ||
213 | |||
214 | ::: | ||
215 | |||
216 | --- | ||
217 | |||
218 | For example, we may replace the reference `neighbors` in the class `Vertex`: | ||
219 | |||
220 | ```refinery | ||
221 | class Vertex { | ||
222 | Vertex[] neighbors | ||
223 | } | ||
224 | ``` | ||
225 | |||
226 | with the graph predicate `neighbors` as follows: | ||
227 | |||
228 | |||
229 | ```refinery | ||
230 | class Vertex { | ||
231 | contains Transition[] outgoingTransition opposite source | ||
232 | Transition[] incomingTransition opposite target | ||
233 | } | ||
234 | |||
235 | class Transition { | ||
236 | container Vertex source opposite outgoingTransition | ||
237 | Vertex[1] target opposite incomingTransition | ||
238 | } | ||
239 | |||
240 | pred neighbors(Vertex v1, Vertex v2) <-> | ||
241 | Transition(t), | ||
242 | source(t, v1), | ||
243 | target(t, v2). | ||
244 | ``` | ||
245 | |||
246 | Since `neighbors` is now computed based on the `Transition` instances and their `source` and `target` references present in the model, the assertion | ||
247 | |||
248 | ```refinery | ||
249 | neighbors(vertex1, vertex2). | ||
250 | ``` | ||
251 | |||
252 | will only be satisfied if a corresponding node `transition1` is present in the generated model that also satisfies | ||
253 | |||
254 | ```refinery | ||
255 | Transition(transition1). | ||
256 | source(transition1, vertex1). | ||
257 | target(transition1, vertex2). | ||
258 | ``` | ||
259 | |||
260 | import DerivedFeature from './DerivedFeature.svg'; | ||
261 | |||
262 | <DerivedFeature /> | ||
263 | |||
264 | ## Error predicates | ||
265 | |||
266 | A common use-case for graph predicates is _model validation_, where a predicate highlights _errors_ in the model. | ||
267 | Such predicates are called _error predicates._ | ||
268 | In a consistent generated model, an error predicates should have no matches. | ||
269 | |||
270 | You can declare error predicates with the `error` keyword: | ||
271 | |||
272 | ```refinery | ||
273 | error regionWithoutEntry(Region r) <-> | ||
274 | !entryInRegion(r, _). | ||
275 | ``` | ||
276 | |||
277 | This is equivalent to asserting that the error predicate is `false` everywhere: | ||
278 | |||
279 | ```refinery | ||
280 | pred regionWithoutEntry(Region r) <-> | ||
281 | !entryInRegion(r, _). | ||
282 | |||
283 | !regionWithoutEntry(*). | ||
284 | ``` | ||