This presentation and instructions to run it @ github.com/konstantinosKokos/Presentations/LSD2021
Terms of the form
is, saw ::
is ::
saw ::
closed ::
...
automatically extracted theorems from lassy
A conversion of Lassy annotations to tecto-grammatic proofs & terms of the above logic.
import pickle
# remark: change this to your downloaded dataset path
dataset_path = '/home/kokos/Projects/lassy-tlg-extraction/data/train_dev_test_0.4.dev0.p'
with open(dataset_path, 'rb') as f:
proofs = sum(pickle.load(f), [])proof look
like..?some_proof = proofs[1312]
print(some_proof)ProofNet(proof_frame=Alle:□ᵈᵉᵗ(ɴ → ɴᴘ), films:ɴ, zijn:◊ᵛᶜᴘᴘᴀʀᴛ → ◊ˢᵘɴᴘ → sᴍᴀɪɴ, heel:□ᵐᵒᵈ(ᴀᴘ → ᴀᴘ), barok:ᴀᴘ, versierd:◊ᵖʳᵉᵈᶜᴀᴘ → ᴘᴘᴀʀᴛ ⊢ sᴍᴀɪɴ, axiom_links={(5, 11), (2, 0), (1, 4), (7, 9), (8, 6), (10, 3)}, name='Treebank/WR-P-E-I-0000000332/WR-P-E-I-0000000332.p.4.s.98_0.xml')
A sequence of words & their corresponding types, together with a conclusion specifying the type of the entire phrase.
print(some_proof.proof_frame)Alle:□ᵈᵉᵗ(ɴ → ɴᴘ), films:ɴ, zijn:◊ᵛᶜᴘᴘᴀʀᴛ → ◊ˢᵘɴᴘ → sᴍᴀɪɴ, heel:□ᵐᵒᵈ(ᴀᴘ → ᴀᴘ), barok:ᴀᴘ, versierd:◊ᵖʳᵉᵈᶜᴀᴘ → ᴘᴘᴀʀᴛ ⊢ sᴍᴀɪɴ
print(some_proof.proof_frame.get_words())['Alle', 'films', 'zijn', 'heel', 'barok', 'versierd']
print(some_proof.proof_frame.get_types())[□ᵈᵉᵗ(ɴ(-,0) → ɴᴘ(+,1)), ɴ(+,2), ◊ᵛᶜᴘᴘᴀʀᴛ(-,3) → ◊ˢᵘɴᴘ(-,4) → sᴍᴀɪɴ(+,5), □ᵐᵒᵈ(ᴀᴘ(-,6) → ᴀᴘ(+,7)), ᴀᴘ(+,8), ◊ᵖʳᵉᵈᶜᴀᴘ(-,9) → ᴘᴘᴀʀᴛ(+,10)]
print(some_proof.proof_frame.conclusion)sᴍᴀɪɴ(-,11)
a bijection between positive and negative atoms (a compressed representation of a proof)
print(some_proof.axiom_links){(5, 11), (2, 0), (1, 4), (7, 9), (8, 6), (10, 3)}
(optionally) telling us its lassy origins
print(some_proof.name)Treebank/WR-P-E-I-0000000332/WR-P-E-I-0000000332.p.4.s.98_0.xml
An intuitionistic logic proof is one and the same to a λ-term (or program)
some_proof.print_term(show_words=True)'((zijn ▵ᵛᶜ((versierd ▵ᵖʳᵉᵈᶜ((▾ᵐᵒᵈ(heel) barok))))) ▵ˢᵘ((▾ᵈᵉᵗ(Alle) films)))'
..modalities (& term decorations) can be dropped to fall back to simple terms
some_proof.print_term(show_words=True, show_decorations=False)'((zijn (versierd (heel barok))) (Alle films))'
From the full collection of proof frames we can aggregate a mapping Word {Type}
from LassyExtraction.aethel import ProofNet
from LassyExtraction.milltypes import WordType
from collections import Counter, defaultdict
def make_lexicon(proofs: list[ProofNet], simple: bool = False) -> dict[str, Counter[WordType]]:
c = defaultdict(lambda: Counter())
for pn in proofs:
for word, wordtype in zip(pn.proof_frame.get_words(), pn.proof_frame.get_types()):
wordtype = wordtype.depolarize()
c[word.lower()][wordtype.decolor() if simple else wordtype] += 1
return c
simple_lexicon = make_lexicon(proofs, True)
deco_lexicon = make_lexicon(proofs, False)simple_lexicon['lezen']Counter({ᴡᴡ: 20,
ɪɴғ: 11,
ᴠᴢ → ɪɴғ: 3,
ʙᴡ → ɪɴғ: 1,
ɴᴘ → ɪɴғ: 6,
ᴠɴᴡ → ɪɴғ: 1,
ᴀᴅᴊ → ssᴜʙ: 1,
ɴᴘ: 3,
ᴘᴘ → ɪɴғ: 2,
ᴠᴢ → ɴᴘ → ɪɴғ: 1,
ᴡʜsᴜʙ → ɪɴғ: 4,
ᴠᴢ → ʙᴡ → ɴᴘ → ɪɴғ: 1})
We can filter sentences satisfying arbitrary predicates
from typing import Callable
def get_proofs(proofs: list[ProofNet], predicate: Callable[[ProofNet], bool]) -> list[ProofNet]:
return list(filter(predicate, proofs))..like containing a specific word, word-type combination, dependency label, etc.
def containing_word(word: str) -> Callable[[ProofNet], bool]:
def f(pn: ProofNet) -> bool:
return word in (w.lower() for w in pn.proof_frame.get_words())
return f
def containing_dep(dep: str) -> Callable[[ProofNet], bool]:
def f(pn: ProofNet) -> bool:
return dep in set.union(*[t.colors() for t in pn.proof_frame.get_types()])
return f
def containing_word_as(word: str, wordtype: WordType) -> Callable[[ProofNet], bool]:
def f(pn: ProofNet) -> bool:
return any(map(lambda w, t: w.lower() == word and t.depolarize() == wordtype,
pn.proof_frame.get_words(),
pn.proof_frame.get_types()))
return ffrom LassyExtraction.milltypes import AtomicType
eenden = get_proofs(proofs, containing_word_as('eenden', AtomicType('N')))Proofs can be directly compared to the original Lassy annotations
from LassyExtraction.lassy import Lassy, et
from LassyExtraction.viz import ToGraphViz
# remark: change this to point to the LassySmall dir
root_dir = '/home/kokos/Projects/Lassy 4.0/LassySmall'
lassy = Lassy(root_dir = root_dir, ignore='/home/kokos/Projects/lassy-tlg-extraction/LassyExtraction/utils/ignored.txt')
viz = ToGraphViz()
def find_in_lassy(name: str) -> et:
source, _ = name.split('_')
return lassy[f'{root_dir}/{source}.xml'][2]Ignoring 156 samples..
Dataset constructed with 65045 samples.
lassy_tree = find_in_lassy(eenden[0].name)lassy_tree = find_in_lassy('Treebank/dpc-ind-001645-nl-sen/dpc-ind-001645-nl-sen.p.12.s.1_1')viz(lassy_tree)A neural parser based on a seq2seq module to translate phrases to proof frames and a permutation module to align positive & negative atoms
from Parser.neural.inference import get_model
weights_path = './stored_models/model_weights.model' # remark: change this
device = 'cuda' # switch
model = get_model(device=device, weights_path=weights_path)Initializing model...
Initialized.
Loading pre-trained parameters...
Loading model parameters...
Loaded.
Computing semantics
from LassyExtraction.terms import *
from LassyExtraction.milltypes import *
from functools import partial
from typing import Callable, TypeVar
Meaning = TypeVar('Meaning')
# some standard types
_np, _s = AtomicType('NP'), AtomicType('S')
_itv = FunctorType(_np, _s)
_tv = FunctorType(_np, _itv)
_adj = FunctorType(_np, _np)
# -- corresponding terms
np = lambda x: Lex(_type=_np, idx=x)
itv = lambda x: Lex(_type=_itv, idx=x)
tv = lambda x: Lex(_type=_tv, idx=x)
adj = lambda x: Lex(_type=_adj, idx=x)
# common sentence structures
s1 = Application(itv(1), np(0))
s2 = Application(Application(tv(1), np(2)), np(0))
def meaning(term: Term, word_meanings: dict[int, Meaning]) -> Meaning:
if isinstance(term, Lex):
return word_meanings[term.idx]
if isinstance(term, Application):
return meaning(term.functor, word_meanings)(meaning(term.argument, word_meanings))
raise TypeError('only 0-order implicative fragment please!')John likes Mary but Mary is a duck!
E = str
T = bool
ET = Callable[[E], T]
EET = Callable[[E], ET]
John: E = 'John'
Mary: E = 'Mary'
talks: ET = lambda x: x == John
swims: ET = lambda x: True
flies: ET = lambda x: x == Mary
human: ET = lambda x: talks(x)
duck: ET = lambda x: swims(x) and flies(x)
is_a: EET = lambda y: lambda x: y(x)
likes: EET = lambda y: lambda x: True if x == John and y == Mary else False