61A-013-Constraints_..

61A Lecture 13
Wednesday, September 28
Tuesday, September 27, 2011
Dictionaries
{'Dem': 0}
2
Tuesday, September 27, 2011
Limitations on Dictionaries
Dictionaries are unordered collections of key-value pairs.
Dictionaries do have two restrictions:
•
A key of a dictionary cannot be an object of a mutable
built-in type.
•
Two keys cannot be equal. There can be at most one value for
a given key.
This first restriction is tied to Python's underlying
implementation of dictionaries.
The second restriction is an intentional consequence of the
dictionary abstraction.
3
Tuesday, September 27, 2011
Implementing Dictionaries
def make_dict():
"""Return a functional implementation of a dictionary."""
records = []
def getitem(key):
for k, v in records:
if k == key:
return v
def setitem(key, value):
Question: Do we need a
nonlocal statement here?
for item in records:
if item[0] == key:
item[1] = value
return
records.append([key, value])
def dispatch(message, key=None, value=None):
if message == 'getitem':
return getitem(key)
elif message == 'setitem':
setitem(key, value)
elif message == 'keys':
return tuple(k for k, _ in records)
elif message == 'values':
return tuple(v for _, v in records)
Demo
return dispatch
4
Tuesday, September 27, 2011
Message Passing
An approach to organizing the relationship among different
pieces of a program
Different objects pass messages to each other
• What is your fourth element?
• Change your third element to this new value. (please)
Encapsulates the behavior of all
operations on a piece of data
within one function that responds
to different messages.
Important historical interest: the
message passing approach strongly
influenced object-oriented
programming (next lecture).
5
Tuesday, September 27, 2011
Dispatch Dictionaries
Enumerating different messages in a conditional statement
isn't very convenient:
 Equality tests are repetitive
 We can't add new messages without writing new code
A dispatch dictionary has messages as keys and functions (or
data objects) as values.
Dictionaries handle the message look-up logic; we concentrate
on implementing useful behavior.
Demo
In Javascript, all objects are just dictionaries
6
Tuesday, September 27, 2011
Example: Constraint Programming
a + b = c
p * v = n * k * t
a = c - b
b = c - a
9 * c = 5 * (f - 32)
Algebraic equations are declarative. They describe how
different quantities relate to one another.
Python functions are procedural. They describe how to compute
a particular result from a particular set of inputs.
Constraint programming:
 We define the relationship between quantities
 We provide values for the "known" quantities
 The system computes values for the "unknown" quantities
Challenge: We want a general means of combination.
7
Tuesday, September 27, 2011
A Constraint Network for Temperature Conversion
Combination idea: All intermediate quantities have values too.
u
u
v
Both sides equal:
they must be
the same quantity
9 * celsius = 5 * (fahrenheit - 32)
This quantity
relates directly
to celsius
celsius
a
w
b
9
*
This quantity
relates directly
to fahrenheit
c
u
c
*
v
a
b
x
5
a
b
y
+
c
fahrenheit
32
8
Tuesday, September 27, 2011
Anatomy of a Constraint
celsius
a
w
b
Blue names are
"connectors"
*
c
u
Constraints compute
values for "unknown"
connectors
Boxes are
"constraints"
• Connectors represent quantities that have values.
• Constraints spread information among connectors.
• A constraint can receive two messages from its connectors:
 'new_val' indicates that some connector that is connected
to the constraint has a new value.
 'forget' indicates that some connector that is connected
to the constraint has forgotten its value.
9
Tuesday, September 27, 2011
Constructing a Constraint Network
def make_converter(celsius, fahrenheit):
"""Make a temperature conversion network."""
u, v, w, x, y = [make_connector() for _ in range(5)]
multiplier(celsius, w, u)
multiplier(v, x, u)
adder(v, y, fahrenheit)
constant(w, 9)
constant(x, 5)
constant(y, 32)
celsius
a
w
b
9
*
c
u
c
*
v
a
b
x
5
a
b
y
+
c
fahrenheit
32
celsius = make_connector('Celsius')
fahrenheit = make_connector('Fahrenheit')
make_converter(celsius, fahrenheit)
Demo
10
Tuesday, September 27, 2011
The Messages of a Connector
connector = make_connector('Celsius')
connector['set_val'](source, value) indicates that the
source is requesting the connector to set its current
value to value.
connector['has_val']() returns whether the connector
already has a value.
connector['val'] is the current value of the connector.
connector['forget'](source) tells the connector that the
source is requesting it to forget its value.
connector['connect'](source) tells the connector to
participate in a new constraint, the source.
11
Tuesday, September 27, 2011
multiplier(c,
multiplier(v, w,
x, u)
u)
multiplier(v,
adder(v, y, f)x, u)
def adder_constraint(a,
b, c):
adder(v,
y, f)
constant(w,
9)
"""The
constraint
that a + b = c.
constant(w,
9)
constant(x,
5)
Implementing
constant(x,
5)
constant(y, an
32)Adder Constraint
>>>
a, b, c =32)
[make_connector(name) for name in ('a', 'b', 'c')]
constant(y,
constraint = adder_constraint(a,
b, c)
def >>>
adder_constraint(a,
b, c):
def >>>
adder_constraint(a,
b, a
c):
a['set_val']('user',
2)
"""The
constraint that
+ b = c.
a"""The
= 2 constraint that a + b = c.
>>>
3)
>>> b['set_val']('user',
a, b, c = [make_connector(name)
for name in ('a', 'b', 'c')]
b, c = [make_connector(name)
name in ('a', 'b', 'c')]
b>>>
= 3a,
>>>
constraint
= adder_constraint(a,for
b, c)
= adder_constraint(a,
b, c)
c>>>
= 5constraint
>>>
a['set_val']('user',
2)
>>>
2)
"""
a = a['set_val']('user',
2
a
= new_value():
2
def
>>>
b['set_val']('user', 3)
>>>
b['set_val']('user',
3)
bv, cv = [connector['has_val']()
for connector in (a, b, c)]
b = av,
3
b
3
c =
= if
5 av and bv:
c
= 5
c['set_val'](constraint, a['val'] + b['val'])
"""
"""
av and cv:
def elif
new_value():
def av,
new_value():
b['set_val'](constraint,
c['val'] for
é a['val'])
bv, cv = [connector['has_val']()
connector in (a, b, c)]
av,
bv,
cv
=
[connector['has_val']()
for
connector in (a, b, c)]
elif
bvand
andbv:
cv:
if
av
# We
willbv:
implement this function
momentarily!
if
av
and
a['set_val'](constraint,
c['val']
é+ b['val'])
c['set_val'](constraint,
a['val']
c['set_val'](constraint, a['val'] + b['val'])
b['val'])
elif
av
and
cv:
elif av and cv:
def forget_value():
b['set_val'](constraint,
c['val']
b['set_val'](constraint,
c['val'] é
é a['val'])
a['val'])
for
connector
in
(a,
b,
c):
elif
bv
and
cv:
elifconnector['forget'](constraint)
bv and cv:
a['set_val'](constraint,
a['set_val'](constraint, c['val']
c['val'] é
é b['val'])
b['val'])
constraint
= {'new_val': new_value, 'forget': forget_value}
def
forget_value():
def forget_value():
for
connector
in
(a,
b,
c):
for
connector
in
(a,
b,
c):
for connector
in
(a,
b,
c):
connector['forget'](constraint)
connector['forget'](constraint)
connector['connect'](constraint)
constraint
=
{'new_val':
new_value,
'forget':
forget_value}
constraint
=
{'new_val':
new_value,
'forget':
forget_value}
return constraint
for
for connector
connector in
in (a,
(a, b,
b, c):
c):
connector['connect'](constraint)
12
connector['connect'](constraint)
def make_ternary_constraint(a,
b, c, ab, ca, cb):
"""The
constraint
Tuesday, September
27, 2011
return
constraint that ab(a,b)=c and ca(c,a)=b and cb(c,b) = a."""
for connector['forget'](constraint)
connector in (a, b, c):
connector['forget'](constraint)
constraint
= {'new_val': new_value, 'forget': forget_value}
constraint = {'new_val': new_value, 'forget': forget_value}
constraint
= {'new_val':
new_value, 'forget': forget_value}
for connector
in (a, b, c):
for connector
in (a, b, c):
connector['connect'](constraint)
for connector['connect'](constraint)
connector in (a, b, c):
connector['connect'](constraint)
return
constraint
Connectors
Relations
return constraint
return constraint
def make_ternary_constraint(a, b, c, ab, ca, cb):
def make_ternary_constraint(a,
b, c, ab,
cb): and cb(c,b)=a."""
"""The constraint that ab(a,b)=c
and ca,
ca(c,a)=b
def make_ternary_constraint(a,
b, c, and
ab, ca(c,a)=b
ca, cb): and cb(c,b) = a."""
"""The
constraint that ab(a,b)=c
def new_value():
"""The
that ab(a,b)=c and ca(c,a)=b
and cb(c,b)
= a."""
def new_value():
13_constraints.py
av,constraint
bv, cv = [connector['has_val']()
for connector
in (a,
b, c)]
def av,
new_value():
bv,
cv
=
[connector['has_val']()
for
connector
in
(a,
b,
c)]
if av and bv:
av,
bv,
cv
=cv:
[connector['has_val']()
for connector
in (a, b, c)]
if
av
and
bv:
elif
bv
and
c['set_val'](constraint,
ab(a['val'],
b['val']))
if
av
andand
bv:cv:
c['set_val'](constraint,
ab(a['val'],
a['set_val'](constraint,
cb(c['val'], b['val']))
elif
av
c['set_val'](constraint,
ab(a['val'],
av and cv:
def elif
forget_value():
b['set_val'](constraint,
ac(c['val'], b['val']))
a['val']))
elif
av and cv:
for b['set_val'](constraint,
connector
in (a, b, c): ac(c['val'], a['val']))
b['set_val'](constraint,
ac(c['val'], a['val']))
13_constraints.py
connector['forget'](constraint)
constraint = {'new_val': new_value, 'forget': forget_value}
elif bv and
for connector
incv:
(a, b, c):
a['set_val'](constraint, cb(c['val'], b['val']))
connector['connect'](constraint)
def forget_value():
return
constraint
for connector in (a, b, c):
connector['forget'](constraint)
from operator
import add, sub, mul, truediv
constraint = {'new_val': new_value, 'forget': forget_value}
for connector
in (a, b, c):
def adder(a,
b, c):
connector['connect'](constraint)
"""The
constraint that a + b = c."""
return make_ternary_constraint(a,
constraint
b, c, add, sub, sub)
Generalizing to a Multiplication Constraint
frommultiplier(a,
operator import
add, sub, mul, truediv
def
b, c):
"""The constraint that a * b = c."""
def return
adder(a,
b, c):
make_ternary_constraint(a,
b, c, mul, truediv, truediv)
"""The constraint that a + b = c."""
return make_ternary_constraint(a,
b, c, add, sub, sub)
def constant(connector,
value):
"""The constraint that connector = value."""
def constraint
multiplier(a,
b, c):
= {}
Tuesday, September 27, 2011
13
Contradiction
detected: 77.0 vs 212
>>>
fahrenheit['set_val']('user',
212)
>>>
celsius['forget']('user')
def Contradiction
constant(connector,
value):
detected:
77.0 vs 212
>>>
celsius['forget']('user')
Celsius
is
forgotten
>>>
celsius['forget']('user')
"""The
constraint
that connector = value."""
Celsius
is
forgotten
Fahrenheit
forgotten
constraint
= {}
Celsius
is is
forgotten
>>>
celsius['forget']('user')
Fahrenheit
is
forgotten
connector['set_val'](constraint,
value)
Fahrenheit
is
forgotten
Celsius is forgotten
>>>
fahrenheit['set_val']('user',
212)
return
constraint
Fahrenheit
is
forgotten
>>>
fahrenheit['set_val']('user',
212)
Fahrenheit
= 212
>>>
fahrenheit['set_val']('user',
212)
Fahrenheit
= 212
Celsius
=
100.0
def >>>
make_connector(name=None):
Fahrenheit
= 212
fahrenheit['set_val']('user',
212)
Celsius
= 100.0
"""
"""A connector
between constraints.
Celsius
= 100.0
Fahrenheit
= 212
"""
informant
None
"""
Celsius
= =
100.0
informant
=
None
constraints
=
[]
>>>
celsius
make_connector('Celsius')
informant
=
None
"""
constraints
=
[]
def
value):
>>> set_value(source,
fahrenheit
= make_connector('Fahrenheit')
constraints
=
[]
informant
=
None
def
value):
nonlocal
informant
>>> set_value(source,
make_converter(celsius,
fahrenheit)
def
set_value(source,
value):
constraints
=
[]
nonlocal
informant
= connector['val']
nonlocal
informantvalue):
def val
set_value(source,
val
= connector['val']
if
val
is
None:
>>> val
celsius['set_val']('user',
25)
= connector['val']
nonlocal
informant
if
val
is
None:
informant,
= source, value
Celsius
= connector['val']
25
if val
is None:connector['val']
val
=
informant,
connector['val']
= source, value
Fahrenheit
=
77.0
if
name
is
not
None:
informant,
connector['val']
= source, value
if val
None:
if is
name
is
not
None:
print(name,
'=', value) = source, value
if name
is connector['val']
not None:
informant,
print(name,
'=', value)
>>> fahrenheit['set_val']('user',
212)
inform_all_except(source,
'new_val',
constraints)
print(name,
'=',
value)
if
name
is
not
None:
inform_all_except(source,
'new_val', constraints)
Contradiction
detected: '=',
77.0 value)
vs 212
else:
inform_all_except(source,
'new_val', constraints)
print(name,
else:
if
val
!=
value:
else:
inform_all_except(source,
'new_val', constraints)
if val
!= value:
>>> celsius['forget']('user')
print('Contradiction
detected:',
val, 'vs', value)
if
val
!=
value:
else:
print('Contradiction
detected:',
val, 'vs', value)
Celsius
is forgotten
def
forget_value(source):
print('Contradiction
detected:',
val, 'vs', value)
if val
!= value:
def
forget_value(source):
Fahrenheit
is
forgotten
informant
def nonlocal
forget_value(source):
print('Contradiction
detected:', val, 'vs', value)
nonlocal
informant
if
informant
== source:
informant
def
forget_value(source):
if
informant
==connector['val']
source:
>>> nonlocal
fahrenheit['set_val']('user',
212)
informant,
= None, None
if
informant
==
source:
nonlocal
informant
informant,
connector['val']
= None, None
Fahrenheit
=
212
if name is
not
None:
if
informant
==
source:
informant,
connector['val']
= None, None
name
is not None:
Celsius if
= 100.0
print(name,
'is
forgotten')
informant,
= None, None
if name
is connector['val']
not None:
print(name,
'is forgotten')
"""
inform_all_except(source,
'forget',
constraints)
if =name
is not None:
print(name,
'is forgotten')
informant
None
inform_all_except(source,
'forget',
constraints)
connector
= {'val':
None,
print(name,
'is
forgotten')
inform_all_except(source,
'forget',
constraints)
constraints
='set_val':
[]
connector
= {'val':
None,
set_value,'forget', constraints)
inform_all_except(source,
connector
= {'val':
None,
def set_value(source,
value):
'set_val':
set_value,
'forget':
forget_value,
connector
=
{'val':
None,
'set_val':
set_value,
nonlocal 'forget':
informant
forget_value,
'has_val':forget_value,
lambda: connector['val'] is not None,
'set_val':
set_value,
'forget':
val = connector['val']
'has_val':
lambda:
connector['val']
is not None,
'connect':
lambda
source:
constraints.append(source)}
'forget':
forget_value,
'has_val':
lambda:
connector['val']
is not None,
if val is'connect':
None:
lambda source: constraints.append(source)}
'has_val':
lambda:
connector['val']
is not None,
'connect':
lambda source:
constraints.append(source)}
informant,
connector['val']
= source,
value
13_constraints.py
'connect':
lambda source: constraints.append(source)}
if name
is not None:
print(name, '=', value)
return connector
inform_all_except(source, 'new_val', constraints)
else:
def inform_all_except(source,
message, constraints):
if val != value:
"""Inform all
constraints of the message,
except
print('Contradiction
detected:',
val,source."""
'vs', value)
Tuesday, September 27, 2011
for c in constraints:
Implementing a Connector
Page 4
14