Constraint propagation

Sometimes, we want to implement implicit functions that define constraints rather than explicit functions, i.e, any change in one of the quantities in an equation (eg. F=ma) should reflect in the other quantities in the equation.

The implementation is done by defining primitive constraints and then forming networks of these constraints to implement more complex constraints. The adder primitive constraint implementing sum=a1+a2 can be implemented as:

(define (adder a1 a2 sum)
  (define (process-new-value)
    (cond ((and (has-value? a1) (has-value? a2))
           (set-value! sum
                       (+ (get-value a1) (get-value a2))
                       me))
          ((and (has-value? a1) (has-value? sum))
           (set-value! a2
                       (- (get-value sum) (get-value a1))
                       me))
          ((and (has-value? a2) (has-value? sum))
           (set-value! a1
                       (- (get-value sum) (get-value a2))
                       me))))
  (define (process-forget-value)
    (forget-value! sum me)
    (forget-value! a1 me)
    (forget-value! a2 me)
    (process-new-value))
  (define (me request)
    (cond ((eq? request 'I-have-a-value)  
           (process-new-value))
          ((eq? request 'I-lost-my-value) 
           (process-forget-value))
          (else 
           (error "Unknown request -- ADDER" request))))
  (connect a1 me)
  (connect a2 me)
  (connect sum me)
  me)

where a1 and a2 are connectors (stateful objects) that hold appropriate values and communicate to the constraints they are connected to when they get or lose values, very similar to event-driven simulation, except now the procedures are bidirectional but not timed.


Links

Sources