So I tried to learn computers and stuff but it’s pretty hard and the payoff is pretty slim.

So I’ll post a few things about TRPG design.

Example: White Wolf Mage effects correspond to GURPS and even AD&D effects.

Paradox in Mage is not an original White Wolf mechanic:  AD&D 2nd Edition had optional rules for wild magic and tainted magic.

# From the Wayback Machine, part 2

http://web.archive.org/web/20131010153918/http://www.ibm.com/developerworks/linux/library/l-glpk2/

This article is the second in a three-part series on the GNU Linear Programming Kit (GLPK). For an introduction to GLPK, read the first installment in the series, “The GNU Linear Programming Kit, Part 1: Introduction to linear optimization.”

The diet problem

This problem comes from Operations Research: Applications and Algorithms, 4th Edition, by Wayne L. Winston (Thomson, 2004); see Resources below for a link.

My diet requires that all the food I eat come from one of the four “basic food groups”: chocolate cake, ice cream, soda, and cheesecake. At present, the following four foods are available for consumption: brownies, chocolate ice cream, cola, and pineapple cheesecake. Each brownie costs \$0.50, each scoop of chocolate ice cream costs \$0.20, each bottle of cola costs \$0.30, and each piece of pineapple cheesecake \$0.80. Each day, I must ingest at least 500 calories, 6 oz of chocolate, 10 oz of sugar, and 8 oz of fat. The nutrition content per unit of each food is shown in the table below. Satisfy my daily nutritional requirements at minimum cost.

To summarize the important information about the problem:

Food cost per unit

• Brownie: \$0.50
• Chocolate ice cream: \$0.20 (scoop)
• Soda: \$0.30 (bottle)
• Pineapple cheesecake: \$0.80 (piece)

Daily food needs

• 500 calories
• 6 oz chocolate
• 10 oz sugar
• 8 oz fat

Food nutritional content

• Brownie: 400 calories, 3 oz chocolate, 2 oz sugar, 2 oz fat
• Chocolate ice cream (scoop): 200 calories, 2 oz chocolate, 2 oz sugar, 4 oz fat
• Soda (1 bottle): 150 calories, 0 oz chocolate, 4 oz sugar, 1 oz fat
• Pineapple cheesecake (1 piece): 500 calories, 0 oz chocolate, 4 oz sugar, 5 oz fat

Modeling

Modeling this diet problem should be easier after solving Giapetto’s problem in Part 1. Let’s determine the decision variables first.

The goal is to satisfy the daily nutritional needs at a minimum cost. Therefore, the decision variables are the amount of each food type to buy:

Food variables

• `x1`: brownies
• `x2`: scoops of chocolate ice cream
• `x3`: bottles of cola
• `x4`: pieces of pineapple cheesecake

Now the objective (target) function can be written in terms of the decision variables. The cost of the diet `z` needs to be minimized:

The next step is to write the inequalities for the constraints. Note that there’s a minimum amount of calories, chocolate, sugar, and fat that the food in the diet should provide. Each of these four requirements is a constraint, so the constraints can be written like this:

Note that pineapple cheesecake and soda do not contain chocolate.

And finally, the sign constraints:

Try to imagine the feasible region for this problem. The problem has four variables. So, the universe has four axes. That’s hard to plot, so use your imagination. At first the solution could be anywhere in the four-dimensional space. With each constraint, the solution space shrinks to what looks like a polyhedron.

GNU MathProg solution for the diet problem

Note: The line numbers in Listing 1 are for reference only.

Listing 1. GNU MathProg solution of the diet problem

 ``` 1 # 2 # Diet problem 3 # 4 # This finds the optimal solution for minimizing the cost of my diet 5 # 6 7 /* sets */ 8 set FOOD; 9 set NEED; 10 11 /* parameters */ 12 param NutrTable {i in FOOD, j in NEED}; 13 param Cost {i in FOOD}; 14 param Need {j in NEED}; 15 16 /* decision variables: x1: Brownie, x2: Ice cream, x3: soda, x4: cake*/ 17 var x {i in FOOD} >= 0; 18 19 /* objective function */ 20 minimize z: sum{i in FOOD} Cost[i]*x[i]; 21 22 /* Constraints */ 23 s.t. const{j in NEED} : sum{i in FOOD} NutrTable[i,j]*x[i] >= Need[j]; 24 25 /* data section */ 26 data; 27 28 set FOOD := Brownie "Ice cream" soda cake; 29 set NEED := Calorie Chocolate Sugar Fat; 30 31 param NutrTable: Calorie Chocolate Sugar Fat:= 32 Brownie 400 3 2 2 33 "Ice cream" 200 2 2 4 34 soda 150 0 4 1 35 cake 500 0 4 5; 36 37 param Cost:= 38 Brownie 0.5 39 "Ice cream" 0.2 40 soda 0.3 41 cake 0.8; 42 43 param Need:= 44 Calorie 500 45 Chocolate 6 46 Sugar 10 47 Fat 8; 48 49 end; ```

Lines 8 and 9 define two sets: `FOOD` and `NEED`, but the elements of those two sets are declared in the data section on lines 28 and 29. The `FOOD` set contains the four food types given.

Because the space character separates the elements of a set, the `Ice cream` element needs double quotes around the name (instead of the less attractive `icecream`, for example). If you want to use non-ASCII characters in MathProg names, you should use double quotes as well even if there are no spaces.

Back to the model section. The `NEED` set holds the four dietary needs. Lines 12, 13, and 14 define the problem parameters: `Need``Cost`, and `NutrTable` (the nutrition table). There is a cost for each `FOOD` item. There is an amount for each nutritional need in the `NEED` set. Try to use a differently named index variable for each set consistently; it’s a good idea, especially when debugging. In this case, the `FOOD` set uses `i` and the `NEED` set uses `j`. The `Cost`and `Need` parameters in the data section are defined in lines 37 through 47.

The nutritional table defined on line 12 and filled with data in lines 31 through 35 has two dimensions, since each food provides multiple nutritional values. Therefore, the nutrition table `NutrTable` is a mapping between theFOOD and NEED sets. Note that the row and column order is the same as the element order in each set, and the index variable names are consistent between lines 12, 13, and 14.

Throughout this exercise, the `i` axes are the rows, and the `j` axes are the columns as expected by most mathematicians in the audience. The syntax for two-dimensional parameter declaration (up to N columns and M rows) is:

Listing 2. Syntax for two-dimensional parameter declaration

 ```param label : J_SET_VAL_1 J_SET_VAL_2 ... J_SET_VAL_N := I_SET_VAL_1 param_1_1 param_1_2 ... param_1_N I_SET_VAL_2 param_2_1 param_2_2 ... param_2_N ... ... ... ... ... I_SET_VAL_M param_M_1 param_M_2 ... param_M_N; ```

Don’t forget the `:=` at the end of the first line, and the `;` at the end of the last line.

Line 17 declares the decision variables and states that each of them can’t be a negative value. It’s a very simple definition, because the array `x` is defined automatically with the elements of the `FOOD` set.

The objective function on line 20 minimizes the total food cost as the total of each decision variable (amount of food) multiplied by that food’s cost per unit. Note that the `i` index is on the `FOOD` set.

Line 23 declares all the four dietary constraints. It’s written in a very compact and elegant style, so pay attention! The definition on the left of the colon `:` tells GLPK to create a constraint for each need in the `NEED` set:`const[Calorie]``const[Chocolate]``const[Sugar]`, and `const[Fat]`. Each of these constraints takes every nutritional need, and adds up the amount of each food type multiplied by how much of that need it can satisfy per food unit; this total must be equal to or greater than the minimal need for that nutrient for a balanced diet.

Expanded, this declaration would look like this (`i` covers the `FOOD` set):

Listing 3. The output of glpsol for this problem

 ```Problem: diet Rows: 5 Columns: 4 Non-zeros: 18 Status: OPTIMAL Objective: z = 0.9 (MINimum) No. Row name St Activity Lower bound Upper bound Marginal ------ ------------ -- ------------- ------------- ------------- ------------- 1 z B 0.9 2 const[Calorie] B 750 500 3 const[Chocolate] NL 6 6 0.025 4 const[Sugar] NL 10 10 0.075 5 const[Fat] B 13 8 No. Column name St Activity Lower bound Upper bound Marginal ------ ------------ -- ------------- ------------- ------------- ------------- 1 x[Brownie] NL 0 0 0.275 2 x['Ice cream'] B 3 0 3 x[soda] B 1 0 4 x[cake] NL 0 0 0.5 Karush-Kuhn-Tucker optimality conditions: KKT.PE: max.abs.err. = 1.82e-13 on row 2 max.rel.err. = 2.43e-16 on row 2 High quality KKT.PB: max.abs.err. = 0.00e+00 on row 0 max.rel.err. = 0.00e+00 on row 0 High quality KKT.DE: max.abs.err. = 5.55e-17 on column 3 max.rel.err. = 4.27e-17 on column 3 High quality KKT.DB: max.abs.err. = 0.00e+00 on row 0 max.rel.err. = 0.00e+00 on row 0 High quality End of output ```

These results show that the minimum cost and optimal value of the diet is \$0.90. Which constraints have bound this solution?

The second section of the report says that both the chocolate and sugar constraints are lower-bounded, so this diet uses the minimum amount of chocolate and sugar required. The marginal tells us that if we could relax the chocolate constraint by one unit (5 oz instead of 6 oz), the objective function would improve by 0.025 (and would go from 0.9 to 0.875). Analogously, if we could relax the sugar constraint by one unit, the objective function would improve by 0.075. This makes sense: eat less, pay less. It’s important to do these common sense checks on marginals and amounts. For example, if you are told it’s optimal to eat 200 lbs. of chocolate and no calories, you should be a little suspicious (and grateful, maybe).

The third section reports the optimal values of the decision variables: 3 scoops of ice cream and one bottle of soda. The brownie and pineapple cheesecake have a marginal value, because their value is at the limit of their sign constraints. This marginal says that if the value of the brownie variable could ever be -1, the objective function would improve by 0.275, but of course that is not useful for this problem’s setup.

Post office problem

From “Operations Research“:

A post office requires a different number of full-time employees working on different days of the week [summarized below]. Union rules state that each full-time employee must work for 5 consecutive days and then receive two days off. For example, an employee who works on Monday to Friday must be off on Saturday and Sunday. The post office wants to meet its daily requirements using only full-time employees. Minimize the number of employees that must be hired.

To summarize the important information about the problem:

• Every full-time worker works for 5 consecutive days and takes 2 days off
• Day 1 (Monday): 17 workers needed
• Day 2 : 13 workers needed
• Day 3 : 15 workers needed
• Day 4 : 19 workers needed
• Day 5 : 14 workers needed
• Day 6 : 16 workers needed
• Day 7 (Sunday) : 11 workers needed

The post office needs to minimize the number of employees it needs to hire to meet its demand.

Modeling

Let’s start with the decision variables for this problem. You could try with seven variables, one for each day of the week, with a value equal to the number of employees that work on each day of the week. Although this seems to solve the problem at first, it can’t enforce the constraint that an employee will work five days per week, because working on a given day can’t require working on the next day.

The correct approach should guarantee that an employee that starts working on day `i` will also work on the four following days, so the correct approach is to use `xi` as the number of employees that start working on day `i`. With this approach, it’s much simpler to enforce the consecutiveness constraint. The decision variables are then:

• `x1`: number of employees who start working on Monday
• `x2`: number of employees who start working on Tuesday
• `x3`: number of employees who start working on Wednesday
• `x4`: number of employees who start working on Thursday
• `x5`: number of employees who start working on Friday
• `x6`: number of employees who start working on Saturday
• `x7`: number of employees who start working on Sunday

The objective function that needs to be minimized is the amount of employees hired, which is given by:

Now, what are the constraints? Each day of the week has a constraint to ensure the minimum amount of workers for that day. Let’s take Monday as an example. Who will be working on Mondays? The first (partial) answer that comes to mind is “the people who start working on Mondays.” But who else? Well, considering that employees must work for five consecutive days, it’s expected that the employees that started working on Sunday will also be working on Monday (remember the problem definition). This same reasoning is used to conclude that the employees who start working on Saturday, Friday, and Thursday will also be working on Monday.

This constraint ensures that there will be at least 17 employees working on Monday.

Similarly:

Don’t forget the sign constraints, of course:

GNU MathProg solution for the post office problem

Note: The line numbers in Listing 4 are for reference only.

Listing 4. Post office problem solution

 ``` 1 # 2 # Post office problem 3 # 4 # This finds the optimal solution for minimizing the number of full-time 5 # employees to the post office problem 6 # 7 8 /* sets */ 9 set DAYS; 10 11 /* parameters */ 12 param Need {i in DAYS}; 13 14 /* Decision variables. x[i]: No. of workers starting at day i */ 15 var x {i in DAYS} >= 0; 16 17 /* objective function */ 18 minimize z: sum{i in DAYS} x[i]; 19 20 /* Constraints */ 21 22 s.t. mon: sum{i in DAYS: i<>'Tue' and i<>'Wed'} x[i] >= Need['Mon']; 23 s.t. tue: sum{i in DAYS: i<>'Wed' and i<>'Thu'} x[i] >= Need['Tue']; 24 s.t. wed: sum{i in DAYS: i<>'Thu' and i<>'Fri'} x[i] >= Need['Wed']; 25 s.t. thu: sum{i in DAYS: i<>'Fri' and i<>'Sat'} x[i] >= Need['Thu']; 26 s.t. fri: sum{i in DAYS: i<>'Sat' and i<>'Sun'} x[i] >= Need['Fri']; 27 s.t. sat: sum{i in DAYS: i<>'Sun' and i<>'Mon'} x[i] >= Need['Sat']; 28 s.t. sun: sum{i in DAYS: i<>'Mon' and i<>'Tue'} x[i] >= Need['Sun']; 29 30 data; 31 32 set DAYS:= Mon Tue Wed Thu Fri Sat Sun; 33 34 param Need:= 35 Mon 17 36 Tue 13 37 Wed 15 38 Thu 19 39 Fri 14 40 Sat 16 41 Sun 11; 42 43 end; ```

Line 9 declares a set named `DAYS`, and its elements (just the days of the week, starting with Monday) are declared on line 32 in the data section.

Line 12 declares the parameter `Need` for each day in the `DAYS` set. Lines 34 through 41 define the values for this parameter: the minimum employees needed on each day of the week.

Line 15 declares the decision variables as an array of seven variables defined on the `DAYS` set, representing the number of people that start work that day.

Lines 22 through 28 define one constraint for each day of the week. Note that it would be too boring to write seven inequalities as a sum of five decision variables that are not necessarily in order, because in some constraints, the index may overlap the index 7. GNU MathProg offers expressions for the program writer to make this easier.

Each constraint is the total of all the decision variables, except for those two that should not take part in that specific day (instead of including the five others specifically). This expression is used inside the braces `{}` that define the index of the summation. The syntax for an expression is:

`{index_variable in your_set: your_expression}`

The expression can use logical comparisons. In this case, the Monday constraint uses: `i<>'Tue' and i<>'Wed'`, which means “when `i` is different from `Tue` and also when `i` is different from `Wed`.” The same happens analogously for all the other constraints.

The `==` logical comparison can also be used in expressions.

Listing 5. Solution of this problem in glpsol

 ```Problem: post Rows: 8 Columns: 7 Non-zeros: 42 Status: OPTIMAL Objective: z = 22.33333333 (MINimum) No. Row name St Activity Lower bound Upper bound Marginal ------ ------------ -- ------------- ------------- ------------- ------------- 1 z B 22.3333 2 mon NL 17 17 0.333333 3 tue B 15 13 4 wed NL 15 15 0.333333 5 thu NL 19 19 0.333333 6 fri NL 14 14 < eps 7 sat NL 16 16 0.333333 8 sun B 15.6667 11 No. Column name St Activity Lower bound Upper bound Marginal ------ ------------ -- ------------- ------------- ------------- ------------- 1 x[Mon] B 1.33333 0 2 x[Tue] B 5.33333 0 3 x[Wed] NL 0 0 < eps 4 x[Thu] B 7.33333 0 5 x[Fri] NL 0 0 0.333333 6 x[Sat] B 3.33333 0 7 x[Sun] B 5 0 Karush-Kuhn-Tucker optimality conditions: KKT.PE: max.abs.err. = 3.55e-15 on row 6 max.rel.err. = 2.37e-16 on row 6 High quality KKT.PB: max.abs.err. = 0.00e+00 on row 0 max.rel.err. = 0.00e+00 on row 0 High quality KKT.DE: max.abs.err. = 5.55e-17 on column 1 max.rel.err. = 2.78e-17 on column 1 High quality KKT.DB: max.abs.err. = 5.55e-17 on row 6 max.rel.err. = 5.55e-17 on row 6 High quality End of output ```

Hey, wait a minute! No one can make 1.33333 employees start working on Monday! Remember the comment earlier about common-sense checks. This is one of them.

GLPK has to consider the decision variables as integer variables. Fortunately, MathProg has a nice way of declaring integer variables. Just change line 15 like so:

`var x {i in DAYS} >=0, integer;`

That’s pretty simple. The output `glpsol` prints out for an integer problem is a little bit different:

Listing 6. glpsol output for the integer-constrained post office problem

 ```Reading model section from post-office-int.mod... Reading data section from post-office-int.mod... 50 lines were read Generating z... Generating mon... Generating tue... Generating wed... Generating thu... Generating fri... Generating sat... Generating sun... Model has been successfully generated lpx_simplex: original LP has 8 rows, 7 columns, 42 non-zeros lpx_simplex: presolved LP has 7 rows, 7 columns, 35 non-zeros lpx_adv_basis: size of triangular part = 7 0: objval = 0.000000000e+00 infeas = 1.000000000e+00 (0) 7: objval = 2.600000000e+01 infeas = 0.000000000e+00 (0) * 7: objval = 2.600000000e+01 infeas = 0.000000000e+00 (0) * 10: objval = 2.233333333e+01 infeas = 0.000000000e+00 (0) OPTIMAL SOLUTION FOUND Integer optimization begins... Objective function is integral + 10: mip = not found yet >= -inf (1; 0) + 19: mip = 2.300000000e+01 >= 2.300000000e+01 0.0% (9; 0) + 19: mip = 2.300000000e+01 >= tree is empty 0.0% (0; 17) INTEGER OPTIMAL SOLUTION FOUND Time used: 0.0 secs Memory used: 0.2M (175512 bytes) lpx_print_mip: writing MIP problem solution to `post-office-int.sol'... ```

Note that the output now shows that an integer optimal solution is found, and that before that happens, GLPK has calculated the optimal solution for the relaxed problem (the problem that does not require the variables to be integer).

Listing 7. The integer solution of the post office problem

 ```Problem: post Rows: 8 Columns: 7 (7 integer, 0 binary) Non-zeros: 42 Status: INTEGER OPTIMAL Objective: z = 23 (MINimum) 22.33333333 (LP) No. Row name Activity Lower bound Upper bound ------ ------------ ------------- ------------- ------------- 1 z 23 2 mon 18 17 3 tue 13 13 4 wed 15 15 5 thu 19 19 6 fri 14 14 7 sat 16 16 8 sun 20 11 No. Column name Activity Lower bound Upper bound ------ ------------ ------------- ------------- ------------- 1 x[Mon] * 1 0 2 x[Tue] * 2 0 3 x[Wed] * 3 0 4 x[Thu] * 7 0 5 x[Fri] * 1 0 6 x[Sat] * 3 0 7 x[Sun] * 6 0 End of output ```

The first section says that the solution found is integer optimal, and that the value found for the objective function is 23, a minimum. The post office will have to hire 23 full-time employee to satisfy its goals. The optimal value of the relaxed objective function (the one that didn’t consider the decision variables as integers) is also printed here.

Skip the second section of the report for a second. The third section shows the values of the decision variables. Those values are integers that minimize the problem’s objective function.

Now, let’s talk about the second section. It shows the activity of the constraints. Some of them are lower bounded, and you probably expect a marginal value or shadow price by now. It doesn’t make sense, though, to talk about the marginal value in integer problems. The feasible region of integer problems is not a continuous region. In other words, the feasible region is not a polyhedron; it’s composed only of the integer (`x1, x2, ..., xn`) pairs inside or at the boundaries of the actual polyhedron of the relaxed problem. This means that the feasible region consists of discrete points in space and, therefore, a relaxation in one of the constraints may or may not yield a better solution inside the new polyhedron.

A little bit of theory

To understand better what is being discussed here, examine this simple feasible region:

Figure 1. Feasible region for an integer problem

The blue points marked as `x` are integer (`x1,x2`) pairs. This is the integer-feasible region of a `x1 X x2` two-dimensional universe constrained by `x1 >= 0``x2 >= 0`, and `x1 + x2 <= 4`. If the objective function for this simple case was `maximize z = x1 - x2`, it’s clear that the optimal solution would be `(2,0)`, and the constraint would be bounded (because the optimal solution lies on the line ruled by the constraint).

If the constraint was relaxed by one unit, `x1 + x2 <= 5`, the feasible region would now be different.

Figure 2. Feasible region for a different integer problem

The optimal solution would still be the integer point `(2,0)`, though more integer points are feasible now.

Thus, a relaxation of the constraint of an integer problem does not necessarily improve the solution, because the feasible region is discrete and not continuous.

Another question you might ask is: “Is the optimal solution of an integer somehow related to its relaxed problem’s optimal solution?” The answer is in the algebra behind the simplex algorithm, but explaining how it works is beyond the scope of this article. Just know that the optimal solution for a non-integer problem is always one of the polyhedron vertices. Always!

In the first feasible region above, the optimal solution is the right vertex of the solution space, which is a triangle made by all the constraints. The objective function increases in the direction of the directional derivative of the objective function. In the simple case, the directional derivative of `x1 - x2` is `(1,-1)`. Therefore, the optimal solution is the farthest point on the polyhedron boundary from the axis’ origin using the direction `(1,-1)` as a direction vector. Because the integer solution may lie only on a boundary or within the polyhedron, the best-case scenario is to have an integer solution on a vertex. In this particular case, the optimal solution for both the integer and non-integer problems is the same, but that’s not always the case. Why? Because this integer point may not be as far from the origin as the relaxed solution point. For the other non-best-case scenarios, the best integer point would lie within the polyhedron, and the objective function would naturally have worse performance than the one for the relaxed problem, which you might note from the post-office problem.

Remember that this analysis used a simple two-variable maximization problem. The same analysis for a minimization problem looks for the point that minimizes the objective function, which is the one closest to the origin (in the opposite direction of the directional derivative of the objective function).

Conclusion

With the diet problem, you saw how to formulate a simple, multi-variable problem, how to declare bidimensional parameters in GNU MathProg, and how to interpret the results of a minimization problem.

The post office problem introduced MathProg expressions and integer-only decision variables. You saw how to analyze `glpsol` output for the integer problem.

Finally, I discussed feasible region visualization for an integer problem and the way it relates to the objective function for an integer and for the corresponding relaxed problem.

The third and final installment discusses a problem to maximize the profits of a perfume manufacturer, and includes an example that illustrates binary decision variables using a basketball lineup problem.

Resources

Learn

Get products and technologies

• Order the SEK for Linux, a two-DVD set containing the latest IBM trial software for Linux from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
• With IBM trial software, available for download directly from developerWorks, build your next development project on Linux.

Discuss

Rodrigo Ceron Ferreira de Castro is a Staff Software Engineer at the IBM Linux Technology Center. He graduated from the State University of Campinas (UNICAMP) in 2004. He received the State Engineering Institute prize and the Engineering Council Certification of Honor when he graduated. He’s given speeches in open source conferences in Brazil and other countries.

http://web.archive.org/web/20131010153918/http://www.ibm.com/developerworks/linux/library/l-glpk2/

# The GNU Linear Programming Kit, Part 3: Advanced problems and elegant solutions

Maximizing the profitability of perfume and building a better basketball team

Summary:  The GNU Linear Programming Kit (GLPK) is a powerful, proven tool for solving numeric problems with multiple constraints. This article, the third in a three-part series, uses GLPK and the`glpsol` client utility with the GNU MathProg language to solve a perfume production problem and a basketball lineup problem.

View more content in this series

Date:  14 Nov 2006
Level:  Intermediate
Also available in:   Russian

Activity:  24120 views

This article is the third in a three-part series on using the GNU Linear Programming Kit. For an introduction to GLPK, read the first installment in the series, “The GNU Linear Programming Kit, Part 1: Introduction to linear optimization.”

[All company and product names given in the example problems are fictional. -Ed.]

Brute production process

This problem comes from Operations Research: Applications and Algorithms, 4th Edition, by Wayne L. Winston (Thomson, 2004); see Resources below for a link.

Rylon Corporation manufactures Brute and Chanelle perfumes. The raw material needed to manufacture each type of perfume can be purchased for \$3 per pound. Processing one pound of raw material requires 1 hour of laboratory time. Each pound of processed raw material yields 3 ounces of Regular Brute Perfume and 4 ounces of Regular Chanelle Perfume. Regular Brute can be sold for \$7/ounce and Regular Chanelle for \$6/ounce. Rylon also has the option of further processing Regular Brute and Regular Chanelle to produce Luxury Brute, sold at \$18/ounce, and Luxury Chanelle, sold at \$14/ounce. Each ounce of Regular Brute processed further requires an additional 3 hours of lab time and \$4 processing cost and yields 1 ounce of Luxury Brute. Each ounce of regular Chanelle processed further requires an additional 2 hours of lab time and \$4 processing cost and yields 1 ounce of Luxury Chanelle. Each year, Rylon has 6,000 hours of lab time available and can purchase up to 4,000 pounds of raw material. Maximize Rylon’s profit.

To summarize the important information about the problem:

• Rylon Corporation manufactures Brute and Chanelle perfumes.
• Raw material costs \$3 per lb.
• Processing 1 lb. of raw material takes 1 hr. of laboratory time.
• Each lb. of raw material yields 3 oz. of Regular Brute Perfume and 4 oz. of Regular Chanelle Perfume.
• Regular Brute sells for \$7/oz. and Regular Chanelle for \$6/oz.
• Reprocessing yields Luxury Brute, sold at \$18/oz., and Luxury Chanelle, sold at \$14/oz.
• Each oz. of Regular Brute processed further costs an additional 3 hrs. of lab time and \$4 in processing cost and yields 1 oz. of Luxury Brute.
• Each oz. of regular Chanelle processed further costs an additional 2 hrs. of lab time and \$4 of processing cost and yields 1 oz. of Luxury Chanelle.
• Yearly constraints: 6,000 hrs. of lab time available, and Rylon can purchase up to 4,000 lbs. of raw material.

Before modeling this problem, let’s look at the transformation from raw materials to end products:
Figure 1. Overview of Rylon production

Figure 1 shows that all the raw material is transformed into intermediate products (represented by the intermediate balloons in the figure). Take the upper intermediate balloon as an example representing the Regular Brute product. Some of the Regular Brute may be reprocessed to generate the luxury Brute, so the balloon is split in two: the Regular Brute that will be sold (green) and the Regular Brute that will be reprocessed to make Luxury Brute (blue). The same applies to the Chanelle. Note that only the blue part of those two intermediate balloons (and all of the blue part) will be reprocessed.

Modeling the Rylon problem

The decision variables for this particular problem need to cover all the perfumes and materials:

• `x1`: ounces of Regular Brute sold annually
• `x2`: ounces of Luxury Brute sold annually
• `x3`: ounces of Regular Chanelle sold annually
• `x4`: ounces of Luxury Chanelle sold annually
• `x5`: pounds of raw material purchased annually

The objective function can be written to maximize the profit in terms of these decision variables:

The trivial constraints from the problem are related to the laboratory hours available to process the materials. Rylon needs time to process the amount of raw material it buys, which is given by `x5`. Time is also needed to reprocess regular Brute and Chanelle into the luxury Brute and Chanelle, so the total time will depend on `x2` and`x4` in addition to `x5`:

The maximum amount of raw material Rylon can buy is also a constraint:

As always, the sign constraints are important:

If this model is converted to GNU MathProg and solved by `glpsol`, the solver will report that the problem is unbounded: the more Rylon produces, the bigger its profit is. Hey, this sounds like a good business if you want to move to the Caribbean Islands, but common sense says it can’t happen. So, where’s the flaw?

The raw material needs to be bound to the Regular Brute and Chanelle, and those two need to be bound to their Luxury versions. How can two variables be bound? The conversion of raw material to Chanelle and Brute is not 1 pound to one ounce, as you can see in the problem description. Each pound of raw material generates three ounces of regular Brute and four ounces of regular Chanelle (seven ounces of perfume in total). Think of this transformation as a black box. If you put one pound of raw material into the black box, you’ll get exactly 3 ounces of regular Brute and four ounces of regular Chanelle out of the box. If you have processed `N` pounds of raw material, the upper intermediate balloon in Figure 1must be `3N`. This is Regular plus Luxury Brute.

Remember that the transformation of one ounce of Regular perfume yields one ounce of Luxury perfume for both Brute and Chanelle. In addition, the lower intermediate balloon for Regular and Luxury Chanelle is `4N`ounces total. Rylon cannot, say, use `N` pounds of raw material to create `2N` ounces of Regular plus Luxury Brute and `5N` of Regular plus Luxury Chanelle. The 1-to-3 Brute transformation and 1-to-4 Chanelle transformation must both hold, regardless of the Regular-to-Luxury ratios within the Brute and Chanelle production yields.

The amount of perfume produced must have been generated from the raw material. If the equilibrium is not enforced, the results will be wrong! Hence the following constraint:

This equation says that all the Brute produced (which is the Regular Brute for sale plus the Regular Brute for reprocessing) divided by 3 must equal one unit of raw material. Does that sound correct? The problem says that processing one unit of raw material (one pound) generates 3 units (3 ounces) of Regular Brute, and we know that 1 unit of Regular Brute may be transformed to 1 ounce of its Luxury line. Note here again that all the Regular and Luxury units (ounces) of Brute need to be exactly 3 times greater than the units of raw material processed. So, this constraint seems to be okay. Because GLPK requires that all decision variables be on the same side of the inequality, we write the previous equation as follows:

Similarly, we have that:

Does that seem right? One unit of raw material generates 4 ounces of Chanelle (which is Regular plus Luxury), so this seems okay too. For GLPK, the equation is written with all the decision variables on the left:

Now, the problem is complete. Let’s write a GNU MathProg program for it.

GNU MathProg solution for Rylon’s problem

The following code for `glpsol` solves the Rylon problem. (The line numbers are not part of the code itself. I’ve added them to make it easier to reference the code later.)
Listing 1. Rylon production process solution

 ``` 1 # 2 # Rylon production proccess 3 # 4 # This finds the optimal solution for maximizing Rylon's profit 5 # 6 /* sets */ 7 set PROD; 8 9 /* parameters */ 10 param Rev{i in PROD}; 11 param Cost{i in PROD}; 12 param Labh{i in PROD}; 13 14 var x{i in PROD} >= 0; /*x1: No. of oz of Regular Brute 15 x2: No. of oz of Luxury Brute 16 x3: No. of oz of Regular Chanelle 17 x4: No. of oz of Luxury Chanelle 18 x5: No. of lbs of raw material */ 19 20 maximize z: sum{i in PROD} (Rev[i]*x[i] - Cost[i]*x[i]); 21 22 /* Constraints */ 23 s.t. raw: x['raw'] <= 4000; 24 s.t. time: sum{i in PROD} Labh[i]*x[i] <= 6000; 25 s.t. mass1: x['rb'] + x['lb'] - 3*x['raw'] = 0; 26 s.t. mass2: x['rc'] + x['lc'] - 4*x['raw'] = 0; 27 28 data; 29 set PROD:= rb lb rc lc raw; 30 31 param Rev:= 32 rb 7 33 lb 18 34 rc 6 35 lc 14 36 raw 0; 37 38 param Labh:= 39 rb 0 40 lb 3 41 rc 0 42 lc 2 43 raw 1; 44 45 param Cost:= 46 rb 0 47 lb 4 48 rc 0 49 lc 4 50 raw 3; 51 52 end; ```

You should recognize all the declarations of the previous code, but I’ll review them quickly for the sake of completeness.

Line 7 declares a set named `PROD`, whose elements are the Brute and Chanelle products. Line 29 defines the products. The four abbreviations stand for Regular BruteLuxury BruteRegular Chanelle, and Luxury Chanelle, and the last element is the raw material. Raw material is listed as a product so that all the decision variables can be in one set.

Lines 10, 11, and 12 declare the parameters: revenue, cost, and lab hours. The parameters are defined on lines 31 through 50. The lab hours say how long it takes to process a pound of raw material and to reprocess the Regular Brute and Chanelle into their Luxury versions (per ounce). The cost consists of the cost of the raw materials per pound and the cost of reprocessing the Regular Brute and Chanelle per ounce.

Line 14 defines the decision variables, a five-element array on the `PROD` set.

Line 20 defines the objective function, which is just Rylon’s profit (revenue – costs).

Finally, lines 23 through 26 declare the problem’s constraints, discussed above.

Listing 2 shows the output:
Listing 2. The Rylon report from glpsol

 ```Problem: brute Rows: 5 Columns: 5 Non-zeros: 15 Status: OPTIMAL Objective: z = 172666.6667 (MAXimum) No. Row name St Activity Lower bound Upper bound Marginal ------ ------------ -- ------------- ------------- ------------- ------------- 1 z B 172667 2 raw NU 4000 4000 39.6667 3 time NU 6000 6000 2.33333 4 mass1 NS 0 -0 = 7 5 mass2 NS 0 -0 = 6 No. Column name St Activity Lower bound Upper bound Marginal ------ ------------ -- ------------- ------------- ------------- ------------- 1 x[rb] B 11333.3 0 2 x[lb] B 666.667 0 3 x[rc] B 16000 0 4 x[lc] NL 0 0 -0.666667 5 x[raw] B 4000 0 Karush-Kuhn-Tucker optimality conditions: KKT.PE: max.abs.err. = 1.46e-11 on row 1 max.rel.err. = 8.43e-17 on row 1 High quality KKT.PB: max.abs.err. = 0.00e+00 on row 0 max.rel.err. = 0.00e+00 on row 0 High quality KKT.DE: max.abs.err. = 8.88e-16 on column 2 max.rel.err. = 5.92e-17 on column 2 High quality KKT.DB: max.abs.err. = 0.00e+00 on row 0 max.rel.err. = 0.00e+00 on row 0 High quality End of output ```

The first section shows that the solution is optimal and that the objective function equals approximately 172667. The third section has the values of each decision variable. The second section shows the constraints. Note that the mass conservation constraints always have an `=` sign in one of the bounds columns. Their marginal values can’t be analyzed, because it makes no sense to have a marginal value with equality constraints. Do you agree? Send me a note if you have a different opinion.

Let’s make a brief, common-sense analysis here. Rylon has processed 4,000 pounds of raw material. According to the mass conservation (the black box that transforms raw material into Chanelle and Brute), we need to enforce that `N` pounds of raw material produce `3N` ounces of Brute (Regular plus Luxury) and also `4N` ounces of Chanelle (Regular plus Luxury). There are 16,000 ounces of Regular Chanelle and no ounces of Luxury Chanelle. So, we have the 1 (4,000 pounds) to 4 (16,000 ounces) proportion for Chanelle. Analogously, we also have the 1 (4,000 pounds) to 3 (11,333.333 ounces plus 666.667 ounces) proportion for Brute. Good, we are in the real world.

Without the mass conservation constraints, `glpsol` prints this error to `stdout` like so:
Listing 3. Errors without mass conservation

 ```Reading model section from brute-production2.mod... Reading data section from brute-production2.mod... 61 lines were read Generating z... Generating raw... Generating time... Model has been successfully generated lpx_simplex: original LP has 3 rows, 5 columns, 9 non-zeros PROBLEM HAS NO DUAL FEASIBLE SOLUTION If you need actual output for non-optimal solution, use --nopresol Time used: 0.0 secs Memory used: 0.1M (149646 bytes) lpx_print_sol: writing LP problem solution to `brute-production2.sol'... ```

It says that the problem has no dual feasible solution, but what does that mean? Every problem has a dual equivalent. For example, a maximization problem can be rewritten as a minimization problem. The maximization problem is then called the primal problem, and the minimization is the dual problem. If the primal were a minimization problem, then the dual would be a maximization problem. Dual really means secondary or alternate, and primal means primary, or main, in plain English.

A primal problem that has no solution has an unbounded dual problem. An unbounded primal problem has a dual problem that’s not feasible! Because `glpsol` said the dual problem is not feasible, the primal problem is unbounded (the Caribbean Islands situation I mentioned above).

Multiple solution problem

This section of the article describes a simple problem with multiple optimal solutions. All the problems I’ve presented in this series so far had only one optimal solution. A problem with multiple solutions has more than one point in its feasible region that either maximize or minimize a problem. The objective function for these critical points are the same, regardless. Here’s an example.

Modeling a multiple solution problem

Maximize the following objective function:

subject to the following two constraints:

The feasible region for this particular problem is given by the gray area of Figure 2.
Figure 2. Feasible region of the multiple solution problem

Before writing a GNU MathProg program to find the optimal solution for this problem, analyze it a little bit. You may recall from Part 2 in this series that the directional derivative of the objective function gives the direction in which the objective function grows in a maximization problem. Recall that the optimal solution is always on one of the vertices of the polyhedron created by the feasible region. When the directional derivative of the objective function is perpendicular to one of the sides of the polyhedron (formed by the constraints), all the points on that side of the polyhedron will have the same value for the objective function, because they lie on a same iso-quanta.

An iso-quanta is a line in space formed by points with the same objective function value. In this problem,`x1+x2=1` is an iso-quanta, `x1+x2=2` is another iso-quanta, and so on. If a constraint line is the farthest one along the directional derivative of the objective function and is perpendicular to that derivative, then all points on that boundary of the feasible region will make the objective solution reach its optimal value.

GNU MathProg solution for the multiple solution problem

The following is a GNU MathProg solution for the multiple solution problem:
Listing 4. Multiple solution problem solved with MathProg

 ``` 1 # 2 # Multiple solution problem 3 # 4 # This finds the optimal solution for a simple multiple solution problem 5 # 6 7 /* decision variables */ 8 var x1 >=0; 9 var x2 >=0; 10 11 /* objective function */ 12 maximize z: x1 + x2; 13 14 /* constraints */ 15 s.t. Ctr: x1 + x2 <= 5; 16 17 end; ```

This very simple program declares the two decision variables: the constraint and the objective function.

Listing 5 shows the results:
Listing 5. glpsol’s report

 ```Problem: multiple Rows: 2 Columns: 2 Non-zeros: 4 Status: OPTIMAL Objective: z = 5 (MAXimum) No. Row name St Activity Lower bound Upper bound Marginal ------ ------------ -- ------------- ------------- ------------- ------------- 1 z B 5 2 Ctr NU 5 5 1 No. Column name St Activity Lower bound Upper bound Marginal ------ ------------ -- ------------- ------------- ------------- ------------- 1 x1 B 5 0 2 x2 NL 0 0 < eps Karush-Kuhn-Tucker optimality conditions: KKT.PE: max.abs.err. = 0.00e+00 on row 0 max.rel.err. = 0.00e+00 on row 0 High quality KKT.PB: max.abs.err. = 0.00e+00 on row 0 max.rel.err. = 0.00e+00 on row 0 High quality KKT.DE: max.abs.err. = 0.00e+00 on column 0 max.rel.err. = 0.00e+00 on column 0 High quality KKT.DB: max.abs.err. = 0.00e+00 on row 0 max.rel.err. = 0.00e+00 on row 0 High quality End of output ```

The solution is optimal and the objective function value is 5. The second half of the report shows that the constraint is bounded and its marginal value is 1.

In the third section, the variable `x2` is on its lower bound and has a marginal value. This marginal value is reported as `<eps`, which is the unit of precision GLPK uses internally (in math, `epsilon` is commonly used to indicate a very small number, and this is the abbreviation). Something smaller than the minimal precision is very likely to be zero. Therefore, the `x2` variable would change the value of the objective function by 0 if it could ever be relaxed. In other words, a change in `x2` won’t change the objective function’s value, given that the constraint holds for the new (`x1``x2`) pair. So there are multiple points in the feasible region that yield the same value for the objective function. This problem has multiple solutions!

Remember that whenever `glpsol` reports that the marginal value of a variable is `<eps`, that indicates a multiple solution problem.

The set covering problem teaches binary decision variables; that is, they can only be 0 or 1, yes or no.

A basketball coach intends to set up his team for a game. The team consists of seven players from which five will play. The abilities of each player have been measured on a scale of 1 (poor) to 3 (excellent) for the skills of assisting, defending, throwing, and rebounding. There are three positions in which they can play: back-field (B), mid-field (M), and front-field (F). The positions in which each player can play and their abilities are shown in Table 1.

Table 1. Players and their abilities

Player Position Assisting Throwing Rebound Defense
1 B 3 3 1 3
2 M 2 1 3 2
3 BM 2 3 2 2
4 MF 1 3 3 1
5 BF 1 3 1 2
6 MF 3 1 2 3
7 BF 3 2 2 1

The five chosen players must collectively satisfy the following constraints:

• At least three players must be capable of playing back-field.
• At least two players must be capable of playing front-field.
• At least one player must be capable of playing mid-field.
• The five players’ average in terms of assisting, rebounding, defending, and throwing must be at least 2.
• If player 3 is playing, then player 6 can’t play, and vice-versa.
• If player 1 is playing, then players 4 and 5 must also play.
• Either player 2 or player 3 must play.

The objective is to maximize the defensive ability of the players for this game.

First, decide what decision variables to use. There are seven players on the team. Which five of them will play? Seven binary variables will decide if each player i will play (if the decision variable is 1) or not (0):

The objective function seeks to maximize the defensive ability of the players.

There is no need to divide the objective function by 5 to obtain the average, because maximizing `f(x)` and `f(x)`divided by `CONSTANT` is the same. In other words, the division doesn’t change the direction that maximizes the objective function.

Next come the constraints. The first one says that at least three players must be able to play in the back-field. Only players 1, 3, 5, and 7 can do that:

This constraint, therefore, ensures that at least three of those four binary variables will be 1.

Similarly, for the mid-field, one of the players 2, 3, 4, and 6 is needed:

The front-field needs two of the players 4, 5, 6, and 7.

The average ability to assist, rebound, defend, and throw must be at least 2:

The fifth constraint says that if player 3 plays, then player 6 can’t play, and vice-versa. So only one of them can play:

For this equation, if `y3=1`, this constraint forces `y6` to be zero, and vice versa. This constraint forces either player 1 or 6 to be on the team for the game, though. That’s not exactly what the coach wanted. They could both be out of the game occasionally, and this equation would not make it possible.

Now, if `y3=1``y6=0` because the sum can’t be more than 1. If `y6=1`, then `y3=0`. If both `y3` and `y6` are zero, that’s fine too.

The sixth constraint declares that when player 1 plays, players 4 and 5 must also play (perhaps it’s in their contract). So, `y4` and `y5` must be 1 when `y1=1`:

This pair of inequalities is tricky. If `y1=0``y4 >= 0`. This means that player 4 may play if player 1 is not playing. If player 1 plays, however, `y4 >= 1`, so `y4` equals 1. When player 1 is playing, player 4 is also playing. The same type of inequality is enforced for player 5.

The last constraint says that either players 2 or 3 must play. Note that they can’t both play at the same time:

Remember the first try for the constraint regarding players 3 and 6? This constraint enforces that either player 2 or 3 will be on the game, but not both.

There’s a hidden constraint: a basketball team has five players on the court. Therefore:

To ensure that the decision variables are binary, they are declared to take only values in a binary set:

Because this problem is actually the selection of decision variables to cover some constraints, it’s called a set covering problem.

GNU MathProg solution for the set covering problem

As above, the line numbers in this code are not part of the code itself. They have been added only for the sake of making references to the code later.
Listing 6. GNU MathProg solution to the set covering problem

 ``` 1 # 2 # Basketball problem 3 # 4 # This finds the optimal solution for maximizing the team's overall 5 # defensive skill 6 # 7 8 /* sets */ 9 set SKILLS; 10 set POSITIONS; 11 set PLAYERS; 12 13 /* parameters */ 14 param skill {i in PLAYERS, j in SKILLS}; 15 param position {i in PLAYERS, j in POSITIONS}; 16 param min_in_position {i in POSITIONS}; 17 param min_skill_average {i in SKILLS}; 18 19 /* decision variables: yi, i in {1,..,7}. yi = 1 -> player i is on team */ 20 var y {i in PLAYERS} binary >=0; 21 22 /* objective function */ 23 maximize z: sum{i in PLAYERS} skill[i,"defense"]*y[i]; 24 25 /* Constraints */ 26 s.t. pos{j in POSITIONS}: sum{i in PLAYERS} position[i,j]*y[i] >= min_in_position[j]; 27 s.t. players: sum{i in PLAYERS} y[i] = 5; 28 s.t. ctr3: y[3] + y[6] <= 1; 29 s.t. ctr4a: y[4] - y[1] >= 0; 30 s.t. ctr4b: y[5] - y[1] >= 0; 31 s.t. ctr5: y[2] + y[3] = 1; 32 s.t. average{j in SKILLS}: sum{i in PLAYERS} skill[i,j]*y[i]/5 >= min_skill_average[j]; 33 34 data; 35 set PLAYERS := 1 2 3 4 5 6 7; 36 set POSITIONS := Back Mid Front; 37 set SKILLS := assist throw rebound defense; 38 39 param min_in_position:= 40 Back 3 41 Mid 1 42 Front 2; 43 44 param min_skill_average:= 45 assist 2 46 throw 2 47 rebound 2 48 defense 2; 49 50 param position: Back Mid Front:= 51 1 1 0 0 52 2 0 1 0 53 3 1 1 0 54 4 1 0 1 55 5 1 0 1 56 6 0 1 1 57 7 1 0 1; 58 59 60 param skill: assist throw rebound defense:= 61 1 3 3 1 3 62 2 2 1 3 2 63 3 2 3 2 2 64 4 1 3 3 1 65 5 1 3 1 2 66 6 3 1 2 3 67 7 3 2 2 1; 68 69 end; ```

There are four parameters:

• `skill` is a two-dimensional table where `i` denotes the player and `j` lists the skills. The values of this table are the numerical ability of player `i` with skill `j`.
• `position` is a two-dimensional binary table with players along `i` and `j` listing the positions. This table determines whether player `i` can play on position `j` (if the value is 1).
• `min_in_position` is defined on the `POSITIONS` set to define the minimum number of players that can play position `i`.
• `min_skill_average` is defined on the `SKILLS` set to set the minimum average the playing players must have regarding skill `i`. This average happens to be the same for all of them as given in the problem, but it should be declared separately in case the coach needs to shift the team’s balance.

The heart of this problem is the declaration of the decision variables as binary variables. They are as easy to declare as integer variables. Just add a `binary` to the declaration as shown on line 20.

Here’s the output:
Listing 7. glpsol report for the set covering problem

 ```Problem: basketball Rows: 13 Columns: 7 (7 integer, 7 binary) Non-zeros: 62 Status: INTEGER OPTIMAL Objective: z = 11 (MAXimum) 11 (LP) No. Row name Activity Lower bound Upper bound ------ ------------ ------------- ------------- ------------- 1 z 11 2 pos[Back] 3 3 3 pos[Mid] 2 1 4 pos[Front] 3 2 5 players 5 5 = 6 ctr3 1 1 7 ctr4a 0 -0 8 ctr4b 0 -0 9 ctr5 1 1 = 10 average[assist] 2 2 11 average[throw] 2.2 2 12 average[rebound] 2 2 13 average[defense] 2.2 2 No. Column name Activity Lower bound Upper bound ------ ------------ ------------- ------------- ------------- 1 y[1] * 1 0 1 2 y[2] * 1 0 1 3 y[3] * 0 0 1 4 y[4] * 1 0 1 5 y[5] * 1 0 1 6 y[6] * 1 0 1 7 y[7] * 0 0 1 End of output ```

Note that all the decision variables are either 0 or 1, as expected.

Conclusion

This series of articles analyzed six problems, each one teaching a new concept through modeling the problem, writing it in a simple and elegant way in the GNU MathProg language so that GLPK can solve it, and analyzing the results.

The uses of linear programming and Operations Research to search for optimal solutions do not end with the problems in this series of articles. Areas such as petroleum, chemistry, food, and finance use linear solvers and Operations Research heavily.

I encourage you to use and share the concepts discussed here, so that more people learn the power of linear programming and Operations Research. I hope you found the material useful.

# Akinokure gets his facts wrong

Akinokure is self-righteous about nerd self-righteousness.

That’s where Microsoft came in, delivering a user-friendly system to the average person. How else did it become so damn common? Microsoft was not driven by an abstract ideology but by concrete concerns about whether users would buy it or not. And whaddaya know, Windows works way better for starving black Africans than Linux does. Once again, practical demolishes theoretical.

In summary, nerds don’t want to democratize access to what is truly best for the average Third Worlder. Rather, they want to develop and disseminate an alternative to the so-called best — something that would prove how idiotic, clumsy, and evil the corporate developers were, and how smart, clever, and noble the Open Source crusaders were.

He is just plain wrong.

Bill Gates didn’t get Microsoft to an illegal monopoly by providing a good product.  Bill Gates provided a bad, buggy, incompetently-made product.

Bill Gates’ family used connections to get stupid US government buyers to waste lots of money on bad Microsoft products.

Bill Gates does not help people with his Microsoft activities.  (Many people argue that Gates’ alleged charity activities are also just for show – I won’t investigate those allegations.)

Microsoft was NEVER interested in whether average users would buy their products uncoerced – Microsoft was concerned with breaking the law to get government officials to betray the public trust by making the US government dependent on Microsoft.

After the US government had been captured, everyone else followed suit.

And users did NOT get a user-friendly system – they got user-hostile systems that made information access as painful as a root canal.

# Phil Zimmerman promises secure phone in February

https://www.blackphone.ch/

Blackphone is the world’s first smartphone to put privacy and control ahead of everything else. Ahead of carriers. Ahead of advertising. Blackphone is re-shaping the landscape of personal communications.

Pre-ordering begins at Mobile World Congress, Barcelona, Spain. February 24, 2014