Simple Example#

Let’s model the following linear problem:

\[\begin{split}\min \ c_1 x + c_2 y \\ \text{s.t.} \ \ a x + b y \leq c \\ x, y \geq 0\end{split}\]

The complete model file is shown below. This example demonstrates the basic structure of a DecisionAI model, including:

import pulp
from pydantic import Field

from decision_ai import InputData, PulpDecisionAIModel, PulpVariables, Solution, constraint
from decision_ai.typing import ConstraintGenerator


class ExampleInputData(InputData):
    a: int
    b: int
    c: int
    cost: dict[str, int]


class Variables(PulpVariables):
    x: pulp.LpVariable = Field(..., description="Variable x")
    y: pulp.LpVariable = Field(..., description="Variable y")

    @staticmethod
    def init_x(input_: ExampleInputData) -> pulp.LpVariable:  # noqa: ARG004
        return pulp.LpVariable("x", lowBound=0)

    @staticmethod
    def init_y(input_: ExampleInputData) -> pulp.LpVariable:  # noqa: ARG004
        return pulp.LpVariable("y", lowBound=0)


class Model(PulpDecisionAIModel[ExampleInputData, Variables]):
    variables_class = Variables

    @constraint
    def constraint_1(input_: ExampleInputData, variables: Variables) -> ConstraintGenerator:
        yield input_.a * variables.x + input_.b * variables.y <= input_.c

    def set_up_objective(self, input_: ExampleInputData, prob: pulp.LpProblem, variables: Variables) -> pulp.LpProblem:
        prob += pulp.lpSum([input_.cost["c1"] * variables.x, input_.cost["c2"] * variables.y])
        return prob

    def solution_to_str(self, input_: ExampleInputData, solution: Solution) -> str:
        return f"Objective value: {solution.objective}\n\nStatus: {solution.status}"

    def solve(self, prob: pulp.LpProblem, variables: Variables, input_: ExampleInputData, **kwargs) -> Solution:
        prob.solve()
        return self._create_solution(prob, variables)