Help making GAMS code as idiomatic and terse as possible

Archive of Gamsworld Google Group
Post Reply
Archiver
User
User
Posts: 7876
Joined: 7 years ago

Help making GAMS code as idiomatic and terse as possible

Post by Archiver »


Hello,

I'm trying to make the following GAMS model as terse (in particular, as few lines as possible) and as idiomatic as possible.
In words, the file defines a graph with five nodes, and six directed edges, each with its own capacity and cost.
The total flow into node 5 should equal one.
Nodes 2 through 4 should conserve flow - i.e. flow in = flow out.
Node 1 can produce as must flow as it likes, and as I said before, node 5 must consume exactly 1 unit of flow.
The objective function is to minimize the cost of sending this unit of flow from node 1 to node 5.

Here is my attempt, as an amateur:

SET nodes /n1*n5/;
ALIAS(nodes,nodefrom,nodeto);
SET midnodes(nodes) /n2*n4/;
SET lastnode(nodes) /n5/;
SET edges(nodes,nodes) / n1 . n2
n1 . n3
n1 . n4
n2 . n5
n3 . n5
n4 . n5 /;
PARAMETER cost(nodes,nodes) / n1.n2 1
n1.n3 2
n1.n4 3
n2.n5 2
n3.n5 2
n4.n5 2 /;
PARAMETER cap(nodes,nodes) / n1.n2 0.5
n1.n3 0.4
n1.n4 0.6
n2.n5 0.3
n3.n5 0.6
n4.n5 0.5 /;
VARIABLE flow(nodefrom,nodeto), z;
flow.LO(edges) = 0;
flow.UP(edges) = cap(edges);
EQUATION flowcost;
flowcost.. z =e= sum(edges, cost(edges) * flow(edges));
EQUATION unitflow;
unitflow.. 1 =e= sum((nodefrom,lastnode)$edges(nodefrom,lastnode), flow(nodefrom,lastnode));
EQUATION flowcon(midnodes);
flowcon(midnodes).. sum(nodefrom$edges(nodefrom,midnodes), flow(nodefrom,midnodes)) =e=
sum(nodeto$edges(midnodes,nodeto), flow(midnodes,nodeto));
MODEL mincostflow /all/;
SOLVE mincostflow USING lp MINIMIZING z;

I realize its probably not possible to make the PARAMETER section any shorter without harming readability, so I'm not so worried about that.
I'm more interested to know if:
- Can the flow conservation constraint be shortened somehow? "midnodes" is repeated 6 times in two lines.
- Can the EQUATION and its definition be defined on one line, e.g. for flowcost?
- Can I do without lastnode?

I'm sure to the expert eye, other opportunities for refinement could present themselves - I want to present GAMS in the best light possible.

Thanks,
Iain

--
To unsubscribe from this group and stop receiving emails from it, send an email to gamsworld+unsubscribe@googlegroups.com.
To post to this group, send email to gamsworld@googlegroups.com.
Visit this group at https://groups.google.com/group/gamsworld.
For more options, visit https://groups.google.com/d/optout.
Archiver
User
User
Posts: 7876
Joined: 7 years ago

Re: Help making GAMS code as idiomatic and terse as possible

Post by Archiver »


Hi!, I feel that that's about "as low as you go" on syntax for what you're trying to express. Perhaps there is room for more tersing but have into account that (as in all high-level software development) readibility is a more valuable asset than elegance (which is an asset I also value highly).

You certainly don't need nodefrom and nodeto sets:

flowcon(midnodes).. sum(nodes$edges(nodes,midnodes), flow(nodes,midnodes)) =e=
sum(nodes$edges(midnodes,nodes), flow(midnodes,nodes));

Personally, I like this style (some extra modifications for set flexibility)

sets allNodes /n1*n5/
sources(allNodes)
sinks(allNodes)
midNodes(allNodes);

*this is only for your network structure
sources(allNodes)=yes$(ord(allNodes)=1);
sinks(allNodes)=yes$(ord(allNodes)=card(allNodes));
*this was recently taught to me by Steven Dirkse
midNodes(allNodes)=not(sources(allNodes)) and not(sinks(allNodes));

SET edges(allnodes,allnodes) / n1 . n2
n1 . n3
n1 . n4
n2 . n5
n3 . n5
n4 . n5 /;
PARAMETER cost(allnodes,allnodes) / n1.n2 1
n1.n3 2
n1.n4 3
n2.n5 2
n3.n5 2
n4.n5 2 /;
PARAMETER cap(allnodes,allnodes) / n1.n2 0.5
n1.n3 0.4
n1.n4 0.6
n2.n5 0.3
n3.n5 0.6
n4.n5 0.5 /;

*you can't use an assigned set as a domain (sinks), but...
parameter sinkFlow(allnodes) /n5 1/;

VARIABLE flow(allnodes,allnodes), z;
flow.LO(edges) = 0;
flow.UP(edges) = cap(edges);

alias(allNodes,i);
alias(sources,j);
alias(sinks,k);
alias(midNodes,t);
alias(edges,e);


EQUATION flowcost;
flowcost.. z =e= sum(e, cost(e) * flow(e));
EQUATION sinksEq;
sinksEq(k).. sinkFlow(k) =e= sum(i$edges(i,k), flow(i,k));
EQUATION flowcon;
flowcon(t).. sum(i$e(i,t), flow(i,t)) =e= sum(i$edges(t,i), flow(t,i));

MODEL mincostflow /all/;
SOLVE mincostflow USING lp MINIMIZING z;

Again: This is mostly a matter of personal taste.

Best Regards and hope to be helpful!
Claudio


On Fri, Feb 12, 2016 at 12:29 AM, Iain Dunning wrote:

Hello,

I'm trying to make the following GAMS model as terse (in particular, as few lines as possible) and as idiomatic as possible.
In words, the file defines a graph with five nodes, and six directed edges, each with its own capacity and cost.
The total flow into node 5 should equal one.
Nodes 2 through 4 should conserve flow - i.e. flow in = flow out.
Node 1 can produce as must flow as it likes, and as I said before, node 5 must consume exactly 1 unit of flow.
The objective function is to minimize the cost of sending this unit of flow from node 1 to node 5.

Here is my attempt, as an amateur:

SET nodes /n1*n5/;
ALIAS(nodes,nodefrom,nodeto);
SET midnodes(nodes) /n2*n4/;
SET lastnode(nodes) /n5/;
SET edges(nodes,nodes) / n1 . n2
n1 . n3
n1 . n4
n2 . n5
n3 . n5
n4 . n5 /;
PARAMETER cost(nodes,nodes) / n1.n2 1
n1.n3 2
n1.n4 3
n2.n5 2
n3.n5 2
n4.n5 2 /;
PARAMETER cap(nodes,nodes) / n1.n2 0.5
n1.n3 0.4
n1.n4 0.6
n2.n5 0.3
n3.n5 0.6
n4.n5 0.5 /;
VARIABLE flow(nodefrom,nodeto), z;
flow.LO(edges) = 0;
flow.UP(edges) = cap(edges);
EQUATION flowcost;
flowcost.. z =e= sum(edges, cost(edges) * flow(edges));
EQUATION unitflow;
unitflow.. 1 =e= sum((nodefrom,lastnode)$edges(nodefrom,lastnode), flow(nodefrom,lastnode));
EQUATION flowcon(midnodes);
flowcon(midnodes).. sum(nodefrom$edges(nodefrom,midnodes), flow(nodefrom,midnodes)) =e=
sum(nodeto$edges(midnodes,nodeto), flow(midnodes,nodeto));
MODEL mincostflow /all/;
SOLVE mincostflow USING lp MINIMIZING z;

I realize its probably not possible to make the PARAMETER section any shorter without harming readability, so I'm not so worried about that.
I'm more interested to know if:
- Can the flow conservation constraint be shortened somehow? "midnodes" is repeated 6 times in two lines.
- Can the EQUATION and its definition be defined on one line, e.g. for flowcost?
- Can I do without lastnode?

I'm sure to the expert eye, other opportunities for refinement could present themselves - I want to present GAMS in the best light possible.

Thanks,
Iain

--
To unsubscribe from this group and stop receiving emails from it, send an email to gamsworld+unsubscribe@googlegroups.com.
To post to this group, send email to gamsworld@googlegroups.com.
Visit this group at https://groups.google.com/group/gamsworld.
For more options, visit https://groups.google.com/d/optout.


--
To unsubscribe from this group and stop receiving emails from it, send an email to gamsworld+unsubscribe@googlegroups.com.
To post to this group, send email to gamsworld@googlegroups.com.
Visit this group at https://groups.google.com/group/gamsworld.
For more options, visit https://groups.google.com/d/optout.
Archiver
User
User
Posts: 7876
Joined: 7 years ago

Re: Help making GAMS code as idiomatic and terse as possible

Post by Archiver »

Iain,

Your question was an interesting one for a rainy night. A lot of this is just a matter of taste: I've taken the liberty of doing some minimal changes to come up with the "how-I-would-do-it" model (attached). Some of the changes are very minor: using the alternative curly braces for sums, grouping the declarations, indentation. Others are less minor. For a variable like flow, it's better to declare it positive. That way, you don't store the lower bounds of zero, since that is the default/declared lower bound. You can always set a non-zero lower bound, which will get stored. For the flow conservation constraint, I have

equation flowcon(nodes);
flowcon(midnodes(n)).. sum{edges(nodefrom,n), flow(nodefrom,n)} =e= sum{edges(n,nodeto), flow(n,nodeto)};

Note there are no explicit $-conditions, but the sums are exactly the same. Internally, they are identical. It's just clearer, IMHO, not to use the explicit $-conditions in many cases, including this one. More subtle is the declaration (over the entire set) vs. the definition (over the subset midnodes with a sort of dummy index n). This is handy. The dummy index n lets you copy-paste commonly used expressions or code fragments, because it's generic. You only mention midnodes once, to specify the nodes n you really want to generate this equation for. If your set midnodes was dynamic, you couldn't declare the equation over the midnodes set, so the distinction between declaring over static sets and defining over potentially dynamic sets would be an important one.

It is not possible to declare and define an equation in the same statement. You can do it in the same line, but that's probably not what you mean.

EQUATION flowcost; flowcost.. z =e= sum(edges, cost(edges) * flow(edges));

I liked Claudio's breakdown into sources and sinks. One neat/cute/potentially-powerful change would be to compute the source and sink sets based on the arc set. You could have multiple sources and multiple sinks. The flow conservation would then look something like this:

flowcon(n)$[not source(n)].. sum{edges(nodefrom,n), flow(nodefrom,n)} =e= sum{edges(n,nodeto), flow(n,nodeto)} + 1$sink(n);

and you could drop the unitflow equation. You could do something similar to your original model too, so that you only need to define source and sink sets, not the midnodes set. How you define the source & sink sets is independent of that. That is attached as steve2.

-Steve

On Thu, Feb 11, 2016 at 10:29 PM, Iain Dunning wrote:

Hello,

I'm trying to make the following GAMS model as terse (in particular, as few lines as possible) and as idiomatic as possible.
In words, the file defines a graph with five nodes, and six directed edges, each with its own capacity and cost.
The total flow into node 5 should equal one.
Nodes 2 through 4 should conserve flow - i.e. flow in = flow out.
Node 1 can produce as must flow as it likes, and as I said before, node 5 must consume exactly 1 unit of flow.
The objective function is to minimize the cost of sending this unit of flow from node 1 to node 5.

Here is my attempt, as an amateur:

SET nodes /n1*n5/;
ALIAS(nodes,nodefrom,nodeto);
SET midnodes(nodes) /n2*n4/;
SET lastnode(nodes) /n5/;
SET edges(nodes,nodes) / n1 . n2
n1 . n3
n1 . n4
n2 . n5
n3 . n5
n4 . n5 /;
PARAMETER cost(nodes,nodes) / n1.n2 1
n1.n3 2
n1.n4 3
n2.n5 2
n3.n5 2
n4.n5 2 /;
PARAMETER cap(nodes,nodes) / n1.n2 0.5
n1.n3 0.4
n1.n4 0.6
n2.n5 0.3
n3.n5 0.6
n4.n5 0.5 /;
VARIABLE flow(nodefrom,nodeto), z;
flow.LO(edges) = 0;
flow.UP(edges) = cap(edges);
EQUATION flowcost;
flowcost.. z =e= sum(edges, cost(edges) * flow(edges));
EQUATION unitflow;
unitflow.. 1 =e= sum((nodefrom,lastnode)$edges(nodefrom,lastnode), flow(nodefrom,lastnode));
EQUATION flowcon(midnodes);
flowcon(midnodes).. sum(nodefrom$edges(nodefrom,midnodes), flow(nodefrom,midnodes)) =e=
sum(nodeto$edges(midnodes,nodeto), flow(midnodes,nodeto));
MODEL mincostflow /all/;
SOLVE mincostflow USING lp MINIMIZING z;

I realize its probably not possible to make the PARAMETER section any shorter without harming readability, so I'm not so worried about that.
I'm more interested to know if:
- Can the flow conservation constraint be shortened somehow? "midnodes" is repeated 6 times in two lines.
- Can the EQUATION and its definition be defined on one line, e.g. for flowcost?
- Can I do without lastnode?

I'm sure to the expert eye, other opportunities for refinement could present themselves - I want to present GAMS in the best light possible.

Thanks,
Iain

--
Attachments
steve2.gms
(866 Bytes) Downloaded 267 times
steve.gms
(935 Bytes) Downloaded 263 times
Archiver
User
User
Posts: 7876
Joined: 7 years ago

Re: Help making GAMS code as idiomatic and terse as possible

Post by Archiver »


Thanks to you both, those are some nice tricks/insights!

On Monday, February 15, 2016 at 9:01:41 PM UTC-5, Steven Dirkse wrote:

Iain,

Your question was an interesting one for a rainy night. A lot of this is just a matter of taste: I've taken the liberty of doing some minimal changes to come up with the "how-I-would-do-it" model (attached). Some of the changes are very minor: using the alternative curly braces for sums, grouping the declarations, indentation. Others are less minor. For a variable like flow, it's better to declare it positive. That way, you don't store the lower bounds of zero, since that is the default/declared lower bound. You can always set a non-zero lower bound, which will get stored. For the flow conservation constraint, I have

equation flowcon(nodes);
flowcon(midnodes(n)).. sum{edges(nodefrom,n), flow(nodefrom,n)} =e= sum{edges(n,nodeto), flow(n,nodeto)};

Note there are no explicit $-conditions, but the sums are exactly the same. Internally, they are identical. It's just clearer, IMHO, not to use the explicit $-conditions in many cases, including this one. More subtle is the declaration (over the entire set) vs. the definition (over the subset midnodes with a sort of dummy index n). This is handy. The dummy index n lets you copy-paste commonly used expressions or code fragments, because it's generic. You only mention midnodes once, to specify the nodes n you really want to generate this equation for. If your set midnodes was dynamic, you couldn't declare the equation over the midnodes set, so the distinction between declaring over static sets and defining over potentially dynamic sets would be an important one.

It is not possible to declare and define an equation in the same statement. You can do it in the same line, but that's probably not what you mean.

EQUATION flowcost; flowcost.. z =e= sum(edges, cost(edges) * flow(edges));

I liked Claudio's breakdown into sources and sinks. One neat/cute/potentially-powerful change would be to compute the source and sink sets based on the arc set. You could have multiple sources and multiple sinks. The flow conservation would then look something like this:

flowcon(n)$[not source(n)].. sum{edges(nodefrom,n), flow(nodefrom,n)} =e= sum{edges(n,nodeto), flow(n,nodeto)} + 1$sink(n);

and you could drop the unitflow equation. You could do something similar to your original model too, so that you only need to define source and sink sets, not the midnodes set. How you define the source & sink sets is independent of that. That is attached as steve2.

-Steve

On Thu, Feb 11, 2016 at 10:29 PM, Iain Dunning wrote:

Hello,

I'm trying to make the following GAMS model as terse (in particular, as few lines as possible) and as idiomatic as possible.
In words, the file defines a graph with five nodes, and six directed edges, each with its own capacity and cost.
The total flow into node 5 should equal one.
Nodes 2 through 4 should conserve flow - i.e. flow in = flow out.
Node 1 can produce as must flow as it likes, and as I said before, node 5 must consume exactly 1 unit of flow.
The objective function is to minimize the cost of sending this unit of flow from node 1 to node 5.

Here is my attempt, as an amateur:

SET nodes /n1*n5/;
ALIAS(nodes,nodefrom,nodeto);
SET midnodes(nodes) /n2*n4/;
SET lastnode(nodes) /n5/;
SET edges(nodes,nodes) / n1 . n2
n1 . n3
n1 . n4
n2 . n5
n3 . n5
n4 . n5 /;
PARAMETER cost(nodes,nodes) / n1.n2 1
n1.n3 2
n1.n4 3
n2.n5 2
n3.n5 2
n4.n5 2 /;
PARAMETER cap(nodes,nodes) / n1.n2 0.5
n1.n3 0.4
n1.n4 0.6
n2.n5 0.3
n3.n5 0.6
n4.n5 0.5 /;
VARIABLE flow(nodefrom,nodeto), z;
flow.LO(edges) = 0;
flow.UP(edges) = cap(edges);
EQUATION flowcost;
flowcost.. z =e= sum(edges, cost(edges) * flow(edges));
EQUATION unitflow;
unitflow.. 1 =e= sum((nodefrom,lastnode)$edges(nodefrom,lastnode), flow(nodefrom,lastnode));
EQUATION flowcon(midnodes);
flowcon(midnodes).. sum(nodefrom$edges(nodefrom,midnodes), flow(nodefrom,midnodes)) =e=
sum(nodeto$edges(midnodes,nodeto), flow(midnodes,nodeto));
MODEL mincostflow /all/;
SOLVE mincostflow USING lp MINIMIZING z;

I realize its probably not possible to make the PARAMETER section any shorter without harming readability, so I'm not so worried about that.
I'm more interested to know if:
- Can the flow conservation constraint be shortened somehow? "midnodes" is repeated 6 times in two lines.
- Can the EQUATION and its definition be defined on one line, e.g. for flowcost?
- Can I do without lastnode?

I'm sure to the expert eye, other opportunities for refinement could present themselves - I want to present GAMS in the best light possible.

Thanks,
Iain

--
To unsubscribe from this group and stop receiving emails from it, send an email to gamsworld+...@googlegroups.com.
To post to this group, send email to gams...@googlegroups.com.
Visit this group at https://groups.google.com/group/gamsworld.
For more options, visit https://groups.google.com/d/optout.




--
Steven Dirkse, Ph.D.
GAMS Development Corp., Washington DC
Voice: (202)342-0180 Fax: (202)342-0181
sdi...@gams.com
http://www.gams.com

--
To unsubscribe from this group and stop receiving emails from it, send an email to gamsworld+unsubscribe@googlegroups.com.
To post to this group, send email to gamsworld@googlegroups.com.
Visit this group at https://groups.google.com/group/gamsworld.
For more options, visit https://groups.google.com/d/optout.
Post Reply