# External Equations#

## Model Interface#

Sometimes, you need to represent a complicated **non-linear** relationship in
your constraints. However, the relationship doesn’t have an easy-to-write algebraic
expression. If you can provide function evaluations and first derivatives, you can
embed your relationship in GAMSPy using External Equations.

Information

This documentation is a shortened version of the GAMS documentation on External Equations. Since many parts are skipped, we suggest reading the original documentation after reading this one.

External equations are **not** compatible with all non-linear solvers. This is
because external module, the library you would need to implement, is limited to
providing functionality for evaluating functions (including their first
derivatives) at a specific point. Some solvers can also benefit from the use of
second-order derivatives by utilizing the Hessian Vector Product
\(\nabla^2f(x)v\) that can be supplied by the external module. As a
result, solvers that require analysis of the algebraic structure of the model
instance **cannot** work with external equations. This limitation affects
deterministic global solvers, as noted in the “Global” column of this table.

Warning

This feature requires a solid understanding of programming in C/C++ or Fortran, compilation, and linking processes.

An **external equation** delegates the handling of the relationship between
variables to an external module. In this context, the
relationship between the variables is not explicitly defined within your system
but is instead governed by the external module. For example, you might specify
that variables x and y are related, but the nature of that relationship is
determined by the external module.

An **external module** is a library responsible for managing all external
equations. It defines and handles the relationships between variables specified
in these equations. For instance, the external module might implement a
function like \(y = sin(x)\).

Here is an example of how an external equation is defined:

```
import gamspy as gp
m = gp.Container()
x = gp.Variable(m)
y = gp.Variable(m)
eq = gp.Equation(m, type="external")
eq[...] = 1 * x + 2 * y == 1 # This certainly is not a linear equation
```

The coefficients indicate the index of the variable within the external module. For instance, in the expression 1*x, x represents the first variable in the external module, and similarly, y represents the second variable. The right-hand side of the equation denotes that this is the first external equation in the system. Consequently, attempting the following would result in a failure:

```
import gamspy as gp
m = gp.Container()
x = gp.Variable(m)
y = gp.Variable(m)
eq1 = gp.Equation(m, type="external")
eq2 = gp.Equation(m, type="external")
# you cannot have two equations with the same index
eq1[...] = 1 * x + 2 * y == 1
eq2[...] = 1 * x + 2 * y == 1 # this should be 2
```

Skipping an equation index also does not work:

```
eq1[...] = 1 * x + 2 * y == 1
eq2[...] = 1 * x + 2 * y == 3
```

When you have n external equations, the indices of these equations must include every number from 1 to n, inclusive.

Variable indices must be consistent as well:

```
eq1[...] = 1 * x + 2 * y == 1
eq2[...] = 2 * x + 3 * y == 2
```

The variable x was designated as the first variable in the external module. In subsequent equations, its index must remain consistent, meaning it should still be indexed as 1. It is not necessary to include every variable in each equation.

Let’s assume we want to represent \(y_1 = sin(x_1)\) and \(y_2 = cos(x_2)\)

```
import gamspy as gp
m = gp.Container()
y1 = gp.Variable(m)
y2 = gp.Variable(m)
x1 = gp.Variable(m)
x2 = gp.Variable(m)
eq1 = gp.Equation(m, type="external")
eq2 = gp.Equation(m, type="external")
eq1[...] = 1*x1 + 3*y1 == 1
eq2[...] = 2*x2 + 4*y2 == 2
```

A small note on what we are representing

Actually, instead of representing \(y_1 = \sin(x_1)\), we represent it as \(\sin(x_1) - y_1 = 0\). When we evaluate the function, we are asked to compute \(\sin(x_1) - y_1\). You’ll notice that when this expression does not equal zero, the equation is not satisfied. However, the solver will adjust the values using derivatives to restore feasibility. Therefore, the derivative of \(\sin(x_1) - y_1\) is taken with respect to both \(x_1\) and \(y_1\). Specifically, the derivative with respect to \(x_1\) is \(\cos(x_1)\), and the derivative with respect to \(y_1\) is \(-1\).

Finally, we need to provide the name of the external module in the model.

```
...
model = gp.Model(
container=m,
equations=m.getEquations(),
problem="nlp",
sense="min",
objective=y1 + y2,
external_module="mylibrary",
)
```

Since no file extension was specified for `external_module`

, GAMSPy will automatically search for the
appropriate file extension based on the operating system: `.dll`

on Windows, `.so`

on Linux, and `.dylib`

on macOS.
The next step is generating the library.

## Programming Interface#

The rest of the documentation remains unchanged, so please refer to the
Programming Interface
for more detailed information. In brief, your task is to download the
geheader.h file and
implement the `gefunc`

function as specified within it. To assist you, we’ve
provided sample external module.
Starting with this template is much easier than building everything from
scratch. The `mylib.cpp`

file contains the library code, and a `CMakeLists.txt`

file is included to help you build the module. The example referenced in the
documentation can be found in `example.py`

. After compiling `mylib.cpp`

into a
library, place the library next to `example.py`

and run the script. We
understand that implementing external equations can be challenging, and we’re
actively exploring automations for specific cases to ease this process.