def init_matchable(self):
        """initialize the property matchable for each edge in the graph. matchable = True means the edge can be included
        in any matching (i.e., it's in at least one cycle or chain)"""

        # initialize all edges to be not-matchable
        for e in self.all_edge_list:
            e.matchable = False

        cycles = self.graph.find_cycles(self.cycle_cap)
        cycle_list = []
        # all edges in a cycle are matchable
        for c in cycles:
            c_obj = Cycle(c)
            c_obj.add_edges(self.graph.es)
            cycle_list.append(c_obj)
            for e in c_obj.edges:
                e.matchable = True

        # all NDDEdges are matchable
        for n in self.altruists:
            for e in n.edges:
                e.matchable = True

        # all pair-pair edges that are within chain distance are matchable
        dists_from_ndd = kidney_utils.get_dist_from_nearest_ndd(
            self.graph, self.altruists
        )
        for e in self.all_edge_list:
            if dists_from_ndd[e.src.id] <= self.chain_cap - 1:
                e.matchable = True

        if self.logger is not None:
            self.logger.info(
                f"matchable edges: {len([e for e in self.all_edge_list if e.matchable])} (out of {len(self.all_edge_list)} total)"
            )
Example #2
0
def add_chain_vars_and_constraints(digraph, ndds, max_chain, m, vtx_to_vars, store_edge_positions=False):
    """Add the IP variables and constraints for chains in PICEF and HPIEF'.

    Args:
        ndds: a list of NDDs in the instance
        max_chain: the chain cap
        m: The Gurobi model
        vtx_to_vars: A list such that for each Vertex v in the Digraph,
            vtx_to_vars[v.id] will contain the Gurobi variables representing
            edges pointing to v.
        store_edge_positions: if this is True, then an attribute grb_edge_positions
            will be added to edges that have associated Gurobi variables.
            edge.grb_edge_positions[i] will indicate the position of the edge respresented
            by edge.grb_vars[i]. (default: False)
    """

    if max_chain > 0:
        for v in digraph.vs:
            v.grb_vars_in = [[] for i in range(max_chain - 1)]
            v.grb_vars_out = [[] for i in range(max_chain - 1)]

        for ndd in ndds:
            ndd_edge_vars = []
            for e in ndd.edges:
                edge_var = m.addVar(vtype=GRB.BINARY)
                e.edge_var = edge_var
                ndd_edge_vars.append(edge_var)
                vtx_to_vars[e.target_v.id].append(edge_var)
                if max_chain > 1:
                    e.target_v.grb_vars_in[0].append(edge_var)
            m.update()
            m.addConstr(quicksum(ndd_edge_vars) <= 1)

        dists_from_ndd = kidney_utils.get_dist_from_nearest_ndd(digraph, ndds)

        # Add pair->pair edge variables, indexed by position in chain
        for e in digraph.es:
            e.grb_vars = []
            if store_edge_positions:
                e.grb_var_positions = []
            for i in range(max_chain - 1):
                if dists_from_ndd[e.src.id] <= i + 1:
                    edge_var = m.addVar(vtype=GRB.BINARY)
                    e.grb_vars.append(edge_var)
                    if store_edge_positions:
                        e.grb_var_positions.append(i + 1)
                    vtx_to_vars[e.tgt.id].append(edge_var)
                    e.src.grb_vars_out[i].append(edge_var)
                    if i < max_chain - 2:
                        e.tgt.grb_vars_in[i + 1].append(edge_var)

        m.update()

        # At each chain position, sum of edges into a vertex must be >= sum of edges out
        for i in range(max_chain - 1):
            for v in digraph.vs:
                m.addConstr(quicksum(v.grb_vars_in[i]) >= quicksum(v.grb_vars_out[i]))
def add_chain_vars_and_constraints(digraph, ndds, max_chain, m, vtx_to_vars,
                                   store_edge_positions=False):
    """Add the IP variables and constraints for chains in PICEF and HPIEF'.

    Args:
        ndds: a list of NDDs in the instance
        max_chain: the chain cap
        m: The Gurobi model
        vtx_to_vars: A list such that for each Vertex v in the Digraph,
            vtx_to_vars[v.id] will contain the Gurobi variables representing
            edges pointing to v.
        store_edge_positions: if this is True, then an attribute grb_edge_positions
            will be added to edges that have associated Gurobi variables.
            edge.grb_edge_positions[i] will indicate the position of the edge respresented
            by edge.grb_vars[i]. (default: False)
    """

    if max_chain > 0:
        for v in digraph.vs:
            v.grb_vars_in  = [[] for i in range(max_chain-1)]
            v.grb_vars_out = [[] for i in range(max_chain-1)]

        for ndd in ndds:
            ndd_edge_vars = []
            for e in ndd.edges:
                edge_var = m.addVar(vtype=GRB.BINARY)
                e.edge_var = edge_var
                ndd_edge_vars.append(edge_var)
                vtx_to_vars[e.target_v.id].append(edge_var)
                if max_chain>1: e.target_v.grb_vars_in[0].append(edge_var)
            m.update()
            m.addConstr(quicksum(ndd_edge_vars) <= 1)

        dists_from_ndd = kidney_utils.get_dist_from_nearest_ndd(digraph, ndds)

        # Add pair->pair edge variables, indexed by position in chain
        for e in digraph.es:
            e.grb_vars = []
            if store_edge_positions:
                e.grb_var_positions = []
            for i in range(max_chain-1):
                if dists_from_ndd[e.src.id] <= i+1:
                    edge_var = m.addVar(vtype=GRB.BINARY)
                    e.grb_vars.append(edge_var)
                    if store_edge_positions:
                        e.grb_var_positions.append(i+1)
                    vtx_to_vars[e.tgt.id].append(edge_var)
                    e.src.grb_vars_out[i].append(edge_var)
                    if i < max_chain-2:
                        e.tgt.grb_vars_in[i+1].append(edge_var)

        m.update()

        # At each chain position, sum of edges into a vertex must be >= sum of edges out
        for i in range(max_chain-1):
            for v in digraph.vs:
                m.addConstr(quicksum(v.grb_vars_in[i]) >= quicksum(v.grb_vars_out[i]))
Example #4
0
def add_chain_vars_and_constraints(
    digraph,
    ndds,
    use_chains,
    max_chain,
    m,
    vtx_to_vars,
    store_edge_positions=False,
    add_o_vars=False,
    num_o_vars=0,
    check_edge_success=False,
):
    """Add the IP variables and constraints for chains in PICEF and HPIEF'.

    Args:
        ndds: a list of NDDs in the instance
        use_chains: boolean: True if chains should be used
        max_chain: the chain cap
        m: The Gurobi model
        vtx_to_vars: A list such that for each Vertex v in the Digraph,
            vtx_to_vars[v.id] will contain the Gurobi variables representing
            edges pointing to v.
        store_edge_positions: if this is True, then an attribute grb_var_positions
            will be added to edges that have associated Gurobi variables.
            edge.grb_var_positions[i] will indicate the position of the edge respresented
            by edge.grb_vars[i]. (default: False)
        add_o_vars: (bool). specific to the heterogeneous edge success probability formulation. add o_vars and big_o_vars
            properties to each edge, and add big_o_vars_in and big_o_vars_out properties to each vertex (analagous to
            v.edges_in and v.edges_out. this requires that e.success_prob is set for each edge
        num_o_vars: (int). if set to >1, then create this number of o and big_o variables, for the DRO-SAA formulation.
            if this is set, each edge needs the property e.realizations - a binary vector of length num_o_vars
    """

    if use_chains:  # max_chain > 0:
        for v in digraph.vs:
            v.grb_vars_in = [[] for i in range(max_chain - 1)]
            v.grb_vars_out = [[] for i in range(max_chain - 1)]
            v.edges_in = [[] for i in range(max_chain - 1)]
            v.edges_out = [[] for i in range(max_chain - 1)]
            if add_o_vars:
                if num_o_vars < 2:
                    v.big_o_vars_in = [[] for i in range(max_chain - 1)]
                    v.big_o_vars_out = [[] for i in range(max_chain - 1)]
                else:
                    # v.big_o_vars[i][n] is the n^th instance of big_o_vars for the i^th edge position...
                    v.big_o_vars_in = [
                        [[] for j in range(num_o_vars)] for i in range(max_chain - 1)
                    ]
                    v.big_o_vars_out = [
                        [[] for j in range(num_o_vars)] for i in range(max_chain - 1)
                    ]

        for ndd in ndds:
            ndd_edge_vars = []
            for e in ndd.edges:
                edge_var = m.addVar(vtype=GRB.BINARY)
                if check_edge_success:
                    if not e.success:
                        m.addConstr(edge_var == 0)
                e.edge_var = edge_var
                ndd_edge_vars.append(edge_var)

                if add_o_vars:
                    if num_o_vars < 2:
                        e.o_var = m.addVar(lb=0, ub=(1 - e.success_prob))
                        e.big_o_var = m.addVar(lb=0, ub=(1 - e.success_prob))
                        m.addConstr(e.big_o_var <= e.edge_var)
                        m.addConstr(e.big_o_var <= e.o_var)
                    else:
                        e.o_vars = m.addVars(num_o_vars, lb=0, ub=1)
                        e.big_o_vars = m.addVars(num_o_vars, lb=0, ub=1)
                        for n in range(num_o_vars):
                            m.addConstr(e.big_o_vars[n] <= e.edge_var)
                            m.addConstr(e.big_o_vars[n] <= e.o_vars[n])
                            m.addConstr(e.o_vars[n] <= e.realizations[n])

                vtx_to_vars[e.tgt.id].append(edge_var)
                if max_chain > 1:
                    e.tgt.grb_vars_in[0].append(edge_var)
                    e.tgt.edges_in[0].append(e)
                    if add_o_vars:
                        if num_o_vars < 2:
                            e.tgt.big_o_vars_in[0].append(e.big_o_var)
                        else:
                            for n in range(num_o_vars):
                                e.tgt.big_o_vars_in[0][n].append(e.big_o_vars[n])

            m.update()
            m.addConstr(quicksum(ndd_edge_vars) <= 1)

        dists_from_ndd = kidney_utils.get_dist_from_nearest_ndd(digraph, ndds)

        # Add pair->pair edge variables, indexed by position in chain
        # e.grb_var are the chain variables for each edge.
        for e in digraph.es:
            e.grb_vars = []
            if add_o_vars:
                if num_o_vars < 2:
                    e.o_vars = []
                    e.big_o_vars = []
                else:
                    # e.o_vars[n][i] is the o-var for the n^th edge realization
                    e.o_vars = []
                    e.big_o_vars = []
            if store_edge_positions:
                e.grb_var_positions = []
            for i in range(max_chain - 1):
                if dists_from_ndd[e.src.id] <= i + 1:
                    edge_var = m.addVar(vtype=GRB.BINARY)
                    if check_edge_success:
                        if not e.success:
                            m.addConstr(edge_var == 0)
                    e.grb_vars.append(edge_var)
                    if add_o_vars:
                        if num_o_vars < 2:
                            o_var = m.addVar(lb=0, ub=(1 - e.success_prob))
                            big_o_var = m.addVar(lb=0, ub=(1 - e.success_prob))
                            e.o_vars.append(o_var)
                            e.big_o_vars.append(big_o_var)
                            m.addConstr(big_o_var <= edge_var)
                            m.addConstr(big_o_var <= o_var)
                        else:
                            o_vars = m.addVars(num_o_vars, lb=0, ub=1)
                            big_o_vars = m.addVars(num_o_vars, lb=0, ub=1)
                            e.o_vars.append(o_vars)
                            e.big_o_vars.append(big_o_vars)
                            for n in range(num_o_vars):
                                m.addConstr(big_o_vars[n] <= edge_var)
                                m.addConstr(big_o_vars[n] <= o_vars[n])
                                m.addConstr(o_vars[n] <= e.realizations[n])
                    if store_edge_positions:
                        e.grb_var_positions.append(i + 1)
                    vtx_to_vars[e.tgt.id].append(edge_var)
                    e.src.grb_vars_out[i].append(edge_var)
                    e.src.edges_out[i].append(e)
                    if add_o_vars:
                        if num_o_vars < 2:
                            e.src.big_o_vars_out[i].append(big_o_var)
                        else:
                            for n in range(num_o_vars):
                                e.src.big_o_vars_out[i][n].append(big_o_vars[n])

                    if i < max_chain - 2:
                        e.tgt.grb_vars_in[i + 1].append(edge_var)
                        e.tgt.edges_in[i + 1].append(e)
                        if add_o_vars:
                            if num_o_vars < 2:
                                e.tgt.big_o_vars_in[i + 1].append(big_o_var)
                            else:
                                for n in range(num_o_vars):
                                    e.tgt.big_o_vars_in[i + 1][n].append(big_o_vars[n])

            # m.update()

        # At each chain position, sum of edges into a vertex must be >= sum of edges out
        for i in range(max_chain - 1):
            for v in digraph.vs:
                m.addConstr(quicksum(v.grb_vars_in[i]) >= quicksum(v.grb_vars_out[i]))
                if add_o_vars:
                    if num_o_vars < 2:
                        assert len(v.edges_out[i]) == len(v.big_o_vars_out[i])
                        m.addConstr(
                            quicksum(v.big_o_vars_in[i])
                            >= quicksum(
                                (1.0 / e.success_prob) * var
                                for e, var in zip(v.edges_out[i], v.big_o_vars_out[i])
                            )
                        )
                    else:
                        for n in range(num_o_vars):
                            m.addConstr(
                                quicksum(v.big_o_vars_in[i][n])
                                >= quicksum(v.big_o_vars_out[i][n])
                            )

        m.update()