def _subtract_quad(self, other_quad): # subtract quad quadterms = self._quadterms for oqv, oqk in other_quad.iter_quads(): update_dict_from_item_value(quadterms, oqv, -oqk) # subtract linear part self._linexpr.subtract(other_quad._linexpr)
def new_linexpr_product(self, linexpr, other): if isinstance(other, Var): return self.new_var_product(other, linexpr) elif isinstance(other, MonomialExpr): return self.new_monomial_product(other, linexpr) elif isinstance(other, LinearExpr): cst1 = linexpr.constant cst2 = other.constant fcc = self.term_dict_type() for lv1, lk1 in linexpr.iter_terms(): for lv2, lk2 in other.iter_terms(): update_dict_from_item_value(fcc, VarPair(lv1, lv2), lk1 * lk2) # this is quad qlinexpr = self.new_linear_expr() # add cst2 * linexp1 qlinexpr._add_expr_scaled(expr=linexpr, factor=cst2) # add cst1 * linexpr2 qlinexpr._add_expr_scaled(expr=other, factor=cst1) # and that's it # fix the constant qlinexpr.constant = cst1 * cst2 quad = QuadExpr(self._model, quads=fcc, linexpr=qlinexpr, safe=True) return quad else: self._unexpected_product_error(linexpr, other)
def quad_matrix_sum(self, matrix, lvars, symmetric=False): # assume matrix is a NxN matrix # vars is a N-vector of variables dcc = self._quad_factory.term_dict_type qterms = dcc() gen_rows = self.generate_rows(matrix) for i, mrow in enumerate(gen_rows): vi = lvars[i] for j, k in enumerate(mrow): if k: vj = lvars[j] if i == j: qterms[VarPair(vi)] = k elif symmetric: if i < j: update_dict_from_item_value( qterms, VarPair(vi, vj), 2 * k) elif i > j: continue else: update_dict_from_item_value(qterms, VarPair(vi, vj), k) return self._to_expr(qcc=qterms)
def _sum_with_iter(self, args): sum_of_nums = 0 lcc = self.counter_type() checker = self._checker qcc = None number_validation_fn = checker.get_number_validation_fn() for item in args: if isinstance(item, LinearOperand): for lv, lk in item.iter_terms(): update_dict_from_item_value(lcc, lv, lk) itc = item.get_constant() if itc: sum_of_nums += itc elif is_number(item): sum_of_nums += number_validation_fn(item) if number_validation_fn else item elif isinstance(item, QuadExpr): for lv, lk in item.linear_part.iter_terms(): update_dict_from_item_value(lcc, lv, lk) if qcc is None: qcc = self.counter_type() for qvp, qk in item.iter_quads(): update_dict_from_item_value(qcc, qvp, qk) sum_of_nums += item.get_constant() else: try: expr = item.to_linear_expr() sum_of_nums += expr.get_constant() for dv, k in expr.iter_terms(): update_dict_from_item_value(lcc, dv, k) except AttributeError: self._model.fatal("Model.sum() expects numbers/variables/expressions, got: {0!s}", item) return self._to_expr(qcc, lcc, sum_of_nums)
def _sparse_quad_matrix_sum(self, sp_coef_mat, lvars, symmetric=False): # assume matrix is a NxN matrix # vars is a N-vector of variables dcc = self._quad_factory.term_dict_type qterms = dcc() for e in range(sp_coef_mat.nnz): k = sp_coef_mat.data[e] if k: row = sp_coef_mat.row[e] col = sp_coef_mat.col[e] vi = lvars[row] vj = lvars[col] update_dict_from_item_value(qterms, VarPair(vi, vj), k) return self._to_expr(qcc=qterms)
def _scal_prod_triple_vars(self, coefs, left_terms, right_terms): # INTERNAL # assuming all arguments are iterable. dcc = self.counter_type qcc = dcc() number_validation_fn = self._checker.get_number_validation_fn() if number_validation_fn: for coef, lterm, rterm in izip(coefs, left_terms, right_terms): safe_coef = number_validation_fn( coef) if number_validation_fn else coef update_dict_from_item_value(qcc, VarPair(lterm, rterm), safe_coef) else: for coef, lterm, rterm in izip(coefs, left_terms, right_terms): update_dict_from_item_value(qcc, VarPair(lterm, rterm), coef) return self._to_expr(qcc=qcc)
def _scal_prod(self, terms, coefs): # INTERNAL checker = self._checker total_num = 0 lcc = self.counter_type() qcc = None number_validation_fn = checker.get_number_validation_fn() for item, coef in izip(terms, coefs): if not coef: continue safe_coef = number_validation_fn( coef) if number_validation_fn else coef if isinstance(item, Var): update_dict_from_item_value(lcc, item, safe_coef) elif isinstance(item, AbstractLinearExpr): total_num += safe_coef * item.get_constant() for lv, lk in item.iter_terms(): update_dict_from_item_value(lcc, lv, lk * safe_coef) elif isinstance(item, QuadExpr): if qcc is None: qcc = self.counter_type() for qv, qk in item.iter_quads(): update_dict_from_item_value(qcc, qv, qk * safe_coef) qlin = item.get_linear_part() for v, k in qlin.iter_terms(): update_dict_from_item_value(lcc, v, k * safe_coef) total_num += safe_coef * qlin.constant # --- try conversion --- else: try: e = item.to_linear_expr() total_num += e.get_constant() for dv, k, in e.iter_terms(): update_dict_from_item_value(lcc, dv, k * safe_coef) except AttributeError: self._model.fatal( "scal_prod accepts variables, expressions, numbers, not: {0!s}", item) return self._to_expr(qcc, lcc, total_num)
def _varlist_to_terms(self, var_list): # INTERNAL: converts a sum of vars to a dict, sorting if needed. linear_term_dict_type = self._linear_factory.term_dict_type try: assume_no_dups = len(var_list) == len(set(var_list)) except TypeError: assume_no_dups = False if assume_no_dups: varsum_terms = linear_term_dict_type() linear_terms_setitem = linear_term_dict_type.__setitem__ for v in var_list: linear_terms_setitem(varsum_terms, v, 1) else: # there might be repeated variables. varsum_terms = linear_term_dict_type() for v in var_list: update_dict_from_item_value(varsum_terms, v, 1) return varsum_terms
def _scal_prod_f_gen(self, dvars, coef_fn, var_key_iter): # var_map is a dictionary of variables. # coef_fn is a function accepting dictionary keys lcc_type = self.counter_type lcc = lcc_type() number_validation_fn = self._checker.get_number_validation_fn() if number_validation_fn: for k, dvar in var_key_iter(dvars): fcoeff = coef_fn(k) safe_coeff = number_validation_fn(fcoeff) if safe_coeff: update_dict_from_item_value(lcc, dvar, safe_coeff) else: for k, dvar in var_key_iter(dvars): fcoeff = coef_fn(k) if fcoeff: update_dict_from_item_value(lcc, dvar, fcoeff) return self._to_expr(qcc=None, lcc=lcc)
def _scal_prod_triple(self, coefs, left_terms, right_terms): # INTERNAL accumulated_ct = 0 qcc = self.counter_type() lcc = self.counter_type() number_validation_fn = self._checker.get_number_validation_fn() for coef, lterm, rterm in izip(coefs, left_terms, right_terms): if coef: safe_coef = number_validation_fn( coef) if number_validation_fn else coef lcst = lterm.get_constant() rcst = rterm.get_constant() accumulated_ct += safe_coef * lcst * rcst for lv, lk in lterm.iter_terms(): for rv, rk in rterm.iter_terms(): coef3 = safe_coef * lk * rk update_dict_from_item_value(qcc, VarPair(lv, rv), coef3) if rcst: for lv, lk in lterm.iter_terms(): update_dict_from_item_value(lcc, lv, safe_coef * lk * rcst) if lcst: for rv, rk in rterm.iter_terms(): update_dict_from_item_value(lcc, rv, safe_coef * rk * lcst) return self._to_expr(qcc, lcc, constant=accumulated_ct)
def _scal_prod_f(self, var_map, coef_fn, assume_alldifferent): if assume_alldifferent: return self._scal_prod_f_alldifferent(var_map, coef_fn) else: # var_map is a dictionary of variables. # coef_fn is a function accepting dictionary keys lcc_type = self.counter_type lcc = lcc_type() number_validation_fn = self._checker.get_number_validation_fn() if number_validation_fn: for k, dvar in iteritems(var_map): fcoeff = coef_fn(k) safe_coeff = number_validation_fn(fcoeff) if safe_coeff: update_dict_from_item_value(lcc, dvar, safe_coeff) else: for k, dvar in iteritems(var_map): fcoeff = coef_fn(k) if fcoeff: update_dict_from_item_value(lcc, dvar, fcoeff) return self._to_expr(qcc=None, lcc=lcc)
def _sumsq(self, args): accumulated_ct = 0 number_validation_fn = self._checker.get_number_validation_fn() qcc = self._quad_factory.term_dict_type() lcc = self._linear_factory.term_dict_type() for item in args: if isinstance(item, Var): update_dict_from_item_value(qcc, VarPair(item, item), 1) elif isinstance(item, MonomialExpr): mcoef = item._coef # noinspection PyPep8 mvar = item._dvar update_dict_from_item_value(qcc, VarPair(mvar, mvar), mcoef**2) elif isinstance(item, LinearExpr): cst = item.get_constant() accumulated_ct += cst**2 for lv1, lk1 in item.iter_terms(): for lv2, lk2 in item.iter_terms(): if lv1 is lv2: update_dict_from_item_value( qcc, VarPair(lv1, lv1), lk1 * lk1) elif lv1._index < lv2._index: update_dict_from_item_value( qcc, VarPair(lv1, lv2), 2 * lk1 * lk2) else: pass if cst: update_dict_from_item_value(lcc, lv1, 2 * cst * lk1) elif isinstance(item, _IAdvancedExpr): fvar = item.functional_var update_dict_from_item_value(qcc, VarPair(fvar), 1) elif isinstance(item, ZeroExpr): pass elif is_number(item): safe_item = number_validation_fn( item) if number_validation_fn else item accumulated_ct += safe_item**2 else: self._model.fatal( "Model.sumsq() expects numbers/variables/linear expressions, got: {0!s}", item) return self._to_expr(qcc, lcc, constant=accumulated_ct)
def _sumsq_vars(self, dvars): qcc = self._quad_factory.term_dict_type() for v in dvars: update_dict_from_item_value(qcc, VarPair(v), 1) return self._to_expr(qcc=qcc)