[人工智能-logic agent]Propositional Logic& First order Logic
Propositional Logic
一些术语
Knowledge Base(KB):已经确认为真的事情,可以理解为解题时的题目条件
world:真值表里的每一行都是一个world
entailment:区别于imply,虽然和imply有相同的真值表,但是entails表示对于任何model,(A entails B)如果A正确那么B也正确。而imply仅仅只对一个model而言
sound algorithm and complete algorithm
An algorithm is sound if, anytime it returns an answer, that answer is true. An algorithm is complete if it guarantees to return a correct answer for any arbitrary input
两种证明方式
伪代码
function PL-TRUE?(a,model) returns true or false
if a is a symbol then return Lookup(a, model)
if Op(a) = ¬ then return not(PL-TRUE?(Arg1(a),model))
if Op(a) = Ù then return and(PL-TRUE?(Arg1(a),model),
PL-TRUE?(Arg2(a),model))
etc.
构建expression
def sentence1():
"""Returns a Expr instance that encodes that the following expressions are all true.
A or B
(not A) if and only if ((not B) or C)
(not A) or (not B) or C
"""
A = Expr('A')
B = Expr('B')
C = Expr('C')
a_or_b = A | B
not_b_or_c = ~B | C
second = ~A % not_b_or_c
third = disjoin(~A,~B,C)
return conjoin(a_or_b, second, third)
CNF(合取范式)
形如 ( or )and ( or )
In Boolean logic, a formula is in conjunctive normal form (CNF)
or clausal normal form if it is a conjunction of one or
more clauses, where a clause is a disjunction of literals;
otherwise put, it is a product of sums or an AND of ORs.
REFERENCE:https://en.wikipedia.org/wiki/Conjunctive_normal_form
e.g.
A
A OR B
(A OR B) AND (C OR D)
CNF与正常人的想法有一些不同,我们平时思考用的是DNF,就是把各种情况枚举出来,但是实际上要将DNF转化成CNF还是很容易的
DNF 转 CNF 技巧
我们考虑否命题,列出所有情况,再取逆,注意到 NOT (A OR B) = (NOT A AND NOT B),把NOT放进去这样就直接转成CNF了
SAT solver
DPLL算法
1.early termination
如果所有clause都可以被满足或者任意一个clause不可以被满足则结束返回
2.pure literals
如果在所有未被满足的clause内,某一个symbol的sign相同,那么将能使剩余clause为True的值赋给那个symbol
3. unit clause
如果某个clause只剩下一个literal没有被赋值,那么将这个对应的symbol赋值是的该clause为True
e.g. 如果A为False,那么(A or B)and (A or not C) 变为 (B)and (not C)那么B和C 就可以直接赋值{B:T,C:F}
伪代码
function DPLL(clauses,symbols,model) returns true or false
if every clause in clauses is true in model then return true
if some clause in clauses is false in model then return false
P,value ←FIND-PURE-SYMBOL(symbols,clauses,model)
if P is non-null then return DPLL(clauses, symbols–P, model∪{P=value})
P,value ←FIND-UNIT-CLAUSE(clauses,model)
if P is non-null then return DPLL(clauses, symbols–P, model∪{P=value})
P ← First(symbols); rest ← Rest(symbols)
return or(DPLL(clauses,rest,model∪{P=true}),
DPLL(clauses,rest,model∪{P=false}))
Efficiency
§ Naïve implementation of DPLL: solve ~100 variable
利用logic agent定位
state estimation:跟踪当前正确的一些信息
对于logic agent, 可以直接询问他自己,调用SAT_SOLVER(KB AND CLAIM).KB为当前的knowledge base, claim为你想证明或证伪的statement. 如果我们证明了一件事,我们将这件事加入KB(KB.append(CLAIM)), 如果我们证伪了,我们将这件事情不会发生的信息加入KB(KB.append(CLAIM))
例子
比如说我们当前在一个迷宫里,我们知道迷宫的地图,但是我们不知道我们在哪里,我们感知到的信息为percept
迷宫里有多少位置满足呢?只有一个位置满足,所以我们知道了我们在迷宫中的具体位置。通常情况下需要在感知和操作来回多次才能确定自己本身的位置
Code
class LogicAgent(Agent):
"""
This very general logic agent finds a path using a supplied planning
algorithm for a supplied planning problem, then returns actions to follow that
path.
As a default, this agent runs positionLogicPlan on a
PositionPlanningProblem to find location (1,1)
Options for fn include:
positionLogicPlan or plp
foodLogicPlan or flp
foodGhostLogicPlan or fglp
Note: You should NOT change any code in LogicAgent
"""
def __init__(self, fn='positionLogicPlan', prob='PositionPlanningProblem', plan_mod=logicPlan):
# Warning: some advanced Python magic is employed below to find the right functions and problems
# Get the planning function from the name and heuristic
if fn not in dir(plan_mod):
raise AttributeError(fn + ' is not a planning function in logicPlan.py.')
func = getattr(plan_mod, fn)
self.planningFunction = lambda x: func(x)
# Get the planning problem type from the name
if prob not in globals().keys() or not prob.endswith('Problem'):
raise AttributeError(prob + ' is not a planning problem type in logicAgents.py.')
self.planType = globals()[prob]
self.live_checking = False
print('[LogicAgent] using problem type ' + prob)
def registerInitialState(self, state):
"""
This is the first time that the agent sees the layout of the game
board. Here, we choose a path to the goal. In this phase, the agent
should compute the path to the goal and store it in a local variable.
All of the work is done in this method!
state: a GameState object (pacman.py)
"""
if self.planningFunction == None:
raise Exception("No planning function provided for LogicAgent")
starttime = time.time()
problem = self.planType(state) # Makes a new planning problem
self.actions = [] # In case planningFunction times out
self.actions = self.planningFunction(problem) # Find a path
totalCost = problem.getCostOfActions(self.actions)
print('Path found with total cost of %d in %.1f seconds' % (totalCost, time.time() - starttime))
# TODO Drop
if '_expanded' in dir(problem):
print('Nodes expanded: %d' % problem._expanded)
def getAction(self, state):
"""
Returns the next action in the path chosen earlier (in
registerInitialState). Return Directions.STOP if there is no further
action to take.
state: a GameState object (pacman.py)
"""
# import ipdb; ipdb.set_trace()
if 'actionIndex' not in dir(self): self.actionIndex = 0
i = self.actionIndex
self.actionIndex += 1
if i < len(self.actions):
return self.actions[i]
else:
print('Oh no! The Pacman agent created a plan that was too short!')
print()
return None
# return Directions.STOP
class LocalizationLogicAgent(LocalizeMapAgent):
def __init__(self, fn='localization', prob='LocalizationProblem', plan_mod=logicPlan, display=None, scripted_actions=[]):
super(LocalizationLogicAgent, self).__init__(fn, prob, plan_mod, display, scripted_actions)
self.num_timesteps = len(scripted_actions) if scripted_actions else 5
def getAction(self, state):
"""
Returns the next action in the path chosen earlier (in
registerInitialState). Return Directions.STOP if there is no further
action to take.
state: a GameState object (pacman.py)
"""
# import ipdb; ipdb.set_trace()
if 'actionIndex' not in dir(self): self.actionIndex = 0
i = self.actionIndex
self.actionIndex += 1
planning_fn_output = None
if i < self.num_timesteps:
proposed_action = self.actions[i]
planning_fn_output = next(self.planning_fn_output)
self.drawPossibleStates(planning_fn_output, direction=self.actions[i])
elif i < len(self.actions):
proposed_action = self.actions[i]
else:
proposed_action = "EndGame"
return proposed_action, planning_fn_output
def moveToNextState(self, action):
oldX, oldY = self.state
dx, dy = Actions.directionToVector(action)
x, y = int(oldX + dx), int(oldY + dy)
if self.problem.walls[x][y]:
raise AssertionError("Taking an action that goes into wall")
pass
else:
self.state = (x, y)
self.visited_states.append(self.state)
def getPercepts(self):
x, y = self.state
north_iswall = self.problem.walls[x][y+1]
south_iswall = self.problem.walls[x][y-1]
east_iswall = self.problem.walls[x+1][y]
west_iswall = self.problem.walls[x-1][y]
return [north_iswall, south_iswall, east_iswall, west_iswall]
def getValidActions(self):
x, y = self.state
actions = []
if not self.problem.walls[x][y+1]: actions.append('North')
if not self.problem.walls[x][y-1]: actions.append('South')
if not self.problem.walls[x+1][y]: actions.append('East')
if not self.problem.walls[x-1][y]: actions.append('West')
return actions
def drawPossibleStates(self, possibleLocations=None, direction="North", pacman_position=None):
import __main__
self.display.clearExpandedCells() # Erase previous colors
self.display.colorCircleCells(possibleLocations, direction=direction, pacman_position=pacman_position)
def sensorAxioms(t: int, non_outer_wall_coords: List[Tuple[int, int]]) -> Expr:
all_percept_exprs = []
combo_var_def_exprs = []
for direction in DIRECTIONS:
percept_exprs = []
dx, dy = DIR_TO_DXDY_MAP[direction]
for x, y in non_outer_wall_coords:
combo_var = PropSymbolExpr(pacman_wall_str, x, y, x + dx, y + dy, time=t)
percept_exprs.append(combo_var)
combo_var_def_exprs.append(combo_var % (
PropSymbolExpr(pacman_str, x, y, time=t) & PropSymbolExpr(wall_str, x + dx, y + dy)))
percept_unit_clause = PropSymbolExpr(blocked_str_map[direction], time = t)
all_percept_exprs.append(percept_unit_clause % disjoin(percept_exprs))
return conjoin(all_percept_exprs + combo_var_def_exprs)
一个简单的logic plan
def positionLogicPlan(problem):
"""
Given an instance of a PositionPlanningProblem, return a list of actions that lead to the goal.
Available actions are ['North', 'East', 'South', 'West']
Note that STOP is not an available action.
"""
walls = problem.walls
width, height = problem.getWidth(), problem.getHeight()
walls_list = walls.asList()
x0, y0 = problem.startState
xg, yg = problem.goal
# Get lists of possible locations (i.e. without walls) and possible actions
all_coords = list(itertools.product(range(width + 2),
range(height + 2)))
non_wall_coords = [loc for loc in all_coords if loc not in walls_list]
actions = [ 'North', 'South', 'East', 'West' ]
KB = []
"*** BEGIN YOUR CODE HERE ***"
KB.append(PropSymbolExpr(pacman_str, x0, y0, 0))
for t in range(50):
#pacman can only be at one location
props = []
for (x,y) in non_wall_coords:
props.append(PropSymbolExpr(pacman_str, x, y, t))
KB.append(exactlyOne(props))
#is there a satisfying model assignment?
knowledge_base = conjoin(KB)
model = findModel(knowledge_base & PropSymbolExpr(pacman_str, xg, yg, t))
if model:
return extractActionSequence(model, actions)
#pacman takes exactly one position per time step
KB.append(exactlyOne([PropSymbolExpr('North', t), PropSymbolExpr('South', t), PropSymbolExpr('East', t), PropSymbolExpr('West', t)]))
#add transition models to KB
for (x,y) in non_wall_coords:
KB.append(pacmanSuccessorStateAxioms(x, y, t+1, walls, pacman_str))
First Order Logic
first order logic 拥有比propositional logic更加优秀的表达能力,以pacman游戏为例,我们在时间t没死等价于我们在t-1时间没死并且我们在t-1时的位置没有和任何鬼重合。如果用propositional logic需要用几千页来表达
一些关键词
term
atomic sentence
complex sentences
注意!!!first order logic 只在variable上使用quantifier,如果是在predicate上使用的话就变成了second order logic
如何解FOL