def main(): x = Variable(1) y = Variable(1) constraints = [x >= 0, y >= 0, x+y == 1] objective = Maximize(square(x) + square(y)) problem = Problem(objective, constraints) print("problem is DCP:", problem.is_dcp()) print("problem is DCCP:", is_dccp(problem)) problem.solve(method='dccp') print('solution (x,y): ', x.value, y.value)
def fit(self, X, y, x_sensitive, **cons_params): """ X: n x d array y: n length vector x_sensitive: n length vector cons_params will be a dictionary cons_params["tau"], cons_params["mu"] and cons_params["EPS"] are the solver related parameters. Check DCCP documentation for details cons_params["cons_type"] specified which type of constraint to apply - cons_type = -1: No constraint - cons_type = 0: Parity - cons_type = 1: Preferred impact - cons_type = 2: Preferred treatment - cons_type = 3: Preferred both cons_params["s_val_to_cons_sum"]: The ramp approximation -- only needed for cons_type 1 and 3 """ n, d = X.shape group_labels = set(x_sensitive) intercept_idx = INTERCEPT_IDX coefficient_idx = np.arange(d) coefficient_idx = coefficient_idx[coefficient_idx != intercept_idx] self._group_labels = group_labels self._intercept_idx = intercept_idx self._coefficient_idx = coefficient_idx self._n = n self._d = d if self.train_multiple: if isinstance(self.lam, float): self.lam = {z: float(self.lam) for z in group_labels} assert isinstance(self.lam, dict) assert group_labels == self.lam.keys() assert isinstance(cons_params, dict) cons_type = cons_params.get('cons_type', self.CONSTRAINT_NONE) assert cons_type in CoupledRiskMinimizer.VALID_CONSTRAINTS if self.loss_function == CoupledRiskMinimizer.LOSS_LOGISTIC: solver_settings = { 'method': 'dccp', 'verbose': cons_params.get('print_flag', False), 'max_iters': cons_params.get('max_iters', 100), # for CVXPY convex solver 'max_iter': cons_params.get( 'max_iter', 50 ), # for the dccp. notice that DCCP hauristic runs the convex program iteratively until arriving at the solution 'mu': cons_params.get('MU', self.MU), 'tau': cons_params.get('TAU', self.TAU), 'tau_max': 1e10, 'feastol': cons_params.get('EPS', self.EPS), 'abstol': cons_params.get('EPS', self.EPS), 'reltol': cons_params.get('EPS', self.EPS), 'feastol_inacc': cons_params.get('EPS', self.EPS), 'abstol_inacc': cons_params.get('EPS', self.EPS), 'reltol_inacc': cons_params.get('EPS', self.EPS), } else: solver_settings = {} ### setup optimization problem obj = 0 np.random.seed(self.random_state ) # set the seed before initializing the values of w if self.train_multiple: w = {} for k in group_labels: idx = x_sensitive == k # setup coefficients and initialize as uniform distribution over [0,1] w[k] = cp.Variable(d) w[k].value = np.random.rand(d) # first term in w is the intercept, so no need to regularize that obj += cp.sum_squares(w[k][coefficient_idx]) * self.lam[k] # setup X_k, y_k = X[idx], y[idx] if self.sparse_formulation: XY = np.concatenate((X_k, y_k[:, np.newaxis]), axis=1) UY, counts = np.unique(XY, return_counts=True, axis=0) pos_idx = np.greater(UY[:, -1], 0) neg_idx = np.logical_not(pos_idx) U = UY[:, 0:d] obj_weights_pos = counts[pos_idx] / float(n) Z_pos = -U[pos_idx, :] obj_weights_neg = counts[neg_idx] / float(n) Z_neg = U[neg_idx, :] if self.loss_function == CoupledRiskMinimizer.LOSS_LOGISTIC: obj += cp.sum( cp.multiply(obj_weights_pos, cp.logistic(Z_pos * w[k]))) obj += cp.sum( cp.multiply(obj_weights_neg, cp.logistic(Z_neg * w[k]))) elif self.loss_function == CoupledRiskMinimizer.LOSS_SVM: obj += cp.sum( cp.multiply(obj_weights_pos, cp.pos(1.0 - Z_pos * w[k]))) obj += cp.sum( cp.multiply(obj_weights_neg, cp.pos(1.0 + Z_neg * w[k]))) else: if self.loss_function == CoupledRiskMinimizer.LOSS_LOGISTIC: obj += cp.sum( cp.logistic(cp.multiply(-y_k, X_k * w[k]))) / float(n) elif self.loss_function == CoupledRiskMinimizer.LOSS_SVM: obj += cp.sum( cp.pos(1.0 - cp.multiply(y_k, X_k * w[k]))) / float(n) # notice that we are dividing by the length of the whole dataset, and not just of this sensitive group. # this way, the group that has more people contributes more to the loss else: w = cp.Variable(d) # this is the weight vector w.value = np.random.rand(d) # regularizer -- first term in w is the intercept, so no need to regularize that obj += cp.sum_squares(w[1:]) * self.lam if self.loss_function == self.LOSS_LOGISTIC: obj += cp.sum(cp.logistic(cp.multiply(-y, X * w))) / float(n) elif self.loss_function == self.LOSS_SVM: obj += cp.sum(cp.maximum( 0.0, 1.0 - cp.multiply(y, X * w))) / float(n) constraints = [] if cons_type in self.VALID_PREFERED_CONSTRAINTS: constraints = self._stamp_preference_constraints( X, x_sensitive, w, cons_type, cons_params.get('s_val_to_cons_sum')) elif cons_type == self.CONSTRAINT_PARITY: constraints = self._stamp_disparate_impact_constraint( X, y, x_sensitive, w, cov_thresh=np.abs(0.0)) prob = cp.Problem(cp.Minimize(obj), constraints) ### solve optimization problem if is_dccp(prob): print("solving disciplined convex-concave program (DCCP)") else: assert prob.is_dcp() print("solving disciplined convex program (DCP)") prob.solve(**solver_settings) print("solver stopped (status: %r)" % prob.status) if prob.status != cp.OPTIMAL: warnings.warn('solver did not recover optimal solution') # check that the fairness constraint is satisfied for f_c in constraints: if not f_c.value: warnings.warn("fairness constraint %r not satisfied!" % f_c) self._prob = prob # store results if self.train_multiple: coefs = {k: np.array(w[k].value).flatten() for k in group_labels} self.w = {k: np.array(v) for k, v in coefs.items()} else: coefs = np.array(w.value).flatten() self.w = np.array(coefs) return coefs
__author__ = 'Xinyue' from cvxpy import * import dccp from dccp.problem import is_dccp x = Variable(2) y = Variable(2) myprob = Problem(Maximize(norm(x-y,2)), [0<=x, x<=1, 0<=y, y<=1]) #myprob = Problem(Minimize(log(x)), [x**2 >= 5]) print "problem is DCP:", myprob.is_dcp() # false print "problem is DCCP:", is_dccp(myprob) # true result = myprob.solve(method = 'dccp', solver = 'SCS', use_indirect = False, warm_start = True) print "========================" print "x =", x.value print "y =", y.value print "cost value =", result[0] print myprob.status
__author__ = 'Xinyue' from cvxpy import * import dccp from dccp.problem import is_dccp import matplotlib.pyplot as plt x = Variable(2) y = Variable(2) myprob = Problem(Maximize(norm(x-y,2)), [0<=x, x<=1, 0<=y, y<=1]) #myprob = Problem(Minimize(log(x)), [x**2 >= 5]) print "problem is DCP:", myprob.is_dcp() # false print "problem is DCCP:", is_dccp(myprob) # true result = myprob.solve(method = 'dccp') print "========================" print "x =", x.value print "y =", y.value print "cost value =", result[0]