GAMS教程 3:子集与条件表达式


GAMS文档:条件表达式

GAMS教程 3:子集与条件表达式

我们在前面的章节中介绍了GAMS中的基础数据类型、GAMS程序的基本结构、GAMS语法、GAMS的基本运算符以及GAMS中的函数。

本节我们将继续深入,讲解GAMS中的子集与逻辑表达式。

1. 子集(Subset/Dynamic Set)

我们在最前面介绍了GAMS中的基础数据类型:集合(Set)、参数(Parameter)、表格(Table)、标量(Scalar)以及变量(Variable)

其中:

  • 变量可以是集合、参数、表格、标量中的任意一种。
  • 集合和参数、表格不同。集合起着索引的作用,而没有具体的值;参数和表格和具体得集合关联,并且对应集合中每一个元素都有对应的值。

本节中我们将介绍集合的另外一个知识,即子集

GAMS中将数学上的子集(Subset)称为动态集合(Dynamic Set),而之所以将子集称为动态集合,是因为GAMS中允许向子集中随时增减元素。相反,前面的介绍的集合中的元素是不能动态增减的,因此相应的称为静态集合(Static Set)

A. 子集的声明

GAMS中子集声明的语法如下

Set dynamic_set_name(parent_set_name)    [/item1, {item2}/];

其中:

  • []表示可选项,{}表示可重复项
  • parent_set_name表示子集的父集。需要注意的是,在声明子集的时候,一定要先声明父集
  • 如果在声明子集的时候指定子集中包含的元素,那么子集中的元素必须包含在父集中。因为GAMS会进行范围检查(Domain Check),如果子集中存在不在父集中的元素的话,那么就会报错。

下面给出一个声明子集的例子。

Set  item            "all items"              / dish, ink, lipstick, pen, pencil, perfume /,
     subitem1(item)  "first subset of item"   / pen, pencil /,
     subitem2(item)  "second subset of item";

需要注意的是,子集的声明和参数很类似,都是变量名字(另一个集合名字)的形式。

Set set_a /one, two, three, four/;

; 子集的()表示声明父集
Set subset(set_a) /one, four/;

; 参数的()是和索引集合关联
Parameter param(set_a) /"one" 1, "two" 2/;

虽然形式一样,但其实两者存在本质上的不同。GAMS编译器会根据上下文来区别对待。

B. 子集动态元素增减

我们前面说过,之所以GAMS中将子集称为动态集合,就是因为GAMS中的子集可以动态增减元素。

GAMS中子集动态增减元素的语法如下

set_name(index_list | label) = yes | no ;

其中:

  • yes/no分别表示将元素加入集合或者从集合中删除
  • index_list表示对某个集合整体设置,而label表示对某个元素进行设置

子集的动态增减如下

Set  item            "all items"              / dish, ink, lipstick, pen, pencil, perfume /
     subitem1(item)  "first subset of item"   / pen, pencil /
     subitem2(item)  "second subset of item";

* subitem1 = /pen, pencil/;
* subitem2 = null;


subitem1('ink')      = yes;   * subitem1 = /pen, pencil, ink/;
subitem1('lipstick') = yes;   * subitem1 = /pen, pencil, ink, lipstick/;
subitem2(item)       = yes;   * subitem2 = /disk, ink, lipstick, pen, pencil, perfume/;
subitem2('perfume')  = no ;   * subitem2 = /disk, ink, lipstick, pen, pencil/;

index_list可以是一个静态集合,也可以是一个动态集合,即子集。

2. 条件表达式

GAMS中提供了条件表达式,我们下面就将介绍进行介绍。

1. 条件表达式的声明

GAMS中条件表达式的语法如下

term $ logical_condition

项 $ 逻辑表达式

条件表达式类似于C语言中的三元运算符,但是相比于三元运算符能够根据逻辑表达语句的值在两个项中选择,GAMS中的条件表达式只能根据逻辑表达式的值在有项和无项之间选择

例如下面这段C语言代码

if (b > 1.5)
  a = 2;
else
    a = null;

注意,这里的null并不是C语言中的null,而是直接没有这项。上面的C语言的描述使用GAMS的条件表达式描述,如下

a $ (b > 1.5) = 2 ;

再举例来说

Scalar x /15/;
Scalar y;

y = 12$(x < 20);

表示如果x的值小于20,那么y=12,否则y的值就依旧是待定的。

再举一个例子,如果我们想要实现下面这段C语言程序

if (x < 20)
      y = 12;
else
      y = 21;

对应的GAMS的语句为

Scalar x /15/;
Scalar y;

y = 12$(x < 20) + 21$(x >= 20);

注意GAMS中的条件语句在条件不成立的时候直接就没有这一项,所以不要看这里有两个条件语句,y = 12$(x < 20) + 21$(x >= 20)这个表达式,其实在任何时候都只有一项。当然这个其实还和我们的条件有关。

Set A /1, 2, 3, 4, 5, 6/;
Parameter param(A);

param(A) = 1$(ord(A) < 4);

2. 条件表达式与参数

我们上面的例子中展示了如何使用条件表达式为一个标量赋值,条件表达式其实也能够为参数赋值

子集

对于逻辑表达式与子集的结合,我们举例如下

Set        i     / i1*i5 /
           j(i)  / i1*i3 / ;
Parameter  s(i)  / i1 3, i2 5, i3 11, i4 8, i5 1 /
           t(i);

t(i) $ j(i) = s(i) + 3;

首先,集合j是集合i的子集,而逻辑表达式t(i)$j(i)则表示对于集合j(i)中的所有值

因此参数t(i)最终的值为

t(i) = / "i1" 6, "i2" 8, "i3" 14/;

参数

对于逻辑表达式与参数的使用,我们举例如下

Set        i     / i1*i5 /
Parameter  s(i)  / i1 3, i2 5, i3 11, i4 8, i5 1 /
           t(i);

t(i) $ (s(i) < 6) = s(i) + 3;

首先,s(i)t(i)都是参数,而逻辑表达式t(i) $ (s(i) < 6)表示遍历s中的所有元素。当s(i)的值小于6时,存在t(i)这一项,否则就不存在。

所以t(i)最终的值为

t(i) = /"i1" 6, "i2" 8, "i5" 4/

ord函数

逻辑表达式与ord函数的联合使用如下

Set I /a, b, c, d, e, f/;
Parameter X(I) /"a" -1, "b" -2, "c" -3, "d" -4, "e" -5, "f" -6/;
Parameter A(I);

A(I) = 1$(ord(I) <= 2) + X(I-2)$(ord(I) > 2);

ord函数将会返回一个参数,具体来说,该参数的值为/"a" 1, "b" 2, "c" 3, "d" 4, "e" 5, "f" 6/

那么这个时候,情况就转为了上面逻辑表达式与参数的情况。此外,还需要注意的是X(I-2)这一项。GAMS中对参数取值的时候,对index集合进行减法,则只有当括号里的值大于0的时候,才会获取参数的内容。

所以对于X(I-2)这一项,当I > 2的时候,才会取参数X中的值。

所以综上,表达式A(I) = 1$(ord(I) <= 2) + X(I-2)$(ord(I) > 2)的意思就是,让A(I)的前两项是1,后面的项等于X的前面的项。

所以A(I)最终的值为:

A(I) = /"a" 1, "b" 1, "c" -1, "d" -2, "e" -3, "f" -4/;

3. 逻辑表达式与逻辑运算符

上面我们说GAMS中的条件表达式是根据逻辑表达式的值来计算项的,但是我们其实并没有详细的介绍GAMS中的逻辑表达式。

我们下面就将详细讲解GAMS中的逻辑表达式,以及与此相关的逻辑运算符。

逻辑表达式

GAMS中的逻辑表达式的值判断准则很简单,计算得到的逻辑表达式的值为0则为False,除此以外所有值都为True

例如下面的例子

b $ (2*a - 4) = 7;

a = 2时,逻辑表达式2*a - 4的逻辑值为False,其他时候都是True。

注意,集合没有值,而参数有值,所有以后有些情况我们可以这样写

Set i /a * e/,
        j(i) /a, b, c/;

Parameter pm(i) /"a" 1, "b" 2, "c" 3, "d" 4, "e" 5/,
        p1(i),
        p2(i);

* 下面的式子因为pm是参数,所有pm(i)是有值的,因此下面的式子表示当pm(i)不等于0的时候,p1(i)等于1
p1(i) $ (pm(i)) = 1;

* 下面的式子因为j是子集,所以表达的意思遍历子集j中的所有元素
p2(i) $ (j(i)) =

逻辑运算符

GAMS中有很多用于计算逻辑表达式的值的运算符,具体来说有有

GAMS中的逻辑运算符-1

GAMS中的逻辑运算符-2

GAMS中的逻辑运算符-2的真值表

4. 嵌套条件表达式

GAMS中允许使用嵌套表达式,即

term $ (logical_condition1$(logical_condition2$(...)))

但是其实嵌套表达式可以使用and语句来连接,两者在本质上是等价的。

term $ (logical_condition1 and logical_condition2 and ...)

举一个嵌套条件表达式的例子。假设i是一个静态集合,而kj都是i的子集,vu都是参数。那么下面的式子表示让jv的交集中的元素等于v(i)中对应的值。

u(i) $ (j(i)$k(i)) = v(i) ;

我们举具体得值为例

Sets i /i1 * i6/,
    j(i) /i1 * i4/,
    k(i) /i3 * i6/;

Parameters v(i) /"i1" -1, "i2" -2, "i3" -3, "i4" -4, "i5" -5, "i6" -6/,
    u(i);

u(i) $ (j(i)$k(i)) = v(i) ;

display u;

则最终u的值为

u = /"i3" -3, "i4" -4/;

话说回来,其实嵌套的条件表达式并不推荐使用,最好还是使用and条件运算符。

上面的条件用and条件运算符的版本为

u(i) $ (j(i) and k(i)) = v(i) ;

5. 条件表达式与赋值语句

事实上,条件表达式的位置不同,赋值的结果也不同。我们上面出现了条件表达式在=左边、在=右边,但是我们其实没有强调在左边和在右边之间的不同。

其实这两者还是存在微妙的不同的,下面我们就将介绍之间的不同。

$() = xxx:条件表达式在左

条件表达式在=左边表示只有当条件表达式值为真的时候,才会有整个赋值表达式

例如下面的例子

rho(i) $ (sig(i) <> 0) = (1./sig(i)) - 1. ;

假设sig是一个参数,则只有当sig(i)不等于0的时候,才会有rho(i) = (1./sig(I)) -1.这个赋值表达式。

当然,上面说过了只有非0值的逻辑表达式的值为True,所以上面的式子其实可以这样简写

rho(i) $ sig(i)  =  (1./sig(i)) - 1. ;

于此类似的,所有不等于的某个数x的式子可以这样写

s(i) $ (t(i) - x) = t(i);

xxx $= xxx:条件表达式在中间

当条件表达式在中间的时候,形式上有些奇怪,因为没有逻辑表达式。具体形式为

项 $= 项

条件表达式在中间的含义就是只有当右侧的值不为0的时候,才会有整个赋值表达式

例如

Set       i                                         /a,b,c/
Parameter d2(i)  "Data to be used to overwrite d1"  /a 0, b -2 /
          d3(i)  "Empty data parameter";

d3(i) $= d2(i);
* Result: d3('a')=1; d3('b')=-2; d3('c')=1;

xxx = $():条件表达式在右边

当条件表达式在=右侧的时候,表示逻辑表达式的值不为0时,条件表达式的值为项的值,否则为0

例子就是前面的例子:

A(I) = 1$(ord(I) <= 2) + X(I-2)$(ord(I) > 2);

6. 条件表达式与等式(Equation)

GAMS中的等式与赋值语句是完全不同的两码事。等式就是首先使用equation声明,而后具体定义的式子。等式中使用的符号和正常的=<>不同,就是因为等式是和赋值语句是完全不同的两码事。

上面介绍了赋值语句和条件表达式的细节,下面将介绍等式与条件表达式之间的细节。

条件表达式出现在等式定义中

当条件表达式出现在等式中,含义其实和条件表达式出现在赋值语句的右侧相同,都是当逻辑表达式的值不为0的时候,存在该项,否则该项为0

注意,在等式中,条件表达式和等式中的等号=e=的位置其实没有关系,不管在那边都是上面的含义。

所以下面的式子

equation eq2;

eq2(i)..      
    sum(j, x(i,j)) $ b =e=  -s(i) $ b;

表达就是当b = 0时,等式eq2就被定义为0 =e= 0

条件表达式出现在等式定义前

当条件表达式出现在等式定义前,表达的含义是当逻辑表达式的值为0的时候,不会定义该等式,否则定义该等式

所以下面的式子

equation eq1(i);

eq1(i) $ b..  
    sum(j, x(i,j))     =g=  -s(i);

表达的就是当b = 0时,不定义等式eq1(i),所以此时,仅有eq1(i)的声明。如果稍后不定义而直接在模型中使用的话,就会报错。


文章作者: Jack Wang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Jack Wang !
  目录