Module tf.search.relations
Search by relational patterns between nodes
Expand source code Browse git
"""
# Search by relational patterns between nodes
"""
import collections
import types
import re
from itertools import chain
import array
from ..parameters import OTYPE, OSLOTS, OMAP
from ..core.helpers import makeIndex
from ..core.timestamp import DEEP
from .syntax import reTp
# LOW-LEVEL NODE RELATIONS SEMANTICS ###
def _l_em(n):
return ()
def _l_eq(n):
return (n,)
def _l_uneq(n, m):
return n != m
def _l_lt(n, m):
return n < m
def _l_gt(n, m):
return n > m
def _l_ranklt(Crank):
def func(n, m):
return Crank[n - 1] < Crank[m - 1]
return func
def _l_rankgt(Crank):
def func(n, m):
return Crank[n - 1] > Crank[m - 1]
return func
def basicRelations(searchExe, api):
C = api.C
F = api.F
Fs = api.Fs
E = api.E
Crank = C.rank.data
ClevDown = C.levDown.data
ClevUp = C.levUp.data
(CfirstSlots, ClastSlots) = C.boundary.data
Eoslots = E.oslots.data
slotType = F.otype.slotType
maxSlot = F.otype.maxSlot
maxSlotP = maxSlot + 1
sets = searchExe.sets
setInfo = searchExe.setInfo
searchExe.featureValueIndex = {}
Sindex = searchExe.featureValueIndex
def isSlotType(nType):
if nType == ".":
return None
if sets is not None and nType in sets:
if nType in setInfo:
return setInfo[nType]
nodes = sets[nType]
allSlots = all(n < maxSlotP for n in nodes)
if allSlots:
setInfo[nType] = True
return True
allNonSlots = all(n > maxSlot for n in nodes)
if allNonSlots:
setInfo[nType] = False
return False
setInfo[nType] = None
return None
return nType == slotType
# EQUAL
def spinEqual(fTp, tTp):
def doyarns(yF, yT):
x = set(yF) & set(yT)
return (x, x)
return doyarns
def equalR(fTp, tTp):
return _l_eq
# UNEQUAL
def unequalR(fTp, tTp):
return _l_uneq
# CANONICAL BEFORE
def canonicalBeforeR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_lt
return _l_ranklt(Crank)
# CANONICAL AFTER
def canonicalAfterR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_gt
return _l_rankgt(Crank)
# SAME SLOTS
def spinSameSlots(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
def doyarns(yF, yT):
x = set(yF) & set(yT)
return (x, x)
return doyarns
elif isSlotF or isSlotT:
def doyarns(yS, y2):
sindex = {}
for m in y2:
ss = Eoslots[m - maxSlotP] if m > maxSlot else (m,)
if len(ss) == 1:
sindex.setdefault(ss[0], set()).add(m)
nyS = yS & set(sindex.keys())
ny2 = set(chain.from_iterable(sindex[s] for s in nyS))
return (nyS, ny2)
if isSlotF:
return doyarns
else:
def xx(yF, yT):
(nyT, nyF) = doyarns(yT, yF)
return (nyF, nyT)
return xx
else:
def doyarns(yF, yT):
sindexF = {}
for n in yF:
s = frozenset(Eoslots[n - maxSlotP] if n > maxSlot else (n,))
sindexF.setdefault(s, set()).add(n)
sindexT = {}
for m in yT:
s = frozenset(Eoslots[m - maxSlotP] if m > maxSlot else (m,))
sindexT.setdefault(s, set()).add(m)
nyS = set(sindexF.keys()) & set(sindexT.keys())
nyF = set(chain.from_iterable(sindexF[s] for s in nyS))
nyT = set(chain.from_iterable(sindexT[s] for s in nyS))
return (nyF, nyT)
return doyarns
def sameSlotsR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_eq
else:
def xx(n):
nmin = n - 1
if n < maxSlotP:
nA = array.array("I", (n,))
# nA = (n,)
yield n
for m in ClevUp[nmin]:
if Eoslots[m - maxSlotP] == nA:
yield m
return
nSlots = Eoslots[n - maxSlotP]
if len(nSlots) == 1:
slot1 = nSlots[0]
nA = array.array("I", (slot1,))
# nA = tuple(slot1,)
yield n
yield slot1
for m in ClevUp[nmin]:
if Eoslots[m - maxSlotP] == nA:
yield m
return
yield n
for m in ClevUp[nmin]:
if n in ClevUp[m - 1]:
yield m
return xx
# OVERLAP
def spinOverlap(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
def doyarns(yF, yT):
x = set(yF) & set(yT)
return (x, x)
return doyarns
elif isSlotF or isSlotT:
def doyarns(yS, y2):
sindex = {}
for m in y2:
for s in Eoslots[m - maxSlotP] if m > maxSlot else (m,):
sindex.setdefault(s, set()).add(m)
nyS = yS & set(sindex.keys())
ny2 = set(chain.from_iterable(sindex[s] for s in nyS))
return (nyS, ny2)
if isSlotF:
return doyarns
else:
def xx(yF, yT):
(nyT, nyF) = doyarns(yT, yF)
return (nyF, nyT)
return xx
else:
def doyarns(yF, yT):
REDUCE_FACTOR = 0.4
SIZE_LIMIT = 10000
sindexF = {}
for n in yF:
for s in Eoslots[n - maxSlotP] if n > maxSlot else (n,):
sindexF.setdefault(s, set()).add(n)
sindexT = {}
for m in yT:
for s in Eoslots[m - maxSlotP] if m > maxSlot else (m,):
sindexT.setdefault(s, set()).add(m)
nyS = set(sindexF.keys()) & set(sindexT.keys())
lsF = len(sindexF)
lsT = len(sindexT)
lsI = len(nyS)
if lsF == lsT: # spinning is completely useless
return (yF, yT)
if lsI > REDUCE_FACTOR * lsT and lsT > SIZE_LIMIT:
# spinning is not worth it
return (yF, yT)
nyF = set(chain.from_iterable(sindexF[s] for s in nyS))
nyT = set(chain.from_iterable(sindexT[s] for s in nyS))
return (nyF, nyT)
return doyarns
def overlapR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_eq
elif isSlotT:
def func(n):
return Eoslots[n - maxSlotP] if n > maxSlot else (n,)
return func
elif isSlotF:
def func(n):
return chain(ClevUp[n - 1], (n,))
return func
else:
def xx(n):
nSlots = Eoslots[n - maxSlotP] if n > maxSlot else (n,)
return chain(
nSlots, set(chain.from_iterable(ClevUp[s - 1] for s in nSlots))
)
return xx
# DIFFERENT SLOTS
def diffSlotsR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_uneq
elif isSlotT:
def func(n, m):
return (Eoslots[m - maxSlotP] if m > maxSlot else (m,)) != (n,)
return func
elif isSlotF:
def func(n, m):
return (Eoslots[n - maxSlotP] if n > maxSlot else (n,)) != (m,)
return func
else:
def func(n, m):
return frozenset(
Eoslots[n - maxSlotP] if n > maxSlot else (n,)
) != frozenset(Eoslots[m - maxSlotP] if m > maxSlot else (m,))
return func
# DISJOINT SLOTS
def disjointSlotsR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_uneq
elif isSlotT:
def func(n, m):
return m not in frozenset(
Eoslots[n - maxSlotP] if n > maxSlot else (n,)
)
return func
elif isSlotF:
def func(n, m):
return n not in frozenset(
Eoslots[m - maxSlotP] if m > maxSlot else (m,)
)
return func
else:
def func(n, m):
return (
len(
frozenset(Eoslots[n - maxSlotP] if n > maxSlot else (n,))
& frozenset(Eoslots[m - maxSlotP] if m > maxSlot else (m,))
)
== 0
)
return func
# EMBEDDED IN
def inR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_em
elif isSlotT:
return _l_em
elif isSlotF:
def func(n):
return ClevUp[n - 1]
return func
else:
def func(n):
return ClevUp[n - 1]
return func
# EMBEDS
def hasR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_em
elif isSlotF:
return _l_em
elif isSlotT:
def func(n):
return Eoslots[n - maxSlotP] if n > maxSlot else ()
return func
else:
if isSlotT is None:
def func(n):
return (
chain(ClevDown[n - maxSlotP], Eoslots[n - maxSlotP])
if n > maxSlot
else ()
)
return func
else:
def func(n):
return ClevDown[n - maxSlotP] if n > maxSlot else ()
return func
# BEFORE WRT SLOTS
def slotBeforeR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_lt
elif isSlotF:
def func(n, m):
return n < (Eoslots[m - maxSlotP][0] if m > maxSlot else m)
return func
elif isSlotT:
def func(n, m):
return (Eoslots[n - maxSlotP][-1] if n > maxSlot else n) < m
return func
else:
def func(n, m):
return (Eoslots[n - maxSlotP][-1] if n > maxSlot else n) < (
Eoslots[m - maxSlotP][0] if m > maxSlot else m
)
return func
# AFTER WRT SLOTS
def slotAfterR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_gt
elif isSlotF:
def func(n, m):
return n > (Eoslots[m - maxSlotP][-1] if m > maxSlot else m)
return func
elif isSlotT:
def func(n, m):
return (Eoslots[n - maxSlotP][0] if n > maxSlot else n) > m
return func
else:
def func(n, m):
return (Eoslots[n - maxSlotP][0] if n > maxSlot else n) > (
Eoslots[m - maxSlotP][-1] if m > maxSlot else m
)
return func
# START AT SAME SLOT
def sameFirstSlotR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_eq
elif isSlotF:
if isSlotT is None:
def func(n):
return chain(CfirstSlots[n - 1], (n,))
return func
else:
def func(n):
return CfirstSlots[n - 1]
return func
elif isSlotT:
def func(n):
return ((Eoslots[n - maxSlotP][0] if n > maxSlot else n),)
return func
else:
def xx(n):
fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n
fnmin = fn - 1
if isSlotT is None:
return chain(CfirstSlots[fnmin], (fn,))
else:
return CfirstSlots[fnmin]
return xx
# ENDS AT SAME SLOT
def sameLastSlotR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_eq
elif isSlotF:
if isSlotT is None:
def func(n):
return chain(ClastSlots[n - 1], (n,))
return func
else:
def func(n):
return ClastSlots[n - 1]
return func
elif isSlotT:
def func(n):
return ((Eoslots[n - maxSlotP][-1] if n > maxSlot else n),)
return func
else:
def xx(n):
ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n
lnmin = ln - 1
if isSlotT is None:
return chain(ClastSlots[lnmin], (ln,))
else:
return ClastSlots[lnmin]
return xx
# START AND END AT SAME SLOT
def sameBoundaryR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
return _l_eq
elif isSlotF:
if isSlotT is None:
def xx(n):
nmin = n - 1
fok = set(chain(CfirstSlots[nmin], (n,)))
lok = set(chain(ClastSlots[nmin], (n,)))
return fok & lok
else:
def xx(n):
nmin = n - 1
fok = set(CfirstSlots[nmin])
lok = set(ClastSlots[nmin])
return fok & lok
return xx
elif isSlotT:
def xx(n):
slots = Eoslots[n - maxSlotP] if n > maxSlot else (n,)
fs = slots[0]
ls = slots[-1]
return (fs,) if fs == ls else ()
return xx
else:
if isSlotT is None:
def xx(n):
fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n
ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n
fnmin = fn - 1
lnmin = ln - 1
fok = set(chain(CfirstSlots[fnmin], (fn,)))
lok = set(chain(ClastSlots[lnmin], (ln,)))
return fok & lok
else:
def xx(n):
fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n
ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n
fnmin = fn - 1
lnmin = ln - 1
fok = set(CfirstSlots[fnmin])
lok = set(ClastSlots[lnmin])
return fok & lok
return xx
# FIRST SLOTS ARE k-CLOSE
def nearFirstSlotR(k):
def zz(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
def func(n):
return range(max((1, n - k)), min((maxSlot, n + k)) + 1)
return func
elif isSlotF:
if isSlotT is None:
def xx(n):
near = range(max((1, n - k)), min((maxSlot, n + k)) + 1)
return chain(
near,
chain.from_iterable(CfirstSlots[lf - 1] for lf in near),
)
else:
def xx(n):
near = range(max((1, n - k)), min((maxSlot, n + k)) + 1)
return chain.from_iterable(CfirstSlots[lf - 1] for lf in near)
return xx
elif isSlotT:
def xx(n):
fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n
return range(max((1, fn - k)), min((maxSlot, fn + k)) + 1)
return xx
else:
if isSlotT is None:
def xx(n):
fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n
near = range(max((1, fn - k)), min((maxSlot, fn + k)) + 1)
return chain(
near,
chain.from_iterable(CfirstSlots[lf - 1] for lf in near),
)
else:
def xx(n):
fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n
near = range(max((1, fn - k)), min((maxSlot, fn + k)) + 1)
return chain.from_iterable(CfirstSlots[lf - 1] for lf in near)
return xx
return zz
# LAST SLOTS ARE k-CLOSE
def nearLastSlotR(k):
def zz(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
def func(n):
return range(max((1, n - k)), min((maxSlot, n + k)) + 1)
return func
elif isSlotF:
if isSlotT is None:
def xx(n):
near = range(max((1, n - k)), min((maxSlot, n + k)) + 1)
return chain(
near, chain.from_iterable(ClastSlots[lf - 1] for lf in near)
)
else:
def xx(n):
near = range(max((1, n - k)), min((maxSlot, n + k)) + 1)
return chain.from_iterable(ClastSlots[lf - 1] for lf in near)
return xx
elif isSlotT:
def xx(n):
ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n
return range(max((1, ln - k)), min((maxSlot, ln + k)) + 1)
return xx
else:
if isSlotT is None:
def xx(n):
ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n
near = range(max((1, ln - k)), min((maxSlot, ln + k)) + 1)
return chain(
near, chain.from_iterable(ClastSlots[lf - 1] for lf in near)
)
else:
def xx(n):
ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n
near = range(max((1, ln - k)), min((maxSlot, ln + k)) + 1)
return chain.from_iterable(ClastSlots[lf - 1] for lf in near)
return xx
return zz
# FIRST SLOTS ARE k-CLOSE and LAST SLOTS ARE k-CLOSE
def nearBoundaryR(k):
def zz(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
def func(n):
return range(max((1, n - k)), min((maxSlot, n + k)) + 1)
return func
elif isSlotF:
if isSlotT is None:
def xx(n):
near = set(range(max((1, n - k)), min((maxSlot, n + k)) + 1))
fok = set(
chain.from_iterable(CfirstSlots[lf - 1] for lf in near)
)
lok = set(
chain.from_iterable(ClastSlots[lf - 1] for lf in near)
)
return near | (fok & lok)
else:
def xx(n):
near = range(max((1, n - k)), min((maxSlot, n + k)) + 1)
fok = set(
chain.from_iterable(CfirstSlots[lf - 1] for lf in near)
)
lok = set(
chain.from_iterable(ClastSlots[lf - 1] for lf in near)
)
return fok & lok
return xx
elif isSlotT:
def xx(n):
slots = Eoslots[n - maxSlotP] if n > maxSlot else (n,)
fs = slots[0]
ls = slots[-1]
fok = set(range(max((1, fs - k)), min((maxSlot, fs + k)) + 1))
lok = set(range(max((1, ls - k)), min((maxSlot, ls + k)) + 1))
return fok & lok
return xx
else:
if isSlotT is None:
def xx(n):
fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n
ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n
nearf = range(max((1, fn - k)), min((maxSlot, fn + k)) + 1)
nearl = range(max((1, ln - k)), min((maxSlot, ln + k)) + 1)
fok = set(
chain(
nearf,
chain.from_iterable(
CfirstSlots[lf - 1] for lf in nearf
),
)
)
lok = set(
chain(
nearl,
chain.from_iterable(ClastSlots[lf - 1] for lf in nearl),
)
)
return fok & lok
else:
def xx(n):
fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n
ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n
nearf = range(max((1, fn - k)), min((maxSlot, fn + k)) + 1)
nearl = range(max((1, ln - k)), min((maxSlot, ln + k)) + 1)
fok = set(
chain.from_iterable(CfirstSlots[lf - 1] for lf in nearf)
)
lok = set(
chain.from_iterable(ClastSlots[lf - 1] for lf in nearl)
)
return fok & lok
return xx
return zz
# FIRST ENDS WHERE SECOND STARTS
def adjBeforeR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
def func(n):
return (n + 1,) if n < maxSlot else ()
return func
else:
if isSlotT:
def xx(n):
if n == maxSlot:
return ()
myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1
if myNext > maxSlot:
return ()
return (myNext,)
elif isSlotT is None:
def xx(n):
if n == maxSlot:
return ()
myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1
if myNext > maxSlot:
return ()
return chain(CfirstSlots[myNext - 1], (myNext,))
else:
def xx(n):
if n == maxSlot:
return ()
myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1
if myNext > maxSlot:
return ()
return CfirstSlots[myNext - 1]
return xx
# FIRST STARTS WHERE SECOND ENDS
def adjAfterR(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
def func(n):
return (n - 1,) if n > 1 else ()
return func
else:
if isSlotT:
def xx(n):
if n <= 1:
return ()
myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1
if myPrev <= 1:
return ()
return (myPrev,)
elif isSlotT is None:
def xx(n):
if n <= 1:
return ()
myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1
if myPrev <= 1:
return ()
return chain((myPrev,), ClastSlots[myPrev - 1])
else:
def xx(n):
if n <= 1:
return ()
myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1
if myPrev <= 1:
return ()
return ClastSlots[myPrev - 1]
return xx
# FIRST ENDS WHERE SECOND STARTS WITHIN k-SLOTS
def nearBeforeR(k):
def zz(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
def func(n):
return range(max((1, n + 1 - k)), min((maxSlot, n + 1 + k)) + 1)
return func
else:
if isSlotT:
def xx(n):
myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1
return range(
max((1, myNext - k)), min((maxSlot, myNext + k)) + 1
)
elif isSlotT is None:
def xx(n):
myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1
near = range(
max((1, myNext - k)), min((maxSlot, myNext + k)) + 1
)
return chain(
near,
chain.from_iterable(CfirstSlots[ls - 1] for ls in near),
)
else:
def xx(n):
myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1
near = range(
max((1, myNext - k)), min((maxSlot, myNext + k)) + 1
)
return chain.from_iterable(CfirstSlots[ls - 1] for ls in near)
return xx
return zz
# FIRST STARTS WHERE SECOND ENDS WITHIN k-SLOTS
def nearAfterR(k):
def zz(fTp, tTp):
isSlotF = isSlotType(fTp)
isSlotT = isSlotType(tTp)
if isSlotF and isSlotT:
def func(n):
return range(max((1, n - 1 - k)), min((maxSlot, n - 1 + k)) + 1)
return func
else:
if isSlotT:
def xx(n):
myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1
return tuple(
range(max((1, myPrev - k)), min((maxSlot, myPrev + k)) + 1)
)
elif isSlotT is None:
def xx(n):
myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1
near = range(
max((1, myPrev - k)), min((maxSlot, myPrev + k)) + 1
)
return chain(
near, chain.from_iterable(ClastSlots[lf - 1] for lf in near)
)
else:
def xx(n):
myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1
near = range(
max((1, myPrev - k)), min((maxSlot, myPrev + k)) + 1
)
return chain.from_iterable(ClastSlots[lf - 1] for lf in near)
return xx
return zz
# SAME FEATURE VALUES
def spinLeftFisRightG(f, g):
def zz(fTp, tTp):
if f not in Sindex:
Sindex[f] = makeIndex(Fs(f).data)
if f != g:
if g not in Sindex:
Sindex[g] = makeIndex(Fs(g).data)
indF = Sindex[f]
indG = Sindex[g]
commonValues = set(indF) if f == g else set(indF) & set(indG)
def doyarns(yF, yT):
fNodes = {
n
for n in chain.from_iterable(indF[v] for v in commonValues)
if n in yF
}
gNodes = {
n
for n in chain.from_iterable(indG[v] for v in commonValues)
if n in yT
}
return (fNodes, gNodes)
return doyarns
return zz
def spinLeftGisRightF(f, g):
return spinLeftFisRightG(g, f)
def leftFisRightGR_ORIG(f, g):
def zz(fTp, tTp):
fData = Fs(f).v
gData = Fs(g).v
def uu(n, m):
nVal = fData(n)
return False if nVal is None else nVal == gData(m)
return uu
return zz
def leftFisRightGR(f, g):
def zz(fTp, tTp):
fData = Fs(f).v if f == OTYPE else Fs(f).data
gData = Fs(g).v if g == OTYPE else Fs(g).data
if f == OTYPE and g == OTYPE:
def uu(n, m):
nVal = fData(n)
return False if nVal is None else nVal == gData(m)
return uu
if f == OTYPE:
def uu(n, m):
nVal = fData(n)
return False if nVal is None else nVal == gData.get(m, None)
return uu
if g == OTYPE:
def uu(n, m):
nVal = fData.get(n, None)
return False if nVal is None else nVal == gData(m)
return uu
def uu(n, m):
nVal = fData.get(n, None)
return False if nVal is None else nVal == gData.get(m, None)
return uu
return zz
def leftGisRightFR(g, f):
return leftFisRightGR(f, g)
# MATCH FEATURE VALUES
def spinLeftFmatchRightG(f, rPat, rRe, g):
def zz(fTp, tTp):
fR = f"{f}~{rPat}"
gR = f"{g}~{rPat}"
if fR not in Sindex:
if f not in Sindex:
Sindex[f] = makeIndex(Fs(f).data)
indFR = {}
for (v, ns) in Sindex[f].items():
vR = rRe.sub("", v)
for n in ns:
indFR.setdefault(vR, set()).add(n)
Sindex[fR] = indFR
if gR not in Sindex:
if g not in Sindex:
Sindex[g] = makeIndex(Fs(g).data)
indGR = {}
for (v, ns) in Sindex[g].items():
vR = rRe.sub("", v)
for n in ns:
indGR.setdefault(vR, set()).add(n)
Sindex[gR] = indGR
indFR = Sindex[fR]
indGR = Sindex[gR]
commonValues = set(indFR) & set(indGR)
def doyarns(yF, yT):
fNodes = {
n
for n in chain.from_iterable(indFR[v] for v in commonValues)
if n in yF
}
gNodes = {
n
for n in chain.from_iterable(indGR[v] for v in commonValues)
if n in yT
}
return (fNodes, gNodes)
return doyarns
return zz
def spinLeftGmatchRightF(f, rPat, rRe, g):
return spinLeftFmatchRightG(g, rPat, rRe, f)
def leftFmatchRightGR(f, rPat, rRe, g):
def zz(fTp, tTp):
fData = Fs(f).v if f == OTYPE else Fs(f).data
gData = Fs(g).v if g == OTYPE else Fs(g).data
if f == OTYPE and g == OTYPE:
def uu(n, m):
nVal = fData(n)
if nVal is None:
return False
nVal = rRe.sub("", nVal)
mVal = gData(m)
if mVal is None:
return False
mVal = rRe.sub("", mVal)
return nVal == mVal
return uu
if f == OTYPE:
def uu(n, m):
nVal = fData(n)
if nVal is None:
return False
nVal = rRe.sub("", nVal)
mVal = gData.get(m, None)
if mVal is None:
return False
mVal = rRe.sub("", mVal)
return nVal == mVal
return uu
if g == OTYPE:
def uu(n, m):
nVal = fData.get(n, None)
if nVal is None:
return False
nVal = rRe.sub("", nVal)
mVal = gData(m)
if mVal is None:
return False
mVal = rRe.sub("", mVal)
return nVal == mVal
return uu
def uu(n, m):
nVal = fData.get(n, None)
if nVal is None:
return False
nVal = rRe.sub("", nVal)
mVal = gData.get(m, None)
if mVal is None:
return False
mVal = rRe.sub("", mVal)
return nVal == mVal
return uu
return zz
def leftGmatchRightFR(g, rPat, rRe, f):
return leftFmatchRightGR(f, rPat, rRe, g)
# UNEQUAL FEATURE VALUES
def leftFunequalRightGR(f, g):
def zz(fTp, tTp):
fData = Fs(f).v if f == OTYPE else Fs(f).data
gData = Fs(g).v if g == OTYPE else Fs(g).data
if f == OTYPE and g == OTYPE:
def uu(n, m):
nVal = fData(n)
mVal = gData(m)
return nVal is None and mVal is None or nVal != mVal
return uu
if f == OTYPE:
def uu(n, m):
nVal = fData(n)
mVal = gData.get(m, None)
return nVal is None and mVal is None or nVal != mVal
return uu
if g == OTYPE:
def uu(n, m):
nVal = fData.get(n, None)
mVal = gData(m)
return nVal is None and mVal is None or nVal != mVal
return uu
def uu(n, m):
nVal = fData.get(n, None)
mVal = gData.get(m, None)
return nVal is None and mVal is None or nVal != mVal
return uu
return zz
def leftGunequalRightFR(g, f):
return leftFunequalRightGR(f, g)
# GREATER FEATURE VALUES
def leftFgreaterRightGR(f, g):
def zz(fTp, tTp):
fData = Fs(f).v if f == OTYPE else Fs(f).data
gData = Fs(g).v if g == OTYPE else Fs(g).data
if f == OTYPE and g == OTYPE:
def uu(n, m):
nVal = fData(n)
mVal = gData(m)
return nVal is not None and mVal is not None and nVal > mVal
return uu
if f == OTYPE:
def uu(n, m):
nVal = fData(n)
mVal = gData.get(m, None)
return nVal is not None and mVal is not None and nVal > mVal
return uu
if g == OTYPE:
def uu(n, m):
nVal = fData.get(n, None)
mVal = gData(m)
return nVal is not None and mVal is not None and nVal > mVal
return uu
def uu(n, m):
nVal = fData.get(n, None)
mVal = gData.get(m, None)
return nVal is not None and mVal is not None and nVal > mVal
return uu
return zz
def leftGlesserRightFR(g, f):
return leftFgreaterRightGR(f, g)
# LESSER FEATURE VALUES
def leftFlesserRightGR(f, g):
def zz(fTp, tTp):
fData = Fs(f).v if f == OTYPE else Fs(f).data
gData = Fs(g).v if g == OTYPE else Fs(g).data
if f == OTYPE and g == OTYPE:
def uu(n, m):
nVal = fData(n)
mVal = gData(m)
return nVal is not None and mVal is not None and nVal < mVal
return uu
if f == OTYPE:
def uu(n, m):
nVal = fData(n)
mVal = gData.get(m, None)
return nVal is not None and mVal is not None and nVal < mVal
return uu
if g == OTYPE:
def uu(n, m):
nVal = fData.get(n, None)
mVal = gData(m)
return nVal is not None and mVal is not None and nVal < mVal
return uu
def uu(n, m):
nVal = fData.get(n, None)
mVal = gData.get(m, None)
return nVal is not None and mVal is not None and nVal < mVal
return uu
return zz
def leftGgreaterRightFR(g, f):
return leftFlesserRightGR(f, g)
# EDGES
def makeEdgeMaps(efName):
def edgeAccess(eFunc, doValues, value):
if doValues:
if value is None:
def func(n):
return (m[0] for m in eFunc(n) if m[1] is None)
return func
elif value is True:
def func(n):
return (m[0] for m in eFunc(n))
return func
elif isinstance(value, types.FunctionType):
def func(n):
return (m[0] for m in eFunc(n) if value(m[1]))
return func
elif isinstance(value, reTp):
def func(n):
return (
m[0]
for m in eFunc(n)
if value is not None and value.search(m[1])
)
return func
else:
(ident, value) = value
if ident is None and value is True:
def func(n):
return (m[0] for m in eFunc(n))
return func
elif ident:
def func(n):
return (m[0] for m in eFunc(n) if m[1] in value)
return func
else:
def func(n):
return (m[0] for m in eFunc(n) if m[1] not in value)
return func
else:
def func(n):
return eFunc(n)
return func
def edgeRV(value):
def edgeR(fTp, tTp):
Es = api.Es
Edata = Es(efName)
doValues = Edata.doValues
return edgeAccess(Edata.f, doValues, value)
return edgeR
def edgeIRV(value):
def edgeIR(fTp, tTp):
Es = api.Es
Edata = Es(efName)
doValues = Edata.doValues
return edgeAccess(Edata.t, doValues, value)
return edgeIR
def edgeSRV(value):
def edgeSR(fTp, tTp):
Es = api.Es
Edata = Es(efName)
doValues = Edata.doValues
return edgeAccess(Edata.b, doValues, value)
return edgeSR
return (edgeRV, edgeIRV, edgeSRV)
# COLLECT ALL RELATIONS IN A TUPLE
relations = [
(
("=", spinEqual, equalR, "left equal to right (as node)"),
("=", spinEqual, equalR, None),
),
(
("#", 0.999, unequalR, "left unequal to right (as node)"),
("#", 0.999, unequalR, None),
),
(
(
"<",
0.500,
canonicalBeforeR,
"left before right (in canonical node ordering)",
),
(
">",
0.500,
canonicalAfterR,
"left after right (in canonical node ordering)",
),
),
(
("==", spinSameSlots, sameSlotsR, "left occupies same slots as right"),
("==", spinSameSlots, sameSlotsR, None),
),
(
("&&", spinOverlap, overlapR, "left has overlapping slots with right"),
("&&", spinOverlap, overlapR, None),
),
(
("##", 0.990, diffSlotsR, "left and right do not have the same slot set"),
("##", 0.990, diffSlotsR, None),
),
(
("||", 0.900, disjointSlotsR, "left and right do not have common slots"),
("||", 0.900, disjointSlotsR, None),
),
(
("[[", True, hasR, "left embeds right"),
("]]", True, inR, "left embedded in right"),
),
(
("<<", 0.490, slotBeforeR, "left completely before right"),
(">>", 0.490, slotAfterR, "left completely after right"),
),
(
("=:", True, sameFirstSlotR, "left and right start at the same slot"),
("=:", True, sameFirstSlotR, None),
),
(
(":=", True, sameLastSlotR, "left and right end at the same slot"),
(":=", True, sameLastSlotR, None),
),
(
(
"::",
True,
sameBoundaryR,
"left and right start and end at the same slot",
),
("::", True, sameBoundaryR, None),
),
(
("<:", True, adjBeforeR, "left immediately before right"),
(":>", True, adjAfterR, "left immediately after right"),
),
(
(
"=k:",
True,
nearFirstSlotR,
"left and right start at k-nearly the same slot",
),
("=k:", True, nearFirstSlotR, None),
),
(
(
":k=",
True,
nearLastSlotR,
"left and right end at k-nearly the same slot",
),
(":k=", True, nearLastSlotR, None),
),
(
(
":k:",
True,
nearBoundaryR,
"left and right start and end at k-near slots",
),
(":k:", True, nearBoundaryR, None),
),
(
("<k:", True, nearBeforeR, "left k-nearly before right"),
(":k>", True, nearAfterR, "left k-nearly after right"),
),
(
(".f.", spinLeftFisRightG, leftFisRightGR, "left.f = right.f"),
(".f.", spinLeftGisRightF, leftGisRightFR, None),
),
(
(".f=g.", spinLeftFisRightG, leftFisRightGR, "left.f = right.g"),
(".g=f.", spinLeftGisRightF, leftGisRightFR, None),
),
(
(
".f~r~g.",
spinLeftFmatchRightG,
leftFmatchRightGR,
"left.f matches right.g",
),
(".g~r~f.", spinLeftGmatchRightF, leftGmatchRightFR, None),
),
(
(".f#g.", 0.8, leftFunequalRightGR, "left.f # right.g"),
(".g#f.", 0.8, leftGunequalRightFR, None),
),
(
(".f>g.", 0.4, leftFgreaterRightGR, "left.f > right.g"),
(".f<g.", 0.4, leftFlesserRightGR, None),
),
(
(".f<g.", 0.4, leftFlesserRightGR, "left.f < right.g"),
(".f>g.", 0.4, leftFgreaterRightGR, None),
),
]
# BUILD AND INITIALIZE ALL RELATIONAL FUNCTIONS
api.TF.explore(silent=DEEP)
edgeMap = {}
nodeMap = {}
for efName in sorted(api.TF.featureSets["edges"]):
if efName == OSLOTS or efName.startswith(OMAP):
continue
r = len(relations)
(edgeRV, edgeIRV, edgeSRV) = makeEdgeMaps(efName)
doValues = api.TF.features[efName].edgeValues
extra = " with value specification allowed" if doValues else ""
relations.append(
(
(f"-{efName}>", True, edgeRV, f'edge feature "{efName}"{extra}'),
(
f"<{efName}-",
True,
edgeIRV,
f'edge feature "{efName}"{extra} (opposite direction)',
),
)
)
edgeMap[2 * r] = (efName, 1)
edgeMap[2 * r + 1] = (efName, -1)
r = len(relations)
relations.append(
(
(
f"<{efName}>",
True,
edgeSRV,
f'edge feature "{efName}"{extra} (either direction)',
),
(f"<{efName}>", True, edgeSRV, None),
)
)
edgeMap[2 * r] = (efName, 0)
edgeMap[2 * r + 1] = (efName, 0)
lr = len(relations)
relationsAll = []
for (r, rc) in relations:
relationsAll.extend([r, rc])
searchExe.relations = [
dict(
acro=r[0],
spin=r[1],
func=r[2],
desc=r[3],
)
for r in relationsAll
]
searchExe.relationFromName = dict(
((r["acro"], i) for (i, r) in enumerate(searchExe.relations))
)
searchExe.relationLegend = "\n".join(
f'{r["acro"]:>23} {r["desc"]}'
for r in searchExe.relations
if r["desc"] is not None
)
searchExe.relationLegend += f"""
The warp feature "{OSLOTS}" and {OMAP} features cannot be used in searches.
One of the above relations on nodes and / or slots will suit you better.
"""
searchExe.converse = dict(
tuple((2 * i, 2 * i + 1) for i in range(lr))
+ tuple((2 * i + 1, 2 * i) for i in range(lr))
)
searchExe.edgeMap = edgeMap
searchExe.nodeMap = nodeMap
def add_K_Relations(searchExe, varRels):
relations = searchExe.relations
tasks = collections.defaultdict(set)
for (acro, ks) in varRels.items():
j = searchExe.relationFromName[acro]
ji = searchExe.converse[j]
if ji < j:
(j, ji) = (ji, j)
acro = relations[j]["acro"]
acroi = relations[ji]["acro"]
tasks[(j, acro, ji, acroi)] |= ks
for ((j, acro, ji, acroi), ks) in tasks.items():
for k in ks:
newAcro = acro.replace("k", str(k))
newAcroi = acroi.replace("k", str(k))
r = relations[j]
ri = relations[ji]
lr = len(relations)
relations.extend(
[
dict(
name=acro,
acro=newAcro,
spin=r["spin"],
func=r["func"](k),
desc=r["desc"],
),
dict(
name=acroi,
acro=newAcroi,
spin=ri["spin"],
func=ri["func"](k),
desc=ri["desc"],
),
]
)
searchExe.relationFromName[newAcro] = lr
searchExe.relationFromName[newAcroi] = lr + 1
searchExe.converse[lr] = lr + 1
searchExe.converse[lr + 1] = lr
def add_F_Relations(searchExe, varRels):
relations = searchExe.relations
tasks = collections.defaultdict(set)
for (acro, feats) in varRels.items():
j = searchExe.relationFromName[acro]
ji = searchExe.converse[j]
if ji < j:
(j, ji) = (ji, j)
acro = relations[j]["acro"]
acroi = relations[ji]["acro"]
tasks[(j, acro, ji, acroi)] |= feats
for ((j, acro, ji, acroi), feats) in tasks.items():
for featInfo in feats:
if len(featInfo) == 2:
((f, fF), (t, gF)) = featInfo
acroFmt = acro.replace("f", "{f}").replace("g", "{g}")
acroiFmt = acroi.replace("f", "{f}").replace("g", "{g}")
newAcro = acroFmt.format(f=fF, g=gF)
newAcroi = acroiFmt.format(f=fF, g=gF)
fArgs = (fF, gF)
else:
((f, fF), rPat, (t, gF)) = featInfo
acroFmt = (
acro.replace("f", "{f}").replace("r", "{r}").replace("g", "{g}")
)
acroiFmt = (
acroi.replace("f", "{f}").replace("r", "{r}").replace("g", "{g}")
)
newAcro = acroFmt.format(f=fF, r=rPat, g=gF)
newAcroi = acroiFmt.format(f=fF, r=rPat, g=gF)
rRe = re.compile(rPat)
fArgs = (fF, rPat, rRe, gF)
r = relations[j]
ri = relations[ji]
lr = len(relations)
spin = r["spin"]
if isinstance(spin, types.FunctionType):
spin = spin(*fArgs)
spini = ri["spin"]
if isinstance(spini, types.FunctionType):
spini = spini(*fArgs)
func = r["func"](*fArgs)
funci = ri["func"](*fArgs)
relations.extend(
[
dict(
name=acro,
acro=newAcro,
spin=spin,
func=func,
desc=r["desc"],
),
dict(
name=acroi,
acro=newAcroi,
spin=spini,
func=funci,
desc=ri["desc"],
),
]
)
searchExe.relationFromName[newAcro] = lr
searchExe.relationFromName[newAcroi] = lr + 1
searchExe.nodeMap.setdefault(f, set()).add(fF)
searchExe.nodeMap.setdefault(t, set()).add(gF)
searchExe.converse[lr] = lr + 1
searchExe.converse[lr + 1] = lr
def add_V_Relations(searchExe, varRels):
relations = searchExe.relations
tasks = collections.defaultdict(set)
for (acro, vals) in sorted(varRels.items()):
for (eName, val) in vals:
(b, mid, e) = (acro[0], acro[1:-1], acro[-1])
norm = b == "-" and e == ">"
conv = b == "<" and e == "-"
eRel = f"-{eName}>"
eReli = f"<{eName}-"
eRels = f"<{eName}>"
acroi = f"-{mid}>" if conv else f"<{mid}-" if norm else f"<{mid}>"
if conv:
(acro, acroi) = (acroi, acro)
j = searchExe.relationFromName[eRel]
ji = searchExe.relationFromName[eReli]
js = searchExe.relationFromName[eRels]
if norm or conv:
tasks[(eName, j, acro, ji, acroi)].add(val)
else:
tasks[(eName, js, acro, js, acro)].add(val)
for ((eName, j, acro, ji, acroi), vals) in sorted(tasks.items()):
for val in vals:
r = relations[j]
ri = relations[ji]
lr = len(relations)
relations.extend(
[
dict(
acro=acro,
spin=r["spin"],
func=r["func"](val),
desc=r["desc"],
),
dict(
acro=acroi,
spin=ri["spin"],
func=ri["func"](val),
desc=ri["desc"],
),
]
)
searchExe.relationFromName[acro] = lr
searchExe.relationFromName[acroi] = lr + 1
if j == ji:
searchExe.edgeMap[lr] = (eName, 0)
searchExe.edgeMap[lr + 1] = (eName, 0)
else:
searchExe.edgeMap[lr] = (eName, 1)
searchExe.edgeMap[lr + 1] = (eName, -1)
searchExe.converse[lr] = lr + 1
searchExe.converse[lr + 1] = lr
Functions
def add_F_Relations(searchExe, varRels)
-
Expand source code Browse git
def add_F_Relations(searchExe, varRels): relations = searchExe.relations tasks = collections.defaultdict(set) for (acro, feats) in varRels.items(): j = searchExe.relationFromName[acro] ji = searchExe.converse[j] if ji < j: (j, ji) = (ji, j) acro = relations[j]["acro"] acroi = relations[ji]["acro"] tasks[(j, acro, ji, acroi)] |= feats for ((j, acro, ji, acroi), feats) in tasks.items(): for featInfo in feats: if len(featInfo) == 2: ((f, fF), (t, gF)) = featInfo acroFmt = acro.replace("f", "{f}").replace("g", "{g}") acroiFmt = acroi.replace("f", "{f}").replace("g", "{g}") newAcro = acroFmt.format(f=fF, g=gF) newAcroi = acroiFmt.format(f=fF, g=gF) fArgs = (fF, gF) else: ((f, fF), rPat, (t, gF)) = featInfo acroFmt = ( acro.replace("f", "{f}").replace("r", "{r}").replace("g", "{g}") ) acroiFmt = ( acroi.replace("f", "{f}").replace("r", "{r}").replace("g", "{g}") ) newAcro = acroFmt.format(f=fF, r=rPat, g=gF) newAcroi = acroiFmt.format(f=fF, r=rPat, g=gF) rRe = re.compile(rPat) fArgs = (fF, rPat, rRe, gF) r = relations[j] ri = relations[ji] lr = len(relations) spin = r["spin"] if isinstance(spin, types.FunctionType): spin = spin(*fArgs) spini = ri["spin"] if isinstance(spini, types.FunctionType): spini = spini(*fArgs) func = r["func"](*fArgs) funci = ri["func"](*fArgs) relations.extend( [ dict( name=acro, acro=newAcro, spin=spin, func=func, desc=r["desc"], ), dict( name=acroi, acro=newAcroi, spin=spini, func=funci, desc=ri["desc"], ), ] ) searchExe.relationFromName[newAcro] = lr searchExe.relationFromName[newAcroi] = lr + 1 searchExe.nodeMap.setdefault(f, set()).add(fF) searchExe.nodeMap.setdefault(t, set()).add(gF) searchExe.converse[lr] = lr + 1 searchExe.converse[lr + 1] = lr
def add_K_Relations(searchExe, varRels)
-
Expand source code Browse git
def add_K_Relations(searchExe, varRels): relations = searchExe.relations tasks = collections.defaultdict(set) for (acro, ks) in varRels.items(): j = searchExe.relationFromName[acro] ji = searchExe.converse[j] if ji < j: (j, ji) = (ji, j) acro = relations[j]["acro"] acroi = relations[ji]["acro"] tasks[(j, acro, ji, acroi)] |= ks for ((j, acro, ji, acroi), ks) in tasks.items(): for k in ks: newAcro = acro.replace("k", str(k)) newAcroi = acroi.replace("k", str(k)) r = relations[j] ri = relations[ji] lr = len(relations) relations.extend( [ dict( name=acro, acro=newAcro, spin=r["spin"], func=r["func"](k), desc=r["desc"], ), dict( name=acroi, acro=newAcroi, spin=ri["spin"], func=ri["func"](k), desc=ri["desc"], ), ] ) searchExe.relationFromName[newAcro] = lr searchExe.relationFromName[newAcroi] = lr + 1 searchExe.converse[lr] = lr + 1 searchExe.converse[lr + 1] = lr
def add_V_Relations(searchExe, varRels)
-
Expand source code Browse git
def add_V_Relations(searchExe, varRels): relations = searchExe.relations tasks = collections.defaultdict(set) for (acro, vals) in sorted(varRels.items()): for (eName, val) in vals: (b, mid, e) = (acro[0], acro[1:-1], acro[-1]) norm = b == "-" and e == ">" conv = b == "<" and e == "-" eRel = f"-{eName}>" eReli = f"<{eName}-" eRels = f"<{eName}>" acroi = f"-{mid}>" if conv else f"<{mid}-" if norm else f"<{mid}>" if conv: (acro, acroi) = (acroi, acro) j = searchExe.relationFromName[eRel] ji = searchExe.relationFromName[eReli] js = searchExe.relationFromName[eRels] if norm or conv: tasks[(eName, j, acro, ji, acroi)].add(val) else: tasks[(eName, js, acro, js, acro)].add(val) for ((eName, j, acro, ji, acroi), vals) in sorted(tasks.items()): for val in vals: r = relations[j] ri = relations[ji] lr = len(relations) relations.extend( [ dict( acro=acro, spin=r["spin"], func=r["func"](val), desc=r["desc"], ), dict( acro=acroi, spin=ri["spin"], func=ri["func"](val), desc=ri["desc"], ), ] ) searchExe.relationFromName[acro] = lr searchExe.relationFromName[acroi] = lr + 1 if j == ji: searchExe.edgeMap[lr] = (eName, 0) searchExe.edgeMap[lr + 1] = (eName, 0) else: searchExe.edgeMap[lr] = (eName, 1) searchExe.edgeMap[lr + 1] = (eName, -1) searchExe.converse[lr] = lr + 1 searchExe.converse[lr + 1] = lr
def basicRelations(searchExe, api)
-
Expand source code Browse git
def basicRelations(searchExe, api): C = api.C F = api.F Fs = api.Fs E = api.E Crank = C.rank.data ClevDown = C.levDown.data ClevUp = C.levUp.data (CfirstSlots, ClastSlots) = C.boundary.data Eoslots = E.oslots.data slotType = F.otype.slotType maxSlot = F.otype.maxSlot maxSlotP = maxSlot + 1 sets = searchExe.sets setInfo = searchExe.setInfo searchExe.featureValueIndex = {} Sindex = searchExe.featureValueIndex def isSlotType(nType): if nType == ".": return None if sets is not None and nType in sets: if nType in setInfo: return setInfo[nType] nodes = sets[nType] allSlots = all(n < maxSlotP for n in nodes) if allSlots: setInfo[nType] = True return True allNonSlots = all(n > maxSlot for n in nodes) if allNonSlots: setInfo[nType] = False return False setInfo[nType] = None return None return nType == slotType # EQUAL def spinEqual(fTp, tTp): def doyarns(yF, yT): x = set(yF) & set(yT) return (x, x) return doyarns def equalR(fTp, tTp): return _l_eq # UNEQUAL def unequalR(fTp, tTp): return _l_uneq # CANONICAL BEFORE def canonicalBeforeR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_lt return _l_ranklt(Crank) # CANONICAL AFTER def canonicalAfterR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_gt return _l_rankgt(Crank) # SAME SLOTS def spinSameSlots(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: def doyarns(yF, yT): x = set(yF) & set(yT) return (x, x) return doyarns elif isSlotF or isSlotT: def doyarns(yS, y2): sindex = {} for m in y2: ss = Eoslots[m - maxSlotP] if m > maxSlot else (m,) if len(ss) == 1: sindex.setdefault(ss[0], set()).add(m) nyS = yS & set(sindex.keys()) ny2 = set(chain.from_iterable(sindex[s] for s in nyS)) return (nyS, ny2) if isSlotF: return doyarns else: def xx(yF, yT): (nyT, nyF) = doyarns(yT, yF) return (nyF, nyT) return xx else: def doyarns(yF, yT): sindexF = {} for n in yF: s = frozenset(Eoslots[n - maxSlotP] if n > maxSlot else (n,)) sindexF.setdefault(s, set()).add(n) sindexT = {} for m in yT: s = frozenset(Eoslots[m - maxSlotP] if m > maxSlot else (m,)) sindexT.setdefault(s, set()).add(m) nyS = set(sindexF.keys()) & set(sindexT.keys()) nyF = set(chain.from_iterable(sindexF[s] for s in nyS)) nyT = set(chain.from_iterable(sindexT[s] for s in nyS)) return (nyF, nyT) return doyarns def sameSlotsR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_eq else: def xx(n): nmin = n - 1 if n < maxSlotP: nA = array.array("I", (n,)) # nA = (n,) yield n for m in ClevUp[nmin]: if Eoslots[m - maxSlotP] == nA: yield m return nSlots = Eoslots[n - maxSlotP] if len(nSlots) == 1: slot1 = nSlots[0] nA = array.array("I", (slot1,)) # nA = tuple(slot1,) yield n yield slot1 for m in ClevUp[nmin]: if Eoslots[m - maxSlotP] == nA: yield m return yield n for m in ClevUp[nmin]: if n in ClevUp[m - 1]: yield m return xx # OVERLAP def spinOverlap(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: def doyarns(yF, yT): x = set(yF) & set(yT) return (x, x) return doyarns elif isSlotF or isSlotT: def doyarns(yS, y2): sindex = {} for m in y2: for s in Eoslots[m - maxSlotP] if m > maxSlot else (m,): sindex.setdefault(s, set()).add(m) nyS = yS & set(sindex.keys()) ny2 = set(chain.from_iterable(sindex[s] for s in nyS)) return (nyS, ny2) if isSlotF: return doyarns else: def xx(yF, yT): (nyT, nyF) = doyarns(yT, yF) return (nyF, nyT) return xx else: def doyarns(yF, yT): REDUCE_FACTOR = 0.4 SIZE_LIMIT = 10000 sindexF = {} for n in yF: for s in Eoslots[n - maxSlotP] if n > maxSlot else (n,): sindexF.setdefault(s, set()).add(n) sindexT = {} for m in yT: for s in Eoslots[m - maxSlotP] if m > maxSlot else (m,): sindexT.setdefault(s, set()).add(m) nyS = set(sindexF.keys()) & set(sindexT.keys()) lsF = len(sindexF) lsT = len(sindexT) lsI = len(nyS) if lsF == lsT: # spinning is completely useless return (yF, yT) if lsI > REDUCE_FACTOR * lsT and lsT > SIZE_LIMIT: # spinning is not worth it return (yF, yT) nyF = set(chain.from_iterable(sindexF[s] for s in nyS)) nyT = set(chain.from_iterable(sindexT[s] for s in nyS)) return (nyF, nyT) return doyarns def overlapR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_eq elif isSlotT: def func(n): return Eoslots[n - maxSlotP] if n > maxSlot else (n,) return func elif isSlotF: def func(n): return chain(ClevUp[n - 1], (n,)) return func else: def xx(n): nSlots = Eoslots[n - maxSlotP] if n > maxSlot else (n,) return chain( nSlots, set(chain.from_iterable(ClevUp[s - 1] for s in nSlots)) ) return xx # DIFFERENT SLOTS def diffSlotsR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_uneq elif isSlotT: def func(n, m): return (Eoslots[m - maxSlotP] if m > maxSlot else (m,)) != (n,) return func elif isSlotF: def func(n, m): return (Eoslots[n - maxSlotP] if n > maxSlot else (n,)) != (m,) return func else: def func(n, m): return frozenset( Eoslots[n - maxSlotP] if n > maxSlot else (n,) ) != frozenset(Eoslots[m - maxSlotP] if m > maxSlot else (m,)) return func # DISJOINT SLOTS def disjointSlotsR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_uneq elif isSlotT: def func(n, m): return m not in frozenset( Eoslots[n - maxSlotP] if n > maxSlot else (n,) ) return func elif isSlotF: def func(n, m): return n not in frozenset( Eoslots[m - maxSlotP] if m > maxSlot else (m,) ) return func else: def func(n, m): return ( len( frozenset(Eoslots[n - maxSlotP] if n > maxSlot else (n,)) & frozenset(Eoslots[m - maxSlotP] if m > maxSlot else (m,)) ) == 0 ) return func # EMBEDDED IN def inR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_em elif isSlotT: return _l_em elif isSlotF: def func(n): return ClevUp[n - 1] return func else: def func(n): return ClevUp[n - 1] return func # EMBEDS def hasR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_em elif isSlotF: return _l_em elif isSlotT: def func(n): return Eoslots[n - maxSlotP] if n > maxSlot else () return func else: if isSlotT is None: def func(n): return ( chain(ClevDown[n - maxSlotP], Eoslots[n - maxSlotP]) if n > maxSlot else () ) return func else: def func(n): return ClevDown[n - maxSlotP] if n > maxSlot else () return func # BEFORE WRT SLOTS def slotBeforeR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_lt elif isSlotF: def func(n, m): return n < (Eoslots[m - maxSlotP][0] if m > maxSlot else m) return func elif isSlotT: def func(n, m): return (Eoslots[n - maxSlotP][-1] if n > maxSlot else n) < m return func else: def func(n, m): return (Eoslots[n - maxSlotP][-1] if n > maxSlot else n) < ( Eoslots[m - maxSlotP][0] if m > maxSlot else m ) return func # AFTER WRT SLOTS def slotAfterR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_gt elif isSlotF: def func(n, m): return n > (Eoslots[m - maxSlotP][-1] if m > maxSlot else m) return func elif isSlotT: def func(n, m): return (Eoslots[n - maxSlotP][0] if n > maxSlot else n) > m return func else: def func(n, m): return (Eoslots[n - maxSlotP][0] if n > maxSlot else n) > ( Eoslots[m - maxSlotP][-1] if m > maxSlot else m ) return func # START AT SAME SLOT def sameFirstSlotR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_eq elif isSlotF: if isSlotT is None: def func(n): return chain(CfirstSlots[n - 1], (n,)) return func else: def func(n): return CfirstSlots[n - 1] return func elif isSlotT: def func(n): return ((Eoslots[n - maxSlotP][0] if n > maxSlot else n),) return func else: def xx(n): fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n fnmin = fn - 1 if isSlotT is None: return chain(CfirstSlots[fnmin], (fn,)) else: return CfirstSlots[fnmin] return xx # ENDS AT SAME SLOT def sameLastSlotR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_eq elif isSlotF: if isSlotT is None: def func(n): return chain(ClastSlots[n - 1], (n,)) return func else: def func(n): return ClastSlots[n - 1] return func elif isSlotT: def func(n): return ((Eoslots[n - maxSlotP][-1] if n > maxSlot else n),) return func else: def xx(n): ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n lnmin = ln - 1 if isSlotT is None: return chain(ClastSlots[lnmin], (ln,)) else: return ClastSlots[lnmin] return xx # START AND END AT SAME SLOT def sameBoundaryR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: return _l_eq elif isSlotF: if isSlotT is None: def xx(n): nmin = n - 1 fok = set(chain(CfirstSlots[nmin], (n,))) lok = set(chain(ClastSlots[nmin], (n,))) return fok & lok else: def xx(n): nmin = n - 1 fok = set(CfirstSlots[nmin]) lok = set(ClastSlots[nmin]) return fok & lok return xx elif isSlotT: def xx(n): slots = Eoslots[n - maxSlotP] if n > maxSlot else (n,) fs = slots[0] ls = slots[-1] return (fs,) if fs == ls else () return xx else: if isSlotT is None: def xx(n): fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n fnmin = fn - 1 lnmin = ln - 1 fok = set(chain(CfirstSlots[fnmin], (fn,))) lok = set(chain(ClastSlots[lnmin], (ln,))) return fok & lok else: def xx(n): fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n fnmin = fn - 1 lnmin = ln - 1 fok = set(CfirstSlots[fnmin]) lok = set(ClastSlots[lnmin]) return fok & lok return xx # FIRST SLOTS ARE k-CLOSE def nearFirstSlotR(k): def zz(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: def func(n): return range(max((1, n - k)), min((maxSlot, n + k)) + 1) return func elif isSlotF: if isSlotT is None: def xx(n): near = range(max((1, n - k)), min((maxSlot, n + k)) + 1) return chain( near, chain.from_iterable(CfirstSlots[lf - 1] for lf in near), ) else: def xx(n): near = range(max((1, n - k)), min((maxSlot, n + k)) + 1) return chain.from_iterable(CfirstSlots[lf - 1] for lf in near) return xx elif isSlotT: def xx(n): fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n return range(max((1, fn - k)), min((maxSlot, fn + k)) + 1) return xx else: if isSlotT is None: def xx(n): fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n near = range(max((1, fn - k)), min((maxSlot, fn + k)) + 1) return chain( near, chain.from_iterable(CfirstSlots[lf - 1] for lf in near), ) else: def xx(n): fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n near = range(max((1, fn - k)), min((maxSlot, fn + k)) + 1) return chain.from_iterable(CfirstSlots[lf - 1] for lf in near) return xx return zz # LAST SLOTS ARE k-CLOSE def nearLastSlotR(k): def zz(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: def func(n): return range(max((1, n - k)), min((maxSlot, n + k)) + 1) return func elif isSlotF: if isSlotT is None: def xx(n): near = range(max((1, n - k)), min((maxSlot, n + k)) + 1) return chain( near, chain.from_iterable(ClastSlots[lf - 1] for lf in near) ) else: def xx(n): near = range(max((1, n - k)), min((maxSlot, n + k)) + 1) return chain.from_iterable(ClastSlots[lf - 1] for lf in near) return xx elif isSlotT: def xx(n): ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n return range(max((1, ln - k)), min((maxSlot, ln + k)) + 1) return xx else: if isSlotT is None: def xx(n): ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n near = range(max((1, ln - k)), min((maxSlot, ln + k)) + 1) return chain( near, chain.from_iterable(ClastSlots[lf - 1] for lf in near) ) else: def xx(n): ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n near = range(max((1, ln - k)), min((maxSlot, ln + k)) + 1) return chain.from_iterable(ClastSlots[lf - 1] for lf in near) return xx return zz # FIRST SLOTS ARE k-CLOSE and LAST SLOTS ARE k-CLOSE def nearBoundaryR(k): def zz(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: def func(n): return range(max((1, n - k)), min((maxSlot, n + k)) + 1) return func elif isSlotF: if isSlotT is None: def xx(n): near = set(range(max((1, n - k)), min((maxSlot, n + k)) + 1)) fok = set( chain.from_iterable(CfirstSlots[lf - 1] for lf in near) ) lok = set( chain.from_iterable(ClastSlots[lf - 1] for lf in near) ) return near | (fok & lok) else: def xx(n): near = range(max((1, n - k)), min((maxSlot, n + k)) + 1) fok = set( chain.from_iterable(CfirstSlots[lf - 1] for lf in near) ) lok = set( chain.from_iterable(ClastSlots[lf - 1] for lf in near) ) return fok & lok return xx elif isSlotT: def xx(n): slots = Eoslots[n - maxSlotP] if n > maxSlot else (n,) fs = slots[0] ls = slots[-1] fok = set(range(max((1, fs - k)), min((maxSlot, fs + k)) + 1)) lok = set(range(max((1, ls - k)), min((maxSlot, ls + k)) + 1)) return fok & lok return xx else: if isSlotT is None: def xx(n): fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n nearf = range(max((1, fn - k)), min((maxSlot, fn + k)) + 1) nearl = range(max((1, ln - k)), min((maxSlot, ln + k)) + 1) fok = set( chain( nearf, chain.from_iterable( CfirstSlots[lf - 1] for lf in nearf ), ) ) lok = set( chain( nearl, chain.from_iterable(ClastSlots[lf - 1] for lf in nearl), ) ) return fok & lok else: def xx(n): fn = Eoslots[n - maxSlotP][0] if n > maxSlot else n ln = Eoslots[n - maxSlotP][-1] if n > maxSlot else n nearf = range(max((1, fn - k)), min((maxSlot, fn + k)) + 1) nearl = range(max((1, ln - k)), min((maxSlot, ln + k)) + 1) fok = set( chain.from_iterable(CfirstSlots[lf - 1] for lf in nearf) ) lok = set( chain.from_iterable(ClastSlots[lf - 1] for lf in nearl) ) return fok & lok return xx return zz # FIRST ENDS WHERE SECOND STARTS def adjBeforeR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: def func(n): return (n + 1,) if n < maxSlot else () return func else: if isSlotT: def xx(n): if n == maxSlot: return () myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1 if myNext > maxSlot: return () return (myNext,) elif isSlotT is None: def xx(n): if n == maxSlot: return () myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1 if myNext > maxSlot: return () return chain(CfirstSlots[myNext - 1], (myNext,)) else: def xx(n): if n == maxSlot: return () myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1 if myNext > maxSlot: return () return CfirstSlots[myNext - 1] return xx # FIRST STARTS WHERE SECOND ENDS def adjAfterR(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: def func(n): return (n - 1,) if n > 1 else () return func else: if isSlotT: def xx(n): if n <= 1: return () myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1 if myPrev <= 1: return () return (myPrev,) elif isSlotT is None: def xx(n): if n <= 1: return () myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1 if myPrev <= 1: return () return chain((myPrev,), ClastSlots[myPrev - 1]) else: def xx(n): if n <= 1: return () myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1 if myPrev <= 1: return () return ClastSlots[myPrev - 1] return xx # FIRST ENDS WHERE SECOND STARTS WITHIN k-SLOTS def nearBeforeR(k): def zz(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: def func(n): return range(max((1, n + 1 - k)), min((maxSlot, n + 1 + k)) + 1) return func else: if isSlotT: def xx(n): myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1 return range( max((1, myNext - k)), min((maxSlot, myNext + k)) + 1 ) elif isSlotT is None: def xx(n): myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1 near = range( max((1, myNext - k)), min((maxSlot, myNext + k)) + 1 ) return chain( near, chain.from_iterable(CfirstSlots[ls - 1] for ls in near), ) else: def xx(n): myNext = n + 1 if n < maxSlot else Eoslots[n - maxSlotP][-1] + 1 near = range( max((1, myNext - k)), min((maxSlot, myNext + k)) + 1 ) return chain.from_iterable(CfirstSlots[ls - 1] for ls in near) return xx return zz # FIRST STARTS WHERE SECOND ENDS WITHIN k-SLOTS def nearAfterR(k): def zz(fTp, tTp): isSlotF = isSlotType(fTp) isSlotT = isSlotType(tTp) if isSlotF and isSlotT: def func(n): return range(max((1, n - 1 - k)), min((maxSlot, n - 1 + k)) + 1) return func else: if isSlotT: def xx(n): myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1 return tuple( range(max((1, myPrev - k)), min((maxSlot, myPrev + k)) + 1) ) elif isSlotT is None: def xx(n): myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1 near = range( max((1, myPrev - k)), min((maxSlot, myPrev + k)) + 1 ) return chain( near, chain.from_iterable(ClastSlots[lf - 1] for lf in near) ) else: def xx(n): myPrev = n - 1 if n < maxSlot else Eoslots[n - maxSlotP][0] - 1 near = range( max((1, myPrev - k)), min((maxSlot, myPrev + k)) + 1 ) return chain.from_iterable(ClastSlots[lf - 1] for lf in near) return xx return zz # SAME FEATURE VALUES def spinLeftFisRightG(f, g): def zz(fTp, tTp): if f not in Sindex: Sindex[f] = makeIndex(Fs(f).data) if f != g: if g not in Sindex: Sindex[g] = makeIndex(Fs(g).data) indF = Sindex[f] indG = Sindex[g] commonValues = set(indF) if f == g else set(indF) & set(indG) def doyarns(yF, yT): fNodes = { n for n in chain.from_iterable(indF[v] for v in commonValues) if n in yF } gNodes = { n for n in chain.from_iterable(indG[v] for v in commonValues) if n in yT } return (fNodes, gNodes) return doyarns return zz def spinLeftGisRightF(f, g): return spinLeftFisRightG(g, f) def leftFisRightGR_ORIG(f, g): def zz(fTp, tTp): fData = Fs(f).v gData = Fs(g).v def uu(n, m): nVal = fData(n) return False if nVal is None else nVal == gData(m) return uu return zz def leftFisRightGR(f, g): def zz(fTp, tTp): fData = Fs(f).v if f == OTYPE else Fs(f).data gData = Fs(g).v if g == OTYPE else Fs(g).data if f == OTYPE and g == OTYPE: def uu(n, m): nVal = fData(n) return False if nVal is None else nVal == gData(m) return uu if f == OTYPE: def uu(n, m): nVal = fData(n) return False if nVal is None else nVal == gData.get(m, None) return uu if g == OTYPE: def uu(n, m): nVal = fData.get(n, None) return False if nVal is None else nVal == gData(m) return uu def uu(n, m): nVal = fData.get(n, None) return False if nVal is None else nVal == gData.get(m, None) return uu return zz def leftGisRightFR(g, f): return leftFisRightGR(f, g) # MATCH FEATURE VALUES def spinLeftFmatchRightG(f, rPat, rRe, g): def zz(fTp, tTp): fR = f"{f}~{rPat}" gR = f"{g}~{rPat}" if fR not in Sindex: if f not in Sindex: Sindex[f] = makeIndex(Fs(f).data) indFR = {} for (v, ns) in Sindex[f].items(): vR = rRe.sub("", v) for n in ns: indFR.setdefault(vR, set()).add(n) Sindex[fR] = indFR if gR not in Sindex: if g not in Sindex: Sindex[g] = makeIndex(Fs(g).data) indGR = {} for (v, ns) in Sindex[g].items(): vR = rRe.sub("", v) for n in ns: indGR.setdefault(vR, set()).add(n) Sindex[gR] = indGR indFR = Sindex[fR] indGR = Sindex[gR] commonValues = set(indFR) & set(indGR) def doyarns(yF, yT): fNodes = { n for n in chain.from_iterable(indFR[v] for v in commonValues) if n in yF } gNodes = { n for n in chain.from_iterable(indGR[v] for v in commonValues) if n in yT } return (fNodes, gNodes) return doyarns return zz def spinLeftGmatchRightF(f, rPat, rRe, g): return spinLeftFmatchRightG(g, rPat, rRe, f) def leftFmatchRightGR(f, rPat, rRe, g): def zz(fTp, tTp): fData = Fs(f).v if f == OTYPE else Fs(f).data gData = Fs(g).v if g == OTYPE else Fs(g).data if f == OTYPE and g == OTYPE: def uu(n, m): nVal = fData(n) if nVal is None: return False nVal = rRe.sub("", nVal) mVal = gData(m) if mVal is None: return False mVal = rRe.sub("", mVal) return nVal == mVal return uu if f == OTYPE: def uu(n, m): nVal = fData(n) if nVal is None: return False nVal = rRe.sub("", nVal) mVal = gData.get(m, None) if mVal is None: return False mVal = rRe.sub("", mVal) return nVal == mVal return uu if g == OTYPE: def uu(n, m): nVal = fData.get(n, None) if nVal is None: return False nVal = rRe.sub("", nVal) mVal = gData(m) if mVal is None: return False mVal = rRe.sub("", mVal) return nVal == mVal return uu def uu(n, m): nVal = fData.get(n, None) if nVal is None: return False nVal = rRe.sub("", nVal) mVal = gData.get(m, None) if mVal is None: return False mVal = rRe.sub("", mVal) return nVal == mVal return uu return zz def leftGmatchRightFR(g, rPat, rRe, f): return leftFmatchRightGR(f, rPat, rRe, g) # UNEQUAL FEATURE VALUES def leftFunequalRightGR(f, g): def zz(fTp, tTp): fData = Fs(f).v if f == OTYPE else Fs(f).data gData = Fs(g).v if g == OTYPE else Fs(g).data if f == OTYPE and g == OTYPE: def uu(n, m): nVal = fData(n) mVal = gData(m) return nVal is None and mVal is None or nVal != mVal return uu if f == OTYPE: def uu(n, m): nVal = fData(n) mVal = gData.get(m, None) return nVal is None and mVal is None or nVal != mVal return uu if g == OTYPE: def uu(n, m): nVal = fData.get(n, None) mVal = gData(m) return nVal is None and mVal is None or nVal != mVal return uu def uu(n, m): nVal = fData.get(n, None) mVal = gData.get(m, None) return nVal is None and mVal is None or nVal != mVal return uu return zz def leftGunequalRightFR(g, f): return leftFunequalRightGR(f, g) # GREATER FEATURE VALUES def leftFgreaterRightGR(f, g): def zz(fTp, tTp): fData = Fs(f).v if f == OTYPE else Fs(f).data gData = Fs(g).v if g == OTYPE else Fs(g).data if f == OTYPE and g == OTYPE: def uu(n, m): nVal = fData(n) mVal = gData(m) return nVal is not None and mVal is not None and nVal > mVal return uu if f == OTYPE: def uu(n, m): nVal = fData(n) mVal = gData.get(m, None) return nVal is not None and mVal is not None and nVal > mVal return uu if g == OTYPE: def uu(n, m): nVal = fData.get(n, None) mVal = gData(m) return nVal is not None and mVal is not None and nVal > mVal return uu def uu(n, m): nVal = fData.get(n, None) mVal = gData.get(m, None) return nVal is not None and mVal is not None and nVal > mVal return uu return zz def leftGlesserRightFR(g, f): return leftFgreaterRightGR(f, g) # LESSER FEATURE VALUES def leftFlesserRightGR(f, g): def zz(fTp, tTp): fData = Fs(f).v if f == OTYPE else Fs(f).data gData = Fs(g).v if g == OTYPE else Fs(g).data if f == OTYPE and g == OTYPE: def uu(n, m): nVal = fData(n) mVal = gData(m) return nVal is not None and mVal is not None and nVal < mVal return uu if f == OTYPE: def uu(n, m): nVal = fData(n) mVal = gData.get(m, None) return nVal is not None and mVal is not None and nVal < mVal return uu if g == OTYPE: def uu(n, m): nVal = fData.get(n, None) mVal = gData(m) return nVal is not None and mVal is not None and nVal < mVal return uu def uu(n, m): nVal = fData.get(n, None) mVal = gData.get(m, None) return nVal is not None and mVal is not None and nVal < mVal return uu return zz def leftGgreaterRightFR(g, f): return leftFlesserRightGR(f, g) # EDGES def makeEdgeMaps(efName): def edgeAccess(eFunc, doValues, value): if doValues: if value is None: def func(n): return (m[0] for m in eFunc(n) if m[1] is None) return func elif value is True: def func(n): return (m[0] for m in eFunc(n)) return func elif isinstance(value, types.FunctionType): def func(n): return (m[0] for m in eFunc(n) if value(m[1])) return func elif isinstance(value, reTp): def func(n): return ( m[0] for m in eFunc(n) if value is not None and value.search(m[1]) ) return func else: (ident, value) = value if ident is None and value is True: def func(n): return (m[0] for m in eFunc(n)) return func elif ident: def func(n): return (m[0] for m in eFunc(n) if m[1] in value) return func else: def func(n): return (m[0] for m in eFunc(n) if m[1] not in value) return func else: def func(n): return eFunc(n) return func def edgeRV(value): def edgeR(fTp, tTp): Es = api.Es Edata = Es(efName) doValues = Edata.doValues return edgeAccess(Edata.f, doValues, value) return edgeR def edgeIRV(value): def edgeIR(fTp, tTp): Es = api.Es Edata = Es(efName) doValues = Edata.doValues return edgeAccess(Edata.t, doValues, value) return edgeIR def edgeSRV(value): def edgeSR(fTp, tTp): Es = api.Es Edata = Es(efName) doValues = Edata.doValues return edgeAccess(Edata.b, doValues, value) return edgeSR return (edgeRV, edgeIRV, edgeSRV) # COLLECT ALL RELATIONS IN A TUPLE relations = [ ( ("=", spinEqual, equalR, "left equal to right (as node)"), ("=", spinEqual, equalR, None), ), ( ("#", 0.999, unequalR, "left unequal to right (as node)"), ("#", 0.999, unequalR, None), ), ( ( "<", 0.500, canonicalBeforeR, "left before right (in canonical node ordering)", ), ( ">", 0.500, canonicalAfterR, "left after right (in canonical node ordering)", ), ), ( ("==", spinSameSlots, sameSlotsR, "left occupies same slots as right"), ("==", spinSameSlots, sameSlotsR, None), ), ( ("&&", spinOverlap, overlapR, "left has overlapping slots with right"), ("&&", spinOverlap, overlapR, None), ), ( ("##", 0.990, diffSlotsR, "left and right do not have the same slot set"), ("##", 0.990, diffSlotsR, None), ), ( ("||", 0.900, disjointSlotsR, "left and right do not have common slots"), ("||", 0.900, disjointSlotsR, None), ), ( ("[[", True, hasR, "left embeds right"), ("]]", True, inR, "left embedded in right"), ), ( ("<<", 0.490, slotBeforeR, "left completely before right"), (">>", 0.490, slotAfterR, "left completely after right"), ), ( ("=:", True, sameFirstSlotR, "left and right start at the same slot"), ("=:", True, sameFirstSlotR, None), ), ( (":=", True, sameLastSlotR, "left and right end at the same slot"), (":=", True, sameLastSlotR, None), ), ( ( "::", True, sameBoundaryR, "left and right start and end at the same slot", ), ("::", True, sameBoundaryR, None), ), ( ("<:", True, adjBeforeR, "left immediately before right"), (":>", True, adjAfterR, "left immediately after right"), ), ( ( "=k:", True, nearFirstSlotR, "left and right start at k-nearly the same slot", ), ("=k:", True, nearFirstSlotR, None), ), ( ( ":k=", True, nearLastSlotR, "left and right end at k-nearly the same slot", ), (":k=", True, nearLastSlotR, None), ), ( ( ":k:", True, nearBoundaryR, "left and right start and end at k-near slots", ), (":k:", True, nearBoundaryR, None), ), ( ("<k:", True, nearBeforeR, "left k-nearly before right"), (":k>", True, nearAfterR, "left k-nearly after right"), ), ( (".f.", spinLeftFisRightG, leftFisRightGR, "left.f = right.f"), (".f.", spinLeftGisRightF, leftGisRightFR, None), ), ( (".f=g.", spinLeftFisRightG, leftFisRightGR, "left.f = right.g"), (".g=f.", spinLeftGisRightF, leftGisRightFR, None), ), ( ( ".f~r~g.", spinLeftFmatchRightG, leftFmatchRightGR, "left.f matches right.g", ), (".g~r~f.", spinLeftGmatchRightF, leftGmatchRightFR, None), ), ( (".f#g.", 0.8, leftFunequalRightGR, "left.f # right.g"), (".g#f.", 0.8, leftGunequalRightFR, None), ), ( (".f>g.", 0.4, leftFgreaterRightGR, "left.f > right.g"), (".f<g.", 0.4, leftFlesserRightGR, None), ), ( (".f<g.", 0.4, leftFlesserRightGR, "left.f < right.g"), (".f>g.", 0.4, leftFgreaterRightGR, None), ), ] # BUILD AND INITIALIZE ALL RELATIONAL FUNCTIONS api.TF.explore(silent=DEEP) edgeMap = {} nodeMap = {} for efName in sorted(api.TF.featureSets["edges"]): if efName == OSLOTS or efName.startswith(OMAP): continue r = len(relations) (edgeRV, edgeIRV, edgeSRV) = makeEdgeMaps(efName) doValues = api.TF.features[efName].edgeValues extra = " with value specification allowed" if doValues else "" relations.append( ( (f"-{efName}>", True, edgeRV, f'edge feature "{efName}"{extra}'), ( f"<{efName}-", True, edgeIRV, f'edge feature "{efName}"{extra} (opposite direction)', ), ) ) edgeMap[2 * r] = (efName, 1) edgeMap[2 * r + 1] = (efName, -1) r = len(relations) relations.append( ( ( f"<{efName}>", True, edgeSRV, f'edge feature "{efName}"{extra} (either direction)', ), (f"<{efName}>", True, edgeSRV, None), ) ) edgeMap[2 * r] = (efName, 0) edgeMap[2 * r + 1] = (efName, 0) lr = len(relations) relationsAll = [] for (r, rc) in relations: relationsAll.extend([r, rc]) searchExe.relations = [ dict( acro=r[0], spin=r[1], func=r[2], desc=r[3], ) for r in relationsAll ] searchExe.relationFromName = dict( ((r["acro"], i) for (i, r) in enumerate(searchExe.relations)) ) searchExe.relationLegend = "\n".join( f'{r["acro"]:>23} {r["desc"]}' for r in searchExe.relations if r["desc"] is not None ) searchExe.relationLegend += f""" The warp feature "{OSLOTS}" and {OMAP} features cannot be used in searches. One of the above relations on nodes and / or slots will suit you better. """ searchExe.converse = dict( tuple((2 * i, 2 * i + 1) for i in range(lr)) + tuple((2 * i + 1, 2 * i) for i in range(lr)) ) searchExe.edgeMap = edgeMap searchExe.nodeMap = nodeMap