def test_intrinsic_fuctions(self): m = ConcreteModel() m.x = Var() e = differentiate(log(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(m.x**-1.)) e = differentiate(log10(log10(m.x)), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(1./log(10)*m.x**-1.*log(m.x)**-1.))
def test_sqrt_function(self): m = ConcreteModel() m.x = Var() e = differentiate(sqrt(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(0.5 * m.x**-0.5))
def test_intrinsic_functions5(self): m = ConcreteModel() m.x = Var() e = differentiate(log10(log10(m.x)), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(m.x**-1.0 * 1.0/log(10) * log(m.x)**-1.0))
def test_hessian(self): m = ConcreteModel() m.I = RangeSet(4) m.x = Var(m.I) idxMap = {} hessian = [] for i in m.I: for j in m.I: idxMap[i, j] = len(hessian) hessian.append((m.x[i], m.x[j])) expr = m.x[1] + m.x[2] * m.x[3]**2 ans = differentiate(expr, wrt_list=hessian) self.assertEqual(len(ans), len(m.I)**2) for i in m.I: for j in m.I: self.assertEqual(str(ans[idxMap[i, j]]), str(ans[idxMap[j, i]])) # 0 calculated by sympy self.assertEqual(str(ans[idxMap[1, 1]]), "0.0") self.assertEqual(str(ans[idxMap[2, 2]]), "0.0") self.assertEqual(str(ans[idxMap[3, 3]]), "2.0*x[2]") # 0 calculated by bypassing sympy self.assertEqual(str(ans[idxMap[4, 4]]), "0.0") self.assertEqual(str(ans[idxMap[2, 3]]), "2.0*x[3]")
def test_intrinsic_functions2(self): m = ConcreteModel() m.x = Var() e = differentiate(exp(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(exp(m.x)))
def test_hessian(self): m = ConcreteModel() m.I = RangeSet(4) m.x = Var(m.I) idxMap = {} hessian = [] for i in m.I: for j in m.I: idxMap[i,j] = len(hessian) hessian.append((m.x[i], m.x[j])) expr = m.x[1]+m.x[2]*m.x[3]**2 ans = differentiate(expr, wrt_list=hessian) self.assertEqual(len(ans), len(m.I)**2) for i in m.I: for j in m.I: self.assertEqual(str(ans[idxMap[i,j]]), str(ans[idxMap[j,i]])) # 0 calculated by sympy self.assertEqual(str(ans[idxMap[1,1]]), "0.0") self.assertEqual(str(ans[idxMap[2,2]]), "0.0") self.assertEqual(str(ans[idxMap[3,3]]), "2.0*x[2]") # 0 calculated by bypassing sympy self.assertEqual(str(ans[idxMap[4,4]]), "0.0") self.assertEqual(str(ans[idxMap[2,3]]), "2.0*x[3]")
def getHessian(objective): gradient = getGradient(objective) varList = list(identify_variables(objective.expr)) hessian = [ differentiate(gradient[i], wrt_list=varList) for i, v in enumerate(varList) ] return hessian
def test_param(self): m = ConcreteModel() m.x = Var() m.p = Param(mutable=True, initialize=5) e = differentiate(m.p * m.x, wrt=m.x) self.assertIs(type(e), float) self.assertEqual(e, 5.0)
def test_param(self): m = ConcreteModel() m.x = Var() m.p = Param(mutable=True, initialize=5) e = differentiate(m.p*m.x, wrt=m.x) self.assertIs(type(e), float) self.assertEqual(e, 5.0)
def test_single_derivatives4(self): m = ConcreteModel() m.x = Var() m.y = Var() e = differentiate(m.y, wrt=m.x) self.assertIn(type(e), (int,float)) self.assertEqual(e, 0)
def test_single_derivatives5(self): m = ConcreteModel() m.x = Var() m.y = Var() e = differentiate(m.x*m.y, wrt=m.x) self.assertIs(e, m.y) self.assertEqual(s(e), s(m.y))
def test_single_derivatives7(self): m = ConcreteModel() m.x = Var() m.y = Var() e = differentiate(m.x**2/m.y, wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(2.*m.x*m.y**-1.))
def test_single_derivatives(self): m = ConcreteModel() m.x = Var() m.y = Var() e = differentiate(1, wrt=m.x) self.assertIn(type(e), (int,float)) self.assertEqual(e, 0) e = differentiate(m.x, wrt=m.x) self.assertIn(type(e), (int,float)) self.assertEqual(e, 1) e = differentiate(m.x**2, wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(2.*m.x)) e = differentiate(m.y, wrt=m.x) self.assertIn(type(e), (int,float)) self.assertEqual(e, 0) e = differentiate(m.x*m.y, wrt=m.x) self.assertIs(e, m.y) self.assertEqual(s(e), s(m.y)) e = differentiate(m.x**2*m.y, wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(2.*m.y*m.x)) e = differentiate(m.x**2/m.y, wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(2.*m.x*m.y**-1.))
def test_Expression_component(self): m = ConcreteModel() m.s = Set(initialize=['A', 'B']) m.x = Var(m.s, domain=NonNegativeReals) def y_rule(m, s): return m.x[s] * 2 m.y = Expression(m.s, rule=y_rule) expr = 1 - m.y['A'] ** 2 jacs = differentiate(expr, wrt_list=[m.x['A'], m.x['B']]) self.assertEqual(str(jacs[0]), "-8.0*x[A]") self.assertEqual(str(jacs[1]), "0.0") expr = 1 - m.y['B'] ** 2 jacs = differentiate(expr, wrt_list=[m.x['A'], m.x['B']]) self.assertEqual(str(jacs[0]), "0.0") self.assertEqual(str(jacs[1]), "-8.0*x[B]")
def test_Expression_component(self): m = ConcreteModel() m.s = Set(initialize=['A', 'B']) m.x = Var(m.s, domain=NonNegativeReals) def y_rule(m, s): return m.x[s] * 2 m.y = Expression(m.s, rule=y_rule) expr = 1 - m.y['A']**2 jacs = differentiate(expr, wrt_list=[m.x['A'], m.x['B']]) self.assertEqual(str(jacs[0]), "-8.0*x[A]") self.assertEqual(str(jacs[1]), "0.0") expr = 1 - m.y['B']**2 jacs = differentiate(expr, wrt_list=[m.x['A'], m.x['B']]) self.assertEqual(str(jacs[0]), "0.0") self.assertEqual(str(jacs[1]), "-8.0*x[B]")
def calc_jacobians(solve_data, config): """Generate a map of jacobians.""" # Map nonlinear_constraint --> Map( # variable --> jacobian of constraint wrt. variable) solve_data.jacobians = ComponentMap() for c in solve_data.mip.MindtPy_utils.constraint_list: if c.body.polynomial_degree() in (1, 0): continue # skip linear constraints vars_in_constr = list(EXPR.identify_variables(c.body)) jac_list = differentiate(c.body, wrt_list=vars_in_constr) solve_data.jacobians[c] = ComponentMap( (var, jac_wrt_var) for var, jac_wrt_var in zip(vars_in_constr, jac_list))
def test_jacobian(self): m = ConcreteModel() m.I = RangeSet(4) m.x = Var(m.I) idxMap = {} jacs = [] for i in m.I: idxMap[i] = len(jacs) jacs.append(m.x[i]) expr = m.x[1]+m.x[2]*m.x[3]**2 ans = differentiate(expr, wrt_list=jacs) self.assertEqual(len(ans), len(m.I)) self.assertEqual(str(ans[0]), "1.0") self.assertEqual(str(ans[1]), "x[3]**2.0") self.assertEqual(str(ans[2]), "2.0*x[2]*x[3]") # 0 calculated by bypassing sympy self.assertEqual(str(ans[3]), "0.0")
def test_jacobian(self): m = ConcreteModel() m.I = RangeSet(4) m.x = Var(m.I) idxMap = {} jacs = [] for i in m.I: idxMap[i] = len(jacs) jacs.append(m.x[i]) expr = m.x[1] + m.x[2] * m.x[3]**2 ans = differentiate(expr, wrt_list=jacs) self.assertEqual(len(ans), len(m.I)) self.assertEqual(str(ans[0]), "1.0") self.assertEqual(str(ans[1]), "x[3]**2.0") self.assertEqual(str(ans[2]), "2.0*x[2]*x[3]") # 0 calculated by bypassing sympy self.assertEqual(str(ans[3]), "0.0")
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)) 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) oa_cuts.add(expr=copysign(1, sign_adjust * dual_value) * (value(constr.body) - rhs + sum( value(jacobians[var]) * (var - value(var)) for var in jacobians)) - slack_var <= 0) counter += 1 config.logger.info('Added %s OA cuts' % counter)
def getGradient(objective): varList = list(identify_variables(objective.expr)) gradient = differentiate(objective.expr, wrt_list=varList) return gradient
def calculate_variable_from_constraint(variable, constraint, eps=1e-8, iterlim=1000, linesearch=True): """Calculate the variable value given a specified equality constraint This function calculates the value of the specified variable necessary to make the provided equality constraint feasible (assuming any other variables values are fixed). The method first attempts to solve for the variable value assuming it appears linearly in the constraint. If that doesn't converge the constraint residual, it falls back on Newton's method using exact (symbolic) derivatives. Parameters: ----------- variable: `pyomo.core.base.var._VarData` The variable to solve for constraint: `pyomo.core.base.constraint._ConstraintData` The equality constraint to use to solve for the variable value eps: `float` The tolerance to use to determine equality [default=1e-8]. iterlim: `int` The maximum number of iterations if this method has to fall back on using Newton's method. Raises RuntimeError on iteration limit [default=1000] linesearch: `bool` Decides whether or not to use the linesearch (recommended). [default=True] Returns: -------- None Note: this is an unconstrained solver and is NOT guaranteed to respect the variable bounds. """ upper = value(constraint.upper) if value(constraint.lower) != upper: raise ValueError("Constraint must be an equality constraint") if variable.value is None: if variable.lb is None: if variable.ub is None: # no variable values, and no lower or upper bound - set # initial value to 0.0 variable.set_value(0) else: # no variable value or lower bound - set to 0 or upper # bound whichever is lower variable.set_value(min(0, variable.ub)) elif variable.ub is None: # no variable value or upper bound - set to 0 or lower # bound, whichever is higher variable.set_value(max(0, variable.lb)) else: # we have upper and lower bounds if variable.lb <= 0 and variable.ub >= 0: # set the initial value to 0 if bounds bracket 0 variable.set_value(0) else: # set the initial value to the midpoint of the bounds variable.set_value((variable.lb + variable.ub) / 2.0) # store the initial value to use later if necessary orig_initial_value = variable.value # solve the common case where variable is linear with coefficient of 1.0 x1 = value(variable) residual_1 = value(constraint.body) variable.set_value(x1 - (residual_1 - upper)) residual_2 = value(constraint.body) # if the variable appears linearly with a coefficient of 1, then we # are done if abs(residual_2 - upper) < eps: return # Assume the variable appears linearly and calculate the coefficient x2 = value(variable) slope = float(residual_1 - residual_2) / (x1 - x2) intercept = (residual_1 - upper) - slope * x1 if slope: variable.set_value(-intercept / slope) if abs(value(constraint.body) - upper) < eps: return # Variable appears nonlinearly; solve using Newton's method variable.set_value(orig_initial_value) # restore initial value expr = constraint.body - constraint.upper expr_deriv = differentiate(expr, wrt=variable) if type(expr_deriv) in native_numeric_types and expr_deriv == 0: raise ValueError("Variable derivative == 0, cannot solve for variable") if abs(value(expr_deriv)) < 1e-12: raise RuntimeError( 'Initial value for variable results in a derivative value that is ' 'very close to zero.\n\tPlease provide a different initial guess.') iter_left = iterlim while abs(value(expr)) > eps and iter_left: iter_left -= 1 if not iter_left: raise RuntimeError( "Iteration limit (%s) reached; remaining residual = %s" % (iterlim, value(expr))) # compute step xk = value(variable) fk = value(expr) fpk = value(expr_deriv) if abs(fpk) < 1e-12: raise RuntimeError( "Newton's method encountered a derivative that was too " "close to zero.\n\tPlease provide a different initial guess " "or enable the linesearch if you have not.") pk = -fk / fpk alpha = 1.0 xkp1 = xk + alpha * pk variable.set_value(xkp1) # perform line search if linesearch: c1 = 0.999 # ensure sufficient progress while alpha > 1e-8: # check if the value at xkp1 has sufficient reduction in # the residual fkp1 = value(expr) if fkp1**2 < c1 * fk**2: # found an alpha value with sufficient reduction # continue to the next step break alpha /= 2.0 xkp1 = xk + alpha * pk variable.set_value(xkp1) if alpha <= 1e-8: raise RuntimeError( "Linesearch iteration limit reached; remaining " "residual = %s." % (value(expr), ))
def test_trig_fuctions(self): m = ConcreteModel() m.x = Var() e = differentiate(sin(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(cos(m.x))) e = differentiate(cos(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(-1.0*sin(m.x))) e = differentiate(tan(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(1.+tan(m.x)**2.)) e = differentiate(sinh(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(cosh(m.x))) e = differentiate(cosh(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(sinh(m.x))) e = differentiate(tanh(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(1.-tanh(m.x)**2.)) e = differentiate(asin(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s((1.-m.x**2.)**-0.5)) e = differentiate(acos(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s(-1.*(1.-m.x**2.)**-0.5)) e = differentiate(atan(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s((1.+m.x**2.)**-1.)) e = differentiate(asinh(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s((1.+m.x**2)**-.5)) e = differentiate(acosh(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s((-1.+m.x**2.)**-.5)) e = differentiate(atanh(m.x), wrt=m.x) self.assertTrue(e.is_expression()) self.assertEqual(s(e), s((1.-m.x**2.)**-1.))
def test_intrinsic_functions3(self): m = ConcreteModel() m.x = Var() e = differentiate(exp(2 * m.x), wrt=m.x) self.assertEqual(s(e), s(2. * exp(2. * m.x)))
def test_trig_fuctions(self): m = ConcreteModel() m.x = Var() e = differentiate(sin(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(cos(m.x))) e = differentiate(cos(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(-1.0*sin(m.x))) e = differentiate(tan(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(1.+tan(m.x)**2.)) e = differentiate(sinh(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(cosh(m.x))) e = differentiate(cosh(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(sinh(m.x))) e = differentiate(tanh(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(1.0-tanh(m.x)**2.0)) e = differentiate(asin(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s((1.0 + (-1.0)*m.x**2.)**-0.5)) e = differentiate(acos(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s(-1.*(1.+ (-1.0)*m.x**2.)**-0.5)) e = differentiate(atan(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s((1.+m.x**2.)**-1.)) e = differentiate(asinh(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s((1.+m.x**2)**-.5)) e = differentiate(acosh(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s((-1.+m.x**2.)**-.5)) e = differentiate(atanh(m.x), wrt=m.x) self.assertTrue(e.is_expression_type()) self.assertEqual(s(e), s((1.+(-1.0)*m.x**2.)**-1.))
m.c3 = Constraint(m.idx, m.t, rule=idxConsidxVar) def idxConsidxVar(m, i, t): return m.x_idx[i, t]**2 == 0 m.c4 = Constraint(m.idx, m.t, rule=idxConsidxVar) #discretizer = TransformationFactory('dae.collocation') #discretizer.apply_to(m, nfe=8, ncp=4) ### To obtain derivatives wrt Objective function print("Jacobian of Objective function:") varList = list(EXPR.identify_variables(m.Obj.expr)) FrstDerivs = differentiate(m.Obj.expr, wrt_list=varList) [ print(' derivative wrt', varList[idx], ':', item) for idx, item in enumerate(FrstDerivs) ] ### To obtain derivatives wrt Constraint 1, a simple un-indexed constraint print("Jacobian of c1 (Simple constraint):") varList = list(EXPR.identify_variables(m.c1.body)) FrstDerivs = differentiate(m.c1.body, wrt_list=varList) [ print(' derivative wrt', varList[idx], ':', item) for idx, item in enumerate(FrstDerivs) ] ### To obtain derivatives wrt Constraint 2, an indexed constraint
def calculate_variable_from_constraint(variable, constraint, eps=1e-8, iterlim=1000, linesearch=True, alpha_min=1e-8): """Calculate the variable value given a specified equality constraint This function calculates the value of the specified variable necessary to make the provided equality constraint feasible (assuming any other variables values are fixed). The method first attempts to solve for the variable value assuming it appears linearly in the constraint. If that doesn't converge the constraint residual, it falls back on Newton's method using exact (symbolic) derivatives. Parameters: ----------- variable: `pyomo.core.base.var._VarData` The variable to solve for constraint: `pyomo.core.base.constraint._ConstraintData` The equality constraint to use to solve for the variable value eps: `float` The tolerance to use to determine equality [default=1e-8]. iterlim: `int` The maximum number of iterations if this method has to fall back on using Newton's method. Raises RuntimeError on iteration limit [default=1000] linesearch: `bool` Decides whether or not to use the linesearch (recommended). [default=True] alpha_min: `float` The minimum fractional step to use in the linesearch [default=1e-8]. Returns: -------- None Note: this is an unconstrained solver and is NOT guaranteed to respect the variable bounds. """ upper = value(constraint.upper) if value(constraint.lower) != upper: raise ValueError("Constraint must be an equality constraint") if variable.value is None: if variable.lb is None: if variable.ub is None: # no variable values, and no lower or upper bound - set # initial value to 0.0 variable.set_value(0) else: # no variable value or lower bound - set to 0 or upper # bound whichever is lower variable.set_value(min(0, variable.ub)) elif variable.ub is None: # no variable value or upper bound - set to 0 or lower # bound, whichever is higher variable.set_value(max(0, variable.lb)) else: # we have upper and lower bounds if variable.lb <= 0 and variable.ub >= 0: # set the initial value to 0 if bounds bracket 0 variable.set_value(0) else: # set the initial value to the midpoint of the bounds variable.set_value((variable.lb + variable.ub) / 2.0) # store the initial value to use later if necessary orig_initial_value = variable.value # solve the common case where variable is linear with coefficient of 1.0 x1 = value(variable) # Note: both the direct (linear) calculation and Newton's method # below rely on a numerically feasible initial starting point. # While we have strategies for dealing with hitting numerically # invalid (e.g., sqrt(-1)) conditions below, if the initial point is # not valid, we will allow that exception to propagate up try: residual_1 = value(constraint.body) except: logger.error( "Encountered an error evaluating the expression at the " "initial guess.\n\tPlease provide a different initial guess.") raise variable.set_value(x1 - (residual_1 - upper)) residual_2 = value(constraint.body, exception=False) # If we encounter an error while evaluating the expression at the # linear intercept calculated assuming the derivative was 1. This # is most commonly due to nonlinear expressions (like sqrt()) # becoming invalid/complex. We will skip the rest of the # "shortcuts" that assume the expression is linear and move directly # to using Newton's method. if residual_2 is not None and type(residual_2) is not complex: # if the variable appears linearly with a coefficient of 1, then we # are done if abs(residual_2 - upper) < eps: return # Assume the variable appears linearly and calculate the coefficient x2 = value(variable) slope = float(residual_1 - residual_2) / (x1 - x2) intercept = (residual_1 - upper) - slope * x1 if slope: variable.set_value(-intercept / slope) body_val = value(constraint.body, exception=False) if body_val is not None and abs(body_val - upper) < eps: return # Variable appears nonlinearly; solve using Newton's method variable.set_value(orig_initial_value) # restore initial value expr = constraint.body - constraint.upper expr_deriv = differentiate(expr, wrt=variable) if type(expr_deriv) in native_numeric_types and expr_deriv == 0: raise ValueError("Variable derivative == 0, cannot solve for variable") if abs(value(expr_deriv)) < 1e-12: raise RuntimeError( 'Initial value for variable results in a derivative value that is ' 'very close to zero.\n\tPlease provide a different initial guess.') iter_left = iterlim fk = residual_1 - upper while abs(fk) > eps and iter_left: iter_left -= 1 if not iter_left: raise RuntimeError( "Iteration limit (%s) reached; remaining residual = %s" % (iterlim, value(expr))) # compute step xk = value(variable) try: fk = value(expr) if type(fk) is complex: raise ValueError( "Complex numbers are not allowed in Newton's method.") except: # We hit numerical problems with the last step (possible if # the line search is turned off) logger.error( "Newton's method encountered an error evaluating the " "expression.\n\tPlease provide a different initial guess " "or enable the linesearch if you have not.") raise fpk = value(expr_deriv) if abs(fpk) < 1e-12: raise RuntimeError( "Newton's method encountered a derivative that was too " "close to zero.\n\tPlease provide a different initial guess " "or enable the linesearch if you have not.") pk = -fk / fpk alpha = 1.0 xkp1 = xk + alpha * pk variable.set_value(xkp1) # perform line search if linesearch: c1 = 0.999 # ensure sufficient progress while alpha > alpha_min: # check if the value at xkp1 has sufficient reduction in # the residual fkp1 = value(expr, exception=False) # HACK for Python3 support, pending resolution of #879 # Issue #879 also pertains to other checks for "complex" # in this method. if type(fkp1) is complex: # We cannot perform computations on complex numbers fkp1 = None if fkp1 is not None and fkp1**2 < c1 * fk**2: # found an alpha value with sufficient reduction # continue to the next step fk = fkp1 break alpha /= 2.0 xkp1 = xk + alpha * pk variable.set_value(xkp1) if alpha <= alpha_min: residual = value(expr, exception=False) if residual is None or type(residual) is complex: residual = "{function evaluation error}" raise RuntimeError( "Linesearch iteration limit reached; remaining " "residual = %s." % (residual, ))
def add_outer_approximation_cuts(nlp_result, solve_data, config): """Add outer approximation cuts to the linear GDP model.""" m = solve_data.linear_GDP GDPopt = m.GDPopt_utils sign_adjust = -1 if GDPopt.objective.sense == minimize else 1 # copy values over for var, val in zip(GDPopt.working_var_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.') nonlinear_constraints = ComponentSet(GDPopt.working_nonlinear_constraints) counter = 0 for constr, dual_value in zip(GDPopt.working_constraints_list, nlp_result.dual_values): if dual_value is None or constr not in nonlinear_constraints: 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)) # TODO make this more efficient by not having to use differentiate() # at each iteration. constr_vars = list(EXPR.identify_variables(constr.body)) jac_list = differentiate(constr.body, wrt_list=constr_vars) jacobians = ComponentMap(zip(constr_vars, jac_list)) # 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() oa_cuts.add(expr=copysign(1, sign_adjust * dual_value) * (value(constr.body) + sum( value(jacobians[var]) * (var - value(var)) for var in constr_vars)) + slack_var <= 0) counter += 1 config.logger.info('Added %s OA cuts' % counter)
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)) 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) oa_cuts.add( expr=copysign(1, sign_adjust * dual_value) * ( value(constr.body) - rhs + sum( value(jacobians[var]) * (var - value(var)) for var in jacobians)) - slack_var <= 0) counter += 1 config.logger.info('Added %s OA cuts' % counter)