% This file is part of the Attempto Parsing Engine (APE). % Copyright 2008-2013, Kaarel Kaljurand . % % The Attempto Parsing Engine (APE) is free software: you can redistribute it and/or modify it % under the terms of the GNU Lesser General Public License as published by the Free Software % Foundation, either version 3 of the License, or (at your option) any later version. % % The Attempto Parsing Engine (APE) is distributed in the hope that it will be useful, but WITHOUT % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR % PURPOSE. See the GNU Lesser General Public License for more details. % % You should have received a copy of the GNU Lesser General Public License along with the Attempto % Parsing Engine (APE). If not, see http://www.gnu.org/licenses/. :- module(drs_to_owldrs, [ drs_to_owldrs/2 ]). :- use_module(ape('utils/drs_ops'), [ unary_drs_operator/1 ]). /** DRS to OWL DRS converter The DRS is converted to "OWL DRS" which is a more suitable format for the conversion into OWL. The input must be simplified DRS (drs_to_sdrs.pl) which has been numbervard. The following steps are made: 1. Rewrite implication chains: A => (B => C) ~~> (A & B) => C. 2. Rewrite embedded implications via conjunction and negations: A => B ~~> -(A & -B) 3. Remove double negations: --A ~~> A 4. Remove toplevel be-predicate-conditions 5. Remove named-object-conditions, unify the corresponding discourse referents with named(ProperName) 6. Rewrite certain conditions and sets of conditions: * transitive (_|located-in|_) and comparative (_|taller than|_) adjectives are turned into transitive verbs, * relation/3 + be-predicate are used to construct data properties, * otherwise, relation/3 is used to construct object properties. * "which man" is turned into "what is a man" * Map attributive and predicative positive adjectives (property/3) to nouns (object/6). Supporting comparative and superlative ("richer man", "richest man") probably doesn't make sense as these require more background semantics. @author Kaarel Kaljurand @version 2013-04-07 */ % Operators used in the DRS. :- op(400, fx, -). :- op(500, xfx, =>). :- op(500, xfx, v). %% drs_to_owldrs(+Drs:term, -OwlDrs:term) is det. % % Modifies the DRS to make it easier to convert in to OWL/SWRL. % % @param Drs is Attempto DRS % @param OwlDrs is a modified Attempto DRS % drs_to_owldrs(Drs, OwlDrs) :- drs_to_owldrs(0, Drs, DrsTmp), exclude(is_named(DrsTmp), DrsTmp, OwlDrs). %% drs_to_owldrs(+Level:term, +Drs:term, -OwlDrs:term) is det. % % Modifies the DRS to make it easier to convert in to OWL/SWRL. % The embedding-level is 0 for the top-level DRS-box, and grows % by 0, l(0), l(l(0)), ... % % @param Level denotes the level of embedding of the DRS-box % @param Drs is Attempto DRS % @param OwlDrs is a modified Attempto DRS % drs_to_owldrs(_, [], []) :- !. % Double negation is removed % --A == A drs_to_owldrs(L, [-Drs | Conds], CondsAll) :- drs_to_owldrs(l(L), Drs, [-DrsR]), !, drs_to_owldrs(L, Conds, CondsR), append(DrsR, CondsR, CondsAll). drs_to_owldrs(L, [Drs | Conds], [NDrs | NConds]) :- functor(Drs, Op, 1), unary_drs_operator(Op), !, arg(1, Drs, DrsConds), drs_to_owldrs(l(L), DrsConds, NDrsConds), functor(NDrs, Op, 1), arg(1, NDrs, NDrsConds), drs_to_owldrs(L, Conds, NConds). % Handle sentences like: % For every thing X for every thing Y if X owns something that contains Y then X owns Y. % BUG: maybe allow this for any level, i.e. not just 0 % BUG: this new handling changes the OWL representation of sentences 41, 115, 116, 117. % Also, it makes sentences 66 and 67 correctly succeed as SRWL rules. % Also, it would break sentence 57 "Every man likes no dog.", but we avoid this % by requiring the embedded then-box not to be negated. % But, then we reject this: For everything X for every thing Y if X likes Y then X does not hate Y. % (although this could be mapped to property disjointness axiom). % % BUG: Think about all this in a more systematic way. (The rewriting is definitely % correct, but it might harm completeness.) drs_to_owldrs(0, [IfBox => [EmbeddedIfBox => EmbeddedThenBox] | Conds], DrsR) :- EmbeddedThenBox \= [-_], !, append(IfBox, EmbeddedIfBox, MergedIfAndEmbeddedIfBox), drs_to_owldrs(0, [MergedIfAndEmbeddedIfBox => EmbeddedThenBox | Conds], DrsR). % % Toplevel implications are kept as they are. % drs_to_owldrs(0, [Drs1 => Drs2 | Conds], [Drs1R => Drs2R | CondsR]) :- !, drs_to_owldrs(l(0), Drs1, Drs1R), drs_to_owldrs(l(0), Drs2, Drs2R), drs_to_owldrs(0, Conds, CondsR). /* % This is handled by the double negation removal rule. % A => -B == -(A & B) drs_to_owldrs(l(L), [IfBox => [-ThenBox] | Conds], DrsR) :- !, append(IfBox, ThenBox, IfThenBox), drs_to_owldrs(l(L), [-IfThenBox | Conds], DrsR). */ % Embedded implications are turned into double negations. % A => B == -(A & -B) drs_to_owldrs(l(L), [IfBox => ThenBox | Conds], DrsR) :- !, drs_to_owldrs(l(L), [-[-ThenBox | IfBox] | Conds], DrsR). drs_to_owldrs(L, [DRS1 v DRS2 | Conds], [DRS1R v DRS2R | CondsR]) :- !, drs_to_owldrs(l(L), DRS1, DRS1R), drs_to_owldrs(l(L), DRS2, DRS2R), drs_to_owldrs(L, Conds, CondsR). % list condition that expresses 'at most', 'less than', 'exactly' drs_to_owldrs(L, [List | Conds], [ListR | CondsR]) :- is_list(List), !, drs_to_owldrs(L, List, ListR), drs_to_owldrs(L, Conds, CondsR). drs_to_owldrs(L, Conds, CondsR) :- rewrite_relation(Conds, FilteredConds), drs_to_owldrs(L, FilteredConds, CondsR). % (Fallback) drs_to_owldrs(L, [Cond | Conds], [Cond | CondsR]) :- drs_to_owldrs(L, Conds, CondsR). % of-constructions with data items % % John's age is 30. % John's address is "Poland". % % BUG: those should be rejected I guess because data properties % cannot have inverses. (This can be done later, by % not allowing a data object to be a subject.) % % 30 is an age of John. % "Poland" is an address of John. % % Note that in addition to relation/3 we also remove the object/6. % rewrite_relation(Conds, [predicate(C, PropertyName, A, DataItem)-SId/'' | FilteredConds]) :- remove_many([ predicate(C, be, D1, DataItem)-SId/_, object(D2, PropertyName, countable, na, eq, 1)-SId/_, relation(D3, of, A)-SId/_ ], Conds, FilteredConds), D1 == D2, D2 == D3, is_dataitem(DataItem), !. rewrite_relation(Conds, [predicate(C, PropertyName, A, DataItem)-SId/'' | FilteredConds]) :- remove_many([ predicate(C, be, DataItem, D1)-SId/_, object(D2, PropertyName, countable, na, eq, 1)-SId/_, relation(D3, of, A)-SId/_ ], Conds, FilteredConds), D1 == D2, D2 == D3, is_dataitem(DataItem), !. % of-constructions without data items % % We handle differently the sentences with copula and without copula. % The following sentences % % John's father is Bill. % Bill is a father of John. % % will be represented as: % % PropertyAssertion(ObjectProperty(father), Individual(John), Individual(Bill)) % rewrite_relation(Conds, [predicate(_, RelationalNoun, A, B)-SId/'' | FilteredConds]) :- remove_many([ predicate(_, be, D1, B)-SId/_, object(D2, RelationalNoun, countable, na, eq, 1)-SId/_, relation(D3, of, A)-SId/_ ], Conds, FilteredConds), D1 == D2, D2 == D3, !. rewrite_relation(Conds, [predicate(_, RelationalNoun, A, B)-SId/'' | FilteredConds]) :- remove_many([ predicate(_, be, B, D1)-SId/_, object(D2, RelationalNoun, countable, na, eq, 1)-SId/_, relation(D3, of, A)-SId/_ ], Conds, FilteredConds), D1 == D2, D2 == D3, !. % But the following sentences % % John's father likes Bill. % Bill likes John's father. % % will be handled by keeping the object/6 in the returned list. % % PropertyAssertion(ObjectProperty(father), Individual(John), nodeID($VAR(2))) % ClassAssertion(Class(owl:Thing), nodeID($VAR(2))) % % Some other tests: % % - John's brother likes everybody. % - John's brother likes Mary. An age of the brother is 10. % - Everybody's age is 31. (SWRL? Currently buggy.) % - Everybody's address is "Poland". (SWRL? Currently buggy.) % - Everybody's ancestor is Adam. % - Everybody's ancestor is not Adam. % - Every part of EU is a country. % % Note that one should be able to derive from "Every part of EU is a country. Estonia is a part of EU." % that "Estonia is a country." without having to assert that "Estonia is a part." % To make it possible we currently do not create a class "part", i.e. relational nouns % only create properties. % % We used to return: % %== % object(D, RelationalNoun, countable, na, eq, 1)-Id %== % % But now we return: % %== % object(D, something, dom, na, na, na)-Id %== % rewrite_relation(Conds, [ predicate(_, RelationalNoun, A, D1)-SId/'', object(D1, something, dom, na, na, na)-SId/'' | FilteredConds]) :- remove_many([ object(D1, RelationalNoun, countable, na, eq, 1)-SId/_, relation(D2, of, A)-SId/_ ], Conds, FilteredConds), D1 == D2. % predicate/4 % % Transitive and comparative adjectives (with arguments) % are mapped to transitive verbs. This is debatable (and not reversible). % BUG: We do not currently check the Degree of property/4. % Note that it can be one of {pos, pos_as, comp, comp_than, sup} % The outcome is that "John is as rich as Mary." == "John is richer than Mary." % which is not wanted. I.e. one should use different property names and additionally % assert disjointness of the properties. % % Examples: % % John is fond-of Mary. # pos % England is located-in UK. # pos % John is as rich as Mary. # pos_as % John is fonder-of Mary. # comp % John is taller than Mary. # comp_than % John is more rich than Mary. # comp_than % John is fondest-of Mary. # sup rewrite_relation(Conds, [predicate(RefPred, PropertyName, RefArg1, RefArg2)-SId/'' | FilteredConds]) :- remove_many([ predicate(RefPred, be, RefArg1, RefRel1)-SId/_, property(RefRel2, PropertyName, _Degree, RefArg2)-SId/_ ], Conds, FilteredConds), RefRel1 == RefRel2. % Which man ... ? --> What is a man that ... ? % Turns plural into singular. % BUG: turns mass into countable (can we have 'mass' with 'which' at all?) % % BUG: does not work: Which member of Attempto is a productive-developer? rewrite_relation(Conds, [ query(NewRef1, QueryWord)-SId/'', object(WhichRef1, Name, countable, na, eq, 1)-SId/'', predicate(_, be, NewRef1, WhichRef1)-SId/'' | FilteredConds]) :- remove_many([ query(WhichRef1, QueryWord)-SId/_, object(WhichRef2, Name, _, na, _, _)-SId/_ ], Conds, FilteredConds), WhichRef1 == WhichRef2. % Attributive positive adjectives % E.g. Every man likes a red cat. -> Every man likes a cat that is a red. rewrite_relation(Conds, [ object(Ref1, Noun, Count, na, Eq, Num)-SId/TId, object(NewRef, Adj, countable, na, eq, 1)-SId/'', predicate(_, be, Ref1, NewRef)-SId/'' | FilteredConds]) :- remove_many([ object(Ref1, Noun, Count, na, Eq, Num)-SId/TId, property(Ref2, Adj, pos)-SId/_ ], Conds, FilteredConds), Ref1 == Ref2. % Predicative positive adjectives % E.g. John is rich. -> John is a rich. rewrite_relation(Conds, [ object(Ref1, Adj, countable, na, eq, 1)-SId/'', predicate(PRef, be, Arg1, Ref1)-SId/TId | FilteredConds]) :- remove_many([ property(Ref1, Adj, pos)-SId/_, predicate(PRef, be, Arg1, Ref2)-SId/TId ], Conds, FilteredConds), Ref1 == Ref2. %% remove_many(+ConditionsToRemove:list, +AllConditions:list, RemainingConditions:list) is det. % % remove_many([], Conds, Conds). remove_many([H | T], Conds, FilteredConds) :- select(H, Conds, NewConds), remove_many(T, NewConds, FilteredConds). %% is_dataitem(+DataItem:term) is det. % % Succeeds if DataItem is an ACE int, real, or string. % % @param DataItem is an ACE data item (a number or a string) % is_dataitem(X) :- nonvar(X), is_dataitem_(X). is_dataitem_(int(_)). is_dataitem_(real(_)). is_dataitem_(string(_)). %% is_named(+CondList:list, +Condition:term) % % This is used for excluding certain conditions % in the top-level DRS. % % @bug is this still needed? % is_named(CondList, predicate(_, be, X, Y)-_) :- member(query(A, _)-_, CondList), (A == X ; A == Y), !, fail. % Filters out: % John is a man. % A man is a manager. % A man is John. % Note that the rule is so complex because we want to % avoid filtering out numbers, strings, and the like. /* is_named(_, predicate(_, be, X, Y)-_) :- var(X), var(Y), !, X = Y. */ % This filters out: John is John. (probably not wanted) %is_named(_, predicate(_, be, named(N), named(N))-_).