Skip to content

Commit 6252d74

Browse files
committed
removed function patterns.edge_matches_pattern
1 parent 8578b0e commit 6252d74

5 files changed

Lines changed: 91 additions & 148 deletions

File tree

docs/manual/patterns.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,37 @@
22

33
Hyperbase implements language to define hyperedge patterns. These patterns are valid hyperedges themselves, and are defined with the help of special atoms: *wildcards* and *variables*. The former are useful for simple matching, while the latter allow for the extraction of specific parts of a hyperedge.
44

5-
## Searching with wildcards
5+
## Matching with wildcards
66

77
The basic wildcard is `*`. It matches any hyperedge. It allows for the definition of patterns like this:
88

9+
```clojure
10+
(plays/P.so * *)
911
```
10-
(plays/P * *)
12+
13+
Here's how it can be used to match hyperedges:
14+
15+
```python
16+
from hyperbase import hedge
17+
pattern = hedge("(plays/P.so * *)")
18+
edge = hedge("(plays/P.so alice/C chess/C)")
19+
edge.match(pattern) # returns [{}]
20+
edge = hedge("(likes/P.so alice/C chess/C)")
21+
edge.match(pattern) # returns []
1122
```
1223

13-
We can use them to search the hypergraph for matching hyperedges. For example:
24+
Notice that, in the first case, a list with an empty dictionary is returned (`[{}]`) to indicate a match. We will see later how these dictionaries are used to return the values taken by pattern variables. In the second case the edge does not match the pattern, so an empty list is returned.
1425

15-
```pycon
16-
>>> from hyperbase import *
17-
>>> hg = hgraph('example.db')
18-
>>> hg.add('(plays/P.so alice/C chess/C)')
19-
(plays/P.so alice/C chess/C)
20-
>>> list(hg.search('(plays/P.so * *)'))
21-
[(plays/P.so alice/C chess/C)]
26+
Since an empty list evaluates to False, this method can also be used as a boolean condition:
27+
28+
```python
29+
from hyperbase import hedge
30+
pattern = hedge("(plays/P.so * *)")
31+
edge = hedge("(plays/P.so alice/C chess/C)")
32+
if edge.match(pattern):
33+
print("match found") # this is printed
34+
else:
35+
print("no match")
2236
```
2337

2438
There are two more wildcards:
@@ -30,19 +44,19 @@ Furthermore, it is possible to specify types and roles in wildcards, as in any o
3044

3145
It is possible to specify the optional presence of further arguments with the special atom `...`, which simply indicates that any number (including zero) hyperedges may be present at that point. For instance:
3246

33-
```
47+
```clojure
3448
(plays/P * *)
3549
```
3650

3751
does not match:
3852

39-
```
53+
```clojure
4054
(plays/P.sox alice/C chess/C (at/T (the/M club/C)))
4155
```
4256

4357
but this pattern does:
4458

45-
```
59+
```clojure
4660
(plays/P * * ...)
4761
```
4862

src/hyperbase/patterns/__init__.py

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,74 @@
1+
from __future__ import annotations
2+
3+
from hyperbase.hyperedge import Hyperedge, hedge
14
from hyperbase.patterns.common import common_pattern
2-
from hyperbase.patterns.entrypoints import match_pattern, edge_matches_pattern
5+
from hyperbase.patterns.matcher import Matcher
36
from hyperbase.patterns.merge import merge_patterns
47
from hyperbase.patterns.properties import (is_wildcard, is_pattern, is_full_pattern, is_fun_pattern,
58
is_unordered_pattern)
6-
from hyperbase.patterns.utils import more_general
9+
from hyperbase.patterns.utils import _normalize_fun_patterns, more_general
710
from hyperbase.patterns.variables import (all_variables, apply_vars, apply_variables, extract_vars_map, is_variable,
811
contains_variable, remove_variables)
912

1013

14+
def match_pattern(
15+
edge: Hyperedge | str | list[object] | tuple[object, ...],
16+
pattern: Hyperedge | str | list[object] | tuple[object, ...],
17+
curvars: dict[str, Hyperedge] | None = None
18+
) -> list[dict[str, Hyperedge]]:
19+
"""
20+
Matches an edge to a pattern. This means that, if the edge fits the
21+
pattern, then a list of dictionaries will be returned. If the pattern
22+
specifies variables, then the returned dictionaries will be populated
23+
with the values for each pattern variable. There can be more than one
24+
dictionary in the list if there are multiple ways of matching the
25+
variables. If the pattern specifies no variables but the edge matches
26+
it, then a list with a single empty dictionary is returned. If the
27+
edge does not match the pattern, an empty list is returned.
28+
29+
Patterns are themselves edges. They can match families of edges
30+
by employing special atoms:
31+
32+
- `\\*` represents a general wildcard (matches any entity)
33+
- `.` represents an atomic wildcard (matches any atom)
34+
- `(\\*)` represents an edge wildcard (matches any non-atom)
35+
- `...` at the end indicates an open-ended pattern.
36+
37+
The wildcards (`\\*`, `.` and `(\\*)`) can be used to specify variables,
38+
for example `\\*x`, `(CLAIM)` or `.ACTOR`. In case of a match, these
39+
variables are assigned the hyperedge they correspond to. For example, consider
40+
the edge:
41+
42+
`(is/P.so (my/Mp name/Cn) mary/Cp)`
43+
44+
- matching to the pattern: `(is/P.so (my/Mp name/Cn) \\*)`
45+
produces the result: `[{}]`
46+
- matching to the pattern: `(is/P.so (my/Mp name/Cn) \\*NAME)`
47+
produces the result: `[{'NAME', mary/Cp}]`
48+
- matching to the pattern: `(is/P.so . \\*NAME)`
49+
produces the result: `[]`
50+
"""
51+
_edge = hedge(edge)
52+
_pattern = hedge(pattern)
53+
if _edge is None or _pattern is None:
54+
return []
55+
_pattern = _normalize_fun_patterns(_pattern)
56+
57+
matcher: Matcher = Matcher(
58+
edge=_edge,
59+
pattern=_pattern,
60+
curvars=curvars,
61+
)
62+
63+
return matcher.results
64+
65+
1166
__all__ = [
1267
'all_variables',
1368
'apply_vars',
1469
'apply_variables',
1570
'common_pattern',
1671
'contains_variable',
17-
'edge_matches_pattern',
1872
'extract_vars_map',
1973
'is_full_pattern',
2074
'is_fun_pattern',

src/hyperbase/patterns/counter.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from hyperbase import hedge
66
from hyperbase.hyperedge import Hyperedge
7-
from hyperbase.patterns.entrypoints import edge_matches_pattern
87

98

109
class PatternCounter:
@@ -34,22 +33,22 @@ def __init__(
3433

3534
def _matches_expansions(self, edge: Hyperedge) -> bool:
3635
for expansion in self.expansions:
37-
if edge_matches_pattern(edge, expansion):
36+
if edge.match(expansion):
3837
return True
3938
return False
4039

4140
def _force_subtypes(self, edge: Hyperedge) -> bool:
4241
force_subtypes = False
4342
for st_pattern in self.match_subtypes:
44-
if edge_matches_pattern(edge, st_pattern):
43+
if edge.match(st_pattern):
4544
force_subtypes = True
4645
return force_subtypes
4746

4847
def _force_root_expansion(self, edge: Hyperedge) -> tuple[bool, bool]:
4948
force_root = False
5049
force_expansion = False
5150
for root_pattern in self.match_roots:
52-
if edge_matches_pattern(edge, root_pattern):
51+
if edge.match(root_pattern):
5352
force_root = True
5453
force_expansion = True
5554
elif _inner_edge_matches_pattern(edge, root_pattern):
@@ -141,13 +140,13 @@ def _edge2pattern(edge: Hyperedge, root: bool = False, subtype: bool = False) ->
141140
return hedge('{}.{}'.format(pattern, ar))
142141

143142

144-
def _inner_edge_matches_pattern(edge: Hyperedge, pattern: str, hg: object = None) -> bool:
143+
def _inner_edge_matches_pattern(edge: Hyperedge, pattern: str) -> bool:
145144
if edge.atom:
146145
return False
147146
for subedge in edge:
148-
if edge_matches_pattern(subedge, pattern, hg=hg):
147+
if subedge.match(pattern):
149148
return True
150149
for subedge in edge:
151-
if _inner_edge_matches_pattern(subedge, pattern, hg=hg):
150+
if _inner_edge_matches_pattern(subedge, pattern):
152151
return True
153152
return False

src/hyperbase/patterns/entrypoints.py

Lines changed: 0 additions & 83 deletions
This file was deleted.

tests/test_patterns.py

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import unittest
22

33
from hyperbase import hedge
4-
from hyperbase.patterns import (match_pattern, edge_matches_pattern, is_wildcard, is_pattern, is_full_pattern,
4+
from hyperbase.patterns import (match_pattern, is_wildcard, is_pattern, is_full_pattern,
55
apply_vars, is_unordered_pattern, common_pattern, more_general,
66
is_variable, contains_variable, merge_patterns)
77

@@ -468,47 +468,6 @@ def test_match_pattern_debug_case_2(self):
468468
'cellular/Ma/en usage/Cc.s/en))))', '(said/Pd.{sr}.<f----- */C *)'),
469469
[{}])
470470

471-
def test_edge_matches_pattern_simple1(self):
472-
self.assertTrue(edge_matches_pattern(hedge('(a b)'), '(a b)'))
473-
474-
def test_edge_matches_pattern_simple2(self):
475-
self.assertFalse(edge_matches_pattern(hedge('(a b)'), '(a a)'))
476-
477-
def test_edge_matches_pattern_wildcard1(self):
478-
self.assertTrue(edge_matches_pattern(hedge('(is/Pd hyperbase/Cp.s great/C)'), '(is/Pd hyperbase/Cp.s *)'))
479-
480-
def test_edge_matches_pattern_wildcard2(self):
481-
self.assertFalse(edge_matches_pattern(hedge('(was/Pd hyperbase/Cp.s great/C)'), '(is/Pd hyperbase/Cp.s *)'))
482-
483-
def test_edge_matches_pattern_atomic_wildcard1(self):
484-
self.assertTrue(edge_matches_pattern(hedge('(is/Pd hyperbase/Cp.s great/C)'), '(is/Pd hyperbase/Cp.s .)'))
485-
486-
def test_edge_matches_pattern_atomic_wildcard2(self):
487-
self.assertFalse(
488-
edge_matches_pattern(hedge('(was/Pd hyperbase /Cp.s great/C)'), '(is/Pd.sc hyperbase/Cp.s .)'))
489-
490-
def test_edge_matches_pattern_atomic_wildcard3(self):
491-
self.assertFalse(
492-
edge_matches_pattern(hedge('(is/Pd hyperbase/Cp.s (fairly/M great/C))'), '(is/Pd hyperbase/Cp.s .)'))
493-
494-
def test_edge_matches_pattern_edge_wildcard1(self):
495-
self.assertTrue(
496-
edge_matches_pattern(hedge('(is/Pd hyperbase/Cp.s (fairly/M great/C))'), '(is/Pd hyperbase/Cp.s (*))'))
497-
498-
def test_edge_matches_pattern_edge_wildcard2(self):
499-
self.assertFalse(edge_matches_pattern(hedge('(is/Pd hyperbase/Cp.s great/C)'), '(is/Pd hyperbase/Cp.s (*))'))
500-
501-
def test_edge_matches_pattern_open_ended1(self):
502-
self.assertTrue(edge_matches_pattern(hedge('(is/Pd hyperbase/Cp.s great/C)'), '(is/Pd hyperbase/Cp.s * ...)'))
503-
504-
def test_edge_matches_pattern_open_ended2(self):
505-
self.assertTrue(
506-
edge_matches_pattern(hedge('(is/Pd hyperbase/Cp.s great/C extra/C)'), '(is/Pd hyperbase/Cp.s * ...)'))
507-
508-
def test_edge_matches_pattern_open_ended3(self):
509-
self.assertFalse(
510-
edge_matches_pattern(hedge('(is/Pd humanity/Cp.s great/C extra/C)'), '(is/Pd hyperbase/Cp.s * ...)'))
511-
512471
def test_match_pattern_complex(self):
513472
s = ('(says/Pd.rr.|f--3s-/en (calls/Pr.so.|f--3s-/en */C (*/M (draconian/Ma/en (+/B.am/. coronavirus/Cc.s/en '
514473
'restrictions/Cc.p/en)))) */R)')

0 commit comments

Comments
 (0)