逻辑建模
在讲解逻辑智能体之前,首先让我们用Python实现对基本逻辑符号、语句的建模。
1.Expr类:将输入(“x",“2”)等转换为智能体可以理解的命题符号,其中expr函数实现了将(“==>”)转换为(“>>”)的功能,以求将逻辑连接词标准化。
class Expr:
"""A symbolic mathematical expression. We use this class for logical
expressions, and for terms within logical expressions. In general, an
Expr has an op (operator) and a list of args. The op can be:
Null-ary (no args) op:
A number, representing the number itself. (e.g. Expr(42) => 42)
A symbol, representing a variable or constant (e.g. Expr('F') => F)
Unary (1 arg) op:
'~', '-', representing NOT, negation (e.g. Expr('~', Expr('P')) => ~P)
Binary (2 arg) op:
'>>', '<<', representing forward and backward implication
'+', '-', '*', '/', '**', representing arithmetic operators
'<', '>', '>=', '<=', representing comparison operators
'<=>', '^', representing logical equality and XOR
N-ary (0 or more args) op:
'&', '|', representing conjunction and disjunction
A symbol, representing a function term or FOL proposition
Exprs can be constructed with operator overloading: if x and y are Exprs,
then so are x + y and x & y, etc. Also, if F and x are Exprs, then so is
F(x); it works by overloading the __call__ method of the Expr F. Note
that in the Expr that is created by F(x), the op is the str 'F', not the
Expr F. See http://www.python.org/doc/current/ref/specialnames.html
to learn more about operator overloading in Python.
WARNING: x == y and x != y are NOT Exprs. The reason is that we want
to write code that tests 'if x == y:' and if x == y were the same
as Expr('==', x, y), then the result would always be true; not what a
programmer would expect. But we still need to form Exprs representing
equalities and disequalities. We concentrate on logical equality (or
equivalence) and logical disequality (or XOR). You have 3 choices:
(1) Expr('<=>', x, y) and Expr('^', x, y)
Note that ^ is bitwose XOR in Python (and Java and C++)
(2) expr('x <=> y') and expr('x =/= y').
See the doc string for the function expr.
(3) (x % y) and (x ^ y).
It is very ugly to have (x % y) mean (x <=> y), but we need
SOME operator to make (2) work, and this seems the best choice.
WARNING: if x is an Expr, then so is x + 1, because the int 1 gets
coerced to an Expr by the constructor. But 1 + x is an error, because
1 doesn't know how to add an Expr. (Adding an __radd__ method to Expr
wouldn't help, because int.__add__ is still called first.) Therefore,
you should use Expr(1) + x instead, or ONE + x, or expr('1 + x').
"""
def __init__(self, op, *args):
"Op is a string or number; args are Exprs (or are coerced to Exprs)."
assert isinstance(op, str) or (isnumber(op) and not args)
self.op = num_or_str(op)
self.args = tuple(map(expr, args)) ## Coerce args to Exprs
if not args and not is_prop_symbol(self.op):
raise SyntaxError("Unacceptable symbol base name (%s). Name must start with an upper-case alphabetic character that and is not TRUE or FALSE." % self.op)
def __call__(self, *args):
"""Self must be a symbol with no args, such as Expr('F'). Create a new
Expr with 'F' as op and the args as arguments."""
assert is_symbol(self.op) and not self.args
return Expr(self.op, *args)
def __repr__(self):
"Show something like 'P' or 'P(x, y)', or '~P' or '(P | Q | R)'"
if not self.args: # Constant or proposition with arity 0
return str(self.op)
elif is_symbol(self.op): # Functional or propositional operator
return '%s(%s)' % (self.op, ', '.join(map(repr, self.args)))
elif len(self.args) == 1: # Prefix operator
return self.op + repr(self.args[0])
else: # Infix operator
return '(%s)' % (' '+self.op+' ').join(map(repr, self.args))
def __eq__(self, other):
"""x and y are equal iff their ops and args are equal."""
return (other is self) or (isinstance(other, Expr)
and self.op == other.op and self.args == other.args)
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
"Need a hash method so Exprs can live in dicts."
return hash(self.op) ^ hash(tuple(self.args))
# See http://www.python.org/doc/current/lib/module-operator.html
# Not implemented: not, abs, pos, concat, contains, *item, *slice
def __lt__(self, other): return Expr('<', self, other)
def __le__(self, other): return Expr('<=', self, other)
def __ge__(self, other): return Expr('>=', self, other)
def __gt__(self, other): return Expr('>', self, other)
def __add__(self, other): return Expr('+', self, other)
def __sub__(self, other): return Expr('-', self, other)
def __and__(self, other): return Expr('&', self, other)
def __div__(self, other): return Expr('/', self, other)
def __truediv__(self, other):return Expr('/', self, other)
def __invert__(self): return Expr('~', self)
def __lshift__(self, other): return Expr('<<', self, other)
def __rshift__(self, other): return Expr('>>', self, other)
def __mul__(self, other): return Expr('*', self, other)
def __neg__(self): return Expr('-', self)
def __or__(self, other): return Expr('|', self, other)
def __pow__(self, other): return Expr('**', self, other)
def __xor__(self, other): return Expr('^', self, other)
def __mod__(self, other): return Expr('<=>', self, other)
2.PropSymbolExpr类:将人类表达的语言,例如:(bob, x,y,t)(bob在t时刻位于x,y位置,转换为智能体可以理解的命题符号。
class PropSymbolExpr(Expr):
"""An extension of Expr intended to represent a symbol. This SymbolExpr
is a convenience for naming symbols, especially symbols whose names
indicate an indexed value (e.g. Position[x,y] or Fluent[t]).
Symbol name must begin with a capital letter. This class helps to add
brackets with enumerated indices to the end of the name.
"""
def __init__(self, sym_str, *index):
"""Constructor taking a propositional logic symbol name and an optional set of index values,
creating a symbol with the base name followed by brackets with the specific
indices.
sym_str: String representing base name for symbol. Must begin with a capital letter.
Examples:
>>> red = PropSymbolExpr("R")
>>> print(red)
R
>>> turnLeft7 = PropSymbolExpr("Left",7)
>>> print(turnLeft7)
Left[7]
>>> pos_2_3 = PropSymbolExpr("P",2,3)
>>> print(pos_2_3)
P[2,3]
"""
if not is_prop_symbol(sym_str):
raise SyntaxError("Unacceptable symbol base name (%s). Name must start with an upper-case alphabetic character that and is not TRUE or FALSE." % sym_str)
self.sym_str = sym_str
self.indicies = index
if len(index) == 0:
Expr.__init__(self, sym_str)
elif len(index) == 1:
Expr.__init__(self, '%s[%d]' % (sym_str, index[0]))
elif len(index) == 2:
Expr.__init__(self, '%s[%d,%d]' % (sym_str, index[0], index[1]))
elif len(index) == 3:
Expr.__init__(self, '%s[%d,%d,%d]' % (sym_str, index[0], index[1], index[2]))
elif len(index) == 4:
Expr.__init__(self, '%s[%d,%d,%d,%d]' % (sym_str, index[0], index[1], index[2], index[3]))
elif len(index) == 5:
Expr.__init__(self, '%s[%d,%d,%d,%d,%d]' % (sym_str, index[0], index[1], index[2], index[3], index[4]))
else:
raise SyntaxError("Too many arguments to SymbolExpr constructor. SymbolExpr(symbol_str, [index1], [index2], [index3]")
def getBaseName(self):
return self.sym_str
def getIndex(self):
return self.indicies
3.to_cnf()函数,将语句s转换为CNF合取范式,CNF的意思即为几个由“或”相连的字句,通过“与”连接,如:(A | B)&(~A | ~B)。
代码较为复杂,运用了递归求解,可参考资源中to_cnf。
4.pycoSAT()函数,调用了pycosat模块求解可满足语句的模型,即求出语句中命题符号哪些取值可以使得语句为真,若无解,则返回False。具体代码参考资源中pycoSAT。