Metadata-Version: 2.4
Name: domain-checked
Version: 0.1.0
Summary: Structured algebraic modeling with classes and type checking.
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: gamspy>=1.17.0
Requires-Dist: pandas>=2.3.3
Dynamic: license-file

# "domain-checked": structured algebraic modeling with classes and type checking

Instances of a class represent members of the set. Attributes of the class represent parameters, variables and equations related to the set. An instance provides a convenient way to access the data corresponding to a specific member of the set.

It is easy to make some parameters, variables and equations time-variant by defining a new class with attributes corresponding to the time-variant components. Defining scenarios can be done in a similar way.

A multi-dimensional parameter, variable or equation is stored in a descriptor object. Calling an attribute of an instance returns a a slice of the descriptor object corresponding to the set member.

## Examples

Here is [an example from GAMSpy documentation](https://gamspy.readthedocs.io/en/latest/user/whatisgamspy.html#why-is-gamspy-fast):

```python
import gamspy as gp
from numpy.random import uniform

# Formulate the problem.
m = gp.Container()
i = gp.Set(m)
j = gp.Set(m)
a = gp.Parameter(m, domain=[i, j])
b = gp.Parameter(m, domain=[i])
x = gp.Variable(m, domain=[i, j])
e = gp.Equation(m, domain=[i])
e[i] = gp.Sum(j, a[i, j] * x[i, j]) >= b[i]

# Supply data.
data = uniform(0, 1, (500, 1000))
data[data > 0.01] = 0
i.setRecords(range(500))
j.setRecords(range(1000))
a.setRecords(data)
b.setRecords(uniform(0, 1, 500))
```

```python
class I:
    a = ParVarEqnDescriptor(Parameter)
    b = ParVarEqnDescriptor(Parameter)
    x = ParVarEqnDescriptor(Variable)
    e = ParVarEqnDescriptor(Equation)

class J:
    a = ParVarEqnDescriptor(Parameter)
    x = ParVarEqnDescriptor(Variable)


I.a[I] = gp.Sum(J, I.a[J] * I.x[J]) >= I.b[I]

# Supply data.
data = uniform(0, 1, (500, 1000))
data[data > 0.01] = 0
I.setRecords(range(500))
J.setRecords(range(1000))
I.a.setRecords(data)
I.b.setRecords(uniform(0, 1, 500))
```

## Extension

### Database schema generation

The values of a parameter are stored in a multi-indexed series (as Pandas dataframe), so a multi-dimensional parameter is stored in coordinate format. This makes it easy to work with large sparse data. Loading and saving data to a database. All the parameter and variable tables are [in third normal form](https://en.wikipedia.org/wiki/Third_normal_form).

It is possible to [exchange data with a database using GAMS code](https://gams.com/48/docs/UG_DataExchange_Databases.html), but it requires writing repetitive raw string:

```GAMS
[...]

Model transport / all /;

solve transport using lp minimizing z;

embeddedCode Connect:
- GAMSReader:
    symbols:
      - name: x
- Projection:
    name: x.l(i,j)
    newName: x_level(i,j)
- SQLWriter:
    connection: {'drivername': 'mysql+pymysql', 'username': 'root', 'password': 'strong_password', 'host':'localhost', 'port': 3306, 'database': 'testdb'}
    connectionType: sqlalchemy
    ifExists: replace
    symbols:
      - name: x_level
        tableName: xLevel
endEmbeddedCode
```

Such code is automatically generated by `set-as-class`, as both the input and the result follow the coordinate format:

```console
mysql> select * from xLevel;
+-----------+----------+-------+
| i         | j        | value |
+-----------+----------+-------+
| seattle   | new-york |    50 |
| seattle   | chicago  |   300 |
| seattle   | topeka   |     0 |
| san-diego | new-york |   275 |
| san-diego | chicago  |     0 |
| san-diego | topeka   |   275 |
+-----------+----------+-------+
6 rows in set (0.00 sec)
```

### Set tables and tensor tables

### GUI

It is easy to implement the generation of small-scale GUIs for data input and result.
