def test_cos(self): m = pe.ConcreteModel() m.x = pe.Var(initialize=2.0) e = pe.cos(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol)
def test_sum(self): m = pe.ConcreteModel() m.x = pe.Var(initialize=2.0) m.y = pe.Var(initialize=3.0) e = 2.0*m.x + 3.0*m.y - m.x*m.y derivs = reverse_ad(e) symbolic = reverse_sd(e) self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.y], pe.value(symbolic[m.y]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol)
def test_nested(self): m = pe.ConcreteModel() m.x = pe.Var(initialize=2) m.y = pe.Var(initialize=3) m.p = pe.Param(initialize=0.5, mutable=True) e = pe.exp(m.x**m.p + 3.2*m.y - 12) derivs = reverse_ad(e) symbolic = reverse_sd(e) self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.y], pe.value(symbolic[m.y]), tol+3) self.assertAlmostEqual(derivs[m.p], pe.value(symbolic[m.p]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol) self.assertAlmostEqual(derivs[m.p], approx_deriv(e, m.p), tol)
def test_expressiondata(self): m = pe.ConcreteModel() m.x = pe.Var(initialize=3) m.e = pe.Expression(expr=m.x * 2) @m.Expression([1, 2]) def e2(m, i): if i == 1: return m.x + 4 else: return m.x ** 2 m.o = pe.Objective(expr=m.e + 1 + m.e2[1] + m.e2[2]) derivs = reverse_ad(m.o.expr) symbolic = reverse_sd(m.o.expr) self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol)
def test_expressiondata(self): m = pe.ConcreteModel() m.x = pe.Var(initialize=3) m.e = pe.Expression(expr=m.x * 2) @m.Expression([1, 2]) def e2(m, i): if i == 1: return m.x + 4 else: return m.x**2 m.o = pe.Objective(expr=m.e + 1 + m.e2[1] + m.e2[2]) derivs = reverse_ad(m.o.expr) symbolic = reverse_sd(m.o.expr) self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol)
import pyomo.environ as pe from pyomo.contrib.derivatives.differentiate import reverse_ad, reverse_sd m = pe.ConcreteModel() m.x = pe.Var(initialize=2) m.y = pe.Var(initialize=3) m.p = pe.Param(initialize=0.5, mutable=True) e = pe.exp(m.x**m.p + 0.1*m.y) derivs = reverse_ad(e) print('dfdx: ', derivs[m.x]) print('dfdy: ', derivs[m.y]) print('dfdp: ', derivs[m.p]) derivs = reverse_sd(e) print('dfdx: ', derivs[m.x]) print('dfdy: ', derivs[m.y]) print('dfdp: ', derivs[m.p])
def add_outer_approximation_cuts(nlp_result, solve_data, config): """Add outer approximation cuts to the linear GDP model.""" with time_code(solve_data.timing, 'OA cut generation'): m = solve_data.linear_GDP GDPopt = m.GDPopt_utils sign_adjust = -1 if solve_data.objective_sense == minimize else 1 # copy values over for var, val in zip(GDPopt.variable_list, nlp_result.var_values): if val is not None and not var.fixed: var.value = val # TODO some kind of special handling if the dual is phenomenally small? config.logger.debug('Adding OA cuts.') counter = 0 if not hasattr(GDPopt, 'jacobians'): GDPopt.jacobians = ComponentMap() for constr, dual_value in zip(GDPopt.constraint_list, nlp_result.dual_values): if dual_value is None or constr.body.polynomial_degree() in (1, 0): continue # Determine if the user pre-specified that OA cuts should not be # generated for the given constraint. parent_block = constr.parent_block() ignore_set = getattr(parent_block, 'GDPopt_ignore_OA', None) config.logger.debug('Ignore_set %s' % ignore_set) if (ignore_set and (constr in ignore_set or constr.parent_component() in ignore_set)): config.logger.debug( 'OA cut addition for %s skipped because it is in ' 'the ignore set.' % constr.name) continue config.logger.debug("Adding OA cut for %s with dual value %s" % (constr.name, dual_value)) # Cache jacobians jacobians = GDPopt.jacobians.get(constr, None) if jacobians is None: constr_vars = list( identify_variables(constr.body, include_fixed=False)) if len(constr_vars) >= 1000: jac_map = reverse_ad(constr.body) jacobians = ComponentMap( (v, jac_map[v]) for v in constr_vars) GDPopt.jacobians[constr] = jacobians else: jac_list = differentiate(constr.body, wrt_list=constr_vars) jacobians = ComponentMap(zip(constr_vars, jac_list)) GDPopt.jacobians[constr] = jacobians # Create a block on which to put outer approximation cuts. oa_utils = parent_block.component('GDPopt_OA') if oa_utils is None: oa_utils = parent_block.GDPopt_OA = Block( doc="Block holding outer approximation cuts " "and associated data.") oa_utils.GDPopt_OA_cuts = ConstraintList() oa_utils.GDPopt_OA_slacks = VarList(bounds=(0, config.max_slack), domain=NonNegativeReals, initialize=0) oa_cuts = oa_utils.GDPopt_OA_cuts slack_var = oa_utils.GDPopt_OA_slacks.add() rhs = value(constr.lower) if constr.has_lb() else value( constr.upper) try: new_oa_cut = (copysign(1, sign_adjust * dual_value) * (value(constr.body) - rhs + sum( value(jacobians[var]) * (var - value(var)) for var in jacobians)) - slack_var <= 0) if new_oa_cut.polynomial_degree() not in (1, 0): for var in jacobians: print(var.name, value(jacobians[var])) oa_cuts.add(expr=new_oa_cut) counter += 1 except ZeroDivisionError: config.logger.warning( "Zero division occured attempting to generate OA cut for constraint %s.\n" "Skipping OA cut generation for this constraint." % (constr.name, )) # Simply continue on to the next constraint. config.logger.info('Added %s OA cuts' % counter)