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.
| Womens | Mens | |
| Price | $105 | $100 |
| Raw Materials | $45 | $50 |
| Cutting Time | 2 minutes | 10 minutes |
| Sewing Time | 15 minutes | 10 minutes |
| Total Time | 17 minutes | 20 minutes |
| Weekly Demand | 120 shirts | 120 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 ---------- ==========
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.
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.

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