exupero's blog
RSSApps

Constraining input based on the current state

In the previous post we explored some different ways of constraining input to our simulated manufacturing line, but none of them had any information about the current state of the line. To calculate how many pennies to add based on what the line produced in the previous iteration, we can update our simulate function to pass the current state to the generate-capacities:

(defn simulate [initial-state update-state iterations generate-capacities]
  (let [rng (java.util.Random. 10)
        roll #(.nextInt rng 1 7)]
    (take (inc iterations)
      (iterate #(update-state % (generate-capacities roll %))
               initial-state))))

Then we can write a capacities generating function that sets the number of incoming pennies based on, say, what the bottle actually produced (rather than what it had the capacity to produce):

(defn constrain-input-to-bottleneck-production
  [[incoming & capacities] processed]
  (cons (nth processed 3 incoming)
        capacities))
(def constrained-to-bottleneck-production-steps
  (simulate initial-state update-state 100
    (fn [roll {:keys [processed]}]
      (-> (repeatedly 7 roll)
          more-efficient
          (constrain-input-to-bottleneck-production processed)))))

If we only allow into the line what the bottleneck produced, we should avoid accumulating work in progress, which it appears to do quite well:

Work in progressStep 10Step 20Step 30Step 40Step 50Step 60Step 70Step 80Step 90Step 1000510152025

Unfortunately, having too little work in progress also starves the bottleneck fairly often:

Work in progressStep 10Step 20Step 30Step 40Step 50Step 60Step 70Step 80Step 90Step 10002468101214
Output
Original290
Improved336
Capacity-constrained331
Production-constrained312

It looks like we're better off constraining input based on the bottleneck's capacity rather than its actual production. When we do, we sometimes input too much, but that extra work keeps the bottleneck busy and improves output.