-
Notifications
You must be signed in to change notification settings - Fork 0
/
Solver.hs
139 lines (122 loc) · 5.93 KB
/
Solver.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
{-# LANGUAGE OverloadedStrings #-}
{-|
Module: Solver
Description: Main execution module for CNP programs. Provides the solveBool method which takes
an NExecution, and returns true if the given goal follows from the program given in the execution directive.
Together with the Unification module, it provides an implementation of a Resolution Search.
-}
module Solver (solveBool
,varNamesInLits -- for tests
,resolveClauses -- for tests
,renameDuplicateVariables -- for tests
,groundClauseExhaustively -- for tests
,ResolutionResult(..) -- for tests
) where
import Unification
import Flattening
import Saturation
import Language
import Data.List (find, (\\), nub, intersect, sort, any)
import Text.PrettyPrint.HughesPJClass
-- to turn on console debugging
-- import Debug.Trace
-- to turn off console debugging
-- trace _ a = a
{-| Represents the result of a resolution attempt. -}
data ResolutionResult = ResolvesToFalse
| ResolvesToTrue
| ResolvesToClause {resolvent::CFDisjunction}
| DoesNotResolve
deriving (Eq, Show)
-- | Returns true if the given goal (in execution) follows from the given program.
solveBool :: NExecution -> Bool
solveBool nExec =
let cfProg = flatten $ saturate nExec
kb = sort $ map groundClauseExhaustively (disjunctions $ cfKnowledgeBase cfProg)
uGoal = groundClauseExhaustively $ cfUserGoal cfProg
in not $ goalFollowsFromKB kb uGoal
-- (trace ("KB:\n" ++ (prettyShow kb) ++ "\n") kb)
-- (trace ("GOAL:" ++ (prettyShow uGoal) ++ "\n") uGoal)
isTrueClause :: CFDisjunction -> Bool
isTrueClause disj = any ((==) literalTrue) (literals disj)
goalFollowsFromKB :: [CFDisjunction] -> CFDisjunction -> Bool
goalFollowsFromKB kb gl =
let allResolutionAttempts = [(resolveClauses gl b) | b<-kb]
falseResolventsNr = length $ filter (\u -> u == ResolvesToFalse) allResolutionAttempts
newResolventsUnGr = [c | ResolvesToClause c <- allResolutionAttempts]
newResolventsGr = map groundClauseExhaustively newResolventsUnGr
in if isTrueClause gl then True
else if gl == Disjunction [] then True
else if not (falseResolventsNr == 0) then False
else let forks = map (goalFollowsFromKB (gl:kb)) newResolventsGr
in not $ any ((==) False) forks
instance Pretty ResolutionResult where
pPrint (ResolvesToClause c) = text ("ResolvesTo ") <> pPrint c
pPrint o = text (show o)
-- | attempts to resolve two clauses using the resolution rule. unifies on the first
-- found unifying literal only.
resolveClauses :: CFDisjunction -> CFDisjunction -> ResolutionResult
resolveClauses (Disjunction []) _ = error "empty disjunction as #1 of resolveClauses"
resolveClauses _ (Disjunction []) = error "empty disjunction as #2 of resolveClauses"
resolveClauses (Disjunction leftLitsUnsafe) (Disjunction rightLits) =
let leftLits = renameDuplicateVariables leftLitsUnsafe rightLits
allUniAttempts = [(l1, l2, unifyLiterals l1 l2) | l1 <- leftLits, l2 <- rightLits]
firstUnifying = find (\(_,_,u) -> not (u == DoNotUnify)) allUniAttempts
in case firstUnifying of
Nothing -> DoesNotResolve
Just (l1, l2, uResult) ->
let litsExceptResolved = (leftLits \\ [l1]) ++ (rightLits \\ [l2])
in if null litsExceptResolved then ResolvesToFalse else
case uResult of
UnifyWith subs -> ResolvesToClause $ Disjunction $ applySubstitutionsToLiterals subs litsExceptResolved
-- | Applies a repetitive search until none of the literals
-- of a disjunction unifies with ground atoms.
groundClauseExhaustively :: CFDisjunction -> CFDisjunction
groundClauseExhaustively disj =
Disjunction $ groundLiteralsExhaustively $ literals disj
-- | Repeats unifying if at least one literal unifies with
-- a ground predicate. When no literal unifies, returns the
-- remaining list of literals, modulo substitutions.
groundLiteralsExhaustively :: [CFLiteral] -> [CFLiteral]
groundLiteralsExhaustively [] = []
groundLiteralsExhaustively literals =
let firstUnification = unifyOneLiteralWithGround literals
in case firstUnification of
(DoNotUnify, _) -> literals -- none of the literals could be ground.
(UnifyWith subs, litsRemaining) ->
let newLits = applySubstitutionsToLiterals subs litsRemaining
in groundLiteralsExhaustively newLits
(UnifyAsFalse, litsNew) -> groundLiteralsExhaustively litsNew
(UnifyAsTrue, _) -> [literalTrue]
-- | renames conflicting variables in leftLits to [AA..ZZ] so none occur in rightLits.
renameDuplicateVariables :: [CFLiteral] -> [CFLiteral] -> [CFLiteral]
renameDuplicateVariables leftLits rightLits =
let candidateVarNames2 = [[x,y]|x<-['A'..'Z'], y<-['A'..'Z']]
varsInLeft = varNamesInLits leftLits
varsInRight = varNamesInLits rightLits
varsCommon = intersect varsInLeft varsInRight
varsFresh = candidateVarNames2 \\ (varsInLeft ++ varsInRight)
substitutions = [Substitution a (VarTerm b) | (a,b) <- zip varsCommon varsFresh]
newLiteralsLeft = applySubstitutionsToLiterals substitutions leftLits
in if (length substitutions < length varsCommon)
then error "While renaming duplicate variables, ran out of variable names in range 'AA'..'ZZ'."
else newLiteralsLeft
-- | returns the complete unique list of variable names in a list of literals.
varNamesInLits :: [CFLiteral] -> [String]
varNamesInLits lits =
let allAtoms = map atom lits
allTerms = concat $ map atomTerms allAtoms
in nub $ concat $ map varNamesInTerm allTerms
-- | returns the variable names in a term.
varNamesInTerm :: CFTerm -> [String]
varNamesInTerm term =
case term of
(ConstantTerm _) -> []
(VarTerm v) -> [show term]
(ConsTerm c) -> varNamesInCons c
-- | returns the variable names in a cons list, recursively.
varNamesInCons :: CFCons -> [String]
varNamesInCons c =
case c of
ConsNil -> []
Cons h t -> (varNamesInTerm h) ++ (varNamesInCons t)