182 KiB
182 KiB
In [1]:
class Form(frozenset):
def __str__(self):
# Because frozenset is immutable, and the contents are all string or frozenset,
# we can cache the string repr of a form.
try:
return self._str
except AttributeError:
self._str = '(%s)' % ' '.join(sorted(map(str, self)))
return self._str
__repr__ = __str__
def F(*terms):
'''Create a Form from terms.'''
return Form([
term if isinstance(term, (basestring, Form)) else F(*term)
for term in terms
])In [2]:
a, b, c = 'abc'In [3]:
A = F(a, b, c)
AOut [3]:
(a b c)
In [4]:
B = F(a, (b, (c,)))
BOut [4]:
(((c) b) a)
In [5]:
C = F((a, b, c))
COut [5]:
((a b c))
In [6]:
F(a, (b,), a, (b,))Out [6]:
((b) a)
In [7]:
F(b, a, c) == F(a, b, c)Out [7]:
True
In [8]:
F(A, (B, (C,)), a)Out [8]:
(((((a b c))) (((c) b) a)) (a b c) a)
In [9]:
Mark = F()
MarkOut [9]:
()
In [10]:
Void = F(Mark)
VoidOut [10]:
(())
In [11]:
env = dict(a=Mark, b=Mark, c=Mark)In [12]:
def reify(form, environment):
if isinstance(form, basestring):
return environment.get(form, form)
return Form(reify(inner, environment) for inner in form)In [13]:
for form in (A, B, C):
print form, u'⟶', reify(form, env)(a b c) ⟶ (()) (((c) b) a) ⟶ (((()) ()) ()) ((a b c)) ⟶ ((()))
In [14]:
def void(form):
return any(not void(i) for i in form)In [15]:
def value_of(form, m=Mark, v=Void):
return (m, v)[void(form)]In [16]:
for form in (A, B, C):
arith = reify(form, env)
print form, u'⟶', arith, u'⟶', value_of(arith)(a b c) ⟶ (()) ⟶ (()) (((c) b) a) ⟶ (((()) ()) ()) ⟶ (()) ((a b c)) ⟶ ((())) ⟶ ()
In [17]:
from itertools import product, izip
BASE = Void, Mark
def environments_of_variables(*variables):
universe = [BASE] * len(variables)
for values in product(*universe):
yield dict(izip(variables, values))
envs = list(environments_of_variables(*'abc'))
envsOut [17]:
[{'a': (()), 'b': (()), 'c': (())},
{'a': (()), 'b': (()), 'c': ()},
{'a': (()), 'b': (), 'c': (())},
{'a': (()), 'b': (), 'c': ()},
{'a': (), 'b': (()), 'c': (())},
{'a': (), 'b': (()), 'c': ()},
{'a': (), 'b': (), 'c': (())},
{'a': (), 'b': (), 'c': ()}]In [18]:
def format_env(env, m='()', v=' '):
return ' '.join((v, m)[not env[k]] for k in sorted(env))
# Note that Mark is an empty frozenset so in a Boolean context in Python it is False,
# likewise Void is a set with one member, so Python considers it True in a Boolean context.
# The `not` in the expression is just to force such a Boolean context, and we compensate
# by putting `v` in the zero-is-False position in the indexed tuple.In [19]:
print 'i a b c i in Binary'
for i, env in enumerate(envs):
print i, format_env(env, v='--'), '%3s' % (bin(i)[2:],)i a b c i in Binary 0 -- -- -- 0 1 -- -- () 1 2 -- () -- 10 3 -- () () 11 4 () -- -- 100 5 () -- () 101 6 () () -- 110 7 () () () 111
In [20]:
print B
print '-----------'
for i, env in enumerate(envs):
e = reify(B, env)
print i, format_env(env, v='--'), u'⟶', e, u'⟶', value_of(e, m='()', v='')(((c) b) a) ----------- 0 -- -- -- ⟶ ((((())) (())) (())) ⟶ () 1 -- -- () ⟶ (((())) (())) ⟶ 2 -- () -- ⟶ ((((())) ()) (())) ⟶ () 3 -- () () ⟶ (((()) ()) (())) ⟶ () 4 () -- -- ⟶ ((((())) (())) ()) ⟶ 5 () -- () ⟶ (((())) ()) ⟶ 6 () () -- ⟶ ((((())) ()) ()) ⟶ 7 () () () ⟶ (((()) ()) ()) ⟶
In [21]:
def truth_table_3(expression):
print expression
print ' a b c | Value'
print '---------+------'
for E in envs:
e = reify(expression, E)
print format_env(E), '|', value_of(e, m='()', v='')In [22]:
truth_table_3(B)(((c) b) a)
a b c | Value
---------+------
| ()
() |
() | ()
() () | ()
() |
() () |
() () |
() () () |
In [23]:
nor = lambda *bits: F(*bits)
or_ = lambda *bits: F(bits)
and_ = lambda *bits: Form(F(bit) for bit in bits)
nand = lambda *bits: nor(and_(*bits))
nxor = eqiv = lambda a, b: F((a, (b,)), ((a,), b))
xor = lambda a, b: F(nxor(a, b))
# To build logical expressions with Void as Boolean True use these functions.
anti_nor = nand
anti_or = and_
anti_and = or_
anti_nand = nor
anti_eqiv = xor
anti_xor = eqivIn [24]:
a, b, c = 'abc'
some_expressions = (
nor(a, b, c),
or_(a, b, c),
and_(a, b, c),
nand(a, b, c),
xor(a, b),
eqiv(a, b),
xor(a, xor(b, c)),
)
for expression in some_expressions:
print expression(a b c) ((a b c)) ((a) (b) (c)) (((a) (b) (c))) ((((a) b) ((b) a))) (((a) b) ((b) a)) ((((((((b) c) ((c) b)))) a) (((((b) c) ((c) b))) (a))))
In [25]:
def yield_variables_of(expression):
'''Yield all string members of an expression.'''
if isinstance(expression, basestring):
yield expression
else:
for inner in expression:
for leaf in yield_variables_of(inner):
yield leaf
def collect_names(expression):
'''Return a set of the variables mentioned in an expression.'''
return set(yield_variables_of(expression))
def truth_table(expression):
'''Print a truth table for an expression.'''
names = sorted(collect_names(expression))
header = ' ' + ' '.join(names)
n = 1 + len(header)
header += ' | Value'
print expression
print header
print '-' * n + '+------'
for env in environments_of_variables(*names):
e = reify(expression, env)
print format_env(env), '|', ['()', ''][void(e)]In [26]:
truth_table(nor(a, b, c))(a b c)
a b c | Value
---------+------
| ()
() |
() |
() () |
() |
() () |
() () |
() () () |
In [27]:
truth_table(or_(a, b, c))((a b c))
a b c | Value
---------+------
|
() | ()
() | ()
() () | ()
() | ()
() () | ()
() () | ()
() () () | ()
In [28]:
truth_table(and_(a, b, c))((a) (b) (c))
a b c | Value
---------+------
|
() |
() |
() () |
() |
() () |
() () |
() () () | ()
In [29]:
truth_table(xor(a, b))((((a) b) ((b) a)))
a b | Value
------+------
|
() | ()
() | ()
() () |
In [30]:
truth_table(eqiv(a, b))(((a) b) ((b) a))
a b | Value
------+------
| ()
() |
() |
() () | ()
In [31]:
truth_table(xor(a, xor(b, c)))((((((((b) c) ((c) b)))) a) (((((b) c) ((c) b))) (a))))
a b c | Value
---------+------
|
() | ()
() | ()
() () |
() | ()
() () |
() () |
() () () | ()
In [32]:
E1 = and_(
or_(and_(a, b), and_(b, c), and_(c, a)), # Any two variables...
nand(a, b, c) # ...but not all three.
)
truth_table(E1)((((((a) (b)) ((a) (c)) ((b) (c))))) ((((a) (b) (c)))))
a b c | Value
---------+------
|
() |
() |
() () | ()
() |
() () | ()
() () | ()
() () () |
In [33]:
e6 = F((a,), b, (c,))
truth_table(e6)((a) (c) b)
a b c | Value
---------+------
|
() |
() |
() () |
() |
() () | ()
() () |
() () () |
In [34]:
e4 = ( a, (b,), (c,))
e6 = ((a,), b, (c,))
e7 = ((a,), (b,), c )
E2 = or_(e4, e6, e7)
truth_table(E2)((((a) (b) c) ((a) (c) b) ((b) (c) a)))
a b c | Value
---------+------
|
() |
() |
() () | ()
() |
() () | ()
() () | ()
() () () |
In [35]:
truth_table(eqiv(E1, E2))(((((((((a) (b)) ((a) (c)) ((b) (c))))) ((((a) (b) (c)))))) ((((a) (b) c) ((a) (c) b) ((b) (c) a)))) (((((((a) (b)) ((a) (c)) ((b) (c))))) ((((a) (b) (c))))) (((((a) (b) c) ((a) (c) b) ((b) (c) a))))))
a b c | Value
---------+------
| ()
() | ()
() | ()
() () | ()
() | ()
() () | ()
() () | ()
() () () | ()
In [36]:
a, b = 'ab'
half_bit_adder = {
'Sum': xor(a, b),
'Carry': and_(a, b),
}
for name, expr in half_bit_adder.items():
print name
truth_table(expr)
printCarry
((a) (b))
a b | Value
------+------
|
() |
() |
() () | ()
Sum
((((a) b) ((b) a)))
a b | Value
------+------
|
() | ()
() | ()
() () |
In [37]:
Sum = F(( (a, b, (c,)), (a, (b,), c), ((a,), b, c), ((a,), (b,), (c,)) ),)
Carry = F(( (a, (b,), (c,)), ((a,), b, (c,)), ((a,), (b,), c), ((a,), (b,), (c,)) ),)In [38]:
print 'Sum'
truth_table(Sum)
print
print 'Carry'
truth_table(Carry)Sum
((((a) (b) (c)) ((a) b c) ((b) a c) ((c) a b)))
a b c | Value
---------+------
|
() | ()
() | ()
() () |
() | ()
() () |
() () |
() () () | ()
Carry
((((a) (b) (c)) ((a) (b) c) ((a) (c) b) ((b) (c) a)))
a b c | Value
---------+------
|
() |
() |
() () | ()
() |
() () | ()
() () | ()
() () () | ()
In [39]:
def full_bit_adder(a, b, c):
return (
F(( (a, b, (c,)), (a, (b,), c), ((a,), b, c), ((a,), (b,), (c,)) ),),
F(( (a, (b,), (c,)), ((a,), b, (c,)), ((a,), (b,), c), ((a,), (b,), (c,)) ),),
)In [40]:
sum0, cout = full_bit_adder('a0', 'b0', 'Cin')
sum1, cout = full_bit_adder('a1', 'b1', cout)
sum2, cout = full_bit_adder('a2', 'b2', cout)
sum3, cout = full_bit_adder('a3', 'b3', cout)
sum4, cout = full_bit_adder('a4', 'b4', cout)
sum5, cout = full_bit_adder('a5', 'b5', cout)
sum6, cout = full_bit_adder('a6', 'b6', cout)
sum7, cout = full_bit_adder('a7', 'b7', cout)In [41]:
map(len, map(str, (sum0, sum1, sum2, sum3, sum4, sum5, sum6, sum7, cout)))Out [41]:
[63, 327, 1383, 5607, 22503, 90087, 360423, 1441767, 1441773]
In [42]:
def full_bit_adder(a, b, c):
return (
xor(xor(a, b), c),
or_(and_(a, b), and_(c, xor(a, b))),
)In [43]:
sum0, cout = full_bit_adder('a0', 'b0', 'Cin')In [44]:
print 'Sum'
truth_table(sum0)
print
print 'Carry'
truth_table(cout) Sum
((((((((a0) b0) ((b0) a0)))) Cin) (((((a0) b0) ((b0) a0))) (Cin))))
Cin a0 b0 | Value
-------------+------
|
() | ()
() | ()
() () |
() | ()
() () |
() () |
() () () | ()
Carry
((((((((a0) b0) ((b0) a0)))) (Cin)) ((a0) (b0))))
Cin a0 b0 | Value
-------------+------
|
() |
() |
() () | ()
() |
() () | ()
() () | ()
() () () | ()
In [45]:
sum1, cout = full_bit_adder('a1', 'b1', cout)
sum2, cout = full_bit_adder('a2', 'b2', cout)
sum3, cout = full_bit_adder('a3', 'b3', cout)
sum4, cout = full_bit_adder('a4', 'b4', cout)
sum5, cout = full_bit_adder('a5', 'b5', cout)
sum6, cout = full_bit_adder('a6', 'b6', cout)
sum7, cout = full_bit_adder('a7', 'b7', cout)In [46]:
map(len, map(str, (sum0, sum1, sum2, sum3, sum4, sum5, sum6, sum7, cout)))Out [46]:
[67, 159, 251, 343, 435, 527, 619, 711, 371]
In [47]:
import unittestIn [48]:
class UnwrapTest0(unittest.TestCase):
def testMark(self):
self.assertEqual(Mark, simplify(Mark))
def testVoid(self):
self.assertEqual(Void, simplify(Void))
def testLeaf(self):
self.assertEqual('a', simplify('a'))
def simplify(form):
# Three easy cases, for strings, Mark, or Void, just return it.
if isinstance(form, basestring) or form in BASE:
return form
if __name__ == '__main__':
unittest.main(argv=['ignored', 'UnwrapTest0'], exit=False)... ---------------------------------------------------------------------- Ran 3 tests in 0.004s OK
In [49]:
class UnwrapTest1(unittest.TestCase):
def testNegatedLeaf(self):
a = nor('a')
self.assertEqual(a, simplify(a))
def simplify(form):
# Three easy cases, for strings, Mark, or Void, just return it.
if isinstance(form, basestring) or form in BASE:
return form
# We know it's a Form and it's not empty (else it would be the Mark and
# returned above.)
# Let's just recurse.
return Form(simplify(inner) for inner in form)
if __name__ == '__main__':
unittest.main(argv=['ignored', 'UnwrapTest1'], exit=False). ---------------------------------------------------------------------- Ran 1 test in 0.001s OK
In [50]:
class UnwrapTest2(unittest.TestCase):
def testUnwrapLeaf(self):
'''((a)) = a'''
a = or_('a')
self.assertEqual('a', simplify(a))
def testDoNotUnwrapTwoLeaves(self):
'''((a b)) = ((a b))'''
a = or_('a', 'b')
self.assertEqual(a, simplify(a))
def simplify(form):
# Three easy cases, for strings, Mark, or Void, just return it.
if isinstance(form, basestring) or form in BASE:
return form
# We know it's a Form and it's not empty (else it would be the Mark and
# returned above.)
# Let's just recurse.
result = Form(simplify(inner) for inner in form)
# Check for ((a)) and return just a.
# If there is more than one item in the inner container ((a b..))
# then we must keep the outer containers.
if len(result) == 1:
inner, = result # inner = (a)
if isinstance(inner, Form) and len(inner) == 1:
a, = inner
return a
return result
if __name__ == '__main__':
unittest.main(argv=['ignored', 'UnwrapTest2'], exit=False).. ---------------------------------------------------------------------- Ran 2 tests in 0.002s OK
In [51]:
class UnwrapTest3(unittest.TestCase):
def testMultiUnwrapLeaf(self):
A = 'a'
B = nor(A)
a = nor(B)
self.assertEqual(A, simplify(a))
a = nor(a)
self.assertEqual(B, simplify(a))
a = nor(a)
self.assertEqual(A, simplify(a))
a = nor(a)
self.assertEqual(B, simplify(a))
a = nor(a)
self.assertEqual(A, simplify(a))
a = nor(a)
self.assertEqual(B, simplify(a))
def testMultiDoNotUnwrapTwoLeaves(self):
e = F('a', 'b')
f = a = nor(e)
self.assertEqual(f, simplify(a))
a = nor(a)
self.assertEqual(e, simplify(a))
a = nor(a)
self.assertEqual(f, simplify(a))
a = nor(a)
self.assertEqual(e, simplify(a))
a = nor(a)
self.assertEqual(f, simplify(a))
a = nor(a)
self.assertEqual(e, simplify(a))
# Technically, several of the tests above are redundant,
# I'm not willing to figure out the right point ot stop
# right now, so I just do extra tests.
if __name__ == '__main__':
unittest.main(argv=['ignored', 'UnwrapTest3'], exit=False).. ---------------------------------------------------------------------- Ran 2 tests in 0.003s OK
In [52]:
class UnwrapTest4(unittest.TestCase):
def testMultiUnwrapLeaf(self):
a, b, c = 'abc'
f = F(a,((b, c),))
e = F(a, b, c)
self.assertEqual(e, simplify(f))
def testMulti_blah_Leaf(self):
a, b, c = 'abc'
f = F(a,(((b, c),),),)
e = F(a, (b, c))
self.assertEqual(e, simplify(f))
def testMulti_blah_blah_Leaf(self):
a, b, c, d = 'abcd'
f = F(a,((((b, c),), d),))
e = F(a, b, c, d)
self.assertEqual(e, simplify(f))
def simplify(form):
# Three easy cases, for strings, Mark, or Void, just return it.
if isinstance(form, basestring) or form in BASE:
return form
# We know it's a Form and it's not empty (else it would be the Mark and
# returned above.)
result = []
for inner in simplify_gen(form): # Use the generator instead of recursing into simplify().
result.append(inner)
result = Form(result)
# Check for ((a)) and return just a.
# If there is more than one item in the inner container ((a b..))
# then we must keep the outer containers.
if len(result) == 1:
inner, = result # inner = (a)
if isinstance(inner, Form):
if len(inner) == 1:
a, = inner
return a
else:
# len(inner) cannot be 0, because that means form is Void
# and would already have been returned.
assert len(inner) > 1, repr(inner)
# What to do here?
# We cannot yield the items in inner into the containing context
# because we don't have it (or even know if it exists.)
# Therefore we need a different simplify() generator function that yields
# the simplified contents of a form, and we have to call that instead
# of recurring on simplify() above.
pass
return result
def simplify_gen(form):
for inner in form:
inner = simplify(inner)
# Now inner is simplified, except for ((a b...)) which simplify() can't handle.
# Three easy cases, strings, Mark, or Void.
if isinstance(inner, basestring):
yield inner
continue
if inner == Mark:
yield inner
assert False # The simplify() function will not keep iterating after this.
return # Partial implementation of ()A = ().
if inner == Void:
continue # Omit Void. Implementation of (()) = .
# We know it's a Form and it's not empty (else it would be the Mark and
# yielded above.)
# Check for ((...)) and return just ... .
if len(inner) > 1: # (foo bar)
yield inner
continue
assert len(inner) == 1, repr(inner) # Just in case...
inner_inner, = inner
if isinstance(inner_inner, Form): # inner_inner = (...)
for inner_inner_inner in inner_inner:
yield inner_inner_inner
continue
#else: # inner_inner = foo ; inner = (foo)
yield inner
if __name__ == '__main__':
unittest.main(argv=['ignored', 'UnwrapTest4'], exit=False)... ---------------------------------------------------------------------- Ran 3 tests in 0.005s OK
In [53]:
class MarkTest0(unittest.TestCase):
def testMarkOccludes0(self):
a, b, c = 'abc'
f = F(a, (), b, c)
self.assertEqual(Void, simplify(f))
def testMarkOccludes1(self):
a, b, c = 'abc'
f = F(a, (b, c, ()))
e = F(a)
self.assertEqual(e, simplify(f))
def testMarkOccludes2(self):
a, b, c = 'abc'
f = F(a, (b, ((), c)))
e = F(a, (b,))
self.assertEqual(e, simplify(f))
def simplify(form):
# Three easy cases, for strings, Mark, or Void, just return it.
if isinstance(form, basestring) or form in BASE:
return form
# We know it's a Form and it's not empty (else it would be the Mark and
# returned above.)
result = []
for inner in simplify_gen(form):
if inner == Mark:
return Void # Discard any other inner forms, form is Void.
result.append(inner)
result = Form(result)
# Check for ((a)) and return just a.
# If there is more than one item in the inner container ((a b..))
# then we must keep the outer containers.
if len(result) == 1:
inner, = result # inner = (a)
if isinstance(inner, Form):
if len(inner) == 1:
a, = inner
return a
return result
if __name__ == '__main__':
unittest.main(argv=['ignored', 'MarkTest0'], exit=False)... ---------------------------------------------------------------------- Ran 3 tests in 0.004s OK
Warning:
Output truncated. This notebook contains too many cells to display efficiently.