def main(): # Parameter estimation using timeseries data # Vars to estimate theta_names = ['k1', 'k2', 'k3'] # Data, includes multiple sensors for ca and cc file_dirname = dirname(abspath(str(__file__))) file_name = abspath(join(file_dirname, 'reactor_data_timeseries.csv')) data = pd.read_csv(file_name) # Group time series data into experiments, return the mean value for sv and caf # Returns a list of dictionaries data_ts = parmest.group_data(data, 'experiment', ['sv', 'caf']) def SSE_timeseries(model, data): expr = 0 for val in data['ca']: expr = expr + ((float(val) - model.ca)**2) * (1 / len(data['ca'])) for val in data['cb']: expr = expr + ((float(val) - model.cb)**2) * (1 / len(data['cb'])) for val in data['cc']: expr = expr + ((float(val) - model.cc)**2) * (1 / len(data['cc'])) for val in data['cd']: expr = expr + ((float(val) - model.cd)**2) * (1 / len(data['cd'])) return expr pest = parmest.Estimator(reactor_design_model, data_ts, theta_names, SSE_timeseries) obj, theta = pest.theta_est() print(obj) print(theta)
def test_propagate_uncertainty_error(self): ''' It tests a TypeError when the modle_uncertian of function propagate_uncertainty is neither python function nor Pyomo ConcreteModel ''' from idaes.apps.uncertainty_propagation.examples.rooney_biegler import rooney_biegler_model variable_name = ['asymptote', 'rate_constant'] data = pd.DataFrame(data=[[1, 8.3], [2, 10.3], [3, 19.0], [4, 16.0], [5, 15.6], [7, 19.8]], columns=['hour', 'y']) def SSE(model, data): expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index) return expr parmest_class = parmest.Estimator(rooney_biegler_model, data, variable_name, SSE) obj, theta, cov = parmest_class.theta_est(calc_cov=True) model_uncertain = ConcreteModel() model_uncertain.asymptote = Var(initialize=15) model_uncertain.rate_constant = Var(initialize=0.5) model_uncertain.obj = Objective( expr=model_uncertain.asymptote * (1 - exp(-model_uncertain.rate_constant * 10)), sense=minimize) with pytest.raises(TypeError): propagate_results = propagate_uncertainty(1, theta, cov, variable_name)
def main(): # Vars to estimate theta_names = ['asymptote', 'rate_constant'] # Data data = pd.DataFrame(data=[[1, 8.3], [2, 10.3], [3, 19.0], [4, 16.0], [5, 15.6], [7, 19.8]], columns=['hour', 'y']) # Sum of squared error function def SSE(model, data): expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index) return expr # Create an instance of the parmest estimator pest = parmest.Estimator(rooney_biegler_model, data, theta_names, SSE) # Parameter estimation obj, theta = pest.theta_est() # Find the objective value at each theta estimate asym = np.arange(10, 30, 2) rate = np.arange(0, 1.5, 0.1) theta_vals = pd.DataFrame(list(product(asym, rate)), columns=['asymptote', 'rate_constant']) obj_at_theta = pest.objective_at_theta(theta_vals) # Run the likelihood ratio test LR = pest.likelihood_ratio_test(obj_at_theta, obj, [0.8, 0.85, 0.9, 0.95]) # Plot results parmest.graphics.pairwise_plot( LR, theta, 0.8, title='LR results within 80% confidence region')
def main(): # Parameter estimation using multisensor data # Vars to estimate theta_names = ['k1', 'k2', 'k3'] # Data, includes multiple sensors for ca and cc file_dirname = dirname(abspath(str(__file__))) file_name = abspath(join(file_dirname, 'reactor_data_multisensor.csv')) data = pd.read_csv(file_name) # Sum of squared error function def SSE_multisensor(model, data): expr = ((float(data['ca1']) - model.ca)**2)*(1/3) + \ ((float(data['ca2']) - model.ca)**2)*(1/3) + \ ((float(data['ca3']) - model.ca)**2)*(1/3) + \ (float(data['cb']) - model.cb)**2 + \ ((float(data['cc1']) - model.cc)**2)*(1/2) + \ ((float(data['cc2']) - model.cc)**2)*(1/2) + \ (float(data['cd']) - model.cd)**2 return expr pest = parmest.Estimator(reactor_design_model, data, theta_names, SSE_multisensor) obj, theta = pest.theta_est() print(obj) print(theta)
def main(): # Vars to estimate theta_names = ['k1', 'k2', 'E1', 'E2'] # Data, list of json file names data = [] file_dirname = dirname(abspath(str(__file__))) for exp_num in range(10): file_name = abspath( join(file_dirname, 'exp' + str(exp_num + 1) + '.out')) data.append(file_name) # Note, the model already includes a 'SecondStageCost' expression # for sum of squared error that will be used in parameter estimation pest = parmest.Estimator(generate_model, data, theta_names) ### Parameter estimation with bootstrap resampling bootstrap_theta = pest.theta_est_bootstrap(100) bootstrap_theta.to_csv('bootstrap_theta.csv') ### Compute objective at theta for likelihood ratio test k1 = np.arange(4, 24, 3) k2 = np.arange(40, 160, 40) E1 = np.arange(29000, 32000, 500) E2 = np.arange(38000, 42000, 500) theta_vals = pd.DataFrame(list(product(k1, k2, E1, E2)), columns=theta_names) obj_at_theta = pest.objective_at_theta(theta_vals) obj_at_theta.to_csv('obj_at_theta.csv')
def main(): # Vars to estimate theta_names = ['asymptote', 'rate_constant'] # Data data = pd.DataFrame(data=[[1, 8.3], [2, 10.3], [3, 19.0], [4, 16.0], [5, 15.6], [7, 19.8]], columns=['hour', 'y']) # Sum of squared error function def SSE(model, data): expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index) return expr # Create an instance of the parmest estimator pest = parmest.Estimator(rooney_biegler_model, data, theta_names, SSE) # Parameter estimation and covariance n = 6 # total number of data points used in the objective (y in 6 scenarios) obj, theta, cov = pest.theta_est(calc_cov=True, cov_n=n) # Plot theta estimates using a multivariate Gaussian distribution parmest.graphics.pairwise_plot( (theta, cov, 100), theta_star=theta, alpha=0.8, distributions=['MVN'], title='Theta estimates within 80% confidence region') # Assert statements compare parameter estimation (theta) to an expected value relative_error = abs(theta['asymptote'] - 19.1426) / 19.1426 assert relative_error < 0.01 relative_error = abs(theta['rate_constant'] - 0.5311) / 0.5311 assert relative_error < 0.01
def setUp(self): from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import rooney_biegler_model # Note, the data used in this test has been corrected to use data.loc[5,'hour'] = 7 (instead of 6) data = pd.DataFrame(data=[[1, 8.3], [2, 10.3], [3, 19.0], [4, 16.0], [5, 15.6], [7, 19.8]], columns=['hour', 'y']) theta_names = ['asymptote', 'rate_constant'] def SSE(model, data): expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index) return expr solver_options = { 'tol': 1e-8, } self.data = data self.pest = parmest.Estimator(rooney_biegler_model, data, theta_names, SSE, solver_options=solver_options)
def main(): # Vars to estimate theta_names = ['k1', 'k2', 'k3'] # Data file_dirname = dirname(abspath(str(__file__))) file_name = abspath(join(file_dirname, 'reactor_data.csv')) data = pd.read_csv(file_name) # Sum of squared error function def SSE(model, data): expr = (float(data['ca']) - model.ca)**2 + \ (float(data['cb']) - model.cb)**2 + \ (float(data['cc']) - model.cc)**2 + \ (float(data['cd']) - model.cd)**2 return expr # Create an instance of the parmest estimator pest = parmest.Estimator(reactor_design_model, data, theta_names, SSE) # Parameter estimation obj, theta = pest.theta_est() # Parameter estimation with bootstrap resampling bootstrap_theta = pest.theta_est_bootstrap(50) # Plot results parmest.graphics.pairwise_plot(bootstrap_theta, title='Bootstrap theta') parmest.graphics.pairwise_plot( bootstrap_theta, theta, 0.8, ['MVN', 'KDE', 'Rect'], title='Bootstrap theta with confidence regions')
def main(): # Vars to estimate theta_names = ['k1', 'k2', 'k3'] # Data file_dirname = dirname(abspath(str(__file__))) file_name = abspath(join(file_dirname, 'reactor_data.csv')) data = pd.read_csv(file_name) # Sum of squared error function def SSE(model, data): expr = (float(data['ca']) - model.ca)**2 + \ (float(data['cb']) - model.cb)**2 + \ (float(data['cc']) - model.cc)**2 + \ (float(data['cd']) - model.cd)**2 return expr # Create an instance of the parmest estimator pest = parmest.Estimator(reactor_design_model, data, theta_names, SSE) # Parameter estimation obj, theta = pest.theta_est() # Assert statements compare parameter estimation (theta) to an expected value k1_expected = 5.0 / 6.0 k2_expected = 5.0 / 3.0 k3_expected = 1.0 / 6000.0 relative_error = abs(theta['k1'] - k1_expected) / k1_expected assert relative_error < 0.05 relative_error = abs(theta['k2'] - k2_expected) / k2_expected assert relative_error < 0.05 relative_error = abs(theta['k3'] - k3_expected) / k3_expected assert relative_error < 0.05
def main(): # Vars to estimate theta_names = ['asymptote', 'rate_constant'] # Data data = pd.DataFrame(data=[[1, 8.3], [2, 10.3], [3, 19.0], [4, 16.0], [5, 15.6], [7, 19.8]], columns=['hour', 'y']) # Sum of squared error function def SSE(model, data): expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index) return expr # Create an instance of the parmest estimator pest = parmest.Estimator(rooney_biegler_model, data, theta_names, SSE) # Parameter estimation obj, theta = pest.theta_est() # Parameter estimation with bootstrap resampling bootstrap_theta = pest.theta_est_bootstrap(50, seed=4581) # Plot results parmest.graphics.pairwise_plot(bootstrap_theta, title='Bootstrap theta') parmest.graphics.pairwise_plot( bootstrap_theta, theta, 0.8, ['MVN', 'KDE', 'Rect'], title='Bootstrap theta with confidence regions')
def main(dirname): """ dirname gives the location of the experiment input files""" # Semibatch Vars to estimate in parmest theta_names = ['k1', 'k2', 'E1', 'E2'] # Semibatch data: list of dictionaries data = [] for exp_num in range(10): fname = os.path.join(dirname, 'exp' + str(exp_num + 1) + '.out') with open(fname, 'r') as infile: d = json.load(infile) data.append(d) pest = parmest.Estimator(generate_model, data, theta_names) scenmaker = sc.ScenarioCreator(pest, "ipopt") ofile = "delme_exp.csv" print("Make one scenario per experiment and write to {}".format(ofile)) experimentscens = sc.ScenarioSet("Experiments") scenmaker.ScenariosFromExperiments(experimentscens) ###experimentscens.write_csv(ofile) numtomake = 3 print("\nUse the bootstrap to make {} scenarios and print.".format( numtomake)) bootscens = sc.ScenarioSet("Bootstrap") scenmaker.ScenariosFromBoostrap(bootscens, numtomake) for s in bootscens.ScensIterator(): print("{}, {}".format(s.name, s.probability)) for n, v in s.ThetaVals.items(): print(" {}={}".format(n, v))
def setUp(self): from pyomo.contrib.parmest.examples.reactor_design.reactor_design import reactor_design_model # Data from the design data = pd.DataFrame(data=[[1.05, 10000, 3458.4, 1060.8, 1683.9, 1898.5], [1.10, 10000, 3535.1, 1064.8, 1613.3, 1893.4], [1.15, 10000, 3609.1, 1067.8, 1547.5, 1887.8], [1.20, 10000, 3680.7, 1070.0, 1486.1, 1881.6], [1.25, 10000, 3750.0, 1071.4, 1428.6, 1875.0], [1.30, 10000, 3817.1, 1072.2, 1374.6, 1868.0], [1.35, 10000, 3882.2, 1072.4, 1324.0, 1860.7], [1.40, 10000, 3945.4, 1072.1, 1276.3, 1853.1], [1.45, 10000, 4006.7, 1071.3, 1231.4, 1845.3], [1.50, 10000, 4066.4, 1070.1, 1189.0, 1837.3], [1.55, 10000, 4124.4, 1068.5, 1148.9, 1829.1], [1.60, 10000, 4180.9, 1066.5, 1111.0, 1820.8], [1.65, 10000, 4235.9, 1064.3, 1075.0, 1812.4], [1.70, 10000, 4289.5, 1061.8, 1040.9, 1803.9], [1.75, 10000, 4341.8, 1059.0, 1008.5, 1795.3], [1.80, 10000, 4392.8, 1056.0, 977.7, 1786.7], [1.85, 10000, 4442.6, 1052.8, 948.4, 1778.1], [1.90, 10000, 4491.3, 1049.4, 920.5, 1769.4], [1.95, 10000, 4538.8, 1045.8, 893.9, 1760.8]], columns=['sv', 'caf', 'ca', 'cb', 'cc', 'cd']) theta_names = ['k1', 'k2', 'k3'] def SSE(model, data): expr = (float(data['ca']) - model.ca)**2 + \ (float(data['cb']) - model.cb)**2 + \ (float(data['cc']) - model.cc)**2 + \ (float(data['cd']) - model.cd)**2 return expr self.pest = parmest.Estimator(reactor_design_model, data, theta_names, SSE)
def setUp(self): from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import rooney_biegler_model data = pd.DataFrame(data=[[1,8.3],[2,10.3],[3,19.0], [4,16.0],[5,15.6],[6,19.8]], columns=['hour', 'y']) theta_names = ['asymptote', 'rate_constant'] def SSE(model, data): expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index) return expr self.pest = parmest.Estimator(rooney_biegler_model, data, theta_names, SSE)
def main(): # Generate data data = generate_data() data_std = data.std() # Define sum of squared error objective function for data rec def SSE(model, data): expr = ((float(data['ca']) - model.ca)/float(data_std['ca']))**2 + \ ((float(data['cb']) - model.cb)/float(data_std['cb']))**2 + \ ((float(data['cc']) - model.cc)/float(data_std['cc']))**2 + \ ((float(data['cd']) - model.cd)/float(data_std['cd']))**2 return expr ### Data reconciliation theta_names = [] # no variables to estimate, use initialized values pest = parmest.Estimator(reactor_design_model_for_datarec, data, theta_names, SSE) obj, theta, data_rec = pest.theta_est( return_values=['ca', 'cb', 'cc', 'cd', 'caf']) print(obj) print(theta) parmest.graphics.grouped_boxplot(data[['ca', 'cb', 'cc', 'cd']], data_rec[['ca', 'cb', 'cc', 'cd']], group_names=['Data', 'Data Rec']) ### Parameter estimation using reconciled data theta_names = ['k1', 'k2', 'k3'] data_rec['sv'] = data['sv'] pest = parmest.Estimator(reactor_design_model, data_rec, theta_names, SSE) obj, theta = pest.theta_est() print(obj) print(theta) theta_real = {'k1': 5.0 / 6.0, 'k2': 5.0 / 3.0, 'k3': 1.0 / 6000.0} print(theta_real)
def main(): # Data from Table 5.2 in Y. Bard, "Nonlinear Parameter Estimation", (pg. 124) data = [{'experiment': 1, 'x1': 0.1, 'x2': 100, 'y': 0.98}, {'experiment': 2, 'x1': 0.2, 'x2': 100, 'y': 0.983}, {'experiment': 3, 'x1': 0.3, 'x2': 100, 'y': 0.955}, {'experiment': 4, 'x1': 0.4, 'x2': 100, 'y': 0.979}, {'experiment': 5, 'x1': 0.5, 'x2': 100, 'y': 0.993}, {'experiment': 6, 'x1': 0.05, 'x2': 200, 'y': 0.626}, {'experiment': 7, 'x1': 0.1, 'x2': 200, 'y': 0.544}, {'experiment': 8, 'x1': 0.15, 'x2': 200, 'y': 0.455}, {'experiment': 9, 'x1': 0.2, 'x2': 200, 'y': 0.225}, {'experiment': 10, 'x1': 0.25, 'x2': 200, 'y': 0.167}, {'experiment': 11, 'x1': 0.02, 'x2': 300, 'y': 0.566}, {'experiment': 12, 'x1': 0.04, 'x2': 300, 'y': 0.317}, {'experiment': 13, 'x1': 0.06, 'x2': 300, 'y': 0.034}, {'experiment': 14, 'x1': 0.08, 'x2': 300, 'y': 0.016}, {'experiment': 15, 'x1': 0.1, 'x2': 300, 'y': 0.006}] # ======================================================================= # Parameter estimation without covariance estimate # Only estimate the parameter k[1]. The parameter k[2] will remain fixed # at its initial value theta_names = ['k[1]'] pest = parmest.Estimator(simple_reaction_model, data, theta_names) obj, theta = pest.theta_est() print(obj) print(theta) print() #======================================================================= # Estimate both k1 and k2 and compute the covariance matrix theta_names = ['k'] pest = parmest.Estimator(simple_reaction_model, data, theta_names) n = 15 # total number of data points used in the objective (y in 15 scenarios) obj, theta, cov = pest.theta_est(calc_cov=True, cov_n=n) print(obj) print(theta) print(cov)
def make_model(self, theta_names): data = pd.DataFrame(data=[[1, 8.3], [2, 10.3], [3, 19.0], [4, 16.0], [5, 15.6], [7, 19.8]], columns=['hour', 'y']) def rooney_biegler_model_alternate(data): ''' Alternate model definition used in a unit test Here, the fitted parameters are defined as a single variable over a set A bit silly for this specific example ''' model = pyo.ConcreteModel() model.var_names = pyo.Set( initialize=['asymptote', 'rate_constant']) model.theta = pyo.Var(model.var_names, initialize={ 'asymptote': 15, 'rate_constant': 0.5 }) model.theta[ 'asymptote'].fixed = True # parmest will unfix theta variables, even when they are indexed model.theta['rate_constant'].fixed = True def response_rule(m, h): expr = m.theta['asymptote'] * ( 1 - pyo.exp(-m.theta['rate_constant'] * h)) return expr model.response_function = pyo.Expression(data.hour, rule=response_rule) def SSE_rule(m): return sum((data.y[i] - m.response_function[data.hour[i]])**2 for i in data.index) model.SSE = pyo.Objective(rule=SSE_rule, sense=pyo.minimize) return model def SSE(model, data): expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index) return expr return parmest.Estimator(rooney_biegler_model_alternate, data, theta_names, SSE)
def setUp(self): import pyomo.contrib.parmest.examples.semibatch.semibatch as sb import json # Vars to estimate in parmest theta_names = ['k1', 'k2', 'E1', 'E2'] self.fbase = os.path.join(testdir,"..","examples","semibatch") # Data, list of dictionaries data = [] for exp_num in range(10): fname = "exp"+str(exp_num+1)+".out" fullname = os.path.join(self.fbase, fname) with open(fullname,'r') as infile: d = json.load(infile) data.append(d) # Note, the model already includes a 'SecondStageCost' expression # for the sum of squared error that will be used in parameter estimation self.pest = parmest.Estimator(sb.generate_model, data, theta_names)
def test_propagate_uncertainty(self): ''' It tests the function propagate_uncertainty with rooney & biegler's model. ''' from idaes.apps.uncertainty_propagation.examples.rooney_biegler import rooney_biegler_model variable_name = ['asymptote', 'rate_constant'] data = pd.DataFrame(data=[[1, 8.3], [2, 10.3], [3, 19.0], [4, 16.0], [5, 15.6], [7, 19.8]], columns=['hour', 'y']) def SSE(model, data): expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index) return expr parmest_class = parmest.Estimator(rooney_biegler_model, data, variable_name, SSE) obj, theta, cov = parmest_class.theta_est(calc_cov=True) model_uncertain = ConcreteModel() model_uncertain.asymptote = Var(initialize=15) model_uncertain.rate_constant = Var(initialize=0.5) model_uncertain.obj = Objective( expr=model_uncertain.asymptote * (1 - exp(-model_uncertain.rate_constant * 10)), sense=minimize) propagate_results = propagate_uncertainty(model_uncertain, theta, cov, variable_name) np.testing.assert_array_almost_equal( propagate_results.gradient_f, [0.9950625870024135, 0.9451480001755206]) assert list(propagate_results.gradient_c) == [] np.testing.assert_array_almost_equal(propagate_results.dsdp.toarray(), [[1., 0.], [0., 1.]]) assert list(propagate_results.propagation_c) == [] assert propagate_results.propagation_f == pytest.approx( 5.45439337747349)
def main(): # Vars to estimate theta_names = ['k1', 'k2', 'E1', 'E2'] # Data, list of dictionaries data = [] file_dirname = dirname(abspath(str(__file__))) for exp_num in range(10): file_name = abspath( join(file_dirname, 'exp' + str(exp_num + 1) + '.out')) with open(file_name, 'r') as infile: d = json.load(infile) data.append(d) # Note, the model already includes a 'SecondStageCost' expression # for sum of squared error that will be used in parameter estimation pest = parmest.Estimator(generate_model, data, theta_names) obj, theta = pest.theta_est() print(obj) print(theta)
def main(): # Vars to estimate theta_names = ['k1', 'k2', 'k3'] # Data file_dirname = dirname(abspath(str(__file__))) file_name = abspath(join(file_dirname, 'reactor_data.csv')) data = pd.read_csv(file_name) # Sum of squared error function def SSE(model, data): expr = (float(data['ca']) - model.ca)**2 + \ (float(data['cb']) - model.cb)**2 + \ (float(data['cc']) - model.cc)**2 + \ (float(data['cd']) - model.cd)**2 return expr # Create an instance of the parmest estimator pest = parmest.Estimator(reactor_design_model, data, theta_names, SSE) # Parameter estimation obj, theta = pest.theta_est() # Find the objective value at each theta estimate k1 = [0.8, 0.85, 0.9] k2 = [1.6, 1.65, 1.7] k3 = [0.00016, 0.000165, 0.00017] theta_vals = pd.DataFrame(list(product(k1, k2, k3)), columns=['k1', 'k2', 'k3']) obj_at_theta = pest.objective_at_theta(theta_vals) # Run the likelihood ratio test LR = pest.likelihood_ratio_test(obj_at_theta, obj, [0.8, 0.85, 0.9, 0.95]) # Plot results parmest.graphics.pairwise_plot( LR, theta, 0.9, title='LR results within 90% confidence region')
def setUp(self): def ABC_model(data): ca_meas = data['ca'] cb_meas = data['cb'] cc_meas = data['cc'] if isinstance(data, pd.DataFrame): meas_t = data.index # time index else: # dictionary meas_t = list(ca_meas.keys()) # nested dictionary ca0 = 1.0 cb0 = 0.0 cc0 = 0.0 m = pyo.ConcreteModel() m.k1 = pyo.Var(initialize=0.5, bounds=(1e-4, 10)) m.k2 = pyo.Var(initialize=3.0, bounds=(1e-4, 10)) m.time = dae.ContinuousSet(bounds=(0.0, 5.0), initialize=meas_t) # initialization and bounds m.ca = pyo.Var(m.time, initialize=ca0, bounds=(-1e-3, ca0 + 1e-3)) m.cb = pyo.Var(m.time, initialize=cb0, bounds=(-1e-3, ca0 + 1e-3)) m.cc = pyo.Var(m.time, initialize=cc0, bounds=(-1e-3, ca0 + 1e-3)) m.dca = dae.DerivativeVar(m.ca, wrt=m.time) m.dcb = dae.DerivativeVar(m.cb, wrt=m.time) m.dcc = dae.DerivativeVar(m.cc, wrt=m.time) def _dcarate(m, t): if t == 0: return pyo.Constraint.Skip else: return m.dca[t] == -m.k1 * m.ca[t] m.dcarate = pyo.Constraint(m.time, rule=_dcarate) def _dcbrate(m, t): if t == 0: return pyo.Constraint.Skip else: return m.dcb[t] == m.k1 * m.ca[t] - m.k2 * m.cb[t] m.dcbrate = pyo.Constraint(m.time, rule=_dcbrate) def _dccrate(m, t): if t == 0: return pyo.Constraint.Skip else: return m.dcc[t] == m.k2 * m.cb[t] m.dccrate = pyo.Constraint(m.time, rule=_dccrate) def ComputeFirstStageCost_rule(m): return 0 m.FirstStageCost = pyo.Expression(rule=ComputeFirstStageCost_rule) def ComputeSecondStageCost_rule(m): return sum( (m.ca[t] - ca_meas[t])**2 + (m.cb[t] - cb_meas[t])**2 + (m.cc[t] - cc_meas[t])**2 for t in meas_t) m.SecondStageCost = pyo.Expression( rule=ComputeSecondStageCost_rule) def total_cost_rule(model): return model.FirstStageCost + model.SecondStageCost m.Total_Cost_Objective = pyo.Objective(rule=total_cost_rule, sense=pyo.minimize) disc = pyo.TransformationFactory('dae.collocation') disc.apply_to(m, nfe=20, ncp=2) return m # This example tests data formatted in 3 ways # Each format holds 1 scenario # 1. dataframe with time index # 2. nested dictionary {ca: {t, val pairs}, ... } data = [[0.000, 0.957, -0.031, -0.015], [0.263, 0.557, 0.330, 0.044], [0.526, 0.342, 0.512, 0.156], [0.789, 0.224, 0.499, 0.310], [1.053, 0.123, 0.428, 0.454], [1.316, 0.079, 0.396, 0.556], [1.579, 0.035, 0.303, 0.651], [1.842, 0.029, 0.287, 0.658], [2.105, 0.025, 0.221, 0.750], [2.368, 0.017, 0.148, 0.854], [2.632, -0.002, 0.182, 0.845], [2.895, 0.009, 0.116, 0.893], [3.158, -0.023, 0.079, 0.942], [3.421, 0.006, 0.078, 0.899], [3.684, 0.016, 0.059, 0.942], [3.947, 0.014, 0.036, 0.991], [4.211, -0.009, 0.014, 0.988], [4.474, -0.030, 0.036, 0.941], [4.737, 0.004, 0.036, 0.971], [5.000, -0.024, 0.028, 0.985]] data = pd.DataFrame(data, columns=['t', 'ca', 'cb', 'cc']) data_df = data.set_index('t') data_dict = { 'ca': {k: v for (k, v) in zip(data.t, data.ca)}, 'cb': {k: v for (k, v) in zip(data.t, data.cb)}, 'cc': {k: v for (k, v) in zip(data.t, data.cc)} } theta_names = ['k1', 'k2'] self.pest_df = parmest.Estimator(ABC_model, [data_df], theta_names) self.pest_dict = parmest.Estimator(ABC_model, [data_dict], theta_names) # Create an instance of the model self.m_df = ABC_model(data_df) self.m_dict = ABC_model(data_dict)
return (float(data['y']) - m.y) ** 2 model.SecondStageCost = Expression(rule=AllMeasurements) def total_cost_rule(m): return m.FirstStageCost + m.SecondStageCost model.Total_Cost_Objective = Objective(rule=total_cost_rule, sense=minimize) return model if __name__ == "__main__": # ======================================================================= # Parameter estimation without covariance estimate # Only estimate the parameter k[1]. The parameter k[2] will remain fixed # at its initial value theta_names = ['k[1]'] pest = parmest.Estimator(simple_reaction_model, data, theta_names) obj, theta = pest.theta_est() print(obj) print(theta) print() #======================================================================= # Estimate both k1 and k2 and compute the covariance matrix theta_names = ['k'] pest = parmest.Estimator(simple_reaction_model, data, theta_names) obj, theta, cov = pest.theta_est(calc_cov=True) print(obj) print(theta) print(cov)
def main(): # Vars to estimate theta_names = ['k1', 'k2', 'k3'] # Data file_dirname = dirname(abspath(str(__file__))) file_name = abspath(join(file_dirname, 'reactor_data.csv')) data = pd.read_csv(file_name) # Create more data for the example N = 50 df_std = data.std().to_frame().transpose() df_rand = pd.DataFrame(np.random.normal(size=N)) df_sample = data.sample(N, replace=True).reset_index(drop=True) data = df_sample + df_rand.dot(df_std)/10 # Sum of squared error function def SSE(model, data): expr = (float(data['ca']) - model.ca)**2 + \ (float(data['cb']) - model.cb)**2 + \ (float(data['cc']) - model.cc)**2 + \ (float(data['cd']) - model.cd)**2 return expr # Create an instance of the parmest estimator pest = parmest.Estimator(reactor_design_model, data, theta_names, SSE) # Parameter estimation obj, theta = pest.theta_est() print(obj) print(theta) ### Parameter estimation with 'leave-N-out' # Example use case: For each combination of data where one data point is left # out, estimate theta lNo_theta = pest.theta_est_leaveNout(1) print(lNo_theta.head()) parmest.graphics.pairwise_plot(lNo_theta, theta) ### Leave one out/boostrap analysis # Example use case: leave 25 data points out, run 20 bootstrap samples with the # remaining points, determine if the theta estimate using the points left out # is inside or outside an alpha region based on the bootstrap samples, repeat # 5 times. Results are stored as a list of tuples, see API docs for information. lNo = 25 lNo_samples = 5 bootstrap_samples = 20 dist = 'MVN' alphas = [0.7, 0.8, 0.9] results = pest.leaveNout_bootstrap_test(lNo, lNo_samples, bootstrap_samples, dist, alphas, seed=524) # Plot results for a single value of alpha alpha = 0.8 for i in range(lNo_samples): theta_est_N = results[i][1] bootstrap_results = results[i][2] parmest.graphics.pairwise_plot(bootstrap_results, theta_est_N, alpha, ['MVN'], title= 'Alpha: '+ str(alpha) + ', '+ \ str(theta_est_N.loc[0,alpha])) # Extract the percent of points that are within the alpha region r = [results[i][1].loc[0,alpha] for i in range(lNo_samples)] percent_true = sum(r)/len(r) print(percent_true)
def quantify_propagate_uncertainty(model_function, model_uncertain, data, theta_names, obj_function=None, tee=False, diagnostic_mode=False, solver_options=None): """This function calculates error propagation of the objective function and constraints. The parmest uses 'model_function' to estimate uncertain parameters. The uncertain parameters in 'model_uncertain' are fixed with the estimated values. The function 'quantify_propagate_uncertainty' calculates error propagation of objective function and constraints in the 'model_uncertain'. The following terms are used to define the output dimensions: Ncon = number of constraints Nvar = number of variables (Nx + Ntheta) Nx = the number of decision (primal) variables Ntheta = number of uncertain parameters. Parameters ---------- model_function : function A python Function that generates an instance of the Pyomo model using 'data' as the input argument model_uncertain : function or Pyomo ConcreteModel Function is a python/ Function that generates an instance of the Pyomo model data : pandas DataFrame, list of dictionary, or list of json file names Data that is used to build an instance of the Pyomo model and build the objective function theta_names : list of strings List of Var names to estimate obj_function : function, optional Function used to formulate parameter estimation objective, generally sum of squared error between measurements and model variables, by default None tee : bool, optional Indicates that ef solver output should be teed, by default False diagnostic_mode : bool, optional If True, print diagnostics from the solver, by default False solver_options : dict, optional Provides options to the solver (also the name of an attribute), by default None Returns ------- tuple results object containing the all information including - results.obj: float Real number. Objective function value for the given obj_function - results.theta: dict Size Ntheta python dictionary. Estimated parameters - results.theta_names: list Size Ntheta list. Names of parameters - results.cov: numpy.ndarray Ntheta by Ntheta matrix. Covariance of theta - results.gradient_f: numpy.ndarray Length Nvar array. Gradient vector of the objective function with respect to the (decision variables, parameters) at the optimal solution - results.gradient_c: scipy.sparse.csr.csr_matrix Ncon by Nvar size sparse matrix. Gradient vector of the constraints with respect to the (decision variables, parameters) at the optimal solution. - results.dsdp: scipy.sparse.csr.csr_matrix Ntheta by Nvar size sparse matrix. Gradient vector of the (decision variables, parameters) with respect to paramerters (=theta_name). number of rows = len(theta_name), number of columns= len(col) - results.propagation_c: numpy.ndarray Length Ncon array. Error propagation in the constraints, dc/dp*cov_p*dc/dp + (dc/dx*dx/dp)*cov_p*(dc/dx*dx/dp) - results.propagation_f: numpy.float64 Real number. Error propagation in the objective function, df/dp*cov_p*df/dp + (df/dx*dx/dp)*cov_p*(df/dx*dx/dp) - results.col: list Size Nvar. List of variable names. Note that variables names includes both decision variable and uncertain parameter names. The order can be mixed. - results.row: list Size Ncon+1. List of constraints and objective function names Raises ------ TypeError When tee entry is not Boolean TypeError When diagnostic_mode entry is not Boolean TypeError When solver_options entry is not None and a Dictionary Warnings When an element of theta_names includes a space """ if not isinstance(tee, bool): raise TypeError('tee must be boolean.') if not isinstance(diagnostic_mode, bool): raise TypeError('diagnostic_mode must be boolean.') if not solver_options == None: if not isinstance(solver_options, dict): raise TypeError('solver_options must be dictionary.') # Remove all "'" and " " in theta_names theta_names, var_dic, variable_clean = clean_variable_name(theta_names) parmest_class = parmest.Estimator(model_function, data, theta_names, obj_function, tee, diagnostic_mode, solver_options) obj, theta, cov = parmest_class.theta_est(calc_cov=True) # Convert theta keys to the original name # Revert theta_names to be original if variable_clean: theta_out = {} for i in var_dic.keys(): theta_out[var_dic[i]] = theta[i] theta_names = [var_dic[v] for v in theta_names] else: theta_out = theta propagate_results = propagate_uncertainty(model_uncertain, theta, cov, theta_names, tee) Output = namedtuple('Output', [ 'obj', 'theta', 'theta_names', 'cov', 'gradient_f', 'gradient_c', 'dsdp', 'propagation_c', 'propagation_f', 'col', 'row' ]) results = Output(obj, theta_out, theta_names, cov, propagate_results.gradient_f, propagate_results.gradient_c, propagate_results.dsdp, propagate_results.propagation_c, propagate_results.propagation_f, propagate_results.col, propagate_results.row) return results
data = pd.read_excel('reactor_data_multisensor.xlsx') # Sum of squared error function def SSE_multisensor(model, data): expr = ((float(data['ca1']) - model.ca)**2)*(1/3) + \ ((float(data['ca2']) - model.ca)**2)*(1/3) + \ ((float(data['ca3']) - model.ca)**2)*(1/3) + \ (float(data['cb']) - model.cb)**2 + \ ((float(data['cc1']) - model.cc)**2)*(1/2) + \ ((float(data['cc2']) - model.cc)**2)*(1/2) + \ (float(data['cd']) - model.cd)**2 return expr pest = parmest.Estimator(reactor_design_model, data, theta_names, SSE_multisensor) obj, theta = pest.theta_est() print(obj) print(theta) ### Parameter estimation with bootstrap resampling bootstrap_theta = pest.theta_est_bootstrap(50) print(bootstrap_theta.head()) parmest.pairwise_plot(bootstrap_theta) parmest.pairwise_plot(bootstrap_theta, theta, 0.8, ['MVN', 'KDE', 'Rect']) ### Likelihood ratio test k1 = [0.83]
# Data data = pd.DataFrame(data=[[1, 8.3], [2, 10.3], [3, 19.0], [4, 16.0], [5, 15.6], [6, 19.8]], columns=['hour', 'y']) # Sum of squared error function def SSE(model, data): expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index) return expr solver_options = {"max_iter": 6000} # not really needed in this case pest = parmest.Estimator(rooney_biegler_model, data, theta_names, SSE, solver_options) obj, theta = pest.theta_est() print(obj) print(theta) ### Parameter estimation with bootstrap resampling bootstrap_theta = pest.theta_est_bootstrap(50, seed=4581) print(bootstrap_theta.head()) parmest.pairwise_plot(bootstrap_theta, title='Bootstrap theta') parmest.pairwise_plot(bootstrap_theta, theta, 0.8, ['MVN', 'KDE', 'Rect'], title='Bootstrap theta with confidence regions')
# Vars to estimate theta_names = ['k1', 'k2', 'k3'] # Data, includes multiple sensors for ca and cc data = pd.read_excel('reactor_data_timeseries.xlsx') # Group time series data into experiments, return the mean value for sv and caf # Returns a list of dictionaries data_ts = parmest.group_data(data, 'experiment', ['sv', 'caf']) def SSE_timeseries(model, data): expr = 0 for val in data['ca']: expr = expr + ((float(val) - model.ca)**2) * (1 / len(data['ca'])) for val in data['cb']: expr = expr + ((float(val) - model.cb)**2) * (1 / len(data['cb'])) for val in data['cc']: expr = expr + ((float(val) - model.cc)**2) * (1 / len(data['cc'])) for val in data['cd']: expr = expr + ((float(val) - model.cd)**2) * (1 / len(data['cd'])) return expr pest = parmest.Estimator(reactor_design_model, data_ts, theta_names, SSE_timeseries) obj, theta = pest.theta_est() print(obj) print(theta)
# Define sum of squared error objective function for data rec def SSE(model, data): expr = ((float(data['ca']) - model.ca)/float(data_std['ca']))**2 + \ ((float(data['cb']) - model.cb)/float(data_std['cb']))**2 + \ ((float(data['cc']) - model.cc)/float(data_std['cc']))**2 + \ ((float(data['cd']) - model.cd)/float(data_std['cd']))**2 return expr ### Data reconciliation theta_names = [] # no variables to estimate, use initialized values pest = parmest.Estimator(reactor_design_model_for_datarec, data, theta_names, SSE) obj, theta, data_rec = pest.theta_est( return_values=['ca', 'cb', 'cc', 'cd', 'caf']) print(obj) print(theta) parmest.grouped_boxplot(data[['ca', 'cb', 'cc', 'cd']], data_rec[['ca', 'cb', 'cc', 'cd']], group_names=['Data', 'Data Rec']) ### Parameter estimation using reconciled data theta_names = ['k1', 'k2', 'k3'] data_rec['sv'] = data['sv'] pest = parmest.Estimator(reactor_design_model, data_rec, theta_names, SSE)
theta_names = ['asymptote', 'rate_constant'] # Data data = pd.DataFrame(data=[[1, 8.3], [2, 10.3], [3, 19.0], [4, 16.0], [5, 15.6], [6, 19.8]], columns=['hour', 'y']) # Sum of squared error function def SSE(model, data): expr = sum((data.y[i] - model.response_function[data.hour[i]])**2 for i in data.index) return expr pest = parmest.Estimator(rooney_biegler_model, data, theta_names, SSE) obj, theta = pest.theta_est() print(obj) print(theta) ### Parameter estimation with bootstrap resampling bootstrap_theta = pest.theta_est_bootstrap(50, seed=4581) print(bootstrap_theta.head()) parmest.pairwise_plot(bootstrap_theta, theta, 0.8, ['MVN', 'KDE', 'Rect']) ### Likelihood ratio test asym = np.arange(10, 30, 2) rate = np.arange(0, 1.5, 0.1)
# Vars to estimate theta_names = ['k1', 'k2', 'E1', 'E2'] # Data, list of dictionaries data = [] for exp_num in range(10): fname = 'exp'+str(exp_num+1)+'.out' with open(fname,'r') as infile: d = json.load(infile) data.append(d) # Note, the model already includes a 'SecondStageCost' expression # for sum of squared error that will be used in parameter estimation pest = parmest.Estimator(generate_model, data, theta_names) obj, theta = pest.theta_est() print(obj) print(theta) ### Parameter estimation with bootstrap resampling bootstrap_theta = pest.theta_est_bootstrap(50) print(bootstrap_theta.head()) parmest.graphics.pairwise_plot(bootstrap_theta, title='Bootstrap theta estimates') parmest.graphics.pairwise_plot(bootstrap_theta, theta, 0.8, ['MVN', 'KDE', 'Rect'], title='Bootstrap theta with confidence regions') ### Likelihood ratio test