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

Fri, 07 Jul 2017

Acrobat Reader 7 for Linux

Adam Langley noticed that Adobe Reader 7 is now available for Linux. The update from version 5 has been long overdue. The new version uses GDK rather than Motif. It has some new features such as Save As Text, and will hopefully eliminate those annoying warnings about a PDF requiring a newer version of Acrobat Reader (though they always seem to be perfectly readable). Multiple PDFs are now opened within a single application window.

Here are some Debian packages I built based on Christian Marillat's packages. I'm sure he'll do a better job soon.

Update: Christian Marillat has added acroread packages to his testing and unstable repositories. You probably want to use those.

tech | Permanent Link

Cast Iron Studio 7 on Linux

There's a shell script on the IBM DeveloperWorks forum describing how to run Cast Iron Studio on Linux, but it's from 2010. This describes the steps I followed to get Cast Iron Studio 7.5 running on Linux.

Download the latest version of Cast Iron Studio, 7.5.1.0 as of 2017-07-07: http://www-01.ibm.com/support/docview.wss?uid=swg24042011

The installation file is distributed as a Windows executable self-extracting zip file. The paths within the zip file contain backslashes rather than path-independent directory delimiters so the zip file needs to be fixed up before extracting it.

Use Info-ZIP's zip to strip the self-extracting stub from the zip file and to fix the structure:

$ zip -F -J 7.5.1.0-WS-WCI-20160324-1342_H11_64.studio.exe --out castiron.zip

Then use 7z's rename function to convert the backslashes to slashes within the archive.

$ 7z rn castiron.zip $(7z l castiron.zip | grep '\\' | awk '{ print $6, gensub(/\\/, "/", "g", $6); }' | paste -s)

Now you can unzip castiron.zip.

Finally, here's the modified version of the shell script I found above used to start Cast Iron Studio. Put it in the directory where you unzipped castiron.zip.

#!/bin/bash

# Tell Java that the window manager is LG3D to avoid grey window
/usr/bin/wmname LG3D

THIS=$(readlink -f $0)
DIST_HOME=$(dirname "$THIS")
cd "$DIST_HOME"

LIB_PATH=""

LIB_PATH=$LIB_PATH:resources

for f in `find plugins -type f -name '*.jar'`; do
   LIB_PATH=$LIB_PATH:$f
done

export OSGI_FRAMEWORK_JAR=org.eclipse.osgi_3.10.1.v20140909-1633.jar
export SET MAIN_CLASS=org.eclipse.core.runtime.adaptor.EclipseStarter
export IH_LOGGING_PROPS="resources/logging.properties"
export ENDORSED="-Djava.endorsed.dirs=endorsed-lib"
export KEYSTORE="-Djavax.net.ssl.keyStore=security/certs"
export TRUSTSTORE="-Djavax.net.ssl.trustStore=security/cacerts"
export LOGIN_CTX="-Djava.security.auth.login.config=security/httpkerb.conf"
export JAAS_CONFIG="-Djava.security.auth.login.config=security/ci_jaas.config"

export JVM_DIRECTIVES="-Dapplication.mode.studio=true -Djava.util.logging.config.file=$IH_LOGGING_PROPS -Dcom.sun.management.jmxremote -Djgoodies.fontSizeHints=SMALL -Djavax.xml.ws.spi.Provider=com.approuter.module.jws.ProviderImpl "-Dinstall4j.launcherId=10" -Dinstall4j.swt=false"

export JAVA_HOME=/usr/lib/jvm/ibm-java-x86_64-80

"$JAVA_HOME/bin/java" -client $ENDORSED $KEYSTORE $TRUSTSTORE $LOGIN_CTX $JAAS_CONFIG $YP_AGENT $PROFILINGAGENT $JVM_DIRECTIVES -Dcom.approuter.maestro.opera.sessionFactory=com.approuter.maestro.opera.ram.RamSessionFactory -Dcom.sun.management.jmxremote -Djgoodies.fontSizeHints=SMALL -Xmx2048M -Xms512M -Xbootclasspath/p:$LIB_PATH -jar $OSGI_FRAMEWORK_JAR

Update JAVA_HOME to the path to your jvm. Performance seems to be better with the IBM jvm than the Oracle jvm.

tech | Permanent Link

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