22
33from __future__ import annotations
44
5+ import time
56from pathlib import Path
67from typing import TYPE_CHECKING , Any
78
@@ -92,6 +93,7 @@ class Model:
9293 "minimize" ,
9394 "maximize" ,
9495 "_logger" ,
96+ "_last_log" ,
9597 ]
9698
9799 def __init__ (
@@ -119,13 +121,16 @@ def __init__(
119121 self ._solver_uses_variable_names = solver_uses_variable_names
120122
121123 self ._logger = None
124+ self ._last_log = None
122125 if verbose :
123126 import logging
124127
125128 self ._logger = logging .getLogger ("pyoframe" )
126129 self ._logger .addHandler (logging .NullHandler ())
127130 self ._logger .setLevel (logging .DEBUG )
128131
132+ self ._last_log = time .time ()
133+
129134 @property
130135 def poi (self ):
131136 """The underlying PyOptInterface model used to interact with the solver.
@@ -340,18 +345,6 @@ def objective(self) -> Objective:
340345 Raises:
341346 ValueError: If the objective has not been defined.
342347
343- Examples:
344- >>> m = pf.Model()
345- >>> m.X = pf.Variable()
346- >>> m.objective
347- Traceback (most recent call last):
348- ...
349- ValueError: Objective is not defined.
350- >>> m.maximize = m.X
351- >>> m.objective
352- <Objective (linear) terms=1>
353- X
354-
355348 See Also:
356349 [`Model.has_objective`][pyoframe.Model.has_objective]
357350 """
@@ -371,9 +364,11 @@ def objective(self, value: Operable):
371364 value ._on_add_to_model (self , "objective" )
372365
373366 @property
374- def minimize (self ) -> Objective | None :
367+ def minimize (self ) -> Objective :
375368 """Sets or gets the model's objective for minimization problems."""
376- if self .sense != ObjSense .MIN :
369+ if self ._objective is None :
370+ raise ValueError ("Objective is not defined." )
371+ if self .sense == ObjSense .MAX :
377372 raise ValueError ("Can't get .minimize in a maximization problem." )
378373 return self ._objective
379374
@@ -386,9 +381,11 @@ def minimize(self, value: Operable):
386381 self .objective = value
387382
388383 @property
389- def maximize (self ) -> Objective | None :
384+ def maximize (self ) -> Objective :
390385 """Sets or gets the model's objective for maximization problems."""
391- if self .sense != ObjSense .MAX :
386+ if self ._objective is None :
387+ raise ValueError ("Objective is not defined." )
388+ if self .sense == ObjSense .MIN :
392389 raise ValueError ("Can't get .maximize in a minimization problem." )
393390 return self ._objective
394391
@@ -414,30 +411,37 @@ def __setattr__(self, __name: str, __value: Any) -> None:
414411 f"Cannot create { __name } since it was already created."
415412 )
416413
417- log = self ._logger is not None
414+ log = self ._logger is not None and isinstance (
415+ __value , (Constraint , Variable )
416+ )
418417
419418 if log :
420- import time
421-
422419 start_time = time .time ()
423420
424421 __value ._on_add_to_model (self , __name )
425422
423+ if log :
424+ solver_time = time .time () - start_time # type: ignore
425+
426426 if isinstance (__value , Variable ):
427427 self ._variables .append (__value )
428428 if self ._var_map is not None :
429429 self ._var_map .add (__value )
430-
431430 if log :
432- self ._logger .debug (
433- f"Added Variable '{ __name } ' (n={ len (__value )} ) to { self .solver .name } in { time .time () - start_time :.3g} seconds"
434- )
431+ type_name = "variable"
435432 elif isinstance (__value , Constraint ):
436433 self ._constraints .append (__value )
437434 if log :
438- self ._logger .debug (
439- f"Added Constraint '{ __name } ' (n={ len (__value )} ) to { self .solver .name } in { time .time () - start_time :.3g} seconds"
440- )
435+ type_name = "constraint"
436+
437+ if log :
438+ elapsed_time = time .time () - self ._last_log # type: ignore
439+ self ._last_log += elapsed_time # type: ignore
440+
441+ self ._logger .debug ( # type: ignore
442+ f"Added { type_name } '{ __name } ' to model ({ elapsed_time :.1f} s elapsed, { solver_time :.1f} s for solver, n={ len (__value )} )" # type: ignore
443+ )
444+
441445 return super ().__setattr__ (__name , __value )
442446
443447 # Defining a custom __getattribute__ prevents type checkers from complaining about attribute access
0 commit comments