Is GAMS's strict domain checking necessary here? Can it be relaxed?

I have encountered what seems like unnecessarily strict domain checking in GAMS. Consider this example:

set abc / a, b, c /;
set ab[abc] / a, b /;
set a[ab] / a /;

parameter foo[abc];
foo[a] = 1;                    # Works fine
foo[abc]$(a[abc]) = 1;         # Domain violation error

Both statements should assign 1 to foo['a'], but the second fails because set a was declared over domain ab, not abc. Since mathematically a ∈ ab ⊆ abc, this operation is well-defined and unambiguous.

This strictness forces workarounds like creating auxiliary sets or restructuring the model hierarchy, without obvious error-prevention benefits.

Two questions:

  1. Is there a rationale for this strictness that I’m missing? What errors does it prevent?

  2. Is there an option to relax this checking, without disabling domain checking for actual set elements.

I understand GAMS prioritizes compile-time safety, but this feels overly restrictive compared to other modeling languages. Would appreciate insights from the community or GAMS developers.

Hi,

I might not get the problem or maybe I am just overly biased being used to this behavior for about 20 years, but to me this behavior is totally expected. If I declare set a[ab], I want to make sure that a is never referenced with any element that is not in ab. Now, you try to reference it with the superset abc which clearly violates the declaration. The error prevents you from accidentally mix incompatible hierarchies.

There two ways to make your example run.

  1. Change

foo[abc]$(a[abc]) = 1;

to

foo[ab]$(a[ab]) = 1;

or

  1. Change

set a[ab] / a /;

to

set a[abc] / a /;

Does this make sense?

Best,
Lutz

Thanks for the quick response.

I understand the workarounds you suggest, but they are quite impractical in a few cases. Mostly for complements, e.g

foo[abc]$(not a[abc])

We could obviously just define everything over a large superset (the a[abc] solution in your comment), but this means defining a lot of variables over much larger sets than needed.

In an ideal world, I would prefer checking for domain violations at the element level, as the hierarchy of sets has many restrictions (for example defining a set as a subset of multiple super-sets), but I assume the tree structure is essential for performance. In this case however, I don’t see how there could be technical barriers, as we already allow foo[a] - but by all means enlighten me!

/Martin

Martin,

This is so deep in the GAMS genes that one needs to find ways to live with this. I understand the reluctance to do wider domains as necessary. There are workarounds for the complement:

foo[abc] = yes; foo[a] = no;

there is a way to relax domain checking entirely for a statement:

$onUni
foo[abc]$(not a[abc]) = yes;
$offUni

-Michael

That’s fair.

Having a operator for explicit element-wise set-membership check, similar to sameas, would be useful.
But otherwise, our best solutions is probably to just turn off domain checking for the sub module where we keep running into issues.

For context, my issues with set structures are specifically in the input-output system of an economic model, where all different demand components (types of consumption, investment, exports etc.) are all subsets of a demand superset. Defining variables such as private consumption over all possible demand types is problematic, but on the other hand, using a superset of demand components makes the input-output system much more compact.

Perhaps a macro can be considered for element-wise set-membership check; one candidate might be:

* element-wise set-membership check
$macro notmember(a,b) prod(sameas(a,b),no)
foo[abc]$notmember(abc,a) = 1;

As Michael suggested, you can do foo[abc] = yes; foo[a] = no;.

If you want to do that repeatedly for different assignments, that is of course not very handy. But you could also define matching subsets and use those directly like this:

set abc / a, b, c /;
set ab[abc] / a, b /;
set a[ab] / a /;

parameter foo[abc];
foo[a] = 1;           
*foo[abc]$(a[abc]) = 1;
Set bc[abc] / #abc /; bc[a] = no;
foo[bc] = 1;