Beispiel #1
0
    def extract_quadratic_coeffs(self, affine_expr, quad_forms):
        """ Assumes quadratic forms all have variable arguments.
            Affine expressions can be anything.
        """
        assert affine_expr.is_dpp()
        # Here we take the problem objective, replace all the SymbolicQuadForm
        # atoms with variables of the same dimensions.
        # We then apply the canonInterface to reduce the "affine head"
        # of the expression tree to a coefficient vector c and constant offset d.
        # Because the expression is parameterized, we extend that to a matrix
        # [c1 c2 ...]
        # [d1 d2 ...]
        # where ci,di are the vector and constant for the ith parameter.
        affine_id_map, affine_offsets, x_length, affine_var_shapes = \
            InverseData.get_var_offsets(affine_expr.variables())
        op_list = [affine_expr.canonical_form[0]]
        param_coeffs = canonInterface.get_problem_matrix(
            op_list, x_length, affine_offsets, self.param_to_size,
            self.param_id_map, affine_expr.size)

        # Iterates over every entry of the parameters vector,
        # and obtains the Pi and qi for that entry i.
        # These are then combined into matrices [P1.flatten(), P2.flatten(), ...]
        # and [q1, q2, ...]
        constant = param_coeffs[-1, :]
        c = param_coeffs[:-1, :].A

        # coeffs stores the P and q for each quad_form,
        # as well as for true variable nodes in the objective.
        coeffs = {}
        # The goal of this loop is to appropriately multiply
        # the matrix P of each quadratic term by the coefficients
        # in param_coeffs. Later we combine all the quadratic terms
        # to form a single matrix P.
        for var in affine_expr.variables():
            # quad_forms maps the ids of the SymbolicQuadForm atoms
            # in the objective to (modified parent node of quad form,
            #                      argument index of quad form,
            #                      quad form atom)
            if var.id in quad_forms:
                var_id = var.id
                orig_id = quad_forms[var_id][2].args[0].id
                var_offset = affine_id_map[var_id][0]
                var_size = affine_id_map[var_id][1]
                c_part = c[var_offset:var_offset + var_size, :]
                if quad_forms[var_id][2].P.value is not None:
                    # Convert to sparse matrix.
                    P = quad_forms[var_id][2].P.value
                    if sp.issparse(P) and not isinstance(P, sp.coo_matrix):
                        P = P.tocoo()
                    else:
                        P = sp.coo_matrix(P)
                    if var_size == 1:
                        c_part = np.ones((P.shape[0], 1)) * c_part
                else:
                    P = sp.eye(var_size, format='coo')
                # We multiply the columns of P, by c_part
                # by operating directly on the data.
                data = P.data[:, None] * c_part[P.col]
                P_tup = (data, (P.row, P.col), P.shape)
                # Conceptually similar to
                # P = P[:, :, None] * c_part[None, :, :]
                if orig_id in coeffs:
                    if 'P' in coeffs[orig_id]:
                        # Concatenation becomes addition when constructing
                        # COO matrix because repeated indices are summed.
                        # Conceptually equivalent to
                        # coeffs[orig_id]['P'] += P_tup
                        acc_data, (acc_row, acc_col), _ = coeffs[orig_id]['P']
                        acc_data = np.concatenate([acc_data, data], axis=0)
                        acc_row = np.concatenate([acc_row, P.row], axis=0)
                        acc_col = np.concatenate([acc_col, P.col], axis=0)
                        P_tup = (acc_data, (acc_row, acc_col), P.shape)
                        coeffs[orig_id]['P'] = P_tup
                    else:
                        coeffs[orig_id]['P'] = P_tup
                else:
                    coeffs[orig_id] = dict()
                    coeffs[orig_id]['P'] = P_tup
                    coeffs[orig_id]['q'] = np.zeros((P.shape[0], c.shape[1]))
            else:
                var_offset = affine_id_map[var.id][0]
                var_size = np.prod(affine_var_shapes[var.id], dtype=int)
                if var.id in coeffs:
                    coeffs[var.id]['q'] += c[var_offset:var_offset +
                                             var_size, :]
                else:
                    coeffs[var.id] = dict()
                    coeffs[var.id]['q'] = c[var_offset:var_offset +
                                            var_size, :]
        return coeffs, constant
    def extract_quadratic_coeffs(self, affine_expr, quad_forms):
        """ Assumes quadratic forms all have variable arguments.
            Affine expressions can be anything.
        """
        assert affine_expr.is_dpp()
        # Here we take the problem objective, replace all the SymbolicQuadForm
        # atoms with variables of the same dimensions.
        # We then apply the canonInterface to reduce the "affine head"
        # of the expression tree to a coefficient vector c and constant offset d.
        # Because the expression is parameterized, we extend that to a matrix
        # [c1 c2 ...]
        # [d1 d2 ...]
        # where ci,di are the vector and constant for the ith parameter.
        affine_id_map, affine_offsets, x_length, affine_var_shapes = \
            InverseData.get_var_offsets(affine_expr.variables())
        op_list = [affine_expr.canonical_form[0]]
        param_coeffs = canonInterface.get_problem_matrix(
            op_list, x_length, affine_offsets, self.param_to_size,
            self.param_id_map, affine_expr.size)

        # TODO vectorize this code.
        # Iterates over every entry of the parameters vector,
        # and obtains the Pi and qi for that entry i.
        # These are then combined into matrices [P1.flatten(), P2.flatten(), ...]
        # and [q1, q2, ...]
        coeff_list = []
        constant = param_coeffs[-1, :]
        for p in range(param_coeffs.shape[1]):
            c = param_coeffs[:-1, p].A.flatten()

            # coeffs stores the P and q for each quad_form,
            # as well as for true variable nodes in the objective.
            coeffs = {}
            # The goal of this loop is to appropriately multiply
            # the matrix P of each quadratic term by the coefficients
            # in param_coeffs. Later we combine all the quadratic terms
            # to form a single matrix P.
            for var in affine_expr.variables():
                # quad_forms maps the ids of the SymbolicQuadForm atoms
                # in the objective to (modified parent node of quad form,
                #                      argument index of quad form,
                #                      quad form atom)
                if var.id in quad_forms:
                    var_id = var.id
                    orig_id = quad_forms[var_id][2].args[0].id
                    var_offset = affine_id_map[var_id][0]
                    var_size = affine_id_map[var_id][1]
                    c_part = c[var_offset:var_offset + var_size]
                    if quad_forms[var_id][2].P.value is not None:
                        P = quad_forms[var_id][2].P.value
                        if c_part.size == 1:
                            P = c_part[0] * P
                        else:
                            P = P @ sp.diags(c_part)
                    else:
                        P = sp.diags(c_part)
                    if orig_id in coeffs:
                        coeffs[orig_id]['P'] += P
                    else:
                        coeffs[orig_id] = dict()
                        coeffs[orig_id]['P'] = P
                        coeffs[orig_id]['q'] = np.zeros(P.shape[0])
                else:
                    var_offset = affine_id_map[var.id][0]
                    var_size = np.prod(affine_var_shapes[var.id], dtype=int)
                    if var.id in coeffs:
                        coeffs[var.id]['P'] += sp.csr_matrix(
                            (var_size, var_size))
                        coeffs[var.id]['q'] += c[var_offset:var_offset +
                                                 var_size]
                    else:
                        coeffs[var.id] = dict()
                        coeffs[var.id]['P'] = sp.csr_matrix(
                            (var_size, var_size))
                        coeffs[var.id]['q'] = c[var_offset:var_offset +
                                                var_size]
            coeff_list.append(coeffs)
        return coeff_list, constant