@@ -239,7 +239,7 @@ def SSE(model):
239239 model: annotated Pyomo model
240240 """
241241 # check if the model has all the required suffixes
242- _check_model_labels_helper (model )
242+ _check_model_labels_helper (model , logging_level = logging . ERROR )
243243
244244 # SSE between the prediction and observation of the measured variables
245245 expr = sum ((y - y_hat ) ** 2 for y_hat , y in model .experiment_outputs .items ())
@@ -256,7 +256,7 @@ def SSE_weighted(model):
256256 model: annotated Pyomo model
257257 """
258258 # check if the model has all the required suffixes
259- _check_model_labels_helper (model )
259+ _check_model_labels_helper (model , logging_level = logging . ERROR )
260260
261261 # Check that measurement errors exist
262262 if hasattr (model , "measurement_error" ):
@@ -287,12 +287,13 @@ def SSE_weighted(model):
287287 )
288288
289289
290- def _check_model_labels_helper (model ):
290+ def _check_model_labels_helper (model , logging_level ):
291291 """
292292 Checks if the annotated Pyomo model contains the necessary suffixes
293293
294294 Argument:
295295 model: annotated Pyomo model for suffix checking
296+ logging_level: logging level specified by the user, e.g., logging.INFO
296297 """
297298 required_attrs = ("experiment_outputs" , "unknown_parameters" )
298299
@@ -304,17 +305,19 @@ def _check_model_labels_helper(model):
304305 f"Experiment model is missing required attribute(s): { missing_str } "
305306 )
306307
307- logger .setLevel (level = logging .INFO )
308- logger .info ("Model has expected labels." )
308+ # set the logging
309+ logger .setLevel (level = logging_level )
310+ if logging_level == logging .INFO :
311+ logger .info ("Model has expected labels." )
309312
310313
311314def _get_labeled_model_helper (experiment ):
312315 """
313316 Checks if the Experiment class object has a "get_labeled_model" function
314317
315318 Argument:
316- experiment: Estimator class object that contains the model for a particular
317- experimental condition
319+ experiment: Estimator class object that contains the Pyomo model
320+ for a particular experimental condition
318321
319322 Returns:
320323 Annotated Pyomo model
@@ -348,19 +351,20 @@ class UnsupportedArgsLib(Enum):
348351
349352
350353# Compute the Jacobian matrix of measured variables with respect to the parameters
351- def _compute_jacobian (experiment , theta_vals , step , solver , tee ):
354+ def _compute_jacobian (experiment , theta_vals , step , solver , tee , logging_level ):
352355 """
353356 Computes the Jacobian matrix of the measured variables with respect to the
354357 parameters using the central finite difference scheme
355358
356359 Arguments:
357- experiment: Estimator class object that contains the model for a particular
358- experimental condition
360+ experiment: Estimator class object that contains the Pyomo model
361+ for a particular experimental condition
359362 theta_vals: dictionary containing the estimates of the unknown parameters
360363 step: float used for relative perturbation of the parameters,
361- e.g., step=0.02 is a 2% perturbation
364+ e.g., step=0.02 is a 2% perturbation
362365 solver: string ``solver`` object specified by the user, e.g., 'ipopt'
363366 tee: boolean solver option to be passed for verbose output
367+ logging_level: logging level specified by the user, e.g., logging.INFO
364368
365369 Returns:
366370 J: Jacobian matrix
@@ -369,7 +373,7 @@ def _compute_jacobian(experiment, theta_vals, step, solver, tee):
369373 model = _get_labeled_model_helper (experiment )
370374
371375 # check if the model has all the required suffixes
372- _check_model_labels_helper (model )
376+ _check_model_labels_helper (model , logging_level )
373377
374378 # fix the value of the unknown parameters to the estimated values
375379 params = [k for k , v in model .unknown_parameters .items ()]
@@ -452,36 +456,37 @@ def _compute_jacobian(experiment, theta_vals, step, solver, tee):
452456
453457# Compute the covariance matrix of the estimated parameters
454458def compute_covariance_matrix (
455- experiment_list , method , theta_vals , step , solver , tee , estimated_var = None
459+ experiment_list ,
460+ method ,
461+ theta_vals ,
462+ step ,
463+ solver ,
464+ tee ,
465+ logging_level ,
466+ estimated_var = None ,
456467):
457468 """
458469 Computes the covariance matrix of the estimated parameters using
459470 'finite_difference' and 'automatic_differentiation_kaug' methods
460471
461472 Arguments:
462- experiment_list: list of Estimator class objects containing the model for
463- different experimental conditions
473+ experiment_list: list of Estimator class objects containing the Pyomo model
474+ for different experimental conditions
464475 method: string ``method`` object specified by the user,
465- e.g., 'finite_difference'
476+ e.g., 'finite_difference'
466477 theta_vals: dictionary containing the estimates of the unknown parameters
467478 step: float used for relative perturbation of the parameters,
468- e.g., step=0.02 is a 2% perturbation
479+ e.g., step=0.02 is a 2% perturbation
469480 solver: string ``solver`` object specified by the user, e.g., 'ipopt'
470481 tee: boolean solver option to be passed for verbose output
482+ logging_level: logging level specified by the user, e.g., logging.INFO
471483 estimated_var: value of the estimated variance of the measurement error
472- in cases where the user does not supply the measurement
473- error standard deviation
484+ in cases where the user does not supply the
485+ measurement error standard deviation
474486
475487 Returns:
476488 cov: covariance matrix of the estimated parameters
477489 """
478- # get the supported methods
479- supported_methods = [e .value for e in CovarianceMethodLib ]
480- if method not in supported_methods :
481- raise ValueError (
482- f"Invalid method: '{ method } '. " f"Choose from { supported_methods } ."
483- )
484-
485490 if method == CovarianceMethodLib .finite_difference .value :
486491 # store the FIM of all experiments
487492 FIM_all_exp = []
@@ -495,6 +500,7 @@ def compute_covariance_matrix(
495500 step = step ,
496501 solver = solver ,
497502 tee = tee ,
503+ logging_level = logging_level ,
498504 estimated_var = estimated_var ,
499505 )
500506 )
@@ -506,7 +512,10 @@ def compute_covariance_matrix(
506512 cov = np .linalg .inv (FIM )
507513 except np .linalg .LinAlgError :
508514 cov = np .linalg .pinv (FIM )
509- logger .info ("The FIM is singular. Using pseudo-inverse instead." )
515+ if logging_level == logging .INFO :
516+ logger .info ("The FIM is singular. Using pseudo-inverse instead." )
517+ else :
518+ print ("The FIM is singular. Using pseudo-inverse instead." )
510519
511520 cov = pd .DataFrame (cov , index = theta_vals .keys (), columns = theta_vals .keys ())
512521 elif method == CovarianceMethodLib .automatic_differentiation_kaug .value :
@@ -532,7 +541,10 @@ def compute_covariance_matrix(
532541 cov = np .linalg .inv (FIM )
533542 except np .linalg .LinAlgError :
534543 cov = np .linalg .pinv (FIM )
535- logger .info ("The FIM is singular. Using pseudo-inverse instead." )
544+ if logging_level == logging .INFO :
545+ logger .info ("The FIM is singular. Using pseudo-inverse instead." )
546+ else :
547+ print ("The FIM is singular. Using pseudo-inverse instead." )
536548
537549 cov = pd .DataFrame (cov , index = theta_vals .keys (), columns = theta_vals .keys ())
538550
@@ -542,35 +554,37 @@ def compute_covariance_matrix(
542554# compute the Fisher information matrix of the estimated parameters using
543555# 'finite_difference'
544556def _finite_difference_FIM (
545- experiment , theta_vals , step , solver , tee , estimated_var = None
557+ experiment , theta_vals , step , solver , tee , logging_level , estimated_var = None
546558):
547559 """
548560 Computes the Fisher information matrix from 'finite_difference' Jacobian matrix
549561 and measurement errors standard deviation defined in the annotated Pyomo model
550562
551563 Arguments:
552- experiment: Estimator class object that contains the model for a particular
553- experimental condition
564+ experiment: Estimator class object that contains the Pyomo model
565+ for a particular experimental condition
554566 theta_vals: dictionary containing the estimates of the unknown parameters
555567 step: float used for relative perturbation of the parameters,
556- e.g., step=0.02 is a 2% perturbation
568+ e.g., step=0.02 is a 2% perturbation
557569 solver: string ``solver`` object specified by the user, e.g., 'ipopt'
558570 tee: boolean solver option to be passed for verbose output
571+ logging_level: logging level specified by the user, e.g., logging.INFO
559572 estimated_var: value of the estimated variance of the measurement error in
560- cases where the user does not supply the measurement error
561- standard deviation
573+ cases where the user does not supply the
574+ measurement error standard deviation
562575
563576 Returns:
564577 FIM: Fisher information matrix about the parameters
565578 """
566579 # compute the Jacobian matrix using finite difference
567- J = _compute_jacobian (experiment , theta_vals , step , solver , tee )
580+ J = _compute_jacobian (experiment , theta_vals , step , solver , tee , logging_level )
568581
569582 # computing the condition number of the Jacobian matrix
570583 cond_number_jac = np .linalg .cond (J )
571-
572- # set up logging
573- logger .info (f"The condition number of the Jacobian matrix is { cond_number_jac } " )
584+ if logging_level == logging .INFO :
585+ logger .info (
586+ f"The condition number of the Jacobian matrix " f"is { cond_number_jac } "
587+ )
574588
575589 # grab the model
576590 model = _get_labeled_model_helper (experiment )
@@ -623,14 +637,14 @@ def _kaug_FIM(experiment, theta_vals, solver, tee, estimated_var=None):
623637 Disclaimer - code adopted from the kaug function implemented in Pyomo.DoE
624638
625639 Arguments:
626- experiment: Estimator class object that contains the model for a particular
627- experimental condition
640+ experiment: Estimator class object that contains the Pyomo model
641+ for a particular experimental condition
628642 theta_vals: dictionary containing the estimates of the unknown parameters
629643 solver: string ``solver`` object specified by the user, e.g., 'ipopt'
630644 tee: boolean solver option to be passed for verbose output
631645 estimated_var: value of the estimated variance of the measurement error in
632- cases where the user does not supply the measurement error
633- standard deviation
646+ cases where the user does not supply the
647+ measurement error standard deviation
634648
635649 Returns:
636650 FIM: Fisher information matrix about the parameters
@@ -732,14 +746,16 @@ class Estimator(object):
732746 A list of experiment objects which creates one labeled model for
733747 each experiment
734748 obj_function: string or function (optional)
735- Built in objective (currently only "SSE ") or custom function used to
736- formulate parameter estimation objective.
749+ Built- in objective ("SSE" or "SSE_weighted ") or custom function
750+ used to formulate parameter estimation objective.
737751 If no function is specified, the model is used
738752 "as is" and should be defined with a "FirstStageCost" and
739753 "SecondStageCost" expression that are used to build an objective.
740754 Default is None.
741755 tee: bool, optional
742756 If True, print the solver output to the screen. Default is False.
757+ logging_level: logging level, optional
758+ e.g., logging.INFO. Default is logging.ERROR.
743759 diagnostic_mode: bool, optional
744760 If True, print diagnostics from the solver. Default is False.
745761 solver_options: dict, optional
@@ -758,6 +774,7 @@ def __init__(
758774 experiment_list ,
759775 obj_function = None ,
760776 tee = False ,
777+ logging_level = logging .ERROR ,
761778 diagnostic_mode = False ,
762779 solver_options = None ,
763780 ):
@@ -770,7 +787,7 @@ def __init__(
770787 model = _get_labeled_model_helper (self .exp_list [0 ])
771788
772789 # check if the model has all the required suffixes
773- _check_model_labels_helper (model )
790+ _check_model_labels_helper (model , logging_level )
774791
775792 # populate keyword argument options
776793 if isinstance (obj_function , str ):
@@ -795,6 +812,7 @@ def __init__(
795812 self .obj_function = obj_function
796813
797814 self .tee = tee
815+ self .logging_level = logging_level
798816 self .diagnostic_mode = diagnostic_mode
799817 self .solver_options = solver_options
800818
@@ -1184,12 +1202,12 @@ def _cov_at_theta(self, method, solver, cov_n, step):
11841202
11851203 Argument:
11861204 method: string ``method`` object specified by the user,
1187- e.g., 'finite_difference'
1205+ e.g., 'finite_difference'
11881206 solver: string ``solver`` object specified by the user, e.g., 'ipopt'
11891207 cov_n: integer, number of datapoints specified by the user which is used
1190- in the objective function
1208+ in the objective function
11911209 step: float used for relative perturbation of the parameters,
1192- e.g., step=0.02 is a 2% perturbation
1210+ e.g., step=0.02 is a 2% perturbation
11931211
11941212 Returns:
11951213 cov: pd.DataFrame, covariance matrix of the estimated parameters
@@ -1306,6 +1324,7 @@ def _cov_at_theta(self, method, solver, cov_n, step):
13061324 solver = solver ,
13071325 step = step ,
13081326 tee = self .tee ,
1327+ logging_level = self .logging_level ,
13091328 estimated_var = measurement_var ,
13101329 )
13111330 elif all (item is not None for item in meas_error ):
@@ -1324,6 +1343,7 @@ def _cov_at_theta(self, method, solver, cov_n, step):
13241343 solver = solver ,
13251344 step = step ,
13261345 tee = self .tee ,
1346+ logging_level = self .logging_level ,
13271347 )
13281348 else :
13291349 raise ValueError (
@@ -1356,6 +1376,7 @@ def _cov_at_theta(self, method, solver, cov_n, step):
13561376 step = step ,
13571377 solver = solver ,
13581378 tee = self .tee ,
1379+ logging_level = self .logging_level ,
13591380 )
13601381 else :
13611382 cov = self .inv_red_hes
0 commit comments