Thu, 07 Sep 2017

Cost Accounting as a Poorly Defined Constrained Optimization Model

I recently read The Goal, a business novel that introduces the Theory of Constraints as a toolset for managing a business in order to "more money now and in the future". One of the criticisms the book makes of the way businesses are often run is that their use of cost accounting to guide decisions fails to maximize the amount of money the business can make. An alternative called throughput accounting is proposed, but after having completed The Goal, I didn't have a clear understanding of how and why cost accounting fails.

So I bought Throughput Accounting to dig in deeper. The book starts with a great example of a simple decision made using cost accounting.

A factory makes mens and womens shirts. To make a shirt, you need some raw materials, plus the labor to cut fabric and sew fabric.

WomensMens
Price$105$100
Raw Materials$45$50
Cutting Time2 minutes10 minutes
Sewing Time15 minutes10 minutes
Total Time17 minutes20 minutes
Weekly Demand120 shirts120 shirts

Making all 120 womens shirts and 120 mens shirts requires 1,440 minutes (10 min/shirt * 120 mens shirts + 2 min/shirt * 120 womens shirts) of cutting time and 3,000 minutes (10 min/shirt * 120 mens shirts + 15 min/shirt * 120 womens shirts) of sewing time. Since we only have 2,400 minutes of sewing time available, we can't make all of the shirts. How many mens shirts and womens shirts should we make?

Gross profit for mens shirts is $50 ($100 sales price - $50 for raw materials). Gross profit for womens shirts is $60 ($105 sales price - $45 for raw materials). Mens shirts require a total of 20 minutes of labor. Womens shirts require a total of 17 minutes of labor.

Since womens shirts bring in more revenue, have a higher gross margin, and require less labor, it seems like a reasonable answer is to produce 120 womens shirts, and then as many mens shirts as we can with the remaining time we have available.

120 womens shirts take 1,800 minutes on the sewing machine, so we can make 60 mens shirts with the remaining 600 minutes available on the sewing machine. (We're not constrained by cutting.) This results in a net profit of $-300, i.e. a loss.

Revenue$18,600
Raw Materials$8,400
Gross Margin$10,200
Operating Expense-$10,500
Net Profit-$300

Throughput Accounting then explains how to calculate the amount of money (throughput) being made on the constrained resource per minute, the sewing machine, to decide how many of each product to make. But I'm going to use MiniZinc to use a contrained optimization solver to maximize net profit, which will give us the same result.

The constraints above, and the goal of trying to maximize net profit, can be translated into a MiniZinc model as follows (this would be done in a more concise manner if there were many products):

int: womens_sewing_minutes = 15;
int: womens_cutting_minutes = 2;
int: mens_sewing_minutes = 10;
int: mens_cutting_minutes = 10;

int: womens_price = 105;
int: mens_price = 100;

int: womens_raw_costs = 45;
int: mens_raw_costs = 50;

int: max_sewing_minutes = 2400;
int: max_cutting_minutes = 2400;

int: operating_expense = 10500;

int: womens_demand = 120;
int: mens_demand = 120;

var 0..mens_demand: mens_produced;
var 0..womens_demand: womens_produced;

var int: total_sewing_minutes = womens_produced * womens_sewing_minutes + mens_produced * mens_sewing_minutes;
var int: total_cutting_minutes = womens_produced * womens_cutting_minutes + mens_produced * mens_cutting_minutes;

var int: revenue = womens_price * womens_produced + mens_price * mens_produced;
var int: raw_materials_cost = (womens_raw_costs * womens_produced + mens_raw_costs * mens_produced);
var int: gross_margin = revenue - raw_materials_cost;

var int: total_cost = raw_materials_cost + operating_expense;

var int: net_profit = gross_margin - operating_expense;

constraint total_sewing_minutes <= max_sewing_minutes;
constraint total_cutting_minutes <= max_cutting_minutes;
solve maximize net_profit;

output ["mens: \(mens_produced)\n", "womens: \(womens_produced)\n", "net profit: \(net_profit)\n", "total cost: \(total_cost)"];

And solving the model tells us that we should produce 120 mens shirts and 80 womens shirts for a net profit of $300, rather than a loss.

$ minizinc clothes.mzn
mens: 120
womens: 80
net profit: 300
total cost: 20100
----------
==========

Intuitively, what's going wrong in the cost accounting solution is that we're lumping together all of the labor and not taking into consideration that the sewing machine is our bottleneck, and that we need to maximize the amount of value we're getting out of the bottleneck. Nonetheless, I struggled to translate this mistake into a MiniZinc model that also gave the wrong answer.

Below is an incorrectly defined constrained optimization model that I think reflects where we go wrong when trying to use cost accounting for decision making. The goal is changed from maximizing net profit to minimizing cost. But we don't want to just minimize cost; we could do that by producing zero shirts. We also want to produce as many shirts as we can. I modelled this as a constraint that says that producing one more mens shirt or one more womens shirt should cause us to exceed our cutting or sewing constraints.

int: womens_sewing_minutes = 15;
int: womens_cutting_minutes = 2;
int: mens_sewing_minutes = 10;
int: mens_cutting_minutes = 10;

int: womens_processing = womens_cutting_minutes + womens_sewing_minutes;
int: mens_processing = mens_cutting_minutes + mens_sewing_minutes;

int: womens_price = 105;
int: mens_price = 100;

int: womens_raw_costs = 45;
int: mens_raw_costs = 50;

int: max_sewing_minutes = 2400;
int: max_cutting_minutes = 2400;

int: operating_expense = 10500;

int: womens_demand = 120;
int: mens_demand = 120;

var 0..mens_demand: mens_produced;
var 0..womens_demand: womens_produced;

var int: total_sewing_minutes = womens_produced * womens_sewing_minutes + mens_produced * mens_sewing_minutes;
var int: total_cutting_minutes = womens_produced * womens_cutting_minutes + mens_produced * mens_cutting_minutes;

var int: womens_revenue = womens_price * womens_produced;
var int: womens_raw_materials_cost = womens_raw_costs * womens_produced;
var int: womens_operating_expenses = operating_expense * womens_produced div (womens_produced + mens_produced);
var int: womens_net_profit = womens_revenue - womens_raw_materials_cost - womens_operating_expenses;

var int: mens_revenue = mens_price * mens_produced;
var int: mens_raw_materials_cost = mens_raw_costs * mens_produced;
var int: mens_operating_expenses = operating_expense * mens_produced div (womens_produced + mens_produced);
var int: mens_net_profit = mens_revenue - mens_raw_materials_cost - mens_operating_expenses;

var int: total_cost = womens_operating_expenses + womens_raw_materials_cost + mens_operating_expenses + mens_raw_materials_cost;

var int: net_profit = womens_net_profit + mens_net_profit;

constraint total_sewing_minutes <= max_sewing_minutes;
constraint total_cutting_minutes <= max_cutting_minutes;

% Maximize the number of items produced by ensuring that it must not be
% possible to produce another item
constraint
   ((mens_produced + 1) * mens_sewing_minutes + womens_produced * womens_sewing_minutes > max_sewing_minutes \/
   (mens_produced + 1) * mens_cutting_minutes + womens_produced * womens_cutting_minutes > max_cutting_minutes)
   /\
   (mens_produced * mens_sewing_minutes + (womens_produced + 1) * womens_sewing_minutes > max_sewing_minutes \/
   mens_produced * mens_cutting_minutes + (womens_produced + 1) * womens_cutting_minutes > max_cutting_minutes);

solve minimize total_cost;

output ["mens: \(mens_produced)\n", "womens: \(womens_produced)\n", "net profit: \(net_profit)\n", "total cost: \(total_cost)"];

Solving this model gives us the original cost accounting result.

$ minizinc clothes-cost.mzn
mens: 60
womens: 120
net profit: -300
total cost: 18900
----------
==========

business | Permanent Link

The state is that great fiction by which everyone tries to live at the expense of everyone else. - Frederic Bastiat