# A match-stick production line

Posted in math on Sunday, May 17 2015

I am reading The Goal and in one chapter there is a toy model of a factory, in which boy scouts move matches from one bowl to the next trying to move matches to the end of the line, how many they can move from their bowl determined by a roll of a dice. Something about the model struck a chord with me, and now I want to make a toy example in Python.

First off I want to create a Producer class that receives material from the previous machine in the production line (or boy scout) into its inventory and then moves some random amount down to the next machine in the line. The whole system receives 3.5 units/time step at the head of the production line, and inventory just falls off the end.

import numpy as np

class Producer(object):
def __init__(self, name):
self.name = name
self.inventory = 0
self.performance = 0.0
self.next = None

def __repr__(self):
return "Id {:d}, Inventory: {:d}, Performance: {:0.1f} ".format(self.name, self.inventory, self.performance)

self.inventory += amount

def move(self):
if self.name < 1:
amount = 3 + np.random.randint(low=0, high=1)

can_make = np.random.randint(low=1, high=6)
if can_make > self.inventory:
does_make = self.inventory
else:
does_make = can_make

self.inventory -= does_make
self.performance += does_make - 3.5

if self.next != None:


The ProductionLine class is an interator, each time the production line is iterated it walks through the list of producers advancing one timestep. This is somewhat dangerous as it retains a memory from one iteration to the next, each step through it changes in some random way.

class ProductionLine(object):
def __init__(self):
# initialize the production line, there nothing here though
self.tail = None
self.cur = None

def __iter__(self):
# return the cursor to the head of the line
return self

def __next__(self):
# walk down the line, advancing one time step at a time
if self.cur != None:
self.cur.move()
if self.cur.next!=None:
self.cur = self.cur.next
return self.cur
else:
raise StopIteration

def append(self, producer):
# Add a new producer to the end of the line

if self.tail != None:
self.tail.next = producer

self.tail = producer


Now I initiate a production line and populate it with 19 producers,

production = ProductionLine()

for i in range(20):
p = Producer(i)
production.append(p)


I can make an animation showing how inventory at each work station fluctuates and what happens to the cumulative production in the plant using matplotlib.

This doesn't look so good, what's going on? Well we have a ratchet happening. The performance of any producer in any given time step has an upper bound given by the previous producer. If the guy before you doesn't give you anything to work on, you can't exactly have a high output right? On the flipside the performance can be as low as possible at any given time. The net effect of this is that positive fluctuations are stymied by upstream variability, whereas negative fluctuations remain.

If there was only one producer we would expect the cumulative performance to fluctuate around the mean (the cumulative performane is mean centered, so zero), but since there is a chain of dependency it ratchets lower and lower.

Related to this inventory piles up in front of some producers who happened to slow down while other producers are starved for inventory when they go faster than their predecesors.