import numpy as np def transform_constraint_function(nlc): ''' The Python interfaces receives the constraints as lb <= constraint(x) <= ub, but the Fortran backend expects the nonlinear constraints to be constraint(x) <= 0. Thus a conversion is needed. In addition to the conversion, we add a check to ensure that the provided lower/upper bounds have a shape consistent with the output of the constraint function. ''' def newconstraint(x): values = np.atleast_1d(np.array(nlc.fun(x), dtype=np.float64)) # Upgrade the lower/upper bounds to vectors if necessary lb = nlc.lb try: _ = len(lb) except TypeError: lb = np.array([nlc.lb]*len(values), dtype=np.float64) ub = nlc.ub try: _ = len(ub) except TypeError: ub = np.array([nlc.ub]*len(values), dtype=np.float64) # Check the shapes and raise an exception if they do not match if len(values) != len(lb): raise ValueError("The number of elements in the constraint function's output does not match the number of elements in the lower bound.") if len(values) != len(ub): raise ValueError("The number of elements in the constraint function's output does not match the number of elements in the upper bound.") # Combine the upper and lower bounds to transform the function into the form # expected by the Fortran backend. return np.concatenate(([lb_ii - vi for lb_ii, vi in zip(lb, values) if lb_ii > -np.inf], [vi - ub_ii for ub_ii, vi in zip(ub, values) if ub_ii < np.inf], )) return newconstraint def process_nl_constraints(nlcs): functions = [] for nlc in nlcs: fun_i = transform_constraint_function(nlc) functions.append(fun_i) def constraint_function(x): values = np.empty(0, dtype=np.float64) for fun in functions: values = np.concatenate((values, fun(x))) return values return constraint_function