Specyfikacja grafu
{ "graph":{ "directed":false, "name":"Przykładowy graf G", "vertices":[ {"index":0,"label":"A"}, {"index":1,"label":"B"}, {"index":2,"label":"C"}, {"index":3,"label":"D"} ], "edges":[ {"u":0,"v":1}, {"u":0,"v":2}, {"u":1,"v":2}, {"u":2,"v":3,"weight":4.5,"label":"most"} ] } }
Format, w którym obie strony produkcji są grafami w powyższym formacie. Do tego dochodzą dwie rzeczy, bez których sama para grafów nie wystarcza:
- odwzorowanie (
mapping): które wierzchołki lewej strony przechodzą na prawą, czyli zostają zachowane; - reguły osadzania (
embedding): co zrobić z krawędziami, które łączyły usuwane wierzchołki z resztą grafu, do którego stosujemy produkcję.
{
"production": {
"name": "P1",
"directed": false,
"left": {
"vertices": [
{ "index": 0, "label": "A" },
{ "index": 1, "label": "X" }
],
"edges": [
{ "u": 0, "v": 1 }
]
},
"right": {
"vertices": [
{ "index": 0, "label": "A" },
{ "index": 1, "label": "Y" },
{ "index": 2, "label": "Z" }
],
"edges": [
{ "u": 0, "v": 1 },
{ "u": 1, "v": 2 }
]
},
"mapping": [
{ "left": 0, "right": 0 }
],
"embedding": [
{ "leftVertex": 1, "neighborLabel": "*", "connectTo": [2] }
]
}
}
Co robi ta produkcja: szuka w grafie wierzchołka X połączonego z A. Wierzchołek A zostaje (jest w mapping). X jest usuwany, a w jego miejsce powstaje ścieżka A – Y – Z. Wszystkie inne krawędzie, które X miał w grafie (poza krawędzią do A), zostają przepięte do Z.
Znaczenie pól:
leftto lewa strona (G): wzorzec, który szukamy w grafie.rightto prawa strona (H): co wstawiamy w miejsce wzorca. Indeksy wleftirightsą niezależne, łączy je tylkomapping.mappingto paryleft → rightdla wierzchołków zachowanych. Z niego wynika:- wierzchołek z
leftbez pary jest usuwany; - wierzchołek z
rightbez pary jest tworzony; - wierzchołek z parą zostaje, razem ze wszystkimi swoimi krawędziami do reszty grafu. Jego etykieta może się zmienić, jeśli w
rightma inną.
- wierzchołek z
embeddingmówi, co zrobić z krawędziami usuwanych wierzchołków:leftVertexto usuwany wierzchołek zleft;neighborLabelto etykieta sąsiada spoza wzorca, którego dotyczy reguła ("*"oznacza dowolną etykietę);connectToto lista wierzchołków zright, do których te krawędzie są przepinane (może być kilka albo żaden).
Zasady poprawności:
- Oba grafy spełniają warunki z poprzedniego formatu: indeksy unikalne, krawędzie wskazują istniejące wierzchołki.
mappingjest różnowartościowe: każdy indeks zlefti zrightwystępuje w nim najwyżej raz.leftVertexwembeddingmusi być wierzchołkiem usuwanym (bez pary wmapping), aconnectTomusi wskazywać istniejące wierzchołkiright.- Jeśli usuwany wierzchołek ma w grafie krawędź do sąsiada, którego nie obejmuje żadna reguła
embedding, trzeba z góry wybrać, co się dzieje:- produkcji nie wolno zastosować, albo
- krawędź jest po prostu usuwana.
Ten wybór warto zapisać jawnie, np. jako pole
"danglingEdges": "forbid"lub"delete".
Rozszerzenia, jeśli będą potrzebne:
- Całą gramatykę zapiszesz jako
{ "startGraph": {...}, "productions": [ ... ] }. - Warunki stosowalności, np. „wierzchołek X nie może mieć sąsiada o etykiecie B”, można dodać jako pole
"negativeConditions"z dodatkowymi grafami. - Etykiety krawędzi (
labelprzy krawędzi) działają po obu stronach tak samo jak w poprzednim formacie.
Przykładowy graf: http://galaxy.agh.edu.pl/~sedziwy/wordpress/wp-content/uploads/graph_json-1.txt graph_json
Gramatyka grafowa
Gramatyka składa się z grafu startowego, zbiorów etykiet i listy produkcji w formacie zaproponowanym wcześniej. Poniżej przykład, który generuje ścieżki typu a – a – … – a – b:
{
"grammar": {
"name": "Gramatyka ścieżek",
"directed": false,
"danglingEdges": "forbid",
"labels": {
"nonterminals": ["S", "A"],
"terminals": ["a", "b"]
},
"startGraph": {
"vertices": [
{ "index": 0, "label": "S" }
],
"edges": []
},
"productions": [
{
"name": "P1",
"description": "S → A – S (wydłużenie ścieżki)",
"left": {
"vertices": [ { "index": 0, "label": "S" } ],
"edges": []
},
"right": {
"vertices": [
{ "index": 0, "label": "A" },
{ "index": 1, "label": "S" }
],
"edges": [ { "u": 0, "v": 1 } ]
},
"mapping": [],
"embedding": [
{ "leftVertex": 0, "neighborLabel": "*", "connectTo": [0] }
]
},
{
"name": "P2",
"description": "A → a",
"left": {
"vertices": [ { "index": 0, "label": "A" } ],
"edges": []
},
"right": {
"vertices": [ { "index": 0, "label": "a" } ],
"edges": []
},
"mapping": [ { "left": 0, "right": 0 } ],
"embedding": []
},
{
"name": "P3",
"description": "S → b (zakończenie)",
"left": {
"vertices": [ { "index": 0, "label": "S" } ],
"edges": []
},
"right": {
"vertices": [ { "index": 0, "label": "b" } ],
"edges": []
},
"mapping": [ { "left": 0, "right": 0 } ],
"embedding": []
}
]
}
}
Nowe pola (poza samymi produkcjami)
name,directed: nazwa gramatyki i typ grafów. Wszystkie produkcje i graf startowy muszą mieć ten sam typ, więcdirectedjest tu raz zamiast w każdej produkcji.danglingEdges: wspólna zasada dla wszystkich produkcji. Mówi, co zrobić z krawędzią usuwanego wierzchołka, której nie obejmuje żadna regułaembedding:"forbid": produkcji nie wolno zastosować;"delete": krawędź jest usuwana.
Pojedyncza produkcja może ją nadpisać własnym polem
danglingEdges.labels: etykiety nieterminalne (do dalszego przepisywania) i terminalne (końcowe). Graf należy do języka gramatyki, gdy wszystkie jego wierzchołki mają etykiety terminalne.startGraph: graf startowy w formacie grafu z wcześniejszej propozycji.productions: lista produkcji w formacie z wcześniejszej propozycji. Poledescriptionjest opcjonalne i służy tylko do czytania przez człowieka.
Jak działa przykład
Wyprowadzenie, w którym stosujemy kolejno P1, P1, P3, P2, P2:
S ⇒P1 A – S ⇒P1 A – A – S ⇒P3 A – A – b ⇒P2 a – A – b ⇒P2 a – a – b
- P1 usuwa
Si wstawiaA – S. Za drugim razemSma już sąsiada (Az poprzedniego kroku). Regułaembeddingprzepina tę krawędź do nowegoA(indeks 0 po prawej stronie), więc ścieżka się nie rozpada. - P2 i P3 tylko zmieniają etykietę. Wierzchołek jest w
mapping, czyli zachowany, więc zachowuje wszystkie swoje krawędzie iembeddingnie jest potrzebny.
Zasady poprawności całej gramatyki
- Zbiory
nonterminalsiterminalssą rozłączne. - Każda etykieta użyta w
startGraphi w produkcjach należy do jednego z tych zbiorów. - Lewa strona każdej produkcji zawiera co najmniej jeden nieterminal, bo inaczej produkcja przepisywałaby gotowy fragment wyniku.
- Nazwy produkcji (
name) są unikalne. - Każda produkcja spełnia warunki z wcześniejszej propozycji: poprawne grafy, różnowartościowe
mapping, poprawne odwołania wembedding.