GLPK/Scalable Vector Graphics

Scalable vector graphics (SVG) is an XML file format to describe two dimensional graphics. SVG 1.1 is the most recent version of the specification.

With a little effort, the MathProg printf statement can be used to generate SVG code.

Traveling salesman problem


The task in the traveling salesman problem is to find the shortest cyclic path through a given set of cities, visiting each city exactly once, and then returning to the start.

The model below shows how to output the solution as a scalable vector graphic.


 * 1) This file demonstrates output to a scalable vector graphic (SVG).
 * 2) Solve with option --nomip to see the difference
 * 3) Traveling salesman problem
 * 1) Solve with option --nomip to see the difference
 * 2) Traveling salesman problem
 * 1) Traveling salesman problem

param filename, symbolic := "out.svg"; param n := 35;
 * 1) output file
 * 1) number of cities

set N := {1..n}; set E := setof{(i,j) in N cross N : i > j} (i,j); set F := setof{(i,j) in N cross N : i != j} (i,j);
 * 1) set of cities
 * 1) set of bidirectional arcs
 * 1) set of unidirectional arcs

param cx{i in N} := Uniform01; param cy{i in N} := Uniform01; param d{(i,j) in E} := sqrt((cx[i]-cx[j])^2+(cy[i]-cy[j])^2);
 * 1) random locations for the cities
 * 1) sum of x- and y- distance
 * 2) param d{(i,j) in E} := abs(cx[i]-cx[j])+abs(cy[i]-cy[j]);
 * 3) maximum of x- and y- distance
 * 4) param d{(i,j) in E} := max(abs(cx[i]-cx[j]),abs(cy[i]-cy[j]));
 * 5) euclidean distance

var x{(i,j) in E}, >=0, <= 1, binary; var f{(i,j) in F}, >= 0;
 * 1) connection
 * 1) flow

minimize dist : sum{(i,j) in E} x[i,j] * d[i,j];
 * 1) Objective

s.t. two{ i in N } : sum{j in N : i > j} x[i,j] + sum{j in N : i < j} x[j,i] = 2;
 * 1) Every city must have two connections for a round trip

s.t. flow1 {(i,j) in F} : f[i,j] <= (if (i==1) then n else n-1) * (if (i < j) then x[j,i] else x[i,j]);
 * 1) The following constraints force the graph to be connected
 * 2) Flow is controlled by binaries

s.t. flow2 {i in N} : sum{(i,j) in F} (f[i,j]-f[j,i]) <= if (i==1) then n-1 else -1;
 * 1) One unit is consumed in each node

s.t. flow3 { i in N } : sum{(i,j) in F} f[j,i] >= 1;
 * 1) There must be flow into every node

solve;

printf "\n" > filename; printf "<!DOCTYPE svg PUBLIC ""-//W3C//DTD SVG 1.1//EN"" \n" >> filename; printf """http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"">\n" >> filename; printf "> filename; printf "xmlns=""http://www.w3.org/2000/svg"">\n" >> filename; for {i in N} printf "\n", cx[i] * 500, cy[i] * 500 >> filename; for {(i,j) in E : x[i,j] == 1} printf "\n", cx[i] * 500, cy[i] * 500, cx[j] * 500, cy[j] * 500 >> filename; for {(i,j) in E : x[i,j] > 0 && x[i,j] < 1} { printf "> filename; printf " style=""stroke:red;stroke-dasharray: 3 3;stroke-width:2""/>\n" >> filename; } printf " \n" >> filename; end;
 * 1) Output the solution as scalable vector graphic
 * 2) write header
 * 1) draw circles for cities
 * 1) draw solid black lines for integer connections
 * 1) draw dashed red lines for fractional connections

Run the model (40 seconds on a Intel Core i5 processor):

$ glpsol --math traveling.mod

This is what the created output file out.svg looks like (some lines removed):

 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">    ...  

The resulting file can be viewed in an up-to-date web browser (like Firefox 3.6 or Internet Explorer 9) or viewed and modified using a (multi-platform) SVG editor (like Inkscape).

Clustering


The example below concerns a clustering problem. Out of 500 towns select 20 to be cluster centers and assign the other towns to the cluster such that the sum of the population weighted euclidian distances between towns and centers is minimized.

param f, symbolic := "ct.svg";
 * 1) Output file

param nc := 20; set C := {1 .. nc};
 * 1) Centers

param nt := 500; set T := {1 .. nt}; param xt{T} := Uniform01; param yt{T} := Uniform01; param pt{T} := ceil(1000 * Uniform01);
 * 1) Towns

param scale := 1000;
 * 1) Image size

param sat := 192; param hue{c in C} := 6 * (c - 1) / nc; param red{c in C} := if hue[c] <= 1 or hue[c] >= 5 then 255 else (if hue[c] >=2 and hue[c] <= 4 then 255 - sat else (if hue[c] <=2 then 255 - sat + sat * (2-hue[c]) else 255 - sat + sat * (hue[c]-4) )); param green{c in C} := if hue[c] >= 1 and hue[c] <= 3 then 255 else (if hue[c] >= 4 then 255 - sat else (if hue[c] <=1 then 255 - sat + sat * hue[c] else 255 - sat + sat * (4-hue[c]) )); param blue{c in C} := if hue[c] >= 3 and hue[c] <= 5 then 255 else (if hue[c] <=2 then 255 - sat else (if hue[c] <=3 then 255 - sat + sat * (hue[c]-2) else 255 - sat + sat * (6-hue[c]) ));
 * 1) Colors
 * 2) saturation [0, 255]

var x{T,T}, binary;

minimize obj : sum{c in T, t in T : c != t} x[c,t] * pt[t] * sqrt((xt[c] - xt[t])^2 + (yt[c] - yt[t])^2);

s.t. cc : sum{c in T} x[c,c] = nc; s.t. ct{c in T, t in T : c != t} : x[c,t] <= x[c,c]; s.t. tc{t in T} : sum{c in T} x[c,t] = 1;

solve;

for {c in T : x[c,c] > .5} { printf "Center %5.4f %5.4f\n", xt[c], yt[c]; for {t in T : x[c,t] > .5} { printf " Town %5.4f %5.4f (%5.0f)\n", xt[t], yt[t], pt[t]; } }


 * 1) Output the solution as scalable vector graphic

printf "\n" > f; printf "<!DOCTYPE svg PUBLIC ""-//W3C//DTD SVG 1.1//EN"" \n" >> f; printf """http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"">\n" >> f; printf "<svg width=""%d"" height=""%d"" version=""1.0"" \n", 1.2 * scale, 1.2 * scale >> f; printf "xmlns=""http://www.w3.org/2000/svg"">\n" >> f;
 * 1) header

printf "<rect x=""0"" y=""0"" width=""%d"" height=""%d""" & " stroke=""none"" fill=""white""/>\n", 1.2 * scale, 1.2 * scale>> f;
 * 1) background

printf "<rect x=""%d"" y=""%d"" width=""%d"" height=""%d""" & " stroke=""black"" stroke-width="".5"" fill=""white""/>\n", .1 * scale, .1 * scale, scale, scale >> f;
 * 1) border

for {t in T} printf {s in T, c in C : x[s,t] > .5 && c = floor( .5 + sum{u in T : u <= s} x[u,u])} "<circle cx=""%f"" cy=""%f"" r=""%f"" stroke=""black"" " & "stroke-width=""1"" fill=""rgb(%d,%d,%d)""/>\n", (.1 + xt[t]) * scale, (.1 + yt[t]) * scale, .007 * sqrt(pt[t]/nt) * scale, red[c], green[c], blue[c] >> f;
 * 1) circles for towns

for {t in T, c in T : x[c,t] > .5} printf "<line x1=""%f"" y1=""%f"" x2=""%f"" y2=""%f""" & " style=""stroke:black;stroke-width:.5""/>\n", (.1 + xt[c]) * scale, (.1 + yt[c]) * scale, (.1 + xt[t]) * scale, (.1 + yt[t]) * scale >> f;
 * 1) lines from towns to assigned centers

printf " \n" >> f;

end;