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