Blending Problem I (BLEND)#

blend.py

"""
## GAMSSOURCE: https://www.gams.com/latest/gamslib_ml/libhtml/gamslib_blend.html
## LICENSETYPE: Demo
## MODELTYPE: LP
## KEYWORDS: linear programming, blending problem, manufacturing, alloy blending


Blending Problem I (BLEND)

A company wishes to produce a lead-zinc-tin alloy at minimal cost.
The problem is to blend a new alloy from other purchased alloys.


Dantzig, G B, Chapter 3.4. In Linear Programming and Extensions.
Princeton University Press, Princeton, New Jersey, 1963.
"""

from __future__ import annotations

import os

import numpy as np

from gamspy import Container
from gamspy import Equation
from gamspy import Model
from gamspy import Parameter
from gamspy import Sense
from gamspy import Set
from gamspy import Sum
from gamspy import Variable


def main():
    m = Container(
        system_directory=os.getenv("SYSTEM_DIRECTORY", None),
        delayed_execution=int(os.getenv("DELAYED_EXECUTION", False)),
    )

    # Set
    alloy = Set(
        m,
        name="alloy",
        records=["a", "b", "c", "d", "e", "f", "g", "h", "i"],
        description="products on the market",
    )
    elem = Set(
        m,
        name="elem",
        records=["lead", "zinc", "tin"],
        description="required elements",
    )

    # Data
    compdat = Parameter(
        m,
        name="compdat",
        domain=[elem, alloy],
        records=np.array([
            [10, 10, 40, 60, 30, 30, 30, 50, 20],
            [10, 30, 50, 30, 30, 40, 20, 40, 30],
            [80, 60, 10, 10, 40, 30, 50, 10, 50],
        ]),
        description="composition data (pct)",
    )
    price = Parameter(
        m,
        name="price",
        domain=alloy,
        records=np.array([4.1, 4.3, 5.8, 6.0, 7.6, 7.5, 7.3, 6.9, 7.3]),
        description="composition data (price)",
    )
    rb = Parameter(
        m,
        name="rb",
        domain=elem,
        records=np.array([30, 30, 40]),
        description="required blend",
    )

    # Variable
    v = Variable(
        m,
        name="v",
        domain=alloy,
        type="Positive",
        description="purchase of alloy (pounds)",
    )

    # Equation
    objective = Sum(alloy, price[alloy] * v[alloy])
    pc = Equation(m, name="pc", domain=elem, description="purchase constraint")
    mb = Equation(m, name="mb", description="material balance")

    pc[elem] = Sum(alloy, compdat[elem, alloy] * v[alloy]) == rb[elem]
    mb[...] = Sum(alloy, v[alloy]) == 1

    b1 = Model(
        m,
        name="b1",
        equations=[pc],
        problem="LP",
        sense=Sense.MIN,
        objective=objective,
    )
    b2 = Model(
        m,
        name="b2",
        equations=[pc, mb],
        problem="LP",
        sense=Sense.MIN,
        objective=objective,
    )

    report = Parameter(
        m,
        name="report",
        domain=[alloy, "*"],
        description="comparison of model 1 and 2",
    )

    b1.solve()

    report[alloy, "blend-1"] = v.l[alloy]
    b2.solve()
    report[alloy, "blend-2"] = v.l[alloy]

    print(report.pivot())

    import math

    assert math.isclose(b2.objective_value, 4.980000, rel_tol=0.001)


if __name__ == "__main__":
    main()