Skip to content

Model class

Represents a mathematical programming model.

The Model class serves as a versatile tool for creating and managing mathematical programming models.

As a factory, the Model class provides methods to create optimization objects, decision variables, and constraints, allowing users to build their models step by step.

The class also offers various accessors and iterators to efficiently navigate and manipulate the modeling objects within the model. Additionally, the Model class manages solving operations.

Source code in pyorlib/model/model.py
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
class Model:
    """
    Represents a mathematical programming model.

    The `Model` class serves as a versatile tool for creating and managing mathematical programming models.

    As a factory, the `Model` class provides methods to create optimization objects, decision variables,
    and constraints, allowing users to build their models step by step.

    The class also offers various accessors and iterators to efficiently navigate and manipulate the
    modeling objects within the model. Additionally, the `Model` class manages solving operations.
    """

    @property
    def name(self) -> str:
        """
        Retrieves the name of the model.
        :return: The name assigned to the model.
        """
        return self._name

    @property
    def dimensions(self) -> Mapping[str, int]:
        """
        Retrieves the dimensions and their sizes of the model.
        :return: A dictionary with dimension names as keys and their sizes as values.
        """
        return self._dimensions

    @property
    def constraints(self) -> List[Element]:
        """
        Retrieves the constraints defined in the model.
        :return: A list containing the constraints.
        """
        return self._engine.constraints

    @property
    def terms(self) -> Mapping[str, Term]:
        """
        Retrieves a dictionary of individual terms used in the model.
        :return: A dictionary where the keys represent the names of the
            terms and the values represent the terms themselves.
        """
        return self._terms

    @property
    def term_sets(self) -> Mapping[str, Mapping[Tuple[int, ...], Term]]:
        """
        Retrieves a dictionary of term sets used in the model.
        :return: A dictionary where the keys represent the names of the sets and the values
            represent the sets themselves. Each set of terms is a dictionary with the indices of
            the terms as keys and the terms themselves as values.
        """
        return self._term_sets

    @property
    def objective_value(self) -> float | None:
        """
        Retrieves the value of the objective function in the model.
        :return: The value of the objective function, or `None` if the model has
            not been solved or an objective function is not defined.
        """
        return self._engine.objective_value

    @property
    def objective_expr(self) -> Element | None:
        """
        Retrieves the expression of the objective function in the model, if available.
        :return: The objective function expression, or `None` if not available.
        """
        return self._engine.objective_expr

    @property
    def solution_status(self) -> SolutionStatus:
        """
        Retrieves an enumeration that represents the state of the solution.
        :return: An enumeration that represents the state of the solution.
        """
        return self._engine.solution_status

    @property
    def float_precision(self) -> int:
        """
        This property is used to get or set the float precision of the model.
        The `float_precision` is an integer number of digits, used in printing the solution and objective.
        :return: The current float precision of the solver.
        """
        return self._float_precision

    @float_precision.setter
    def float_precision(self, float_precision: int) -> None:
        num_digits: int = float_precision

        if float_precision < 0:
            self._logger.warning(f"Negative float precision given: {num_digits}, using 0 instead.")
            num_digits = 0

        self._float_precision: int = num_digits
        """ The float precision is an integer number of digits, used in printing the solution and objective. """

    def __init__(self, engine: Engine, name: str | None = None, debug: bool = False, float_precision: int = 6):
        """
        Initializes a new instance of the `Model` class.
        :param engine: The engine interface to be used for solving the model.
        :param name: An optional name for the model. Defaults to None.
        :param debug: A flag indicating whether debug mode is enabled. Defaults to False.
        :param float_precision: The number of digits used in printing the solution and objective. Defaults to 6.
        """
        # Instance attributes
        self._name: str = name if name else f"model_{str(uuid4())}"
        """ The name of the model. """

        self._logger: Logger = Logger(self._name, debug)
        """ An object used for logging messages from the model. """

        self._engine: Engine = engine
        """ The engine interface used to solve the model. """

        self._dimensions: Dict[str, int] = {}
        """  
        Stores the dimensions of the model. Each dimension is represented by a key-value pair, 
        where the key is the name of the dimension and the value is its size.
        """

        self._terms: Dict[str, Term] = {}
        """ 
        Stores individual terms used in the model. Each term is represented by a key-value pair, where 
        the key is the name of the term and the value is the term itself. 
        A term can be a constant value or a variable.
        """

        self._term_sets: Dict[str, Dict[Tuple[int, ...], Term]] = {}
        """
        Stores sets of terms used in the model. Each set of terms is represented by a key-value pair, 
        where the key is the name of the set and the value is another dictionary. The inner dictionary 
        represents the set of terms, where the keys are indices that uniquely identify each term, 
        and the values are the terms themselves. A term can be a constant value or a variable.

        Example:
            Z_r_s_t: {
                (1, 1, 1): Variable,
                (1, 1, 2): Constant,
                (2, 1, 1): Variable,
            }
        """

        if self._engine is None:
            raise ModelException("The engine interface cannot be None.")

        if self._logger.debug_enabled:  # pragma: no cover
            self._logger.debug(
                f"The '{StdOutColors.PURPLE}{self.name.capitalize()}{StdOutColors.DEFAULT}' has been created."
            )

        self.float_precision = float_precision

    def __save_term(self, term: Term) -> None:
        """
        Saves a single term in the model.
        :param term: The term to be saved.
        :return: None
        """
        self._terms[term.name] = term

    def __save_term_to_set(self, set_name: str, set_index: Tuple[int, ...], term: Term) -> None:
        """
        Saves a term into a set within the model.
        :param set_name: The name of the set where the term will be saved.
        :param set_index: The index position of the term within the set in the model.
        :param term: The term to be saved.
        :return: None
        """
        if not set_name:
            raise ModelException("Set name cannot be empty.")

        self.__save_term(term)

        if set_name not in self._term_sets:
            self._term_sets[set_name] = {}

        self._term_sets[set_name][set_index] = term

    def get_dimension_by_name(self, name: str) -> int:
        """
        Retrieves the size of a dimension in the model based on its name.
        :param name: The name of the dimension.
        :return: The size of the dimension. Returns 0 if the dimension does not exist.
        """
        return self._dimensions.get(name, 0)

    def get_term_by_name(self, name: str) -> Term | None:
        """
        Retrieves a term from the model based on its name.
        :param name: The name of the term.
        :return: The term with the specified name. Returns `None` if the term does not exist.
        """
        return self._terms.get(name, None)

    def get_term_set_by_name(self, name: str) -> Mapping[Tuple[int, ...], Term] | None:
        """
        Retrieves a set of terms from the model based on its name.
        :param name: The name of the set.
        :return: The set of terms with the specified name. Returns `None` if the set does not exist.
        """
        return self._term_sets.get(name, None)

    def add_dimension(self, name: str, value: int) -> int:
        """
        Adds a new dimension to the model.
        :param name: The name of the dimension to be added.
        :param value: The size of the new dimension.
        :return: The dimension that was added to the model.
        """
        if not name or value is None or not isinstance(value, int) or value < 1:
            raise ModelException("Invalid dimension values.")
        self._dimensions[name] = value

        if self._logger.debug_enabled:  # pragma: no cover
            self._logger.debug(
                action="Dimension added: ",
                msg="".join(
                    [
                        f"Name: {StdOutColors.PURPLE}{name}{StdOutColors.DEFAULT} | ",
                        f"val: {StdOutColors.PURPLE}{value}{StdOutColors.DEFAULT}",
                    ]
                ),
            )

        return value

    def add_constant(self, name: str, value_type: ValueType, value: float) -> Constant:
        """
        Adds a new constant to the model.
        :param name: The name of the constant to be added.
        :param value_type: The type of the constant value.
        :param value: The constant value.
        :return: The constant that was added to the model.
        """
        if name in self.terms:
            raise ModelException(f"Duplicate term with name: {name}")

        constant: Constant = Constant(name=name, value_type=value_type, value=value)

        self.__save_term(term=constant)

        if self._logger.debug_enabled:  # pragma: no cover
            self._logger.debug(
                action="Constant added: ",
                msg=constant.get_pretty_string(float_precision=self.float_precision),
            )

        return constant

    def add_variable(
        self,
        name: str,
        value_type: ValueType,
        lower_bound: float = 0,
        upper_bound: float = inf,
    ) -> Variable:
        """
        Adds a new variable to the model.
        :param name: The name of the variable to be added.
        :param value_type: The type of the variable values.
        :param lower_bound: The lower bound of the variable. Default is 0.
        :param upper_bound: The upper bound of the variable. Default is infinity.
        :return: The variable that was added to the model.
        """
        if name in self.terms:
            raise ModelException(f"Duplicate term with name: {name}")

        variable: Variable = self._engine.add_variable(
            name=name, value_type=value_type, lower_bound=lower_bound, upper_bound=upper_bound
        )

        self.__save_term(term=variable)

        if self._logger.debug_enabled:  # pragma: no cover
            self._logger.debug(
                action="Variable added: ",
                msg=variable.get_pretty_string(float_precision=self.float_precision),
            )

        return variable

    def add_constant_to_set(
        self,
        set_name: str,
        set_index: Tuple[int, ...],
        const_name: str,
        value_type: ValueType,
        value: float,
    ) -> Constant:
        """
        Adds a new constant to the model within a set.
        :param set_name: The name of the set where the constant will be added.
        :param set_index: The position of a term within a set representing its indices.
        :param const_name: The name of the constant to be saved.
        :param value_type: The type of the constant value.
        :param value: The constant value.
        :return: The constant that was added to the model.
        """
        if const_name in self.terms:
            raise ModelException(f"Duplicate term with name: {const_name}")

        if set_name in self.term_sets and set_index in self.term_sets[set_name]:
            raise ModelException(f"Duplicate set name and index: {set_name} | {set_index}")

        constant: Constant = Constant(name=const_name, value_type=value_type, value=value)

        self.__save_term_to_set(set_name=set_name, set_index=set_index, term=constant)

        if self._logger.debug_enabled:  # pragma: no cover
            self._logger.debug(
                action="Constant added to set: ",
                msg="".join(
                    [
                        f"Set name: {StdOutColors.PURPLE}{set_name}{StdOutColors.DEFAULT} | ",
                        f"Set index: {StdOutColors.PURPLE}{set_index}{StdOutColors.DEFAULT} | ",
                        constant.get_pretty_string(float_precision=self.float_precision),
                    ]
                ),
            )

        return constant

    def add_variable_to_set(
        self,
        set_name: str,
        set_index: Tuple[int, ...],
        var_name: str,
        value_type: ValueType,
        lower_bound: float = 0,
        upper_bound: float = inf,
    ) -> Variable:
        """
        Adds a new variable to the model within a set.
        :param set_name: The name of the set where the variable will be added.
        :param set_index: The position of a term within a set that represents its indices.
        :param var_name: The name of the variable to be added.
        :param value_type: The type of the variable values.
        :param lower_bound: The lower bound of the variable. Default is 0.
        :param upper_bound: The upper bound of the variable. Default is infinity.
        :return: The variable that was added to the model.
        """
        if var_name in self.terms:
            raise ModelException(f"Duplicate term with name: {var_name}")

        if set_name in self.term_sets and set_index in self.term_sets[set_name]:
            raise ModelException(f"Duplicate set name and index: {set_name} | {set_index}")

        variable: Variable = self._engine.add_variable(
            name=var_name, value_type=value_type, lower_bound=lower_bound, upper_bound=upper_bound
        )

        self.__save_term_to_set(set_name=set_name, set_index=set_index, term=variable)

        if self._logger.debug_enabled:  # pragma: no cover
            self._logger.debug(
                action="Variable added to set: ",
                msg="".join(
                    [
                        f"Set name: {StdOutColors.PURPLE}{set_name}{StdOutColors.DEFAULT} | ",
                        f"Set index: {StdOutColors.PURPLE}{set_index}{StdOutColors.DEFAULT} | ",
                        variable.get_pretty_string(float_precision=self.float_precision),
                    ]
                ),
            )

        return variable

    def add_constraint(self, expression: Element) -> Element:
        """
        Adds a new constraint to the model.
        :param expression: The constraint expression
        :return: An object representing the constraint.
        """
        constraint: Element = self._engine.add_constraint(expression=expression)

        if self._logger.debug_enabled:  # pragma: no cover
            try:
                self._logger.debug(action="Constraint added: ", msg=f"Expr: {expression}")
            except RecursionError:
                self._logger.debug(action="Constraint added: ", msg="Expr: Unprintable expression")

        return constraint

    def set_objective(self, opt_type: OptimizationType, expression: Element) -> Element:
        """
        Defines the objective function.
        :param opt_type: The type of optimization to be performed.
        :param expression: The objective expression.
        :return: The objective function.
        """
        objective: Element = self._engine.set_objective(opt_type=opt_type, expression=expression)

        if self._logger.debug_enabled:  # pragma: no cover
            try:
                self._logger.debug(
                    action="Objective function added: ",
                    msg="".join(
                        [
                            f"Opt Type: {StdOutColors.PURPLE}{opt_type.name.capitalize()}{StdOutColors.DEFAULT} | ",
                            f"Expr: {objective}",
                        ]
                    ),
                )
            except RecursionError:
                self._logger.debug(action="Objective function added: ", msg="Expr: Unprintable expression")

        return objective

    def solve(self) -> None:
        """
        Solves the optimization problem represented by the model.
        :return: None.
        """
        if self._logger.debug_enabled:  # pragma: no cover
            self._logger.debug(f"Solving the model...")

        self._engine.solve()

        if self._logger.debug_enabled:  # pragma: no cover
            self._logger.debug(f"The model has been solved.")

    def print_info(self, display_term_sets: bool = False) -> None:  # pragma: no cover
        """
        Prints information about the model.
        :param display_term_sets: Whether to display information about term sets. Defaults to False.
        :return: None.
        """
        default, debug = StdOutColors.DEFAULT, StdOutColors.PURPLE
        print(f"\n------ MODEL INFORMATION ------\n")
        print("Model properties:")
        print(f"\tName: {debug}{self.name}{default}")
        print("Objective function:")
        print(f"\tExpression: {self.objective_expr}")
        print(f"\tStatus: {debug}{self.solution_status.name}{default}")
        print(
            f"\tValue: {debug}",
            (
                "{0:.{prec}g}".format(self.objective_value, prec=self.float_precision)
                if self.objective_value is not None
                else "--"
            ),
            f"{default}",
        )

        print(f"Dimensions: {debug}{len(self.dimensions)}{default}")
        for name, size in self.dimensions.items():
            print(
                f"\tName: {debug}{name}{default} | ",
                f"Val: {debug}{size}{default}",
            )

        print(f"Terms: {debug}{len(self.terms)}{default}")
        for name, term in self.terms.items():
            print(f"\t{term.get_pretty_string(float_precision=self.float_precision)}")
        if display_term_sets:
            print(f"Terms Sets: {debug}{len(self.term_sets)}{default}")
            for name, terms in self.term_sets.items():
                print(f"\tTerm: {debug}{name}{default}")
                for index, term in terms.items():
                    print(
                        f"\t\tIndex: {debug}{index}{default} | ",
                        f"{term.get_pretty_string(float_precision=self.float_precision)}",
                    )

        constraints = self.constraints
        print(f"Constraints: {debug}{len(constraints)}{default}")
        for exp in constraints:
            try:
                print(f"\tExpression: {exp}")
            except RecursionError:
                print("\tExpression: Print Error")
        print()

    def print_solution(self) -> None:  # pragma: no cover
        """
        Prints the solution of the optimization problem.
        :return: None.
        """
        default, debug = StdOutColors.DEFAULT, StdOutColors.PURPLE
        print(f"\n------ MODEL SOLUTION ------\n")
        print("Objective function:")
        print(f"\tStatus: {debug}{self.solution_status.name}{default}")
        print(
            f"\tValue: {debug}",
            (
                "{0:.{prec}g}".format(self.objective_value, prec=self.float_precision)
                if self.objective_value is not None
                else "--"
            ),
            f"{default}",
        )

        solution_variables: Dict[str, Term] = {
            name: term for name, term in self.terms.items() if (term.is_variable and term.value != 0)
        }

        if solution_variables:
            print("Terms:")
            for name, term in solution_variables.items():
                print(f"\t{term.get_pretty_string(float_precision=self.float_precision)}")

        print()

Attributes

name property

name: str

Retrieves the name of the model.

RETURNS DESCRIPTION
str

The name assigned to the model.

dimensions property

dimensions: Mapping[str, int]

Retrieves the dimensions and their sizes of the model.

RETURNS DESCRIPTION
Mapping[str, int]

A dictionary with dimension names as keys and their sizes as values.

constraints property

constraints: List[Element]

Retrieves the constraints defined in the model.

RETURNS DESCRIPTION
List[Element]

A list containing the constraints.

terms property

terms: Mapping[str, Term]

Retrieves a dictionary of individual terms used in the model.

RETURNS DESCRIPTION
Mapping[str, Term]

A dictionary where the keys represent the names of the terms and the values represent the terms themselves.

term_sets property

term_sets: Mapping[str, Mapping[Tuple[int, ...], Term]]

Retrieves a dictionary of term sets used in the model.

RETURNS DESCRIPTION
Mapping[str, Mapping[Tuple[int, ...], Term]]

A dictionary where the keys represent the names of the sets and the values represent the sets themselves. Each set of terms is a dictionary with the indices of the terms as keys and the terms themselves as values.

objective_value property

objective_value: float | None

Retrieves the value of the objective function in the model.

RETURNS DESCRIPTION
float | None

The value of the objective function, or None if the model has not been solved or an objective function is not defined.

objective_expr property

objective_expr: Element | None

Retrieves the expression of the objective function in the model, if available.

RETURNS DESCRIPTION
Element | None

The objective function expression, or None if not available.

solution_status property

solution_status: SolutionStatus

Retrieves an enumeration that represents the state of the solution.

RETURNS DESCRIPTION
SolutionStatus

An enumeration that represents the state of the solution.

float_precision instance-attribute property writable

float_precision: int = float_precision

This property is used to get or set the float precision of the model. The float_precision is an integer number of digits, used in printing the solution and objective.

RETURNS DESCRIPTION
int

The current float precision of the solver.

Functions

__init__

__init__(engine: Engine, name: str | None = None, debug: bool = False, float_precision: int = 6)

Initializes a new instance of the Model class.

PARAMETER DESCRIPTION
engine

The engine interface to be used for solving the model.

TYPE: Engine

name

An optional name for the model. Defaults to None.

TYPE: str | None DEFAULT: None

debug

A flag indicating whether debug mode is enabled. Defaults to False.

TYPE: bool DEFAULT: False

float_precision

The number of digits used in printing the solution and objective. Defaults to 6.

TYPE: int DEFAULT: 6

Source code in pyorlib/model/model.py
def __init__(self, engine: Engine, name: str | None = None, debug: bool = False, float_precision: int = 6):
    """
    Initializes a new instance of the `Model` class.
    :param engine: The engine interface to be used for solving the model.
    :param name: An optional name for the model. Defaults to None.
    :param debug: A flag indicating whether debug mode is enabled. Defaults to False.
    :param float_precision: The number of digits used in printing the solution and objective. Defaults to 6.
    """
    # Instance attributes
    self._name: str = name if name else f"model_{str(uuid4())}"
    """ The name of the model. """

    self._logger: Logger = Logger(self._name, debug)
    """ An object used for logging messages from the model. """

    self._engine: Engine = engine
    """ The engine interface used to solve the model. """

    self._dimensions: Dict[str, int] = {}
    """  
    Stores the dimensions of the model. Each dimension is represented by a key-value pair, 
    where the key is the name of the dimension and the value is its size.
    """

    self._terms: Dict[str, Term] = {}
    """ 
    Stores individual terms used in the model. Each term is represented by a key-value pair, where 
    the key is the name of the term and the value is the term itself. 
    A term can be a constant value or a variable.
    """

    self._term_sets: Dict[str, Dict[Tuple[int, ...], Term]] = {}
    """
    Stores sets of terms used in the model. Each set of terms is represented by a key-value pair, 
    where the key is the name of the set and the value is another dictionary. The inner dictionary 
    represents the set of terms, where the keys are indices that uniquely identify each term, 
    and the values are the terms themselves. A term can be a constant value or a variable.

    Example:
        Z_r_s_t: {
            (1, 1, 1): Variable,
            (1, 1, 2): Constant,
            (2, 1, 1): Variable,
        }
    """

    if self._engine is None:
        raise ModelException("The engine interface cannot be None.")

    if self._logger.debug_enabled:  # pragma: no cover
        self._logger.debug(
            f"The '{StdOutColors.PURPLE}{self.name.capitalize()}{StdOutColors.DEFAULT}' has been created."
        )

    self.float_precision = float_precision

get_dimension_by_name

get_dimension_by_name(name: str) -> int

Retrieves the size of a dimension in the model based on its name.

PARAMETER DESCRIPTION
name

The name of the dimension.

TYPE: str

RETURNS DESCRIPTION
int

The size of the dimension. Returns 0 if the dimension does not exist.

Source code in pyorlib/model/model.py
def get_dimension_by_name(self, name: str) -> int:
    """
    Retrieves the size of a dimension in the model based on its name.
    :param name: The name of the dimension.
    :return: The size of the dimension. Returns 0 if the dimension does not exist.
    """
    return self._dimensions.get(name, 0)

get_term_by_name

get_term_by_name(name: str) -> Term | None

Retrieves a term from the model based on its name.

PARAMETER DESCRIPTION
name

The name of the term.

TYPE: str

RETURNS DESCRIPTION
Term | None

The term with the specified name. Returns None if the term does not exist.

Source code in pyorlib/model/model.py
def get_term_by_name(self, name: str) -> Term | None:
    """
    Retrieves a term from the model based on its name.
    :param name: The name of the term.
    :return: The term with the specified name. Returns `None` if the term does not exist.
    """
    return self._terms.get(name, None)

get_term_set_by_name

get_term_set_by_name(name: str) -> Mapping[Tuple[int, ...], Term] | None

Retrieves a set of terms from the model based on its name.

PARAMETER DESCRIPTION
name

The name of the set.

TYPE: str

RETURNS DESCRIPTION
Mapping[Tuple[int, ...], Term] | None

The set of terms with the specified name. Returns None if the set does not exist.

Source code in pyorlib/model/model.py
def get_term_set_by_name(self, name: str) -> Mapping[Tuple[int, ...], Term] | None:
    """
    Retrieves a set of terms from the model based on its name.
    :param name: The name of the set.
    :return: The set of terms with the specified name. Returns `None` if the set does not exist.
    """
    return self._term_sets.get(name, None)

add_dimension

add_dimension(name: str, value: int) -> int

Adds a new dimension to the model.

PARAMETER DESCRIPTION
name

The name of the dimension to be added.

TYPE: str

value

The size of the new dimension.

TYPE: int

RETURNS DESCRIPTION
int

The dimension that was added to the model.

Source code in pyorlib/model/model.py
def add_dimension(self, name: str, value: int) -> int:
    """
    Adds a new dimension to the model.
    :param name: The name of the dimension to be added.
    :param value: The size of the new dimension.
    :return: The dimension that was added to the model.
    """
    if not name or value is None or not isinstance(value, int) or value < 1:
        raise ModelException("Invalid dimension values.")
    self._dimensions[name] = value

    if self._logger.debug_enabled:  # pragma: no cover
        self._logger.debug(
            action="Dimension added: ",
            msg="".join(
                [
                    f"Name: {StdOutColors.PURPLE}{name}{StdOutColors.DEFAULT} | ",
                    f"val: {StdOutColors.PURPLE}{value}{StdOutColors.DEFAULT}",
                ]
            ),
        )

    return value

add_constant

add_constant(name: str, value_type: ValueType, value: float) -> Constant

Adds a new constant to the model.

PARAMETER DESCRIPTION
name

The name of the constant to be added.

TYPE: str

value_type

The type of the constant value.

TYPE: ValueType

value

The constant value.

TYPE: float

RETURNS DESCRIPTION
Constant

The constant that was added to the model.

Source code in pyorlib/model/model.py
def add_constant(self, name: str, value_type: ValueType, value: float) -> Constant:
    """
    Adds a new constant to the model.
    :param name: The name of the constant to be added.
    :param value_type: The type of the constant value.
    :param value: The constant value.
    :return: The constant that was added to the model.
    """
    if name in self.terms:
        raise ModelException(f"Duplicate term with name: {name}")

    constant: Constant = Constant(name=name, value_type=value_type, value=value)

    self.__save_term(term=constant)

    if self._logger.debug_enabled:  # pragma: no cover
        self._logger.debug(
            action="Constant added: ",
            msg=constant.get_pretty_string(float_precision=self.float_precision),
        )

    return constant

add_variable

add_variable(name: str, value_type: ValueType, lower_bound: float = 0, upper_bound: float = inf) -> Variable

Adds a new variable to the model.

PARAMETER DESCRIPTION
name

The name of the variable to be added.

TYPE: str

value_type

The type of the variable values.

TYPE: ValueType

lower_bound

The lower bound of the variable. Default is 0.

TYPE: float DEFAULT: 0

upper_bound

The upper bound of the variable. Default is infinity.

TYPE: float DEFAULT: inf

RETURNS DESCRIPTION
Variable

The variable that was added to the model.

Source code in pyorlib/model/model.py
def add_variable(
    self,
    name: str,
    value_type: ValueType,
    lower_bound: float = 0,
    upper_bound: float = inf,
) -> Variable:
    """
    Adds a new variable to the model.
    :param name: The name of the variable to be added.
    :param value_type: The type of the variable values.
    :param lower_bound: The lower bound of the variable. Default is 0.
    :param upper_bound: The upper bound of the variable. Default is infinity.
    :return: The variable that was added to the model.
    """
    if name in self.terms:
        raise ModelException(f"Duplicate term with name: {name}")

    variable: Variable = self._engine.add_variable(
        name=name, value_type=value_type, lower_bound=lower_bound, upper_bound=upper_bound
    )

    self.__save_term(term=variable)

    if self._logger.debug_enabled:  # pragma: no cover
        self._logger.debug(
            action="Variable added: ",
            msg=variable.get_pretty_string(float_precision=self.float_precision),
        )

    return variable

add_constant_to_set

add_constant_to_set(set_name: str, set_index: Tuple[int, ...], const_name: str, value_type: ValueType, value: float) -> Constant

Adds a new constant to the model within a set.

PARAMETER DESCRIPTION
set_name

The name of the set where the constant will be added.

TYPE: str

set_index

The position of a term within a set representing its indices.

TYPE: Tuple[int, ...]

const_name

The name of the constant to be saved.

TYPE: str

value_type

The type of the constant value.

TYPE: ValueType

value

The constant value.

TYPE: float

RETURNS DESCRIPTION
Constant

The constant that was added to the model.

Source code in pyorlib/model/model.py
def add_constant_to_set(
    self,
    set_name: str,
    set_index: Tuple[int, ...],
    const_name: str,
    value_type: ValueType,
    value: float,
) -> Constant:
    """
    Adds a new constant to the model within a set.
    :param set_name: The name of the set where the constant will be added.
    :param set_index: The position of a term within a set representing its indices.
    :param const_name: The name of the constant to be saved.
    :param value_type: The type of the constant value.
    :param value: The constant value.
    :return: The constant that was added to the model.
    """
    if const_name in self.terms:
        raise ModelException(f"Duplicate term with name: {const_name}")

    if set_name in self.term_sets and set_index in self.term_sets[set_name]:
        raise ModelException(f"Duplicate set name and index: {set_name} | {set_index}")

    constant: Constant = Constant(name=const_name, value_type=value_type, value=value)

    self.__save_term_to_set(set_name=set_name, set_index=set_index, term=constant)

    if self._logger.debug_enabled:  # pragma: no cover
        self._logger.debug(
            action="Constant added to set: ",
            msg="".join(
                [
                    f"Set name: {StdOutColors.PURPLE}{set_name}{StdOutColors.DEFAULT} | ",
                    f"Set index: {StdOutColors.PURPLE}{set_index}{StdOutColors.DEFAULT} | ",
                    constant.get_pretty_string(float_precision=self.float_precision),
                ]
            ),
        )

    return constant

add_variable_to_set

add_variable_to_set(set_name: str, set_index: Tuple[int, ...], var_name: str, value_type: ValueType, lower_bound: float = 0, upper_bound: float = inf) -> Variable

Adds a new variable to the model within a set.

PARAMETER DESCRIPTION
set_name

The name of the set where the variable will be added.

TYPE: str

set_index

The position of a term within a set that represents its indices.

TYPE: Tuple[int, ...]

var_name

The name of the variable to be added.

TYPE: str

value_type

The type of the variable values.

TYPE: ValueType

lower_bound

The lower bound of the variable. Default is 0.

TYPE: float DEFAULT: 0

upper_bound

The upper bound of the variable. Default is infinity.

TYPE: float DEFAULT: inf

RETURNS DESCRIPTION
Variable

The variable that was added to the model.

Source code in pyorlib/model/model.py
def add_variable_to_set(
    self,
    set_name: str,
    set_index: Tuple[int, ...],
    var_name: str,
    value_type: ValueType,
    lower_bound: float = 0,
    upper_bound: float = inf,
) -> Variable:
    """
    Adds a new variable to the model within a set.
    :param set_name: The name of the set where the variable will be added.
    :param set_index: The position of a term within a set that represents its indices.
    :param var_name: The name of the variable to be added.
    :param value_type: The type of the variable values.
    :param lower_bound: The lower bound of the variable. Default is 0.
    :param upper_bound: The upper bound of the variable. Default is infinity.
    :return: The variable that was added to the model.
    """
    if var_name in self.terms:
        raise ModelException(f"Duplicate term with name: {var_name}")

    if set_name in self.term_sets and set_index in self.term_sets[set_name]:
        raise ModelException(f"Duplicate set name and index: {set_name} | {set_index}")

    variable: Variable = self._engine.add_variable(
        name=var_name, value_type=value_type, lower_bound=lower_bound, upper_bound=upper_bound
    )

    self.__save_term_to_set(set_name=set_name, set_index=set_index, term=variable)

    if self._logger.debug_enabled:  # pragma: no cover
        self._logger.debug(
            action="Variable added to set: ",
            msg="".join(
                [
                    f"Set name: {StdOutColors.PURPLE}{set_name}{StdOutColors.DEFAULT} | ",
                    f"Set index: {StdOutColors.PURPLE}{set_index}{StdOutColors.DEFAULT} | ",
                    variable.get_pretty_string(float_precision=self.float_precision),
                ]
            ),
        )

    return variable

add_constraint

add_constraint(expression: Element) -> Element

Adds a new constraint to the model.

PARAMETER DESCRIPTION
expression

The constraint expression

TYPE: Element

RETURNS DESCRIPTION
Element

An object representing the constraint.

Source code in pyorlib/model/model.py
def add_constraint(self, expression: Element) -> Element:
    """
    Adds a new constraint to the model.
    :param expression: The constraint expression
    :return: An object representing the constraint.
    """
    constraint: Element = self._engine.add_constraint(expression=expression)

    if self._logger.debug_enabled:  # pragma: no cover
        try:
            self._logger.debug(action="Constraint added: ", msg=f"Expr: {expression}")
        except RecursionError:
            self._logger.debug(action="Constraint added: ", msg="Expr: Unprintable expression")

    return constraint

set_objective

set_objective(opt_type: OptimizationType, expression: Element) -> Element

Defines the objective function.

PARAMETER DESCRIPTION
opt_type

The type of optimization to be performed.

TYPE: OptimizationType

expression

The objective expression.

TYPE: Element

RETURNS DESCRIPTION
Element

The objective function.

Source code in pyorlib/model/model.py
def set_objective(self, opt_type: OptimizationType, expression: Element) -> Element:
    """
    Defines the objective function.
    :param opt_type: The type of optimization to be performed.
    :param expression: The objective expression.
    :return: The objective function.
    """
    objective: Element = self._engine.set_objective(opt_type=opt_type, expression=expression)

    if self._logger.debug_enabled:  # pragma: no cover
        try:
            self._logger.debug(
                action="Objective function added: ",
                msg="".join(
                    [
                        f"Opt Type: {StdOutColors.PURPLE}{opt_type.name.capitalize()}{StdOutColors.DEFAULT} | ",
                        f"Expr: {objective}",
                    ]
                ),
            )
        except RecursionError:
            self._logger.debug(action="Objective function added: ", msg="Expr: Unprintable expression")

    return objective

solve

solve() -> None

Solves the optimization problem represented by the model.

RETURNS DESCRIPTION
None

None.

Source code in pyorlib/model/model.py
def solve(self) -> None:
    """
    Solves the optimization problem represented by the model.
    :return: None.
    """
    if self._logger.debug_enabled:  # pragma: no cover
        self._logger.debug(f"Solving the model...")

    self._engine.solve()

    if self._logger.debug_enabled:  # pragma: no cover
        self._logger.debug(f"The model has been solved.")

print_info

print_info(display_term_sets: bool = False) -> None

Prints information about the model.

PARAMETER DESCRIPTION
display_term_sets

Whether to display information about term sets. Defaults to False.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
None

None.

Source code in pyorlib/model/model.py
def print_info(self, display_term_sets: bool = False) -> None:  # pragma: no cover
    """
    Prints information about the model.
    :param display_term_sets: Whether to display information about term sets. Defaults to False.
    :return: None.
    """
    default, debug = StdOutColors.DEFAULT, StdOutColors.PURPLE
    print(f"\n------ MODEL INFORMATION ------\n")
    print("Model properties:")
    print(f"\tName: {debug}{self.name}{default}")
    print("Objective function:")
    print(f"\tExpression: {self.objective_expr}")
    print(f"\tStatus: {debug}{self.solution_status.name}{default}")
    print(
        f"\tValue: {debug}",
        (
            "{0:.{prec}g}".format(self.objective_value, prec=self.float_precision)
            if self.objective_value is not None
            else "--"
        ),
        f"{default}",
    )

    print(f"Dimensions: {debug}{len(self.dimensions)}{default}")
    for name, size in self.dimensions.items():
        print(
            f"\tName: {debug}{name}{default} | ",
            f"Val: {debug}{size}{default}",
        )

    print(f"Terms: {debug}{len(self.terms)}{default}")
    for name, term in self.terms.items():
        print(f"\t{term.get_pretty_string(float_precision=self.float_precision)}")
    if display_term_sets:
        print(f"Terms Sets: {debug}{len(self.term_sets)}{default}")
        for name, terms in self.term_sets.items():
            print(f"\tTerm: {debug}{name}{default}")
            for index, term in terms.items():
                print(
                    f"\t\tIndex: {debug}{index}{default} | ",
                    f"{term.get_pretty_string(float_precision=self.float_precision)}",
                )

    constraints = self.constraints
    print(f"Constraints: {debug}{len(constraints)}{default}")
    for exp in constraints:
        try:
            print(f"\tExpression: {exp}")
        except RecursionError:
            print("\tExpression: Print Error")
    print()

print_solution

print_solution() -> None

Prints the solution of the optimization problem.

RETURNS DESCRIPTION
None

None.

Source code in pyorlib/model/model.py
def print_solution(self) -> None:  # pragma: no cover
    """
    Prints the solution of the optimization problem.
    :return: None.
    """
    default, debug = StdOutColors.DEFAULT, StdOutColors.PURPLE
    print(f"\n------ MODEL SOLUTION ------\n")
    print("Objective function:")
    print(f"\tStatus: {debug}{self.solution_status.name}{default}")
    print(
        f"\tValue: {debug}",
        (
            "{0:.{prec}g}".format(self.objective_value, prec=self.float_precision)
            if self.objective_value is not None
            else "--"
        ),
        f"{default}",
    )

    solution_variables: Dict[str, Term] = {
        name: term for name, term in self.terms.items() if (term.is_variable and term.value != 0)
    }

    if solution_variables:
        print("Terms:")
        for name, term in solution_variables.items():
            print(f"\t{term.get_pretty_string(float_precision=self.float_precision)}")

    print()