admin 管理员组

文章数量: 1086019


2024年4月16日发(作者:常量元素记忆顺口溜)

10.6.3 执行错误

执行时间错误通常由非法算术操作,如被零出或对负数求对数引起。GAMS 在输出文件中

打印出了出错语句所在的行数和该语句随后被执行的所在行数的信息。如果试图进行一个非

法操作时,从计算机操作系统中得到的含糊信息是不会让 GAMS 程序终止的。GAMS 严格定

义了一个扩展代数,它包括所有非法操作。模型库中问题[CRAZY]包含了所有非标准操作, 可

被执行以便学习这些例外情况。

回忆GAMS 的算法是定义在[-INF,+INF]的闭区间中的,它包含EPS(非常小,但不为零),NA

(缺失),UNDF(非法操作的结果)。非法操作的结果通过整个系统传播,可根据标准的显

示语句显示。然而,记住如果在前面发现错误,那么我们将无法求解一个模型或储存工作文件。

10.6.4 求解错误

求解语句的执行可能引发额外的错误,称为 MATRIX ERRORS,它报告了在将模型转化成

求解模块要求的格式过程中碰到的问题。多数问题通常由非法或不一致边界,或被用于矩阵

系数的一个扩展值域值引起。下面的例子给出了这些错误的一般格式。

一些求解语句要求计算非线性函数和导数。因为这些计算并不是由 GAMS 执行,而是在它

无法直接控制的其他子系统中执行,这些计算产生的错误就被报告在解报告中。除非采用

domlim 选项重新设置,否则如果碰到例外算法,子系统将中断求解过程。它们采用清单形

式被报告出来,如同下面例子所示。

可以看到求解模块状态的值为 5,表明求解模块被中断,因为超过 domlim 规定的计算错误。

计算错误的类型和引起错误的方程同时被给出。

如果由于计算错误,求解模块返回一个中间解(intermediate solution),那么它仍然会试图求

解其后的程序。求解程序可能引起的唯一致命的 GAMS 错误是根本无法返回任何解。如果

这发生了,GAMS 输出文件中将列出所有可能的信息,其后的程序将不被继续求解。

10.7 小结

至此,我们完成了 GAMS 语言基本特点的讨论。接下来的章节是 GAMS 语言的高级应用。

第十一章 条件表达式,赋值和方程

11.1 引言

本章讨论 GAMS 中进行条件赋值、条件表达式和条件方程的方法。前面讨论的指数操作功

能非常强大,但它必须允许某种例外的存在。例如,大货车可能由于桥的负重有限而无法使用

特定的路线,或者经济的某些部门可能无法生产出口产品。我们已经表明,使用指数化表达式

中的子集可以提供处理例外的能力。

11.2 逻辑条件

逻辑条件是特殊的表达式,它们计算“真”或“假”。数值表达式也可作为逻辑条件。另外,

GAMS 还提供了可用来产生逻辑条件的数字关系和逻辑算子。下面四个小节将讨论这些用

来构建复杂逻辑条件的各种建模模块。

11.2.1 作为逻辑条件的数值表达式

➢ 数值表达式也可以作为逻辑条件――结果为零被视为“假”,非零被视为“真”。

下面的数值表达式可用来阐明这点。

这个表达式在 a 等于 2 的时候得到逻辑值“假”,因为此时这个表达式的数值计算结果为 0。对

于所有其他 a 的值,表达式的结果为非零数值,因此相当于逻辑值“真”。

11.2.2 数值关系算子

数值关系算子构成两种数值表达式。出于完整性,所有数值关系算子列表如下:

以下数值关系的例子说明了它作为逻辑条件的使用。

这个条件在

1  a  1

的条件下是假的。对所有其他 a 的值,这个条件是真的。注意,相同

的表达式也可以写成(sqr(a)gt a)。

11.2.3 逻辑算子

GAMS 中可用的逻辑算子如下:

由这些逻辑算子所产生的逻辑运算表如表 11.1 所示。

11.1 逻辑算子的逻辑运算表

11.2.4 集合元素

集合元素也可用做逻辑条件。如果标签是问题中集合的元素,那么结果是逻辑“真”,否则

为逻辑“假”。这与子集和动态集合一起使用。

考虑如下的例子:

集合 subi(i)的结果是:对属于 subi 的所有元素是逻辑真,对集合 i 中不属于 subi 的所有元素

是逻辑假。

采用集合元素作为逻辑条件是 GAMS 非常有用的一个特点,我们将在本章后面说明如何使

用,它的强大功能在后面考虑动态集合的时候变得非常清楚。

11.2.5 包含 Acronyms 的逻辑条件

Acronyms 是字符串值,它只有在和“=”或“<>”算子一起时,才可用于逻辑条件。

考虑如下包含 Acronyms 的逻辑条件的例子:

其中 dayofweek 是一个参数,Wednesday 和 Thursday 是 Acronyms。

11.2.6 逻辑条件的数值

前面四个小节已经描述了 GAMS 中可用做逻辑条件的多种方法的特点。但是,GAMS 却没

有 Boolean 数据类型。

➢ GAMS 遵循这样一个惯例:关系运算的结果是 0,如果关系是假的;结果是 1,如果关

系是真。

考虑如下示例:

这个表达式右边赋值为 2,因为括号内的两个逻辑条件都是真的,因此得到值 1。注意这和

下面的赋值完全不同。

这个表达式计算结果为 1,因为“or”算子使其整个关系计算为真。

11.2.7 混合逻辑条件――算子优先顺序

前面四个小节讨论的构建模块可用来生成更为复杂的逻辑条件。在不使用括号的情况下,

GAMS 逻辑条件中默认的算子优先顺序如表 11.2 所示,采用降序排列。

11.2 算子优先顺序

同等级优先顺序下的算子在表达式中出现的顺序被用做优先规则,其顺序从左到右递减。

➢ 我们通常建议采用括号,而不是依靠算子的优先顺序。这样可避免错误,使意图明确。

考虑如下示例:

它等价于(x-(5*y)) and (z-5)。然而,括号的使用确实使得表达式更易于理解。

11.2.8 混合逻辑条件――例子

一些逻辑条件的简单例子,包含前面小节描述的建筑模块被列在表 11.3 中,用以说明更复

杂的逻辑条件的生成和使用。

表 11.3 逻辑条件的例子

11.3 Dollar 条件

这节将介绍 dollar 算子,它是 GAMS 中功能最强大的特色之一。Dollar 算子与逻辑条件一起

运行。美元符号$(条件)可以读做“使得条件是真”(such that condition is valid),其中条

件是逻辑条件。

➢ Dollar 逻辑条件不能包含变量。但是变量属性(如.l 和.m)却是可用的。

Dollar 算子被用来构建条件赋值,条件表达式和条件方程。下面的小节将提供一个例子来说

明它的使用,我们将分别描述如何使用 dollar 条件来构建条件赋值,表达式和方程。

11.3.1 例子

考虑下面简单的条件:

这在 GAMS 中,可采用 dollar 条件构建如下:

如果条件不满足,a 就不被赋值。我们可将$符号读做“使成立”来阐明它的含义:“a=2,

当 b 大于 1.5 成立的时候”。

11.3.2 嵌套 dollar 条件

Dollar 条件也可嵌套。符号$(条件 1$(条件 2))可读做$(条件 1 and 条件 2)

➢ 对于嵌套 dollar 条件,$以后的表达式必须在括号内

考虑如下例子:

K,s(k),t(k)和 i 是集合,而 u(k)和 a(k)是参数。语句将只对 k 中的某些元素赋值,这些元

素同时也是 s 和 t 中元素。注意条件语句中括号的位置。上述语句也可重写为:

➢ 为了有利于语句的可读性,我们强烈建议使用逻辑与(and)算子,而不是嵌套 dollar

算子。

11.4 条件赋值

本节前面例子中的语句是条件赋值,在这个例子中,dollar 条件放在赋值语句的左边。

➢ Dollar 条件的影响取决于它处在赋值语句的哪边,两边效果是完全不同的。

➢ 许多情况下,可能会采用 dollar 条件两种形式中一种来描述一个赋值。此时,逻辑的清

楚性就成为选用的标准。

下面两个小节描述了 dollar 条件在赋值语句两边的使用。

11.4.1 dollar 置于赋值语句左边

上面阐述的例子将 dollar 条件用于赋值语句的左边。

➢ 对于将 dollar 条件置于左边的赋值语句,赋值不生效除非逻辑条件得到满足。这说明左

边参数的初始值对于不满足条件的标签将保持不变。

➢ 如果赋值语句左边的参数没有事先初始化或赋值,那么对任何不满足条件的标签的赋值

是零。

考虑如下改编自[CHENERY]的例子:

参数 sig(i)已经在模型中事先定义过,上述的语句用来计算 rho(i)。语句上的 dollar 条件保证

不存在被零除的情况。如果与 sig(i)相连的任何值等于零,那么将没有赋值,rho(i)保持为初

始值。因为 rho(i)没在前面被初始化,因此对于 sig(i)等于零的标签,rho 的值为零。

考虑 11.2.1 节解释的惯例:非零意味着“真”,零意味着“假”。因此,上述的赋值可以写成:

11.4.2 dollar 置于赋值语句右边

➢ 对于dollar 条件在右边的赋值语句,赋值都会生效。如果逻辑条件不满足,那么逻辑dollar

条件操作的项赋予零值。

考虑下面的例子,它是在 11.3.1 节描述的例子上进行小幅修改而成的。

表述成文字,这等价于

因此,这意味着一个“if-then-else”的架构,但 else 操作被事先定义,被没有明确给出。

上面例子中的语句可以用明确的“if-then-else” 重写,等价的含义是:

在 else 条件必须明确给出的时候,这种方式会比较清楚。考虑下面改编自[FERTD]的例子,

集合 i 是工厂的集合,现在要计算 mur(i),它表示运输进口原材料的成本。许多情况下,船

运后必须再进行公路运输,因为工厂不在河岸,我们必须加总这两个成本。赋值语句如下:

这表明如果距离表中的输入不是零时,采用这种衔接的运输成本(包含一个固定成分和一个

可变成分)会被加到总成本中。如果没有距离输入,那么就不产生成本,因为没有采用那种

运输模式。

11.4.3 在指数化运算中过滤控制指数

在某些情况下,控制指数可通过不使用 dollar 算子的条件集合来实现过滤。考虑前面过描述

的例子。运输总成本可通过如下方程得到:

其中,shipped 是从 i 到 j 运输的材料总量,totcost 是所有运输的总成本。上述方程也可写成:

然而,如果原先的方程表示为:

此时,集合 j 在 congestfac(j)中脱离 i 单独出现。那么,方程必须被简化为:

应该注意的是在指数化表达式中单独出现 j 时,必须要使用 ij(i,j)而不是 ij。

11.4.4 过滤赋值语句中的集合

考虑如下语句:

其中,k 和 s(k)是集合,而 u 和 a 是参数。这可以重写成:

值得注意的是,通过不带 dollar 算子的条件,我们实现了赋值的过滤。这是一个更加清楚和

更易于理解的赋值表达式。这种特性在处理标签组合(多指数集合)的时候更加有用。

考虑如下计算运输成本的例子,这是一个虚构的从集合地(i)到地区运输中心(j)之间的

包裹快递服务。

集合 ij 表示每个集合点对应的地区运输中心。Factor 是每英里的成本估计。那么,从一个当

地货物集合点(i)到地区运输中心(j)的包裹运输成本(shipcost)可用如下赋值语句表示:

值得注意的是在 shipcost 的赋值中,i 和 j 并没有分开出现。这样,赋值就能简单写成:

如果 i 或 j 在赋值中单独出现,上面的简化就不能实现。例如,考虑到运输成本不仅取决于

运费率(factor)以及两地之间的距离(distance),还取决于地区运输中心的负荷。

Congestfac 是用来表示每个地区运输中心拥挤程度的参数。那么,运输的单位成本可计算如

下:

这就不能写成:

上面的表达式在右边有指数 j,但在左边没有。如前所述,GAMS 将对该赋值语句标记一个

错误。然而,下面的语句是可行的:

在上述赋值中,ij 表示为 i 和 j 的集合对,它们出现在语句的左边。

11.5 条件指数化运算

Dollar 条件的另一个重要用途是控制指数化运算的定义域。这在概念上与 11.3.1 描述的“置

于赋值语句左边的 dollar”类似。

考虑如下改编自[GTM]的例子:

这个语句计算了 supc 有限值范围内的加总。

➢ Dollar 控制的指数操作通常用于控制本身也是集合的情况。这个概念的重要性在动态集

合的讨论中将非常明显。

在第四章,一个集合被用来定义矿山和港口之间的对应关系。另一个典型的例子是定义州和区

域之间关系的集合-集合对应,它用于加总州数据,形成模型需要的数据(区域数据)。

集合 corr 给出了哪个州属于哪个地区的对应关系。参数 income 是每个州的收入。Y(r)可通

过如下赋值语句计算:

对于每个地区 r,对 s 的加总仅局限于那些 corr(r,s)存在的(r,s)对。从概念上讲,集合的存

在和 Boolean 值“真”和算术值“非零”类似。结果是“Vermont”和“maine”被包含在“north”

的总和中,而“south”仅包含“texas”和“folrida”。

注意上面的加总也可以写成 sum(s,income(s)$corr(r,s)),但是,这种格式不如控制加总指数那

么容易阅读。

11.5.1 在指数化运算中过滤控制指数

在某些情况下,控制指数可通过不使用 dollar 算子的条件集合来实现过滤。考虑如下例子,

运输总成本通过如下方程得到:

其中,shipped 是从 i 到 j 的运输材料的总量,totcost 是所有运输的总成本。上述方程可以写

成:

然而,如果原始方程被表示为:

其中,指数 j 在 congestfac(j)中单独出现。那么,方程必须简化为:

记住如果 j 单独出现在指数化表达式中,那么必须使用 ij(i,j)而不是 ij。

11.6 条件方程

Dollar 算子在方程中也可用于例外处理(exception handling)。下面两个小节讨论 dollar 算子

在方程中的两个主要应用――在方程实体中的使用和在定义域上的使用。

11.6.1 代数表达式中的 dollar 算子

方程中的 dollar 算子与 11.4.2 节中讨论的赋值语句中置于右边的 dollar 算子类似,如果我们

将“置于右边”理解为在“..”右边,那么这种类似就更贴切了。如同在赋值操作中一样,dollar 算

子隐含了一个 if-else 的操作,它用于从生成的限制式中排除一部分定义。

考虑如下改编自[CHENERY]的例子。

后面一项仅在 i 中元素属于 t(i)的情况下,才被加到方程的右边。

我们也可像在赋值语句中那样采用 dollar 条件控制指数运算。考虑如下[GTM]中的供给平衡

方程:

11.6.2 定义域的 dollar 控制

这类似于 11.4.1 节讨论的置于赋值语句左边的 dollar 控制,如果我们将“置于左边”理解为

在“..”左边,那么这种类似就更贴切了。

➢ 方程定义域上 dollar 控制的目的是限制生成限制式的数量,使其少于集合定义域所隐含

的数量。

考虑如下改编自[FERTS]的例子:

Cc 是定义在所有单位(m)和地点(i)上的容量限制。

然而,并非在所有地点上都会存在全部的单位类型。对应集合 mpos(m,i)用于限制实际生成

的限制式数量。用 ppos(p,i)限制对 p 的求和是额外的一个 dollar 条件,因为并非所有的程序(p)

都要用于所有地点(i)。

11.6.3 过滤定义域

用于过滤赋值语句和控制指数化运算指数的相同规则也可用于方程定义域。考虑如下采用相

同集合定义的方程:

方程 logical 将连续变量 shipped(i,j)和二元变量 bin(i,j)联系起来。这可简化如下:

应该注意的是如果方程右边包含任何指数单独为 i 或 j 的项,那么方程 logical(i,j)$ij(i,j)就必

须简化为 logical(ij(i,j))。

第十二章 动态集合

12.1 引 言

迄今为止讨乱的所有集合在集合本身被声明的时候,它的元素也被同时声明,并且元素不再

变化。本章,我们将讨论改变集合的元素。元素可以改变的集合称为动态集合,以比较元素不

变的静态集合。这种区别非常重要,本章将详细对其进行讨论。这是一个以前一直避免的话题,

因为对新手来说可能引起混淆。然而,高级用户将发现它的实用性。

11.2 动态集合的元素赋值

集合能够以与其它数据类型类似的方法赋值。不同之处在于算术运算在集合中不能执行,这

不像其它数值型的标识符(参数,变量或方程的子类型)。动态集合通常在赋值定义或方程

定义中用作控制指数,或在 dollar 控制的指数化运算中用做控制实体。

11.2.1 语法

一般情况下,GAMS 中赋元素给动态集合的语法如下:

Set_name 是集合在 GAMS 中的内部名称(也称为标识符)。Yes 和 no 是关键字,GAMS 用

它分别表示是否是所赋集合的元素。

➢ 该遵循的最重要的原则是动态集合通常必须在声明的时候进行定义域检查,以确保它是

一个(或一些)静态集合的子集。

➢ 当然,我们也可能会使用不经过定义域检查的动态集合,这提供了额外的功能,灵活性, 但

不易理解,也比较危险。任何标签都是合法的,只要维度一确定,它就被保留下来。

11.2.2 例子

下面的例子改编自[ZLOOF],用于说明对动态集合的元素赋值。

注意,集合 subitem1 和 subitem2 如同其它集合一样被声明。这两个集合由于赋值而变成动

态的。它们也进行了定义域检查:它们能够有的唯一元素必须也是 item 的元素。Item 是一

个静态集合,因此它的元素被固定了。开始的两个赋值分别给 subitem1 赋予一个新元素。

第三是我们熟悉的指数化赋值的例子:subitem2 被赋予了 item 中的所有元素。显示语句的

输出可展示这些集合的元素,结果如下:

元素以 item 声明设定的顺序显示。

12.2.3 多指数动态集合

如同静态集合,动态集合最高可有 10 维。下面的例子说明了多维集合的赋值问题。

第 4 章讨论静态集合时介绍的星号和括号列表机制也可在动态集合中使用。

11.2.4 动态集合定义域上赋值语句

我们可以在动态集合的定义域上进行赋值,因为动态集合被认为是静态集合的子集。这与采

用动态集合进行定义域检查是不同的。

下面例子改编自 12.2.2 节,说明了动态集合作为定义域的使用。

第一个赋值语句保证 subitem1 是空集。值得注意的是这也可以通过参数实现。例如:

12.2.5 定义在动态集合定义域上的方程

有时我们必须在动态集合之上定义方程。

➢ 技巧是在整个定义上声明方程,但在动态集合上进行定义。

下面的例子说明它的用法:

重复重要的一点:方程在 allr 上声明,但在 r 上引用。然后,allr 中的元素将任意赋予 r。

12.3 使用带有动态集合的 dollar 控制

本章的其余部分要求理解 dollar 条件。所有的 dollar 控制机制都可与动态集合一起使用。事

实上,动态集合所有强大的功能可使用这些 dollar 控制来开发。

值得注意的是动态集合只有 yes 和 no 两个值,因此可以视为逻辑语句。那么,能够在包含

于 dollar 算子内的动态集合之上执行的唯一运算是 not,and,or 或者 xor,以及 12.4 节将要

描述的集合运算。

12.3.1 赋值

动态集合可用于赋值语句内的 dollar 条件中,以定义其它动态集合或参数。

作为说明如何采用一个动态集合来定义其它动态集合的例子,12.2.4 节例子的两个语句可以

等价写成:

它是下面语句的一个简洁格式:

➢ 伴随“置于右边的 dollar”的隐含“else”的值,在集合赋值中是逻辑 no,而不是用于

普通数据的零。

12.2.4 节中的第二个例子可以写成如下形式,以说明定义参数时动态集合的应用。

12.3.2 指数化运算

带有动态集合的 dollar 控制的另一个重要应用是在进行如 sum 和 prud 的指数化运算时控制

定义域。考虑如下 12.3.1 节第二个例子的修改版本。

这个例子只是为了说明。注意上述第二个语句也可简洁地写成:

但这通常是不可行的。考虑如下虚设的一个例子:

上述赋值用于寻找由 bic 提供货物的所有部门的总销售量。动态集合用来限制求和定义域,

这个定义域满足 supply(item,’bic’)为真。

12.3.3 方程

动态集合可用于方程的 dollar 条件中,可作为方程算术表达式的一部分,也可定义方程的定

义域。第一种情况与 12.3.1 节讨论的赋值类似。第二种情况用于限制动态集合定义域上的方

程。12.2.5 例子定义的方程可等价地重写成:

方程 prodbal 的定义域被限制在那些属于动态集合 r 的元素中。

12.3.4 通过动态集合过滤

在条件集合是动态集合时,前面章节解释的过滤过程也是有效的。考虑以前描述过的两个例

子:

这些语句可以重写成:

12.4 集合运算

本节描述如何在 GAMS 中采用动态集合进行各种符号集合运算。并集、交集、补集和差集

等集合运算将在下面小节中分别描述。12.2.2 节的例子再次被用来说明每种运算。

12.4.1 并集

符号“+”用于并集运算。考虑如下例子:

Subitem3 的元素被设定为等于 subitem1 的所有元素和 subitem2 的所有元素。上述的运算等

价于如下较长的表达式:

12.4.2 交集

符号*用于交集运算。考虑如下例子:

Subitem3 中的元素被设定为等于那些同时出现在 subitem1 和 subitem2 中的元素。上述的元

素等价于如下较长的表达式:

12.4.3 补集

算子 not 用于补集运算。考虑如下例子:

Subitem3 中的元素被设定为等于那些所有在 item 中,但不在 subitem1 中的元素。上述的元

素等价于如下更长的表达式:

12.4.4 差集

算子“-”用于差集运算。考虑如下例子:

Subitem3 的元素被设定为属于 subitem1 但不属于 subitem2 的所有元素。上述的运算等价于

如下较长的表达式:

12.5 小结

集合赋值的目的是要基于给定数据(静态集合)进行计算,以用于例外处理。这是另一个例子,

它再次说明了 GAMS 构建模型的原则:输入少量数据,并根据大多数基本信息构建模型。

第十三章 作为序列的集合:有序集合

13.1 引言

在第 4 章关于集合的讨论中,我们说过除非有特殊需要,一维集合应该被视为标签的无顺序集

合。本章我们将讨论集合的一个特殊特征,它在你必须像一个序列一样来处理集合时会使用到。

例如,在明确表示不同时间段条件的经济模型中,有必要涉及到“下一期”和“前一期”,

因为不同时期之间必须存在联系。举另外一个例子,资本存量通常通过如下模型进行跟踪,

模型方程有如下形式:“n 期期末的资本存量等于n-1 期期末的存量加上n 期之间的净收益”。

选址问题中,公式可能要求表达相邻地区,如同城市地图中的坐标方格。行程安排是另一类型

的问题,其集合也要有序列的性质。

➢ 包含时间序列的模型通常称为动态模型,因为它们描述了条件如何随时间改变。不幸的

是,单词“动态”在这里使用的含义不同于动态集合中动态的含义,但这种混淆难以避免。

13.2 有序和无序集合

与用于定义域检查的集合一样,当集合需要像序列一样被引用的时候,会对其施加限制。静

态集合的概念已经在前面介绍过:集合必须在声明的时候采用斜杠中列出标签的方式进行初始

化,其后集合不再变化。

➢ 有序集合必须是静态集合。换句话说,动态集合不可能有顺序。

➢ GAMS 保留了一栏独特的元素――在一个或多个集合中作为元素使用的标签。任意一

个集合中元素的顺序与那些独特元素栏中元素的顺序一样。这说明如果一些标签在一个

更早的定义中使用过,那么集合的顺序可能不像它表面上看起来的那样。

➢ GAMS 顺序排列的标签地图可通过在第一个集合声明之前加入编辑器指令$onuellist。

➢ 控制的一个好方法是:如果某人想要排序的一个集合中的标签还没被使用过,那么它们将

被排序。

我们将展示程序清单被列出后的地图以及其它编辑器地图。下面例子,我们展示了有序集合

和无序集合,以及显示出顺序的地图。输入是:

下面的地图显示了输入顺序(重要的一种顺序)和分类顺序(将标签按字典顺序分类整理而

得)。左边的单个数字是那行第一个标签的序列数。

一个集合可通过将其声明移得更靠近程序开始位置来进行排序。知道这些限制,我们接下去

讨论用于处理作为序列的集合的运算。

13.3 ord 和 card

在第 4 章,我们解释说标签没有数值。使用的例子是标签‘1986’没有数值 1986,标签‘01’

和标签‘1’是不同的。这节我们介绍两个算子,ord 和 card,它们在用于集合的时候,返

回了整数值。然而,返回的整数值并不表示标签的数值,它们用于相同的目的。

下面两个小节依次描述这两个函数。

13.3.1 ord 算子

Ord 返回集合中元素的相对位置。

➢ Ord 仅可与一维静态有序集合一起使用

一些例子说明了这种用法:

上述语句的结果是 val(‘1985’)的值是 1,val(‘1986’)是 2,按顺序逐渐递增。

Ord 经常用于设定反映以某种解析方式增长的数量的向量。例如,假设一个国家在基期有

5600 万人口,并且人口以每年 1.5%的速度增长。那么下一年人口可采用如下方式计算:

这在 GAMS 中模拟一般矩阵运算时非常有用。两维参数中的第一个指数一般可代表行,第

二个代表列,顺序是必要的。下面的例子说明了如何设定矩阵的上三角等于行指数加上列指数,

并且令对角线和下三角等于零。

13.3.2 Card 算子

Card 返回集合元素的个数。Card 可与任何集合一起使用,包括动态集合和无序集合。下面

例子说明它的使用方法:

上述语句的结果是 s 将被赋值 11。

Card 通常用于设定最后一期的某些条件,例如固定一个变量。一个虚构的例子如下:

它仅固定了最后一个元素对应的变量:而对 t 中其它元素没有赋值。这种固定 c 的方法的优

点在于 t 中的元素可以改变,这个语句将会一直固定最后一个元素对应的 c 值。

13.4 lag 和 lead 算子

Lag 和 lead 算子被用来联系集合当前的元素和“下一个”或“上一个”元素。为了采用这些

算子,被讨论的集合必须是有序的。GAMS 提供了两种格式的 lag 和 lead 算子。

这两种类型算子之间的不同之处在于序列终点的处理。GAMS 提供了一些内嵌的工具来处

理这个问题,但在任何包含序列的工作中,用户必须仔细考虑终点的处理,所有模型都将需要

特殊的例外处理逻辑。

线性情况下,集合元素中的终点是单向的。换句话说,第一个元素之前和最后一个元素之后就

不存在其它元素,这可能导致使用不存在的元素。下面一节将讨论 GAMS 是如何处理这个

问题的。Lag 和 lead 算子的格式在对不重复的时间进行建模时是非常有用的。时间集合(假设

为 1990-1997)是一个例子。算子是“+”和“-”。

➢ GAMS 能够根据内容对线性 lag 和 lead 算子(+,-)和算术符号进行区分。

在循环情况下,集合的第一个和最后一个元素假设是相连的,从而形成了元素的循环序列。

其思路是‘first-1’指的是‘last’,‘last+2’与‘first+1’是一样的。所有引用和赋值必

须事先定义。这在对重复时间的建模中非常有用,如年份中的月份,或每天中的小时。我们非

常自然认为 1 月是 12 月后的月份。农场预算模型和劳动力安排模型是循环发生的很好例子。

算子是“++”和“――”。

下面两节将分别描述 lag 和 lead 算子在赋值语句和方程中的应用。

13.5 赋值语句中的 lags 和 leads

Lag 和lead 算子的一个用处是在赋值语句中。赋值语句右边 lag 和lead 算子的运用称为引用,

而在左边称为赋值,它包含赋值定义域的定义。对 lag 和 lead 算子的线性和循环格式,引用

和赋值的概念都是有效的。然而,对循环 lag 和 lead 算子来说,引用和赋值之间区别的重要性

并不明显,因为这种情况下,不会使用到不存在的元素。

➢ 对不存在元素的引用导致一个默认值(本例中是零),而试图对一个不存在的元素进行

赋值的结果是没有赋值产生。

下面两个小节提供例子说明了线性 lag 和 lead 算子在引用和赋值中的用法。13.5.3 节将说明

循环格式的 lag 和 lead 算子的用法。

13.5.1 线性 lag 和 lead 算子――引用

考虑如下例子,其中两个参数 a 和 b 用来说明线性 lag 和 lead 算子在引用中的用法。

Option 语句让小数不显示。结果如下:

对于 a,如同预期,值 1987,1988 直到 1991 是根据标签 y-1987,y-1988 等得到的。b 初始

化为-1。

对于 b,赋值在 t 中所有的元素上进行。对于每个 b,前期 a 的值被赋为当期 b 的值。如果

没有前一期,如 y-1987,那么就采用零。b(‘y-1987’)变为零,代替了前一期的值-1。

13.5.2 线性 lag 和 lead 算子――赋值

考虑下面的例子,其中两个参数 a 和 c 被用来说明线性 lag 和 lead 算子的赋值。

结果显示如下:

对 a 的赋值在 13.5.1 节中解释了。对 c 的赋值就不同了。对序列中 t 的每个元素,寻找 c 中

与 t+2 相应的元素,如果存在,就采用 a(t)的值替代。如果不存在(如 y-1990 和 y-1991),

就不进行赋值。t 中的第一个元素是 y-1987,因此,第一个赋值是对 c(‘y-1989’)进行的,它

有值 a(‘y-1987’),也即,1987。没有对 c(‘y-1987’)或 c(‘y-1988’)进行赋值:两个都保持了原

先的值-1。

滞后值或领先值不必是明确的常数:它可以是任意表达式,只要这个表达式的计算结果为整数。

如果不是整数,就会出现错误。直观上看,负值将导致转换(例如,从 lag 变成 lead)。下面

语句保证将 d(t)都设置为零:

13.5.3 循环 lag 和 lead 算子

下面的例子说明了如何使用循环 lag 和 lead 算子。

结果如下:

参数 lagval2 用于引用,而 lagval1 用于赋值。值得注意的是循环 lag 和 lead 算子不会导致任

何不存在的元素。因此,引用和赋值之间的不同就没那么重要了。注意上述例子中的两个语句:

等价于:

引用和赋值的使用被颠倒过来,但效果一样。

13.6 方程中的 lags 和 leads

前面章节建立的准则很自然就过渡到方程定义的应用中。方程实体中的 Lag 和 lead 运算

(置于‘..’右边)属于引用,如果相应的标签没被定义,那么该项就消失了。‘..’左边的 lag

和 lead 是对方程定义域的修改。线性格式可能引起一个或多个方程消失。

➢ 所有的 lag 和 lead 操作数必须是外生的

下面两个小节提供例子,说明了如何在方程中使用线性格式的 lag 和 lead 算子作为引用和修

改定义域。本节将说明方程中循环格式的 lag 和 lead 算子的运用。

13.6.1 线性 lag 和 lead 算子――定义域控制

考虑下面改编自[RAMSEY]的例子:

上述包含了对 t 的声明,如同一组动态集合,采用明确的方式来处理第一期和最后一期(终

止条件)。

我们感兴趣的是方程 kk,它是资本平衡方程。集合 t 包含元素 1990-2000,因此对 1991 年

到 2000 年将有一个存量限制。写出 1991 年的限制式是:

定义域上的 Lead 算子限制生成的限制式,因此不会出现对不存在变量的引用:这个问题将

有 10 个 kk 限制式,它们定义了 11 个资本值之间的关系。

[RAMSEY]例子中的另一个有意思的地方是限制式 tc 仅对最后一期进行明确定义,因为是

对集合 tlast 进行赋值。注意我们这里是使用动态集合来控制两个方程的定义域。集合 tfirst

也用于模型的其它部分以设定初始条件,特别是第一期(k(‘1990’))的资本存量。

13.6.2 线性 lag 和 lead 算子――引用

13.6.1 节讨论的例子中,方程 kk 可以等价地重写成:

Dollar 条件将导致其中一个方程失效。

然而,在方程定义域中采用 lags 和 leads 通常将导致一个或多个方程失效,这在每个例子中

都不是理想的。考虑前面例子中讨论的,但经过修改的限制式集合。它采用 lag 和 lead 算子

表示,这些算子用于控制方程的定义域。

这里,重要的边界是集合开始的边界而不在末尾。这可以更加简洁地表示成:

一般说来,使用 lag 和 lead 算子作为参考或用在定义域控制中是个人偏好的问题。

13.6.3 循环 lag 和 lead 算子

循环 lag 和 lead 算子的案例中,算子用于定义域控制和用于引用的区别不是很重要。因为它

不会导致任何方程或任何项目失效。考虑如下例子:

这个例子中,生成了如下四个单独的方程:

这里没有方程失效。

13.7 小结

本章介绍了集合排序的概念。GAMS 中处理这些问题的特色包括 ord 和 card 函数,以及我

们详细描述的线性和循环格式的 lag 和 lead 算子。

第十四章 显示语句

14.1 引言

本章我们将提供显示语句更详细的细节,包括用户对版面和输出外观的控制。这些控制是一种

折衷以提供一定程度的灵活性。显示语句不提供符合发行质量的报告功能,但它的目标是提供容

易使用的功能,提供优美的默认报告格式。显示语句的执行只允许数据写入清单文件。

14.2 语法

一般说来,GAMS 中显示语句的语法是:

Ident-ref 是没有列出定义域或驱动指数的集合或参数的名称,或是方程或变量的子域。标识

符引用和文本可以以任何顺序混合或配对,整个语句可跨行连接。

显示语句形成的输出包含标签和数据。对于集合而言,我们采用字符串 yes(表示存在)而

不是数值。

➢ 对所有数据类型,只有非默认值被显示。

默认值一般为零,除了变量和方程的.lo 和.up 子类型。默认值列在表 14.1 中。

表 14.1 .lo 和.up 子类型的默认值

14.3 例子

显示语句的一个例子如下:

输出的清单文件包含下面与显示语句对应的一节。

应该注意的是只有非零的值被显示。在多维标识符的情况下,数据用易于阅读的表格形式报

告。

14.4 显示语句中的标签顺序

显示语句默认的不同维度标识符的版面格式可见表 14.2。表中的数字指的是指数在标识符定

义域列表中位置。作为一个例子,如果我们显示 c,其中 c 已经声明为 c(i,j,k,l),那么 i 标签(第

一个指数)将与平面或单个子表格相连,j 和 k 与行标签相连,l(第四个或最后一个)与列

标题相连。

14.2 display 输出的默认版面

对 7 到 10 个指数,可很自然扩展下去。第一个指数位置的标签变化最慢,位置最高变化最快。其顺

序是根据 GAMS 标签的输入顺序而定的。

指数的顺序通常与符号声明语句中顺序一样。我们可采用满意的顺序来声明它们,或用不同

的顺序对新标识符进行赋值。

➢ 改变显示输出中每个指数位置上标签出现顺序的唯一方法是改变 GAMS 程序中标签出

现的顺序。一个最容易的方式是声明一个集合,它的唯一目的是以我们需要的顺序列出标

签,并使这个集合成为 GAMS 程序的第一个声明。

14.4.1 例子

考虑如下例子。X 有四个维度,或说是有四个指数位置。采用参数格式初始化而后进行显示, 程

序如下:

这个代码段产生了如下输出:

注意这里存在两个子表,对应第一个指数位置的两个标签。X 列表中的零(’first’,’one’,’a’,’ii’)

消失了,因为每个子表中,零值不显示。标签的顺序与输入数据列表中的顺序不同。

14.5 显示控制

GAMS 允许用户修改显示清单中行标签和列标签的个数,以及被显示数据的精确度。全局

显示控制允许用户影响一个以上的显示语句。如果具体数据需要以特殊格式列出,局部显示

控制可用来覆盖全局控制。下面两个小节将依次讨论这两种显示控制。

14.5.1 全局显示控制

这些选项中最简单的一个是控制小数点后面的数字个数,它影响该 option 语句后所有显示

输出的外观, 除非如下所示的特定标识符得到改变。该语句的一般格式为:“ option

decimals=value”,其中 value 是一个 0 到 8 之间的整数。如果你使用 0,小数点将不显示。

数域的宽度不会改变,改变的只是小数位个数,但这种方法可能引起采用正常方式显示的数字

变成 e 格式(科学计数法),也即采用指数表达方式。

考虑如下对前面章节所述例子进行扩展的例子

GAMS 将必要地方的数字改成 e 格式,输出如下:

14.5.2 局部显示控制

这通常在控制具体标识符的小数位的时候使用。一般格式如下:

Ident 表示参数,变量或方程的名称。d-value(与前面相同)必须在 0 到 8 之间。确切地说,d-

value 位的小数将在以后所有 ident 的显示中出现。这种格式可以推广到控制数据的版面。一

般格式是:

这里 r-value 是指数位置的个数,用来形成行标签,c-value 是列标题的个数。

前面一节讨论的例子被进一步扩展,以说明局部显示语句的使用。

输出如下:

结果显示了五个小数位,三个标签用来标记行,一个标签用来标记列。因为这是一个四维结

构,所以没有多余的指数来形成子表格标签(在平面上),可以一次显示结果。Option 语句

会被检测,看标识符维度是否一致,GAMS 在必要的时候会发布错误信息。这是一个将两

个指数分别作为每个行和列的标签,并保留五个小数位置的例子。

输出结果为:

14.5.3 生成列表格式数据的显示语句

这是局部显示控制的特殊使用方法,它用显示语句生成列表格式的数据。这时,每个值对应

的所有标签都会给出,这与在数据初始化中的参数类型一样。格式是:option:ident:d-value:0:c-

value;这里,c-value 设定了一行中显示项目的最大值。实际数字将取决于页宽以及标签的

个数和长度。

采用前面一节相同的例子进行扩展:

这个语句输出结果为:

输出很好地阐明了使用的标签顺序。第一个指数变动最慢,最后一个最快,每个指数从第一

个标签到最后一个标签依次列出,而后它前面的标签再变动。这种排序方式也用在方程和求

解报告的列表中,这些全部是由求解语句产生的。

第十五章 put 书写工具

15.1 引言

本章我们将介绍 GAMS 语言中的 put 书写工具(put writing facility)。书写工具的目的是在

格式控制下,将单个项目输出到不同的文件中。与显示语句不同的是,指数化标识符中整个集

合的值不能采用单一的 put 语句输出(标识符是给数据实体的名称,如参数,集合,变量, 方

程和模型等的名称)。虽然它的结构比较复杂,比显示语句要求更多的编程设计,但却比较

灵活,能够对单个项目的输出进行控制。

本章中,我们将讨论 put 书写工具的工作方式,以及存取文件和采用不同文件属性的文件后

缀来全局编排文件格式的语法。Put 书写工具让用户能够采用 GAMS 系统存储的信息生成有

组织的文件。这些信息在采用多种与标识符、模型和系统相连的后缀的情况下是可用的。文件

的格式编排可通过使用文件后缀和控制字符来实现。

Put 书写工具在 GAMS 执行的时候自动生成文件。文档被写到一个外部文件中,一次一页。

当前页储存在缓存中,在页面长度属性被超过的时候,它将自动被写到一个外部文件中。然而,

当某一特定页在当前处理时,写在上面的信息可以被重写或任意删除。

15.2 语法

格式最简单的 put 书写工具的基本结构为:

其中,fname 表示 GAMS 模型内部使用的外部文件的名称。Items 是输出项目的类型,如解

释性文本,标签,参数,变量或方程值。上面所述的基本结构中,第一行定义了一个或多个你

想写入的文件。第二行令这些被定义的文件中的一个为当前文件,也即被写入的文件。最后

一行表示实际写入当前文件的输出项。

15.3 例子

我们采用小模型来介绍 put 书写工具的基本要素,这是非常有帮助的。这个例子将基于运输

模型[TRNSPORT]构建。下面的程序段可以放在运输模型的末尾以创建一个报告。

第一行中,内部文件名 factors 和 results 被定义,并连接到名称 和 的外

部文件上。这些内部文件名被用在模型内部,来表示模型外部的文件。第二行将

作为当前文件,也即当前可写入的文件。

第三行中,使用一个 put 语句将文本项“transportation model factors”写入文件。注意这里的

文本是有用引号的。引用文本后面的斜杠表示回车。标量 f 后面又紧随着另外一个文本项。

注意这些输出项是采用逗号隔开的。空格,逗号和斜杠作为分隔符隔开不同的输出项。如上

所述,斜杠用做回车,逗号和空格用做项目分隔。这些分隔符使得写入位置是文件中最后一项后

面的那列指针。多数情况下,空格和逗号能交替使用;然而,逗号是更严格的格式,它将消除任

何含糊。

上面程序的第五行中,指针重新定位在输出文件第六行的首列,这里另外一个文本项将被写入。

指针控制字符#和@用于重新定位指针到具体的行或列,它由指针控制字符后面的行数或列

数决定。最后,put 语句以一个分号结束。

接下来,参数 a 和 b 以及它们对应的集合标签被写入文件。指数集合中只有一个元素能采用

一个 put 语句写入。为了写入参数 a 和 b 的所有内容,put 语句被植入到一个在指数集合上

重复迭代的循环中。在上面的例子中,集合元素标签采用集合名称和后缀.tl 来识别。正如

我们见到的,集合元素标签开始于第三列,而参数 a 位于第 15 列。这个例子在参数 b 之前

还显示了另一个引用文本。当语句被执行的时候, 文件的内容如下:

这个输出采用的是默认的文件格式。本章后面将继续讨论改变这些默认值的方法。

在本例的最后两行,文件 被设定为当前文件,变量 x 的值以及它们对应的集合元

素标签被逐行写入。变量 x 输出结果的格式设定为八位字段宽度,其中四位数保留给小数。

注意局部格式选项由冒号分隔开。 文件的内容如下:

有了这个对 put 书写工具的简单介绍,我们就容易想象它的多种用途,如书写报告,将输出

写入一个文件,以便用于另一个计算机程序,或仅仅显示中间计算。但是,put 书写工具的

外表几乎不可能出乱子。下面几节,put 书写工具的许多特征和结构会更详细地被描述,并

以例子进行说明。

15.4 输出文件

如前所述,put 语句允许用户输出到外部文件中。这节我们将描述与外部文件使用相关的多

种特征。

15.4.1 定义文件

定义文件的完整语法是:

其中,file 是关键字,用于定义文件。Fname 是 GAMS 模型的内部文件名称,它指向外部文

件。外部文件是输出实际写入的文件。在文件声明过程中,外部文件名称和解释性文本是可选

的。当外部文件名称被忽略时,GAMS 将提供一个系统默认设定的外部文件名称,通常是

。值得注意的是多个文件可采用一个 file 语句进行定义。考虑如下例子:

第一个输出文件在模型中的名称是 class1,对应计算机系统的一个默认文件 。第二

个输出文件在模型中的名称是 class2,对应外部文件 。最后,特殊的内部文件名 con

被定义,它将输出写到计算机屏幕上。写到屏幕上非常有用,在模型执行期间,它提供给用户

关于模型多个方面的建议。

15.4.2 赋值文件

Put 语句可用于赋值给当前文件,并将输出项写到这个文件中。使用 put 语句的完整语法是:

如同这个语句表示的,多个文件可采用一个 put 语句依次写入。应该注意的是每次只有一个

文件是当前文件。一个内部文件名称的输出项被写入后,语句中下一个内部文件名称将被定位

为当前文件。

15.4.3 关闭文件

关键词 putclose 用于在 GAMS 程序执行过程中关闭一个文件。语法如下:

其中,myfile 是要关闭的文件的内部名称,item(s)是文件关闭之前输入文件的最后项目。如

果 putclose 语句中的内部文件名被省略,那么被关闭的是当前的 put 文件。值得注意的是使

用 putclose 命令以后,要再次使用的时候,文件不必再重新定义。要做的仅是将文件设为当前

文件,然后采用 put 语句的正常使用方法。当然,现存的文件要么被覆盖,要么内容被添加,

这取决于附加的文件后缀的值。

➢ 一个非常有用的应用是从 GAMS 模型中写出求解选项文件。我们可以写出 Option 文件

语句,这可采用 put 以及用 putclose 结束的、处在求解语句之前的文件来实现。

下面的例子阐明了为 MINOS 求解模块创建和关闭的一个 option 文件。

这个程序片段将置于 GAMS 模型中的求解语句之前。

15.4.4 添加内容到文件

Put 书写工具能够添加或覆盖一个现存文件。文件后缀.ap 决定哪种运算会发生。默认的后缀

值 0 将覆盖现存文件,而值 1 将内容添加到现存文件中。让我们考虑一个现存的文件

。采用如下语句可将输出项目添加到该文件:

任何写入 的项目将被加到现存文件内容的末端。如果文件不存在,那么这个文件

将被建立。

15.5 页面格式

文件内的页面也可采用文件后缀进行组织,设定多种特性,如打印格式,页面大小,页宽和

页边,以及哪个文本被显示等。下面文件后缀可用来设置页面格式:

第一,打印控制(print control)(.pc),用来设定外部文件的格式。最后三个选项(4-6)建

立被限定的文件,这在准备直接输出到其他计算机程序,如电子数据表的时候特别有用。

0 基于当前页大小的标准分页。剩余页面由空白行填补。值得注意的是.bm 文件后缀

只有在与这个打印控制选项一起使用的时候才有效。

1 FORTRAN 页面格式。这个选项以标准的 FORTRAN 惯例将数字 1 置于每页的第

2

3

4

5

6

一行第一列。

连续页面(默认设置)。这个选项与.pc 选项等于 0 类似,不同的是文件中的剩余页

面没有采用空白行填满。

插入 ASCII 页面控制符。

格式化输出:非数字输出被引用,每个项目用空格分隔。

格式化输出:非数字输出被引用,每个项目用逗号分隔。

格式化输出:非数字输出被引用,每个项目用制表符分隔。

第二,页面大小(page size)(.ps)用来设定置于文件每个页面的行数。用户可在程序任何地

方对它进行重设。但是,如果设定的值小于已经写入当前页的行数,那么这就会产生错误。

最大值为 130,默认值为 60。

第三,页宽(page width)(.pw)用来设定每页每行的列数。用户可在程序的任何地方对它

进行重设。但是,如果设定的值小于已经写入当前页的行数或列数,那么就会产生错误。默认

值是 132。

第四,上页边距(top margin)(.tm)。设定每页的上页边距的空格行数。这些行是.ps 文件

后缀设定的行数之外额外的行。它只在和.pc 选项为 0 一起使用的时候有效。默认值是 0。

第五,下页边距(bottom margin)(.bm)设定每页的下页边距的空格行数。这些行是.ps 文

件后缀设定的行数之外额外的行。它只在和.pc 选项为 0 一起使用的时候有效。默认值是 0。

第六,大小写字母(alphabetic case)(.case)用来设定输出文件中显示的字母符号的大小写

格式。

0

1

(默认)混合显示大小写。

显示大写字母,而不管输入的时候如何。

为了说明这些文件后缀的使用,下面例子对 文件的格式进行编排,使得每页有 58

行的输出,页宽为 72 个字符,额外的上页边距为 6 行,采用 ASCII 页面控制符(置于每行

中),输出以大写字母显示。

➢ 采用打印控制后缀(.pc)中 4,5 或 6 的值将导致数据被挤压,因此将忽略用户通过@

字符提供的间隔信息。然而,这些值可传到电子数据表中,并由电子数据表读取。

15.6 页面区域

文件每页有三个独立的书写区域,它们是标题区(title block),页首区(header block)和窗

口(window)。这在文件页面区域保持相对不变的时候非常有用。标题区和页首区通常用于

提供文件的组织信息,而窗口用于具体报告。

这些书写区域通常依次按下表顺序出现在一个页面中。应该记住的重要一点是标题区和页首

区本质上与窗口相同,并且完全采用相同的语法规则。但是,窗口是每个页面都必须要有的, 而

标题和页首是可选的。还要记住的另一点是一旦窗口写入东西,任何标题或页首区的进一步修

改将被显示在下一页,而不是显示在当前页。往窗口写入东西最终形成了页面。

在 15.3 描述的例子中,所有数据都被写到窗口中。如果需要更详尽的细节,就可能需要加

入标题,提供模型名称以及页数。另外,也可用页首区显示指令,它是我们一贯需要的,重复

出现在每个页面上的信息。一旦这个信息置于标题或页首区,随后它就显示在每个页面上, 除非进

行修改。这对一个多页的文件是非常重要的。

15.6.1 访问不同的页面区域

每个页面区域可通过使用关键字 put 的不同变种来实现写入。这些变种是:

Puttl 写到标题区

Puthd 写到页首区

Put 写到窗口

一个给定页面中区域的大小完全取决于输入的行数。所有区域的总行数必须在设定的页面大

小范围内。如果写入标题和页首的总行数等于或超过页面大小,程序清单中就会显示出一个超

值错误。这种情况意味着页面没有多余的空间用于窗口输入。

如上所述,将一个输出项目写入到这三个可能的页面区域的语法是基本相同的,唯一不同的地

方是 put 关键字的选择。我们通过写入文本到 的标题区来说明它的使用方法:

本例中,文本‘GAMS put example’被置于标题区的第一行第一列。 文件中任何

后来的页面都将以这个信息开头。

➢ 如果标题区被修改或页首区在当前页已经被写入后才开始,这些修改将出现在下一页,

而不是出现在当前页。

15.5.2 分 页

当页面满的时候将自动分页。然而,窗口必须按顺序使用,以使页面写入输出文件。当没有输

出在页面窗口上的时候,不论标题区或页首区是否有输出项目,页面都不被写到文件中。为了

让只有空白窗口的页面输出到文件,只要写入如下语句:

现在,页面窗口就被初始化了,可以写入到文件中。

15.7 定位页面指针

指针被置于最后写入的字符之后,除非指针采用如下指针控制符进行设定。

#n 移动指针到当前页第 n 行;

@n 移动指针到当前行第 n 列;

/ 移动指针到下一行的第一列。同用做输出项目之间的分隔符。

除了数字以外,任何带数值的表达式或符号可用在#和@字符之后。下面的例子说明了如何

使用这些位置控制,以表格形式写出参数 a(i,j)的值。

15.8 系统后缀

可用以揭示 GAMS 运行信息的完整的系统后缀如下:

考虑前面章节讨论的例子。我们可以将页数加到报告文件的标题上,通过修改 puttl 语句来

实现:

这使得单词“page“之后出现页数,它从第 65 页开始显示在每页的标题上。

15.9 输出项

Put 语句的输出项有如下形式:

Text 任何引用文本,集合元素标签或文本,任何标识符符号文本或系统后缀内容;

Numeric 与参数,变量,方程或任何模型后缀相连的值;

Set values 表示集合元素的存在,只包含 yes 或 no。

识别和使用这些不同类型输出项的方法将在下面小节中讨论。

15.9.1 文本项

文本项是由一对单引号或双引号分开任何字母或数字的组合,也即引用文本。然而,引用文

本的长度,以及任何输出项都有限制。不能存在输出项被放置于页面边缘之外。当页面宽度被

超过的时候,几个星号会被放在行末,程序清单中会记录一个 put 错误。

除了引用文本,其他文本项的输出也是可能,这可通过使用系统和标识符后缀。标识符后缀为:

➢ 标识符符号文本(identifier symbol text)(.ts) 显示与任何标识符相关的文本;

➢ 集合元素标签(set element labels)(.tl) 显示集合的单个元素标签;

➢ 集合元素文本(set element text)(.te(index)) 显示与集合元素相连的文本。注意.te 后

缀需要一个驱动指数。这个驱动指数控制这个集合,它将被显示,但不一定需要与被控制

的集合相同。通常使用的是控制集合指数的一个子集。

➢ 文本填充(text fill)(.tf) 用于控制集合元素缺失文本的显示

 0 不填充

 1 仅填充现有的

 2 (默认)始终填充

下面的例子说明了这些使用方法:

文件 的结果为:

这个例子中,子集 j 标识符的符号文本首先写出,随后是子集的标签以及在其定义域,也即

集合 i 中找到的相关的元素文本。应该注意的是驱动集合 j 用于指明集合 i 的元素文本。因

为集合 i 中 i5 元素没有与其相连的集合元素文本,集合元素标签就被再显示一次。在最后

一行前面写出如下语句:

那么缺失的元素文本就不再用标签文本替代。结果为:

15.9.2 数字项

用于显示数字项的语法一般更容易处理。对于输出参数,只要用到标识符以及它的指数集合。对

于输出变量或方程值,标识符将与变量或方程后缀中一个一起使用。变量和方程的后缀为:

15.9.3 集合值项

集合值项(set value item)很容易操作。要输出集合值项,只要使用标识符及其指数集合。15.91.

节的例子中,我们考虑修改循环语句为:

输出文件的结果为:

第二列表示元素是否属于集合 j。

15.10 全局输出项格式

能够控制输出项的显示格式通常是非常重要的。本节我们将讨论这是如何实现的。出于格式

目的,输出项被分为四种类型,分别是标签,数值,集合值和文本。对于每种类型,字段宽度

(field width)和字段对齐方式(field justification)的全局格式编排是可能的。

15.10.1 字段对齐

可能的全局对齐方式是右对齐(value 1),左对齐(value 2)和居中对齐( value 3)。字段

对齐由下列文件后缀表示:

15.10.2 字段宽度

可采用下列文件后缀:

字段宽度由分配给该字段的空格数指定。使用后缀值 0 可让字段宽度的大小是可变的。这使

得字段宽度可以与要显示的输出项的实际大小相匹配。如果文本输出项与具体的字段不符,

那么它将从右边切断。对于数字输出项,数字的小数部分被四舍五入,采用科学计数法来适应

给定的字段。如果数字还是太大,在输出文件中就用星号代替该数值。

作为一个例子,我们在文件 中设定全局数字字段宽度为四位,而不是默认的 12 位。

我们可使用下面的语句:

15.11 局部输出项格式

我们通常仅需要安排具体 put 项目的格式。为此,我们使用局部编排特性,它将覆盖全局格

式设置。这个特性的语法如下:

Item 之后是对齐符号,字段宽度和要显示的小数位数。小数位的设定仅对数字输出是有效的。下面

局部对齐符号是可以用的。

忽略任何组件都会引起它们对应的全局格式设置被使用。与全局格式一样,当字段宽度给定

为 0 值时,字段宽度的大小是可变的。Item,width 和 decimals 之间用冒号分隔,如上所写。

局部格式特性的使用,以及它是否包含对齐方式,字段宽度或小数位中的一个或全部都是可选的。

下面是说明局部格式特性的例子:

15.12 额外的数字显示控制

除了前面一节提到的数字字段宽度和数字对齐之外,下列文件后缀也可以作为数字显示的全

局设定:

➢ 显示的小数位(number of decimals displayed)(.nd) 设定数字项显示的小数位。0 值的

结果是仅显示数字的整数部分。最大值为 10。默认值为 2

➢ 数字四舍五入格式(numeric round format)(.nr) 这允许用户以科学计数法显示数字,

因为数字小于.nd 后缀允许的小数位时,数字将显示为零。这种情况发生在数字小于.nd

的设定,但又大于由.nz 设定的零容忍水平的情况下。许多情况下,知道这些小数值的

存在是非常重要的。默认是 1。

 0 以 F 格式或 E 格式显示;

 1 四舍五入以适合字段;

 以科学计数法显示。

➢ 数字零容忍水平(numeric zero tolerance)(.nz) 为了显示目的,设定数字被四舍五入

为零的容忍水平。当它设为 0 的时候,四舍五入取决于字段宽度。默认值是 1.0e-5。

一个被显示数字的最大尺度必须在 20 位以内,其中最多有 10 位写入数字,剩下的 10 位用

作正负号,指数符号或用零填充。

15.12.1 例子

下面的例子表明了这些数字文件后缀不同组合下的结果。例子使用了.nd,.nz,.nr,.nw 数

字文件后缀的五种组合。有四个数值与这些组合一起使用,每个数值相对它前面数字改变了三

个小数位。我们选择这些组合来展示 put 语句中一起使用这些后缀值时的不同格式结果。

出于可读性目的,数值采用.nj 后缀设为左对齐,因为数字字段宽度在模型中随着不同后缀

组合而改变。下面是文件 的结果,它显示了数值/后缀组合:

注意在 comb1 中,当数值小于小数位允许的位数时,数值的显示变为指数计数。这是由设

为零的后缀.nr 引起的。特别有趣的是 comb2 和 comb3 的 value3。Value3 大于.nz 中的零容

忍水平,但小于.nd 允许的小数位数,由于.nr 设定为零,数值就以指数格式显示。在 comb3

中,.nr 被设为 1,因此这个数值被四舍五入为 0。在 comb5 中,value1 被四舍五入为一个整

数,因为.nd 设为 0。

15.13 指针控制

在描述采用 put 语句显示不同输出项之后,本节将讨论一些可用的特性,用于在输出文件中

定位这些输出项。GAMS 有几个文件后缀,它们决定了指针和文件最后一行的位置。这些

后缀也可用于重新定位指针,或重新设置最后一行。这样,它们在编排文件输出项格式上就非

常有帮助。这些后缀将根据标题,页首和窗口写入区分类。

15.13.1 当前指针列

这些后缀有与页面窗口坐标对应的数值。因为如此,它们可与指针控制字符联合使用来操作

输出文件中指针的位置。

➢ 更新储存在.cc 后缀中的值的惯例是在 put 语句结束的时候更新。结果,.cc 值在下一个

put 语句的输出项书写过程中保持不变,即使显示多个输出项也是如此。

下面的例子说明了指针控制后缀的更新方法和指针控制字符的使用方法。例子很繁琐,但却

很具指导意义。

下面是 的结果:

一开始,常数 lmarg 设为一个具体的值,用作对齐制表符。保持共同的对齐值的页边距或制

表符的符号在大结构文件中是非常有用的。第一个 put 语句使用了当前列指针控制字符来重

置指针。在这个例子中,指针移到第 8 列, 及其值显示在此。

第二个 put 语句说明了指针控制后缀的更新,通过将字母 x,y 和 z 写在不同的行上来实现。

每个字母都在 值上的指针之前。一开始,指针控制的最大值为 20,因为单个 put 语句被

用于三个输出项中, 值保持不变,结果字母在同一列。这个 put 语句之后, 的

值被更新到 23,这是第二个语句结束后指针的位置(注意与 z 一起显示的额外空格)

15.13.2 当前指针行

这些后缀有页面窗口坐标对应的数值。因此,它们可与指针控制字符联合使用来操作输出文

件中指针的位置。

更新储存在.cr 后缀中的值的惯例是在一个 put 语句结束时更新。结果,.cr 在下一个 put 语

句输出项的书写过程中保持不变,即使多个输出项被显示也是如此。这与.cl 类似。

15.13.3 末行控制

下面后缀控制书写区中使用的最后一行。

与行控制和列控制不同,末行后缀是连续更新的。末行后缀在修改页面不同书写区域上非常有

用。

➢ .tlll 和.hdll 后缀可能没有适用于当前页的值,因为在标题或页首修改的时候,它们对应

于下一页的标题和页首,而窗口的写入却发生在当前页。

➢ 这个后缀不仅可以用于决定书写区域中使用的末行,还可用于删除这个区域中的行。

在下面的例子中,页首区将完全被删除,通过重设.hdll 后缀为零。

这个例子中,一开始页首有写入本文。通过改变.hdll 后缀值为 0,指针被重设到页首区的最

上面。结果页首不再被写入,除非有新内容加到页首区中。

15.14 分页控制

除了自动换页(这发生在到达页面底部的时候),页面也可以提早写入文件中。关键词 putpage

被用于这个目的。Putpage 促使当前页立即写到文件中,使 put 语句能得到新的一页。最简

单的形式中,关键词 putpage 本身被用于输出当前页。另外,它能与输出项一起使用。当它

和输出项一起使用时,页面连同 putpage 语句中包含的输出项被一起写入文件中。Putpage

语句实际上是 put 语句另一个变种。下面的语句,引用文本被置于当前页,它也被写入文件

还有两个额外的后缀能够帮助用户决定文件在何时翻页:

➢ 尾页(last page)(.lp) 表示已经在文件中的页数。应该注意的是将其设为 0 并不会删

掉之前已经写入文件的页面。

➢ 窗口大小(window size)(.ws) 显示可置于窗口中的行数,它考虑进了在当前页标题

区和页首区中的行数以及当前页面大小。.ws 文件后缀由 GAMS 计算,无法由用户改

变。这个后缀在联合使用.ll 文件后缀进行手动分页的时候非常有用。

15.15 例外处理

本节中,我们将讨论例外处理(exception handling)。与其它 GAMS 语句相同,dollar 控制

例外处理可与 put 语句一起使用来控制具体输出项是否被显示。下面的例子中,如果 dollar

条件为真,put 语句被显示;如果为假,put 语句就被忽略。

15.16 与 put 语句相关的错误来源

在使用 put 书写工具的时候,可能会出现两种类型的错误:语法错误和 put 错误。下面小节

将讨论每种类型的错误。

15.16.1 语法错误

语法错误是不正确地使用 GAMS 语言造成的。这些错误与在 GAMS 其它地方发现的错误相

同或相类似,这些错误包括括号不匹配,无定义的标识符,非控制的集合或关键词和后缀的使

用不正确等。这些错误在程序编辑的过程检测到,通常对程序执行来说是致命的。这类型的错

误可在程序清单中识别,错误的地方会标出$符号以及相应的错误数。程序清单包含引起错

误的可能原因的描述。

15.16.2 put 错误

Put 错误是 put 书写工具独特的错误。这类型的错误发生在程序执行的过程中,由一个或多

个文件或页面属性相互冲突造成。这些错误不是致命的,它们被列在程序清单的末尾。它们

之中,具有代表性的是 put 语句试图写到页面之外,如采用@字符将指针移到超过页面宽度

的位置。对于许多这种类型错误,一组额外的星号会出现在输出文件中错误发生的地方。

因为 put 错误不是致命的,它并没有在输出文件中过分强调,因此它们的出现有时就被忽略

了。如果没有回顾程序清单,这些 put 错误可能就不会被发现,特别是在大量输出文件中。

因此,GAMS 包含了如下文件后缀来帮助用户检测错误:

.errors 允许用户显示文件中发生的 put 错误的数量。

为了说明它的使用方法,下面语句可加入到程序的任何位置,以检测发生在该语句之前的错误

数量。可以选择相同的输出文件,也可选择不同的输出文件或适当的控制台:

这个例子中,假设文件 和 已经在前面通过 file 语句定义过了。通过这个语句,

发生在文件 中的 put 错误数量被显示在文件 中。采用 putpage 允许在语句位

置上立即显示到一个 PC 系统的屏幕上,如果控制台是输出设备的话。

15.17 简单的电子数据表/数据库应用

最后一节提供了一个简单的例子,说明如何为输出到电子数据表,数据库或其它软件包做准备,

它允许引入界定文件(delimited file)。如同在 15.3 节中提到的,输出项可以用逗号分隔,文本

项用括号分隔。这可通过使用.pc 后缀值等于 5 来执行。界定文件与普通的 put 文件不同, 所

有的输出项将以可变的字段宽度写入,并以分隔符分开。因此,GAMS 忽略了所有对字段

宽度和对齐方式的全局和局部格式设定。但该注意的是数字项的小数位依然可以通过.nd 文

件后缀来设定。每个输出项在前个分隔符之后立即在同一行中被写入,除非指针被重设。

➢ 这消除了程序中的水平指针重设,它创建了一个被界定的文件。在一个界定文件中水平

重置指针将存在潜在的威胁,因为分隔符可能被覆盖。

15.17.1 例子

下面的例子中,[MEXSS]报告程序的子表格容量被准备为一个界定文件。下面程序片断令.pc

后缀值为 5。程序片断可以置于原先[MEXSS]模型的末尾。

程序片断的第一行将文件 创建为界定文件。注意程序的其余部分,字段宽度,对齐

方式,水平指针重置全部被消除。所有的文本项目都用引号引用。下面是输出文件的结果:

应该注意的是每个输出项都由逗号分隔开,文本输出用引号引用。


本文标签: 集合 文件 语句 输出 使用