From 7474b8cdb39668693ca8bc91f1a24d2be868fa5c Mon Sep 17 00:00:00 2001 From: Mysaa Date: Mon, 27 Nov 2023 20:28:05 +0100 Subject: [PATCH] Ajout d'un format de poids dans nltk --- CategoriesGramaticalesCombinatoire.ods | Bin 25027 -> 29926 bytes phrases.txt | 2 +- test.ipynb | 853 +++++++++++++++++++++++++ 3 files changed, 854 insertions(+), 1 deletion(-) create mode 100644 test.ipynb diff --git a/CategoriesGramaticalesCombinatoire.ods b/CategoriesGramaticalesCombinatoire.ods index 4cecb7e598ffb5c9573d2e0a8ae4472f1a29bf30..378d3e1f8cc9278070c85dd58536fe2e63835163 100644 GIT binary patch delta 28227 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 delta 23309 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

6Vam^Ch+x#y|3FJIdz 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 Y2@g0f=9EY?U5TE&vqYWEBNn6t0PHbJaR2}S diff --git a/phrases.txt b/phrases.txt index 09a0eae..8c58a07 100644 --- a/phrases.txt +++ b/phrases.txt @@ -21,7 +21,7 @@ Le chat la mange avec ses dents Avec quoi le chat mange la souris ? Le rat donne un fromage à la souris Un fromage est donné par le rat à la souris -A quelle souris un fromage est donné par le rat ? +À quelle souris un fromage est donné par le rat ? Il le donne à la souris Il le lui donne Il souhaite que mon voisin lui donne le chat diff --git a/test.ipynb b/test.ipynb new file mode 100644 index 0000000..74dda21 --- /dev/null +++ b/test.ipynb @@ -0,0 +1,853 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from nltk.ccg import chart, lexicon\n", + "from nltk.ccg.lexicon import CCGLexicon, Token, augParseCategory\n", + "from nltk.ccg.chart import CCGChart,CCGLeafEdge\n", + "from nltk.tree import Tree\n", + "import pandas as pd\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Weighed Lexicon" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from warnings import warn\n", + "\n", + "class WeighedToken(Token):\n", + " def __init__(self, token, categ, semantics=None, weight = 1.0):\n", + " super().__init__(token, categ, semantics= semantics)\n", + " self._weight = weight\n", + " def weight(self):\n", + " \"\"\"1.0 is considered the default weight for any token\"\"\"\n", + " try:\n", + " return self._weight\n", + " except AttributeError:\n", + " warn(f\"[{self.token} : {str(self)}] : this token has no weight attribute, defaulted to 1.0.\")\n", + " return 1.0\n", + "\n", + "class WeighedLexicon(CCGLexicon):\n", + " def __init__(self, start, primitives, families, entries):\n", + " super().__init__(start, primitives, families, entries)\n", + "\n", + " def weight(self, entry):\n", + " return entry.weight()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# CYK" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "valz = {\n", + " '>' : 0.8,\n", + " '<' : 0.7\n", + "}\n", + "def rweight(rule):\n", + " s = rule.__str__()\n", + " if s in valz:\n", + " return valz[s]\n", + " else:\n", + " return 1.0 # Base rules weight\n", + "\n", + "# Implements the CYK algorithm, code partly taken from nltk\n", + "def weightedParse(tokens, lex, rules):\n", + " \"\"\"made to take weighed tokens and lexicons\"\"\"\n", + " chart = CCGChart(list(tokens))\n", + " \n", + " # Initialize leaf edges.\n", + " for index in range(chart.num_leaves()):\n", + " for token in lex.categories(chart.leaf(index)):\n", + " new_edge = CCGLeafEdge(index, token, chart.leaf(index))\n", + " new_edge.weight = token.weight()\n", + " chart.insert(new_edge, ())\n", + "\n", + " # Select a span for the new edges\n", + " for span in range(2, chart.num_leaves() + 1):\n", + " for start in range(0, chart.num_leaves() - span + 1):\n", + " \n", + " bestedge = None\n", + " \n", + " # Try all possible pairs of edges that could generate\n", + " # an edge for that span\n", + " for part in range(1, span):\n", + " lstart = start\n", + " mid = start + part\n", + " rend = start + span\n", + "\n", + " for left in chart.select(span=(lstart, mid)):\n", + " for right in chart.select(span=(mid, rend)):\n", + " # Generate all possible combinations of the two edges\n", + " for rule in rules:\n", + " edgez = list(rule.apply(chart, lex, left, right))\n", + " if(len(edgez)==1):\n", + " edge = edgez[0]\n", + " edge.weight = rweight(rule) * left.weight * right.weight\n", + " edge.triple = (rule,left,right)\n", + " if (bestedge == None) or (bestedge.weight < edge.weight):\n", + " bestedge = edge\n", + " elif(len(edgez)!=0):\n", + " print(\"Too many new edges (unsupported rule used)\")\n", + " \n", + " # end for rule loop\n", + " # end for right loop\n", + " # end for left loop\n", + " # end for part loop\n", + " return chart\n", + "\n", + "def wpToTree(edge):\n", + " if isinstance(edge,CCGLeafEdge):\n", + " return Tree((edge.token(),\"Leaf\"),[Tree(edge.token(),[edge.leaf()])])\n", + " else:\n", + " return Tree(\n", + " (chart.Token(None,edge.categ()),edge.triple[0].__str__()),\n", + " [wpToTree(t) for t in (edge.triple[1:])])\n", + "\n", + "def bestTree(tokens, lex, rules):\n", + " # We build the weighgted parse tree using cky\n", + " wChart = weightedParse(tokens, lex, rules)\n", + " # We get the biggest edge\n", + " e = list(wChart.select(start=0,end=len(tokens)))[0]\n", + " # We get the tree that brought us to this edge\n", + " t = wChart._trees(e, True, dict(), Tree)[0]\n", + " # (wpToTree(e),e.weight)\n", + " return (t,e.weight)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Application" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from numbers import Number\n", + "from nltk.sem.logic import Expression\n", + "from nltk.ccg.api import PrimitiveCategory\n", + "\n", + "def to_pseudo_entries(table, consider_semantics = False):\n", + " \"\"\"returns a list of lists in the format ['word', 'category', 'weight', None]\n", + " if consider_semantics == false else ['word', 'category', weight, 'semantic']\n", + " that is left to be converted into tokens by to_wlex_entries\"\"\"\n", + "\n", + " entries = list()\n", + " for line in range(len(table['MOT'])):\n", + " for wdi, word in enumerate(table['MOT'][line].replace(\" \", \"\").split('/')):\n", + " for j in range(3):\n", + " if isinstance(table['Cat'+str(j)][line],str):\n", + " category = table['Cat'+str(j)][line]\n", + " weight = float(table['Weights'+str(j)][line]) if isinstance(table['Weights'+str(j)][line], Number) else 1.0\n", + " if consider_semantics:\n", + " semantic = (table['Sem'+str(j)][line].replace('\\\\\\\\', '\\\\').split('/'))[wdi]\n", + " else:\n", + " semantic = None\n", + " entries.append([word, category, weight, semantic])\n", + " return entries\n", + "\n", + "def to_wlex_entries(pseudo_entries, primitives, families, var=None):\n", + " \"\"\"returns the entries to a weighed lexicon from pseudo_entries generated by to_pseudo_entries\"\"\"\n", + " entries = dict()\n", + " for entry in pseudo_entries:\n", + " if entry[0] not in entries:\n", + " entries[entry[0]] = list()\n", + " categ, _ = augParseCategory(entry[1], primitives, families, var)\n", + " token = WeighedToken(token= entry[0],\n", + " categ= categ,\n", + " semantics= Expression.fromstring(entry[-1]),\n", + " weight= entry[2])\n", + " entries[entry[0]].append(token)\n", + " return entries\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{}\n", + "\\n v.à(v,n)\n", + "\\n v.avec(v,n)\n", + "chat\n", + "\\n m.de(n,m)\n", + "dents\n", + "\\n.donne(n)\n", + "\\n m.donne(n,m)\n", + "\\n.mange(n)\n", + "\\n m.mange(n,m)\n", + "donner\n", + "\\n.donner(n)\n", + "\\n.dormir(n)\n", + "\\n.dormir(n)\n", + "elle\n", + "il\n", + "{}\n", + "{}\n", + "{}\n", + "\\n m.et(n,m)\n", + "\\x y.et(x,y)\n", + "\\v w n.et(v,w,n)\n", + "fromage\n", + "{}\n", + "{}\n", + "{}\n", + "\\P.exists x.P(x)\n", + "\\P.exists x.P(x)\n", + "\\P.exists x.P(x)\n", + "\\P.exists x.P(x)\n", + "\\P.exists x.P(x)\n", + "\\n.mangé(n)\n", + "\\n.mangé(n)\n", + "\\n.donné(n)\n", + "\\n.méchant(n)\n", + "\\n.méchant(n)\n", + "\\n.noir(n)\n", + "\\n.noir(n)\n", + "\\v n.paisiblement(v,n)\n", + "{}\n", + "{}\n", + "{}\n", + "{}\n", + "{}\n", + "{}\n", + "{}\n", + "{}\n", + "{}\n", + "{}\n", + "rat\n", + "soeur\n", + "\\n m.souhaite(m,n)\n", + "\\n m.pourchasse(m,n)\n", + "\\n m.attrappe(m,n)\n", + "souris\n", + "{}\n", + "voisin\n", + "1 le chat dort\n", + "Found derivation tree with weight 0.5599999999999999\n", + " le chat dort\n", + " (N/N) {\\P.exists x.P(x)} N {chat} (S\\N) {\\n.dormir(n)}\n", + "------------------------------------>\n", + " N {exists x.chat(x)}\n", + "----------------------------------------------------------<\n", + " S {dormir(exists x.chat(x))}\n", + "1 il dort\n", + "Found derivation tree with weight 0.7\n", + " il dort\n", + " N {il} (S\\N) {\\n.dormir(n)}\n", + "------------------------------<\n", + " S {dormir(il)}\n", + "1 le chat dort paisiblement\n", + "Found derivation tree with weight 0.39199999999999996\n", + " le chat dort paisiblement\n", + " (N/N) {\\P.exists x.P(x)} N {chat} (S\\N) {\\n.dormir(n)} ((S\\N)\\(S\\N)) {\\v n.paisiblement(v,n)}\n", + "------------------------------------>\n", + " N {exists x.chat(x)}\n", + " --------------------------------------------------------------<\n", + " (S\\N) {\\n.paisiblement(\\n.dormir(n),n)}\n", + "--------------------------------------------------------------------------------------------------<\n", + " S {paisiblement(\\n.dormir(n),exists x.chat(x))}\n", + "2 le chat noir dort\n", + "Found derivation tree with weight 0.39199999999999996\n", + " le chat noir dort\n", + " (N/N) {\\P.exists x.P(x)} N {chat} (N\\N) {\\n.noir(n)} (S\\N) {\\n.dormir(n)}\n", + " ------------------------------<\n", + " N {noir(chat)}\n", + "-------------------------------------------------------->\n", + " N {exists x.noir(chat,x)}\n", + "------------------------------------------------------------------------------<\n", + " S {dormir(exists x.noir(chat,x))}\n", + "1 le méchant chat dort\n", + "Found derivation tree with weight 0.44800000000000006\n", + " le méchant chat dort\n", + " (N/N) {\\P.exists x.P(x)} (N/N) {\\n.méchant(n)} N {chat} (S\\N) {\\n.dormir(n)}\n", + " --------------------------------->\n", + " N {méchant(chat)}\n", + "----------------------------------------------------------->\n", + " N {exists x.méchant(chat,x)}\n", + "---------------------------------------------------------------------------------<\n", + " S {dormir(exists x.méchant(chat,x))}\n", + "2 le très méchant chat dort\n", + "Found derivation tree with weight 0.35840000000000005\n", + " le très méchant chat dort\n", + " (N/N) {\\P.exists x.P(x)} ((N/N)/(N/N)) {{}} (N/N) {\\n.méchant(n)} N {chat} (S\\N) {\\n.dormir(n)}\n", + " ------------------------------------------->\n", + " (N/N) {{}(\\n.méchant(n))}\n", + " ----------------------------------------------------->\n", + " N {{}(\\n.méchant(n),chat)}\n", + "------------------------------------------------------------------------------->\n", + " N {exists x.{}(\\n.méchant(n),chat,x)}\n", + "-----------------------------------------------------------------------------------------------------<\n", + " S {dormir(exists x.{}(\\n.méchant(n),chat,x))}\n", + "7 le chat de la sœur de mon voisin dort\n", + "Found derivation tree with weight 0.11239423999999998\n", + " le chat de la sœur de mon voisin dort\n", + " (N/N) {\\P.exists x.P(x)} N {chat} ((N/N)\\N) {\\n m.de(n,m)} (N/N) {\\P.exists x.P(x)} N {soeur} ((N/N)\\N) {\\n m.de(n,m)} (N/N) {\\P.exists x.P(x)} N {voisin} (S\\N) {\\n.dormir(n)}\n", + " ------------------------------------<\n", + " (N/N) {\\m.de(chat,m)}\n", + " -------------------------------------<\n", + " (N/N) {\\m.de(soeur,m)}\n", + " -------------------------------------->\n", + " N {exists x.voisin(x)}\n", + " --------------------------------------------------------------------------->\n", + " N {de(soeur,exists x.voisin(x))}\n", + " ----------------------------------------------------------------------------------------------------->\n", + " N {exists x.de(soeur,exists x.voisin(x),x)}\n", + " ----------------------------------------------------------------------------------------------------------------------------------------->\n", + " N {de(chat,exists x.de(soeur,exists x.voisin(x),x))}\n", + "------------------------------------------------------------------------------------------------------------------------------------------------------------------->\n", + " N {exists x.de(chat,exists x.de(soeur,exists x.voisin(x),x),x)}\n", + "-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {dormir(exists x.de(chat,exists x.de(soeur,exists x.voisin(x),x),x))}\n", + "2 le chat que mon voisin lui donne mange\n", + "Found derivation tree with weight 0.14049279999999997\n", + " le chat que mon voisin lui donne mange\n", + " (N/N) {\\P.exists x.P(x)} N {chat} ((N\\N)/S) {{}} (N/N) {\\P.exists x.P(x)} N {voisin} ((S\\N)/(S\\N)) {{}} (S\\N) {\\n.donne(n)} (S\\N) {\\n.mange(n)}\n", + " -------------------------------------->\n", + " N {exists x.voisin(x)}\n", + " ----------------------------------------->\n", + " (S\\N) {{}(\\n.donne(n))}\n", + " -------------------------------------------------------------------------------<\n", + " S {{}(\\n.donne(n),exists x.voisin(x))}\n", + " ----------------------------------------------------------------------------------------------->\n", + " (N\\N) {{}({}(\\n.donne(n),exists x.voisin(x)))}\n", + " ---------------------------------------------------------------------------------------------------------<\n", + " N {{}({}(\\n.donne(n),exists x.voisin(x)),chat)}\n", + "----------------------------------------------------------------------------------------------------------------------------------->\n", + " N {exists x.{}({}(\\n.donne(n),exists x.voisin(x)),chat,x)}\n", + "--------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {mange(exists x.{}({}(\\n.donne(n),exists x.voisin(x)),chat,x))}\n", + "8 le chat qui dort est noir\n", + "Found derivation tree with weight 0.25087999999999994\n", + " le chat qui dort est noir\n", + " (N/N) {\\P.exists x.P(x)} N {chat} ((N\\N)/(S\\N)) {{}} (S\\N) {\\n.dormir(n)} ((S\\N)/(N/N)) {{}} (N\\N) {\\n.noir(n)}\n", + " ------------------------------------------>\n", + " (N\\N) {{}(\\n.dormir(n))}\n", + " ----------------------------------------------------<\n", + " N {{}(\\n.dormir(n),chat)}\n", + "------------------------------------------------------------------------------>\n", + " N {exists x.{}(\\n.dormir(n),chat,x)}\n", + " ---------------------------------------->\n", + " (S\\N) {{}(\\n.noir(n))}\n", + "----------------------------------------------------------------------------------------------------------------------<\n", + " S {{}(\\n.noir(n),exists x.{}(\\n.dormir(n),chat,x))}\n", + "1 le chat mange la souris\n", + "Found derivation tree with weight 0.35840000000000005\n", + " le chat mange la souris\n", + " (N/N) {\\P.exists x.P(x)} N {chat} ((S\\N)/N) {\\n m.mange(n,m)} (N/N) {\\P.exists x.P(x)} N {souris}\n", + "------------------------------------>\n", + " N {exists x.chat(x)}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " ------------------------------------------------------------------->\n", + " (S\\N) {\\m.mange(exists x.souris(x),m)}\n", + "-------------------------------------------------------------------------------------------------------<\n", + " S {mange(exists x.souris(x),exists x.chat(x))}\n", + "1 le chat la mange\n", + "Found derivation tree with weight 0.44799999999999995\n", + " le chat la mange\n", + " (N/N) {\\P.exists x.P(x)} N {chat} ((S\\N)/((S\\N)/N)) {{}} ((S\\N)/N) {\\n m.mange(n,m)}\n", + "------------------------------------>\n", + " N {exists x.chat(x)}\n", + " ----------------------------------------------------->\n", + " (S\\N) {{}(\\n m.mange(n,m))}\n", + "-----------------------------------------------------------------------------------------<\n", + " S {{}(\\n m.mange(n,m),exists x.chat(x))}\n", + "1 il la mange\n", + "Found derivation tree with weight 0.5599999999999999\n", + " il la mange\n", + " N {il} ((S\\N)/((S\\N)/N)) {{}} ((S\\N)/N) {\\n m.mange(n,m)}\n", + " ----------------------------------------------------->\n", + " (S\\N) {{}(\\n m.mange(n,m))}\n", + "-------------------------------------------------------------<\n", + " S {{}(\\n m.mange(n,m),il)}\n", + "1 quel chat mange la souris ?\n", + "Found derivation tree with weight 0.2867200000000001\n", + " quel chat mange la souris ?\n", + " ((S/(S\\N))/N) {{}} N {chat} ((S\\N)/N) {\\n m.mange(n,m)} (N/N) {\\P.exists x.P(x)} N {souris} (S\\S) {{}}\n", + "------------------------------>\n", + " (S/(S\\N)) {{}(chat)}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " ------------------------------------------------------------------->\n", + " (S\\N) {\\m.mange(exists x.souris(x),m)}\n", + "------------------------------------------------------------------------------------------------->\n", + " S {{}(chat,\\m.mange(exists x.souris(x),m))}\n", + "-------------------------------------------------------------------------------------------------------------<\n", + " S {{}({}(chat,\\m.mange(exists x.souris(x),m)))}\n", + "1 qui mange la souris ?\n", + "Found derivation tree with weight 0.35840000000000005\n", + " qui mange la souris ?\n", + " (S/(S\\N)) {{}} ((S\\N)/N) {\\n m.mange(n,m)} (N/N) {\\P.exists x.P(x)} N {souris} (S\\S) {{}}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " ------------------------------------------------------------------->\n", + " (S\\N) {\\m.mange(exists x.souris(x),m)}\n", + "----------------------------------------------------------------------------------->\n", + " S {{}(\\m.mange(exists x.souris(x),m))}\n", + "-----------------------------------------------------------------------------------------------<\n", + " S {{}({}(\\m.mange(exists x.souris(x),m)))}\n", + "1 quelle souris mange le chat ?\n", + "Found derivation tree with weight 0.2867200000000001\n", + " quelle souris mange le chat ?\n", + " ((S/(S\\N))/N) {{}} N {souris} ((S\\N)/N) {\\n m.mange(n,m)} (N/N) {\\P.exists x.P(x)} N {chat} (S\\S) {{}}\n", + "-------------------------------->\n", + " (S/(S\\N)) {{}(souris)}\n", + " ------------------------------------>\n", + " N {exists x.chat(x)}\n", + " ----------------------------------------------------------------->\n", + " (S\\N) {\\m.mange(exists x.chat(x),m)}\n", + "------------------------------------------------------------------------------------------------->\n", + " S {{}(souris,\\m.mange(exists x.chat(x),m))}\n", + "-------------------------------------------------------------------------------------------------------------<\n", + " S {{}({}(souris,\\m.mange(exists x.chat(x),m)))}\n", + "1 la souris est mangée par le chat\n", + "Found derivation tree with weight 0.20070400000000002\n", + " la souris est mangée par le chat\n", + " (N/N) {\\P.exists x.P(x)} N {souris} ((S\\N)/Pp) {{}} Pp {\\n.mangé(n)} (((S\\N)\\(S\\N))/N) {{}} (N/N) {\\P.exists x.P(x)} N {chat}\n", + "-------------------------------------->\n", + " N {exists x.souris(x)}\n", + " ----------------------------------->\n", + " (S\\N) {{}(\\n.mangé(n))}\n", + " ------------------------------------>\n", + " N {exists x.chat(x)}\n", + " ------------------------------------------------------------>\n", + " ((S\\N)\\(S\\N)) {{}(exists x.chat(x))}\n", + " -----------------------------------------------------------------------------------------------<\n", + " (S\\N) {{}(exists x.chat(x),{}(\\n.mangé(n)))}\n", + "-------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {{}(exists x.chat(x),{}(\\n.mangé(n)),exists x.souris(x))}\n", + "1 elle est mangée par le chat\n", + "Found derivation tree with weight 0.25088000000000005\n", + " elle est mangée par le chat\n", + " N {elle} ((S\\N)/Pp) {{}} Pp {\\n.mangé(n)} (((S\\N)\\(S\\N))/N) {{}} (N/N) {\\P.exists x.P(x)} N {chat}\n", + " ----------------------------------->\n", + " (S\\N) {{}(\\n.mangé(n))}\n", + " ------------------------------------>\n", + " N {exists x.chat(x)}\n", + " ------------------------------------------------------------>\n", + " ((S\\N)\\(S\\N)) {{}(exists x.chat(x))}\n", + " -----------------------------------------------------------------------------------------------<\n", + " (S\\N) {{}(exists x.chat(x),{}(\\n.mangé(n)))}\n", + "---------------------------------------------------------------------------------------------------------<\n", + " S {{}(exists x.chat(x),{}(\\n.mangé(n)),elle)}\n", + "1 quelle souris est mangée par le chat ?\n", + "Found derivation tree with weight 0.16056320000000004\n", + " quelle souris est mangée par le chat ?\n", + " ((S/(S\\N))/N) {{}} N {souris} ((S\\N)/Pp) {{}} Pp {\\n.mangé(n)} (((S\\N)\\(S\\N))/N) {{}} (N/N) {\\P.exists x.P(x)} N {chat} (S\\S) {{}}\n", + "-------------------------------->\n", + " (S/(S\\N)) {{}(souris)}\n", + " ----------------------------------->\n", + " (S\\N) {{}(\\n.mangé(n))}\n", + " ------------------------------------>\n", + " N {exists x.chat(x)}\n", + " ------------------------------------------------------------>\n", + " ((S\\N)\\(S\\N)) {{}(exists x.chat(x))}\n", + " -----------------------------------------------------------------------------------------------<\n", + " (S\\N) {{}(exists x.chat(x),{}(\\n.mangé(n)))}\n", + "------------------------------------------------------------------------------------------------------------------------------->\n", + " S {{}(souris,{}(exists x.chat(x),{}(\\n.mangé(n))))}\n", + "-------------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {{}({}(souris,{}(exists x.chat(x),{}(\\n.mangé(n)))))}\n", + "1 le chat mange la souris avec ses dents\n", + "Found derivation tree with weight 0.16056320000000004\n", + " le chat mange la souris avec ses dents\n", + " (N/N) {\\P.exists x.P(x)} N {chat} ((S\\N)/N) {\\n m.mange(n,m)} (N/N) {\\P.exists x.P(x)} N {souris} (((S\\N)\\(S\\N))/N) {\\n v.avec(v,n)} (N/N) {\\P.exists x.P(x)} N {dents}\n", + "------------------------------------>\n", + " N {exists x.chat(x)}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " ------------------------------------------------------------------->\n", + " (S\\N) {\\m.mange(exists x.souris(x),m)}\n", + " ------------------------------------->\n", + " N {exists x.dents(x)}\n", + " ------------------------------------------------------------------------->\n", + " ((S\\N)\\(S\\N)) {\\v.avec(v,exists x.dents(x))}\n", + " --------------------------------------------------------------------------------------------------------------------------------------------<\n", + " (S\\N) {avec(\\m.mange(exists x.souris(x),m),exists x.dents(x))}\n", + "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {avec(\\m.mange(exists x.souris(x),m),exists x.dents(x),exists x.chat(x))}\n", + "1 le chat la mange avec ses dents\n", + "Found derivation tree with weight 0.20070400000000002\n", + " le chat la mange avec ses dents\n", + " (N/N) {\\P.exists x.P(x)} N {chat} ((S\\N)/((S\\N)/N)) {{}} ((S\\N)/N) {\\n m.mange(n,m)} (((S\\N)\\(S\\N))/N) {\\n v.avec(v,n)} (N/N) {\\P.exists x.P(x)} N {dents}\n", + "------------------------------------>\n", + " N {exists x.chat(x)}\n", + " ----------------------------------------------------->\n", + " (S\\N) {{}(\\n m.mange(n,m))}\n", + " ------------------------------------->\n", + " N {exists x.dents(x)}\n", + " ------------------------------------------------------------------------->\n", + " ((S\\N)\\(S\\N)) {\\v.avec(v,exists x.dents(x))}\n", + " ------------------------------------------------------------------------------------------------------------------------------<\n", + " (S\\N) {avec({}(\\n m.mange(n,m)),exists x.dents(x))}\n", + "------------------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {avec({}(\\n m.mange(n,m)),exists x.dents(x),exists x.chat(x))}\n", + "0 avec quoi le chat mange la souris ?\n", + "Pas de dérivation tout court :/\n", + "1 le rat donne un fromage à la souris\n", + "Found derivation tree with weight 0.16056320000000004\n", + " le rat donne un fromage à la souris\n", + " (N/N) {\\P.exists x.P(x)} N {rat} ((S\\N)/N) {\\n m.donne(n,m)} (N/N) {\\P.exists x.P(x)} N {fromage} (((S\\N)\\(S\\N))/N) {\\n v.à(v,n)} (N/N) {\\P.exists x.P(x)} N {souris}\n", + "----------------------------------->\n", + " N {exists x.rat(x)}\n", + " --------------------------------------->\n", + " N {exists x.fromage(x)}\n", + " -------------------------------------------------------------------->\n", + " (S\\N) {\\m.donne(exists x.fromage(x),m)}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " ----------------------------------------------------------------------->\n", + " ((S\\N)\\(S\\N)) {\\v.à(v,exists x.souris(x))}\n", + " -------------------------------------------------------------------------------------------------------------------------------------------<\n", + " (S\\N) {à(\\m.donne(exists x.fromage(x),m),exists x.souris(x))}\n", + "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {à(\\m.donne(exists x.fromage(x),m),exists x.souris(x),exists x.rat(x))}\n", + "1 un fromage est donné par le rat à la souris\n", + "Found derivation tree with weight 0.08991539200000002\n", + " un fromage est donné par le rat à la souris\n", + " (N/N) {\\P.exists x.P(x)} N {fromage} ((S\\N)/Pp) {{}} Pp {\\n.donné(n)} (((S\\N)\\(S\\N))/N) {{}} (N/N) {\\P.exists x.P(x)} N {rat} (((S\\N)\\(S\\N))/N) {\\n v.à(v,n)} (N/N) {\\P.exists x.P(x)} N {souris}\n", + "--------------------------------------->\n", + " N {exists x.fromage(x)}\n", + " ----------------------------------->\n", + " (S\\N) {{}(\\n.donné(n))}\n", + " ----------------------------------->\n", + " N {exists x.rat(x)}\n", + " ----------------------------------------------------------->\n", + " ((S\\N)\\(S\\N)) {{}(exists x.rat(x))}\n", + " ----------------------------------------------------------------------------------------------<\n", + " (S\\N) {{}(exists x.rat(x),{}(\\n.donné(n)))}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " ----------------------------------------------------------------------->\n", + " ((S\\N)\\(S\\N)) {\\v.à(v,exists x.souris(x))}\n", + " ---------------------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " (S\\N) {à({}(exists x.rat(x),{}(\\n.donné(n))),exists x.souris(x))}\n", + "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {à({}(exists x.rat(x),{}(\\n.donné(n))),exists x.souris(x),exists x.fromage(x))}\n", + "0 à quelle souris un fromage est donné par le rat ?\n", + "Pas de dérivation tout court :/\n", + "1 il le donne à la souris\n", + "Found derivation tree with weight 0.25088000000000005\n", + " il le donne à la souris\n", + " N {il} ((S\\N)/((S\\N)/N)) {{}} ((S\\N)/N) {\\n m.donne(n,m)} (((S\\N)\\(S\\N))/N) {\\n v.à(v,n)} (N/N) {\\P.exists x.P(x)} N {souris}\n", + " ----------------------------------------------------->\n", + " (S\\N) {{}(\\n m.donne(n,m))}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " ----------------------------------------------------------------------->\n", + " ((S\\N)\\(S\\N)) {\\v.à(v,exists x.souris(x))}\n", + " ----------------------------------------------------------------------------------------------------------------------------<\n", + " (S\\N) {à({}(\\n m.donne(n,m)),exists x.souris(x))}\n", + "------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {à({}(\\n m.donne(n,m)),exists x.souris(x),il)}\n", + "0 il le lui donne\n", + "Pas de dérivation tout court :/\n", + "1 il souhaite que mon voisin lui donne le chat\n", + "Found derivation tree with weight 0.12845056000000002\n", + " il souhaite que mon voisin lui donne le chat\n", + " N {il} ((S\\N)/N) {\\n m.souhaite(m,n)} (N/S) {{}} (N/N) {\\P.exists x.P(x)} N {voisin} ((S\\N)/(S\\N)) {{}} ((S\\N)/N) {\\n m.donne(n,m)} (N/N) {\\P.exists x.P(x)} N {chat}\n", + " -------------------------------------->\n", + " N {exists x.voisin(x)}\n", + " ------------------------------------>\n", + " N {exists x.chat(x)}\n", + " ----------------------------------------------------------------->\n", + " (S\\N) {\\m.donne(exists x.chat(x),m)}\n", + " ------------------------------------------------------------------------------------->\n", + " (S\\N) {{}(\\m.donne(exists x.chat(x),m))}\n", + " ---------------------------------------------------------------------------------------------------------------------------<\n", + " S {{}(\\m.donne(exists x.chat(x),m),exists x.voisin(x))}\n", + " --------------------------------------------------------------------------------------------------------------------------------------->\n", + " N {{}({}(\\m.donne(exists x.chat(x),m),exists x.voisin(x)))}\n", + " ----------------------------------------------------------------------------------------------------------------------------------------------------------------------->\n", + " (S\\N) {\\m.souhaite(m,{}({}(\\m.donne(exists x.chat(x),m),exists x.voisin(x))))}\n", + "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {souhaite(il,{}({}(\\m.donne(exists x.chat(x),m),exists x.voisin(x))))}\n", + "1 il souhaite donner le chat à mon voisin\n", + "Found derivation tree with weight 0.16056320000000004\n", + " il souhaite donner le chat à mon voisin\n", + " N {il} ((S\\N)/N) {\\n m.souhaite(m,n)} (N/N) {\\n.donner(n)} (N/N) {\\P.exists x.P(x)} N {chat} (((S\\N)\\(S\\N))/N) {\\n v.à(v,n)} (N/N) {\\P.exists x.P(x)} N {voisin}\n", + " ------------------------------------>\n", + " N {exists x.chat(x)}\n", + " ---------------------------------------------------------->\n", + " N {donner(exists x.chat(x))}\n", + " ------------------------------------------------------------------------------------------>\n", + " (S\\N) {\\m.souhaite(m,donner(exists x.chat(x)))}\n", + " -------------------------------------->\n", + " N {exists x.voisin(x)}\n", + " ----------------------------------------------------------------------->\n", + " ((S\\N)\\(S\\N)) {\\v.à(v,exists x.voisin(x))}\n", + " -----------------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " (S\\N) {à(\\m.souhaite(m,donner(exists x.chat(x))),exists x.voisin(x))}\n", + "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {à(\\m.souhaite(m,donner(exists x.chat(x))),exists x.voisin(x),il)}\n", + "0 le chat de mon voisin pourchasse et attrape la souris\n", + "Pas de dérivation tout court :/\n", + "2 la souris dort et le chat de mon voisin attrape la souris\n", + "Found derivation tree with weight 0.05035261952\n", + " la souris dort et le chat de mon voisin attrape la souris\n", + " (N/N) {\\P.exists x.P(x)} N {souris} (S\\N) {\\n.dormir(n)} ((S/S)\\S) {\\x y.et(x,y)} (N/N) {\\P.exists x.P(x)} N {chat} ((N/N)\\N) {\\n m.de(n,m)} (N/N) {\\P.exists x.P(x)} N {voisin} ((S\\N)/N) {\\n m.attrappe(m,n)} (N/N) {\\P.exists x.P(x)} N {souris}\n", + "-------------------------------------->\n", + " N {exists x.souris(x)}\n", + "------------------------------------------------------------<\n", + " S {dormir(exists x.souris(x))}\n", + "--------------------------------------------------------------------------------------<\n", + " (S/S) {\\y.et(dormir(exists x.souris(x)),y)}\n", + " ------------------------------------<\n", + " (N/N) {\\m.de(chat,m)}\n", + " -------------------------------------->\n", + " N {exists x.voisin(x)}\n", + " -------------------------------------------------------------------------->\n", + " N {de(chat,exists x.voisin(x))}\n", + " ---------------------------------------------------------------------------------------------------->\n", + " N {exists x.de(chat,exists x.voisin(x),x)}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " ---------------------------------------------------------------------->\n", + " (S\\N) {\\m.attrappe(m,exists x.souris(x))}\n", + " --------------------------------------------------------------------------------------------------------------------------------------------------------------------------<\n", + " S {attrappe(exists x.de(chat,exists x.voisin(x),x),exists x.souris(x))}\n", + "---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->\n", + " S {et(dormir(exists x.souris(x)),attrappe(exists x.de(chat,exists x.voisin(x),x),exists x.souris(x)))}\n", + "1 le chat dort et la souris dort\n", + "Found derivation tree with weight 0.17561599999999997\n", + " le chat dort et la souris dort\n", + " (N/N) {\\P.exists x.P(x)} N {chat} (S\\N) {\\n.dormir(n)} ((S/S)\\S) {\\x y.et(x,y)} (N/N) {\\P.exists x.P(x)} N {souris} (S\\N) {\\n.dormir(n)}\n", + "------------------------------------>\n", + " N {exists x.chat(x)}\n", + "----------------------------------------------------------<\n", + " S {dormir(exists x.chat(x))}\n", + "------------------------------------------------------------------------------------<\n", + " (S/S) {\\y.et(dormir(exists x.chat(x)),y)}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " ------------------------------------------------------------<\n", + " S {dormir(exists x.souris(x))}\n", + "------------------------------------------------------------------------------------------------------------------------------------------------>\n", + " S {et(dormir(exists x.chat(x)),dormir(exists x.souris(x)))}\n", + "2 le chat et la souris dorment\n", + "Found derivation tree with weight 0.25088\n", + " le chat et la souris dorment\n", + " (N/N) {\\P.exists x.P(x)} N {chat} ((N/N)\\N) {\\n m.et(n,m)} (N/N) {\\P.exists x.P(x)} N {souris} (S\\N) {\\n.dormir(n)}\n", + " ------------------------------------<\n", + " (N/N) {\\m.et(chat,m)}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " -------------------------------------------------------------------------->\n", + " N {et(chat,exists x.souris(x))}\n", + "---------------------------------------------------------------------------------------------------->\n", + " N {exists x.et(chat,exists x.souris(x),x)}\n", + "--------------------------------------------------------------------------------------------------------------------------<\n", + " S {dormir(exists x.et(chat,exists x.souris(x),x))}\n", + "2 le chat et la souris dorment\n", + "Found derivation tree with weight 0.25088\n", + " le chat et la souris dorment\n", + " (N/N) {\\P.exists x.P(x)} N {chat} ((N/N)\\N) {\\n m.et(n,m)} (N/N) {\\P.exists x.P(x)} N {souris} (S\\N) {\\n.dormir(n)}\n", + " ------------------------------------<\n", + " (N/N) {\\m.et(chat,m)}\n", + " -------------------------------------->\n", + " N {exists x.souris(x)}\n", + " -------------------------------------------------------------------------->\n", + " N {et(chat,exists x.souris(x))}\n", + "---------------------------------------------------------------------------------------------------->\n", + " N {exists x.et(chat,exists x.souris(x),x)}\n", + "--------------------------------------------------------------------------------------------------------------------------<\n", + " S {dormir(exists x.et(chat,exists x.souris(x),x))}\n" + ] + } + ], + "source": [ + "# Catégories primitives et familles\n", + "primitives = ['S', 'N', 'Pp']\n", + "V = augParseCategory(\"S\\\\N\", primitives = primitives, families={})\n", + "families = {'V': V}\n", + "\n", + "# On importe notre lexique sous forme de tableur\n", + "table = pd.read_excel(\"CategoriesGramaticalesCombinatoire.ods\", engine=\"odf\")\n", + "#print(table.keys())\n", + "\n", + "# On le convertit en Lexique pondéré\n", + "pe = to_pseudo_entries(table, consider_semantics = True)\n", + "#print(pe)\n", + "wEntries = to_wlex_entries(pseudo_entries= pe, primitives= primitives, families= families)\n", + "#print([list(map(lambda x: f\"{k} : \"+ str(x) + str(x._semantics), L)) for k, L in wEntries.items()])\n", + "lex = WeighedLexicon(start= 'S', primitives= primitives, families= families, entries= wEntries)\n", + "\n", + "\n", + "# On récupère le nombre de mots qui ont été définis\n", + "# n = len(table['MOT'])\n", + "\n", + "# On donne la liste des catégories primitives\n", + "# lexstring = ':- S,N,Pp\\n'\n", + "# On ajoute la notation V pour N\\S\n", + "# lexstring += 'V :: S\\\\N\\n'\n", + "\n", + "# On lis les données depuis le tableur en une chaine de caractère parsable\n", + "#for i in range(n):\n", + "# for j in range(3):\n", + "# if isinstance(table['Cat'+str(j)][i],str):\n", + "# for mot in table['MOT'][i].split('/'):\n", + "# lexstring+=mot+' => ' + table['Cat'+str(j)][i] + '\\n'\n", + "\n", + "# Pour inverser les slash dans le lexicon\n", + "#lexstring = lexstring.replace('\\\\','#').replace('/','\\\\').replace('#','/')\n", + "\n", + "# On crée notre lexique\n", + "# lex = lexicon.fromstring(lexstring)\n", + "\n", + "# On crée le parser, on donne l'ensemble des règles qu'il est cencé connaître\n", + "parser = chart.CCGChartParser(lex, chart.DefaultRuleSet)\n", + "#parser = chart.CCGChartParser(lex, chart.ApplicationRuleSet)\n", + "\n", + "printTotal=True\n", + "printDerivations=not printTotal\n", + "\n", + "# On lit les phrases dans le fichier\n", + "with open('phrases.txt') as f:\n", + " lines = f.readlines()\n", + "\n", + " lines.append(\"le chat et la souris dorment\")\n", + " \n", + " for phrase in lines:\n", + " # On met tout en minuscule\n", + " phrase = phrase.lower().strip()\n", + " if printDerivations:\n", + " print(\"=\"*77)\n", + " print('#',phrase)\n", + " # lex = lexicon.fromstring(lexstring)\n", + " parser = chart.CCGChartParser(lex, chart.ApplicationRuleSet)\n", + "\n", + " # Et on affiche tous les arbres de dérivation trouvés\n", + " i=0\n", + " for parse in parser.parse(phrase.split()):\n", + " i+=1\n", + " if printDerivations:\n", + " chart.printCCGDerivation(parse)\n", + " \n", + " if printTotal:\n", + " print(i,phrase)\n", + " \n", + " \n", + " # On affiche la dérivation la meilleure pour l'arbre\n", + " if (i==0):\n", + " print(\"Pas de dérivation tout court :/\")\n", + " else:\n", + "\n", + " t,d = bestTree(phrase.split(), lex, chart.ApplicationRuleSet)\n", + " print(\"Found derivation tree with weight\",d)\n", + " chart.printCCGDerivation(t)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "? => (S\\S) {{}}\n", + "attrape => ((S\\N)/N) {\\n m.attrappe(m,n)}\n", + "avec => (((S\\N)\\(S\\N))/N) {\\n v.avec(v,n)}\n", + "chat => N {chat}\n", + "de => ((N/N)\\N) {\\n m.de(n,m)}\n", + "dents => N {dents}\n", + "donne => (S\\N) {\\n.donne(n)} | ((S\\N)/N) {\\n m.donne(n,m)}\n", + "donner => N {donner} | (N/N) {\\n.donner(n)}\n", + "donné => Pp {\\n.donné(n)}\n", + "dorment => (S\\N) {\\n.dormir(n)}\n", + "dort => (S\\N) {\\n.dormir(n)}\n", + "elle => N {elle}\n", + "est => ((S\\N)/Pp) {{}} | ((S\\N)/(N/N)) {{}} | ((S\\N)/(N\\N)) {{}}\n", + "et => ((N/N)\\N) {\\n m.et(n,m)} | ((S/S)\\S) {\\x y.et(x,y)} | (((S\\N)/(S\\N))\\(S\\N)) {\\v w n.et(v,w,n)}\n", + "fromage => N {fromage}\n", + "il => N {il}\n", + "la => ((S\\N)/((S\\N)/N)) {{}} | (N/N) {\\P.exists x.P(x)}\n", + "le => ((S\\N)/((S\\N)/N)) {{}} | (N/N) {\\P.exists x.P(x)}\n", + "lui => ((S\\N)/(S\\N)) {{}}\n", + "mange => (S\\N) {\\n.mange(n)} | ((S\\N)/N) {\\n m.mange(n,m)}\n", + "mangé => Pp {\\n.mangé(n)}\n", + "mangée => Pp {\\n.mangé(n)}\n", + "mon => (N/N) {\\P.exists x.P(x)}\n", + "méchant => (N/N) {\\n.méchant(n)} | (N\\N) {\\n.méchant(n)}\n", + "noir => (N\\N) {\\n.noir(n)} | (N/N) {\\n.noir(n)}\n", + "paisiblement => ((S\\N)\\(S\\N)) {\\v n.paisiblement(v,n)}\n", + "par => (((S\\N)\\(S\\N))/N) {{}}\n", + "pourchasse => ((S\\N)/N) {\\n m.pourchasse(m,n)}\n", + "que => ((N\\N)/S) {{}} | (N/S) {{}}\n", + "quel => (((((S\\N)\\(S\\N))/N)\\(S/S))/N) {{}} | ((S/(S\\N))/N) {{}}\n", + "quelle => (((((S\\N)\\(S\\N))/N)\\(S/S))/N) {{}} | ((S/(S\\N))/N) {{}}\n", + "qui => ((N\\N)/(S\\N)) {{}} | (S/(S\\N)) {{}}\n", + "quoi => (((S/S)\\(((S\\N)\\(S\\N))/N))/N) {{}}\n", + "rat => N {rat}\n", + "ses => (N/N) {\\P.exists x.P(x)}\n", + "souhaite => ((S\\N)/N) {\\n m.souhaite(m,n)}\n", + "souris => N {souris}\n", + "sœur => N {soeur}\n", + "très => ((N/N)/(N/N)) {{}}\n", + "un => (N/N) {\\P.exists x.P(x)}\n", + "voisin => N {voisin}\n", + "à => (((S\\N)\\(S\\N))/N) {\\n v.à(v,n)}\n" + ] + } + ], + "source": [ + "print(lex)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}