Exemple #1
0
 def get_weight(self, digraph, ndds, edge_success_prob=1.0):
     weight = (sum(
         c.get_weight(digraph, ndds, edge_success_prob)
         for c in self.chains) + sum(
             failure_aware_cycle_weight(c, digraph, edge_success_prob)
             for c in self.cycles))
     return weight
    def __init__(
        self,
        ip_model,
        cycles,
        chains,
        digraph,
        edge_success_prob=1,
        infeasible=False,
        cycle_obj=None,
        cycle_cap=None,
        chain_cap=None,
        matching_edges=None,
    ):
        self.ip_model = ip_model
        self.cycles = cycles
        self.chains = chains
        self.digraph = digraph
        self.infeasible = infeasible
        if self.infeasible:
            self.total_weight = 0
        else:
            self.total_weight = sum(c.weight for c in chains) + sum(
                failure_aware_cycle_weight(c, digraph, edge_success_prob)
                for c in cycles)
        self.edge_success_prob = edge_success_prob
        self.cycle_obj = cycle_obj
        self.matching_edges = matching_edges
        self.cycle_cap = cycle_cap
        self.chain_cap = chain_cap

        if ip_model is not None:
            self.timeout = ip_model.status == GRB.TIME_LIMIT
        else:
            self.timeout = False
Exemple #3
0
    def __init__(
        self,
        ip_model,
        cycles,
        chains,
        digraph,
        edge_success_prob=1,
        infeasible=False,
        robust_weight=0,
        optimistic_weight=0,
        cycle_obj=None,
        chain_restriction=None,
        cycle_restriction=None,
        cardinality_restriction=None,
        cycle_cap=None,
        chain_cap=None,
        matching_edges=None,
        alpha_var=None,
    ):
        self.ip_model = ip_model
        self.cycles = cycles
        self.chains = chains
        self.digraph = digraph
        self.infeasible = infeasible
        if self.infeasible:
            self.total_weight = 0
        else:
            self.total_weight = sum(c.weight for c in chains) + sum(
                failure_aware_cycle_weight(c, digraph, edge_success_prob)
                for c in cycles
            )
        self.edge_success_prob = edge_success_prob
        self.cycle_obj = cycle_obj
        self.matching_edges = matching_edges
        self.robust_weight = robust_weight
        self.optimistic_weight = optimistic_weight
        self.cycle_restriction = cycle_restriction
        self.chain_restriction = chain_restriction
        self.cardinality_restriction = cardinality_restriction
        self.cycle_cap = cycle_cap
        self.chain_cap = chain_cap
        self.alpha_var = alpha_var

        if ip_model is not None:
            self.timeout = ip_model.status == GRB.TIME_LIMIT
        else:
            self.timeout = False
Exemple #4
0
def optimize_SAA_picef(cfg, num_weight_measurements, gamma, alpha):
    m, cycles, cycle_vars, _ = create_picef_model(cfg)

    # add cycle objects
    cycle_list = []
    for c, var in zip(cycles, cycle_vars):
        c_obj = Cycle(c)
        c_obj.add_edges(cfg.digraph.es)
        c_obj.weight = failure_aware_cycle_weight(c_obj.vs, cfg.digraph,
                                                  cfg.edge_success_prob)
        c_obj.grb_var = var
        cycle_list.append(c_obj)

    # add variables for each edge weight measurement
    weight_vars = m.addVars(num_weight_measurements,
                            vtype=GRB.CONTINUOUS,
                            lb=-GRB.INFINITY,
                            ub=GRB.INFINITY)
    for i in range(num_weight_measurements):
        m.addConstr(weight_vars[i] == -(quicksum(e.used_var * e.weight_list[i]
                                                 for e in cfg.digraph.es) +
                                        quicksum(e.weight_list[i] * e.edge_var
                                                 for ndd in cfg.ndds
                                                 for e in ndd.edges)))

    # auxiliary variable
    d_var = m.addVar(vtype=GRB.CONTINUOUS, lb=-GRB.INFINITY, ub=GRB.INFINITY)

    # add pi variables & constraints for SAA
    pi_vars = m.addVars(num_weight_measurements,
                        vtype=GRB.CONTINUOUS,
                        lb=-GRB.INFINITY,
                        ub=GRB.INFINITY)
    for i in range(num_weight_measurements):
        m.addConstr(pi_vars[i] >= weight_vars[i])
        m.addConstr(pi_vars[i] >= (1 + gamma / alpha) * weight_vars[i] -
                    (d_var * gamma) / alpha)

    # objective
    obj = (1.0 /
           float(num_weight_measurements)) * quicksum(pi_vars) + gamma * d_var

    m.setObjective(obj, sense=GRB.MINIMIZE)

    if not cfg.use_chains:
        raise Exception("not implemented")
    elif cfg.edge_success_prob == 1:
        pass
    else:
        raise Exception("not implemented")

    optimize(m)

    pair_edges = [e for e in cfg.digraph.es if e.used_var.x > 0.5]

    if cfg.use_chains:
        matching_chains = kidney_utils.get_optimal_chains(
            cfg.digraph, cfg.ndds, cfg.edge_success_prob)
        ndd_chain_edges = [
            e for ndd in cfg.ndds for e in ndd.edges if e.edge_var.x > 0.5
        ]
    else:
        ndd_chain_edges = []
        matching_chains = []

    matching_edges = pair_edges + ndd_chain_edges

    if cfg.cardinality_restriction is not None:
        if len(matching_edges) > cfg.cardinality_restriction:
            raise Warning(
                "cardinality restriction is violated: restriction = %d edges, matching uses %d edges"
                % (cfg.cardinality_restriction, len(matching_edges)))

    cycles_used = [c for c, v in zip(cycles, cycle_vars) if v.x > 0.5]
    cycle_obj = [c for c in cycle_list if c.grb_var.x > 0.5]

    sol = OptSolution(ip_model=m,
                      cycles=cycles_used,
                      cycle_obj=cycle_obj,
                      chains=matching_chains,
                      digraph=cfg.digraph,
                      edge_success_prob=cfg.edge_success_prob,
                      chain_restriction=cfg.chain_restriction,
                      cycle_restriction=cfg.cycle_restriction,
                      cycle_cap=cfg.max_chain,
                      chain_cap=cfg.max_cycle,
                      cardinality_restriction=cfg.cardinality_restriction)
    sol.add_matching_edges(cfg.ndds)
    kidney_utils.check_validity(sol, cfg.digraph, cfg.ndds, cfg.max_cycle,
                                cfg.max_chain)
    return sol, matching_edges
Exemple #5
0
def optimise_robust_picef(cfg):
    m, cycles, cycle_vars, num_edges_var = create_picef_model(cfg)

    # for use later
    floor_gamma = np.floor(cfg.gamma)
    ceil_gamma = np.ceil(cfg.gamma)
    gamma_frac = cfg.gamma - floor_gamma

    # add cycle vars
    cycle_list = []
    for c, var in zip(cycles, cycle_vars):
        c_obj = Cycle(c)
        c_obj.add_edges(cfg.digraph.es)
        c_obj.weight = cycle_weight(c_obj.vs, cfg.digraph)
        c_obj.grb_var = var
        cycle_list.append(c_obj)

    m.update()

    # gamma is integer
    if gamma_frac == 0:

        if cfg.use_chains:
            for ndd in cfg.ndds:
                for e in ndd.edges:
                    g_var = m.addVar(vtype=GRB.BINARY)
                    d_var = m.addVar(vtype=GRB.BINARY)
                    e.g_var = g_var
                    e.d_var = d_var
                    m.addGenConstrAnd(e.d_var, [e.g_var, e.edge_var])

            m.update()

        # add g and d variables for pair-pair edges
        for e in cfg.digraph.es:
            g_var = m.addVar(vtype=GRB.BINARY)
            d_var = m.addVar(vtype=GRB.BINARY)
            e.g_var = g_var
            e.d_var = d_var
            m.addGenConstrAnd(e.d_var, [e.g_var, e.used_var])

        m.update()

    # gamma is not integer
    else:

        if cfg.use_chains:
            # use both gf (full discount if gf=1, gp=0) and gp (partial discount, if gf=gp=1)
            for ndd in cfg.ndds:
                for e in ndd.edges:
                    gf_var = m.addVar(vtype=GRB.BINARY)
                    df_var = m.addVar(vtype=GRB.BINARY)
                    e.gf_var = gf_var
                    e.df_var = df_var
                    m.addGenConstrAnd(e.df_var, [e.gf_var, e.edge_var])
                    gp_var = m.addVar(vtype=GRB.BINARY)
                    dp_var = m.addVar(vtype=GRB.BINARY)
                    e.gp_var = gp_var
                    e.dp_var = dp_var
                    m.addGenConstrAnd(e.dp_var, [e.gp_var, e.edge_var])

            m.update()

        for e in cfg.digraph.es:
            gf_var = m.addVar(vtype=GRB.BINARY)
            df_var = m.addVar(vtype=GRB.BINARY)
            e.gf_var = gf_var
            e.df_var = df_var
            m.addGenConstrAnd(e.df_var, [e.gf_var, e.used_var])
            gp_var = m.addVar(vtype=GRB.BINARY)
            dp_var = m.addVar(vtype=GRB.BINARY)
            e.gp_var = gp_var
            e.dp_var = dp_var
            m.addGenConstrAnd(e.dp_var, [e.gp_var, e.used_var])

        m.update()

    # discount indicators g follow same ordering as the edge discount values (sort in increasing order)
    if cfg.use_chains:
        ndd_e = [e for ndd in cfg.ndds for e in ndd.edges]
    else:
        ndd_e = []
    all_edges = cfg.digraph.es + ndd_e
    e_sorted = sorted(all_edges, key=lambda x: x.discount, reverse=False)

    # ordering constraints over g
    # gamma is integer
    if gamma_frac == 0:
        for i in range(len(e_sorted) - 1):
            m.addConstr(e_sorted[i].g_var <= e_sorted[i + 1].g_var)

    # gamma is not integer
    else:
        for i in range(len(e_sorted) - 1):
            m.addConstr(e_sorted[i].gf_var <= e_sorted[i + 1].gf_var)
            m.addConstr(e_sorted[i].gp_var <= e_sorted[i + 1].gp_var)

    # number of edges used in matching (include all position-indexed vars)

    # uncertainty budget (number of discounted edges)
    gamma_var = m.addVar(vtype=GRB.CONTINUOUS)
    m.addGenConstrMin(gamma_var, [num_edges_var, cfg.gamma])

    # add a cardinality restriction if necessary
    if cfg.cardinality_restriction is not None:
        m.addConstr(num_edges_var <= cfg.cardinality_restriction)

    m.update()

    # limit number of discounted variables
    # gamma is integer
    if gamma_frac == 0:
        m.addConstr(quicksum(e.d_var for e in all_edges) == gamma_var)

    # gamma is not integer
    else:
        h_var = m.addVar(vtype=GRB.BINARY)
        m.addConstr(cfg.gamma - num_edges_var <= W_small * h_var)
        m.addConstr(num_edges_var - cfg.gamma <= W_small * (1 - h_var))
        m.addConstr(
            quicksum(e.dp_var for e in all_edges) == h_var * num_edges_var +
            (1 - h_var) * ceil_gamma)
        m.addConstr(
            quicksum(e.df_var for e in all_edges) == h_var * num_edges_var +
            (1 - h_var) * floor_gamma)

    # total discount (by edge)
    # gamma is integer
    if gamma_frac == 0:
        total_discount = quicksum(e.discount * e.d_var for e in all_edges)

    # gamma is not integer
    else:
        total_discount = quicksum((1 - gamma_frac) * e.discount * e.df_var for e in all_edges) + \
                         quicksum(gamma_frac * e.discount * e.dp_var for e in all_edges)

    # set a variable for the total (optimistic matching weight)
    total_weight = m.addVar(vtype=GRB.CONTINUOUS)

    m.update()

    if not cfg.use_chains:
        m.addConstr(total_weight == quicksum(
            failure_aware_cycle_weight(c, cfg.digraph, cfg.edge_success_prob) *
            var for c, var in zip(cycles, cycle_vars)))
        obj_expr = total_weight - total_discount
    elif cfg.edge_success_prob == 1:
        m.addConstr(total_weight == (quicksum(
            cycle_weight(c, cfg.digraph) * var
            for c, var in zip(cycles, cycle_vars)) +
                                     quicksum(e.weight * e.edge_var
                                              for ndd in cfg.ndds
                                              for e in ndd.edges) +
                                     quicksum(e.weight * var
                                              for e in cfg.digraph.es
                                              for var in e.grb_vars)))
        obj_expr = total_weight - total_discount
    else:
        raise Warning("not implemented")

    m.setObjective(obj_expr, GRB.MAXIMIZE)

    optimize(m)

    if gamma_frac == 0:  # gamma is integer
        discounted_pair_edges = [e for e in cfg.digraph.es if e.d_var.x > 0]

        for e in discounted_pair_edges:
            e.discount_frac = e.d_var.x

        if cfg.use_chains:
            discounted_ndd_edges = [(i_ndd, e)
                                    for i_ndd, ndd in enumerate(cfg.ndds)
                                    for e in ndd.edges if e.d_var.x > 0.0]

            for _, e in discounted_ndd_edges:
                e.discount_frac = e.d_var.x

    else:  # gamma is not integer

        discounted_pair_edges = [e for e in cfg.digraph.es \
                                 if ((e.df_var.x > 0.0) or (e.dp_var.x > 0.0))]

        for e in discounted_pair_edges:
            e.discount_frac = (
                1 - gamma_frac) * e.df_var.x + gamma_frac * e.dp_var.x

        if cfg.use_chains:
            discounted_ndd_edges = [(i_ndd, e) for i_ndd, ndd in enumerate(cfg.ndds) for e in ndd.edges \
                                    if ((e.df_var.x > 0.0) or (e.dp_var.x > 0.0))]
            for _, e in discounted_ndd_edges:
                e.discount_frac = (
                    1 - gamma_frac) * e.df_var.x + gamma_frac * e.dp_var.x

    if cfg.use_chains:
        ndd_matching_edges = [
            e for ndd in cfg.ndds for e in ndd.edges if e.edge_var.x > 0.5
        ]
    else:
        ndd_matching_edges = []

    used_matching_edges = [e for e in cfg.digraph.es if e.used_var.x > 0.5]

    matching_edges = ndd_matching_edges + used_matching_edges

    if cfg.cardinality_restriction is not None:
        if len(matching_edges) > cfg.cardinality_restriction:
            raise Warning(
                "cardinality restriction is violated: restriction = %d edges, matching uses %d edges"
                % (cfg.cardinality_restriction, len(matching_edges)))

    chains_used = kidney_utils.get_optimal_chains(cfg.digraph, cfg.ndds, cfg.edge_success_prob) if cfg.use_chains \
        else []

    cycles_used = [c for c, v in zip(cycles, cycle_vars) if v.x > 0.5]
    cycle_obj = [c for c in cycle_list if c.grb_var.x > 0.5]

    sol = OptSolution(ip_model=m,
                      cycles=cycles_used,
                      cycle_obj=cycle_obj,
                      chains=chains_used,
                      digraph=cfg.digraph,
                      edge_success_prob=cfg.edge_success_prob,
                      gamma=cfg.gamma,
                      robust_weight=m.objVal,
                      optimistic_weight=total_weight.x,
                      chain_restriction=cfg.chain_restriction,
                      cycle_restriction=cfg.cycle_restriction,
                      cycle_cap=cfg.max_chain,
                      chain_cap=cfg.max_cycle,
                      cardinality_restriction=cfg.cardinality_restriction)
    sol.add_matching_edges(cfg.ndds)
    kidney_utils.check_validity(sol, cfg.digraph, cfg.ndds, cfg.max_cycle,
                                cfg.max_chain)

    return sol, matching_edges
Exemple #6
0
def optimize_picef(cfg):
    m, cycles, cycle_vars, _ = create_picef_model(cfg)

    # add cycle objects
    cycle_list = []
    for c, var in zip(cycles, cycle_vars):
        c_obj = Cycle(c)
        c_obj.add_edges(cfg.digraph.es)
        c_obj.weight = failure_aware_cycle_weight(c_obj.vs, cfg.digraph,
                                                  cfg.edge_success_prob)
        c_obj.grb_var = var
        cycle_list.append(c_obj)

    if not cfg.use_chains:
        obj_expr = quicksum(
            failure_aware_cycle_weight(c, cfg.digraph, cfg.edge_success_prob) *
            var for c, var in zip(cycles, cycle_vars))
    elif cfg.edge_success_prob == 1:
        obj_expr = (quicksum(
            cycle_weight(c, cfg.digraph) * var
            for c, var in zip(cycles, cycle_vars)) +
                    quicksum(e.weight * e.edge_var for ndd in cfg.ndds
                             for e in ndd.edges) +
                    quicksum(e.weight * var for e in cfg.digraph.es
                             for var in e.grb_vars))
    else:
        obj_expr = (quicksum(
            failure_aware_cycle_weight(c, cfg.digraph, cfg.edge_success_prob) *
            var for c, var in zip(cycles, cycle_vars)) +
                    quicksum(e.weight * cfg.edge_success_prob * e.edge_var
                             for ndd in cfg.ndds for e in ndd.edges) +
                    quicksum(
                        e.weight * cfg.edge_success_prob**(pos + 1) * var
                        for e in cfg.digraph.es
                        for var, pos in zip(e.grb_vars, e.grb_var_positions)))
    m.setObjective(obj_expr, GRB.MAXIMIZE)

    optimize(m)

    pair_edges = [e for e in cfg.digraph.es if e.used_var.x > 0.5]

    if cfg.use_chains:
        matching_chains = kidney_utils.get_optimal_chains(
            cfg.digraph, cfg.ndds, cfg.edge_success_prob)
        ndd_chain_edges = [
            e for ndd in cfg.ndds for e in ndd.edges if e.edge_var.x > 0.5
        ]
    else:
        ndd_chain_edges = []
        matching_chains = []

    matching_edges = pair_edges + ndd_chain_edges

    if cfg.cardinality_restriction is not None:
        if len(matching_edges) > cfg.cardinality_restriction:
            raise Warning(
                "cardinality restriction is violated: restriction = %d edges, matching uses %d edges"
                % (cfg.cardinality_restriction, len(matching_edges)))

    cycles_used = [c for c, v in zip(cycles, cycle_vars) if v.x > 0.5]
    cycle_obj = [c for c in cycle_list if c.grb_var.x > 0.5]

    sol = OptSolution(ip_model=m,
                      cycles=cycles_used,
                      cycle_obj=cycle_obj,
                      chains=matching_chains,
                      digraph=cfg.digraph,
                      edge_success_prob=cfg.edge_success_prob,
                      chain_restriction=cfg.chain_restriction,
                      cycle_restriction=cfg.cycle_restriction,
                      cycle_cap=cfg.max_chain,
                      chain_cap=cfg.max_cycle,
                      cardinality_restriction=cfg.cardinality_restriction)
    sol.add_matching_edges(cfg.ndds)
    kidney_utils.check_validity(sol, cfg.digraph, cfg.ndds, cfg.max_cycle,
                                cfg.max_chain)
    return sol, matching_edges
Exemple #7
0
def optimize_DRO_SAA_picef(cfg, num_weight_measurements, gamma, alpha, theta,
                           w_min, w_max):
    """Solve the DRO-SAA formulation of (Ren, 2020)
    
    Arguments:
        cfg: (OptConfig object)
        num_weight_measurements: (int). number of weight measurements associated with each edge
        gamma: (float). parameter balancing between a pure CVar objective (gamma->infinity) and a pure max-expectation
            objective (gamma=0)
        alpha: (float). CVar protection level, should be on [0, 1]
        theta: prediction of distance between assumed distribution and true distribution
        w_min: (float). assumed minimum edge weight of unknown distribution 
        w_max: (float). assumed maximum edge weight of unknown distribution 
    """

    m, cycles, cycle_vars, _ = create_picef_model(cfg)

    # add cycle objects
    cycle_list = []
    for c, var in zip(cycles, cycle_vars):
        c_obj = Cycle(c)
        c_obj.add_edges(cfg.digraph.es)
        c_obj.weight = failure_aware_cycle_weight(c_obj.vs, cfg.digraph,
                                                  cfg.edge_success_prob)
        c_obj.grb_var = var
        cycle_list.append(c_obj)

    # add variables for each edge weight measurement
    weight_vars = m.addVars(num_weight_measurements,
                            vtype=GRB.CONTINUOUS,
                            lb=-GRB.INFINITY,
                            ub=GRB.INFINITY)
    for i in range(num_weight_measurements):
        m.addConstr(weight_vars[i] == -(quicksum(e.used_var * e.weight_list[i]
                                                 for e in cfg.digraph.es) +
                                        quicksum(e.weight_list[i] * e.edge_var
                                                 for ndd in cfg.ndds
                                                 for e in ndd.edges)))

    # auxiliary variables
    d_var = m.addVar(vtype=GRB.CONTINUOUS,
                     lb=-GRB.INFINITY,
                     ub=GRB.INFINITY,
                     name='d')
    lam_var = m.addVar(vtype=GRB.CONTINUOUS,
                       lb=-GRB.INFINITY,
                       ub=GRB.INFINITY,
                       name='lambda')
    s_vars = m.addVars(num_weight_measurements,
                       vtype=GRB.CONTINUOUS,
                       lb=-GRB.INFINITY,
                       ub=GRB.INFINITY,
                       name='s')

    # add eta and mu vars for each edge
    for e in cfg.digraph.es:
        e.eta_vars = m.addVars(num_weight_measurements,
                               2,
                               vtype=GRB.CONTINUOUS,
                               lb=0.0,
                               ub=GRB.INFINITY,
                               name='eta')
        e.mu_vars = m.addVars(num_weight_measurements,
                              2,
                              vtype=GRB.CONTINUOUS,
                              lb=0.0,
                              ub=GRB.INFINITY,
                              name='mu')
    for n in cfg.ndds:
        for e in n.edges:
            # also add the used_var here, for convenience
            e.used_var = e.edge_var
            e.eta_vars = m.addVars(num_weight_measurements,
                                   2,
                                   vtype=GRB.CONTINUOUS,
                                   lb=0.0,
                                   ub=GRB.INFINITY,
                                   name='eta')
            e.mu_vars = m.addVars(num_weight_measurements,
                                  2,
                                  vtype=GRB.CONTINUOUS,
                                  lb=0.0,
                                  ub=GRB.INFINITY,
                                  name='mu')

    # add variables for each edge weight measurement
    weight_vars = m.addVars(num_weight_measurements,
                            vtype=GRB.CONTINUOUS,
                            lb=-GRB.INFINITY,
                            ub=GRB.INFINITY)
    for i in range(num_weight_measurements):
        m.addConstr(weight_vars[i] == -(quicksum(e.used_var * e.weight_list[i]
                                                 for e in cfg.digraph.es) +
                                        quicksum(e.weight_list[i] * e.edge_var
                                                 for ndd in cfg.ndds
                                                 for e in ndd.edges)))

    b1 = 0
    b2 = -d_var * gamma / alpha

    # construct a list of all edges, for convenience
    e_list = cfg.digraph.es + [e for n in cfg.ndds for e in n.edges]

    # add main constraints
    for i_measurement in range(num_weight_measurements):
        # k = 1
        edge_sum_1a = quicksum([
            e.eta_vars[i_measurement, 0] *
            (w_max - e.weight_list[i_measurement]) for e in e_list
        ])
        edge_sum_1b = quicksum([
            e.mu_vars[i_measurement, 0] *
            (e.weight_list[i_measurement] - w_min) for e in e_list
        ])
        m.addConstr(b1 + weight_vars[i_measurement] + edge_sum_1a + edge_sum_1b
                    <= s_vars[i_measurement],
                    name=("s_constr_k1_i%d" % i_measurement))

        # k = 2
        edge_sum_2a = quicksum([
            e.eta_vars[i_measurement, 1] *
            (w_max - e.weight_list[i_measurement]) for e in e_list
        ])
        edge_sum_2b = quicksum([
            e.mu_vars[i_measurement, 1] *
            (e.weight_list[i_measurement] - w_min) for e in e_list
        ])
        m.addConstr(b2 + (1 + gamma / alpha) * weight_vars[i_measurement] +
                    edge_sum_2a + edge_sum_2b <= s_vars[i_measurement],
                    name=("s_constr__k2_i%d" % i_measurement))

        # now for the norm sum (using the 1-norm)
        # for each edge, get |\eta_ik - \mu_ik  - a_k|. then sum all of these to obtain the 1-norm
        e_norm_plus_vars_k1 = m.addVars(len(e_list),
                                        vtype=GRB.CONTINUOUS,
                                        lb=0.0,
                                        ub=GRB.INFINITY,
                                        name=("e_norm_plus_k1_i%d" %
                                              i_measurement))
        e_norm_minus_vars_k1 = m.addVars(len(e_list),
                                         vtype=GRB.CONTINUOUS,
                                         lb=0.0,
                                         ub=GRB.INFINITY,
                                         name=("e_norm_minus_k1_i%d" %
                                               i_measurement))
        e_norm_plus_vars_k2 = m.addVars(len(e_list),
                                        vtype=GRB.CONTINUOUS,
                                        lb=0.0,
                                        ub=GRB.INFINITY,
                                        name=("e_norm_plus_k2_i%d" %
                                              i_measurement))
        e_norm_minus_vars_k2 = m.addVars(len(e_list),
                                         vtype=GRB.CONTINUOUS,
                                         lb=0.0,
                                         ub=GRB.INFINITY,
                                         name=("e_norm_minus_k2_i%d" %
                                               i_measurement))
        for i_e, e in enumerate(e_list):
            # k = 1
            m.addConstr(
                e_norm_plus_vars_k1[i_e] -
                e_norm_minus_vars_k1[i_e] == e.eta_vars[i_measurement, 0] -
                e.mu_vars[i_measurement, 0] + e.used_var)
            # k = 2
            m.addConstr(
                e_norm_plus_vars_k2[i_e] -
                e_norm_minus_vars_k2[i_e] == e.eta_vars[i_measurement, 1] -
                e.mu_vars[i_measurement, 1] + (1 + gamma / alpha) * e.used_var)

        # the 1-norm must be bounded by lambda, for each measurement and for each k
        m.addConstr(
            quicksum(e_norm_plus_vars_k2) +
            quicksum(e_norm_minus_vars_k2) <= lam_var)
        m.addConstr(
            quicksum(e_norm_plus_vars_k1) +
            quicksum(e_norm_minus_vars_k1) <= lam_var)

    # objective
    obj = lam_var * theta + (1.0 / float(num_weight_measurements)
                             ) * quicksum(s_vars) + gamma * d_var

    m.setObjective(obj, sense=GRB.MINIMIZE)

    if not cfg.use_chains:
        raise Exception("not implemented")
    elif cfg.edge_success_prob == 1:
        pass
    else:
        raise Exception("not implemented")

    optimize(m)

    pair_edges = [e for e in cfg.digraph.es if e.used_var.x > 0.5]

    if cfg.use_chains:
        matching_chains = kidney_utils.get_optimal_chains(
            cfg.digraph, cfg.ndds, cfg.edge_success_prob)
        # matching_chains = []
        ndd_chain_edges = [
            e for ndd in cfg.ndds for e in ndd.edges if e.edge_var.x > 0.5
        ]
    else:
        ndd_chain_edges = []
        matching_chains = []

    matching_edges = pair_edges + ndd_chain_edges

    if cfg.cardinality_restriction is not None:
        if len(matching_edges) > cfg.cardinality_restriction:
            raise Warning(
                "cardinality restriction is violated: restriction = %d edges, matching uses %d edges"
                % (cfg.cardinality_restriction, len(matching_edges)))

    cycles_used = [c for c, v in zip(cycles, cycle_vars) if v.x > 0.5]
    cycle_obj = [c for c in cycle_list if c.grb_var.x > 0.5]

    sol = OptSolution(ip_model=m,
                      cycles=cycles_used,
                      cycle_obj=cycle_obj,
                      chains=matching_chains,
                      digraph=cfg.digraph,
                      edge_success_prob=cfg.edge_success_prob,
                      chain_restriction=cfg.chain_restriction,
                      cycle_restriction=cfg.cycle_restriction,
                      cycle_cap=cfg.max_chain,
                      chain_cap=cfg.max_cycle,
                      cardinality_restriction=cfg.cardinality_restriction)
    sol.add_matching_edges(cfg.ndds)
    kidney_utils.check_validity(sol, cfg.digraph, cfg.ndds, cfg.max_cycle,
                                cfg.max_chain)
    return sol, matching_edges
Exemple #8
0
def create_picef_model(cfg, check_edge_success=False):
    """Optimise using the PICEF formulation.

    Args:
        cfg: an OptConfig object
        check_edge_success: (bool). if True, check if each edge has e.success = False. if e.success=False, the edge cannot
            be used.

    Returns:
        an OptSolution object
    """

    cycles = cfg.digraph.find_cycles(cfg.max_cycle)

    m = create_mip_model(time_lim=cfg.timelimit, verbose=cfg.verbose)
    m.params.method = -1

    cycle_vars = [m.addVar(vtype=GRB.BINARY) for __ in cycles]

    vtx_to_vars = [[] for __ in cfg.digraph.vs]

    add_chain_vars_and_constraints(
        cfg.digraph,
        cfg.ndds,
        cfg.use_chains,
        cfg.max_chain,
        m,
        vtx_to_vars,
        store_edge_positions=True,
        check_edge_success=check_edge_success,
    )

    for i, c in enumerate(cycles):
        for v in c:
            vtx_to_vars[v.id].append(cycle_vars[i])

    for l in vtx_to_vars:
        if len(l) > 0:
            m.addConstr(quicksum(l) <= 1)

    # add variables for each pair-pair edge indicating whether it is used in a cycle or chain
    for e in cfg.digraph.es:
        used_in_cycle = []
        for var, c in zip(cycle_vars, cycles):
            if kidney_utils.cycle_contains_edge(c, e):
                used_in_cycle.append(var)

        used_var = m.addVar(vtype=GRB.INTEGER)
        if check_edge_success:
            if not e.success:
                m.addConstr(used_var == 0)

        if cfg.use_chains:
            m.addConstr(used_var == quicksum(used_in_cycle) +
                        quicksum(e.grb_vars))
        else:
            m.addConstr(used_var == quicksum(used_in_cycle))
        e.used_var = used_var

    # add cycle objects
    cycle_list = []
    for c, var in zip(cycles, cycle_vars):
        c_obj = Cycle(c)
        c_obj.add_edges(cfg.digraph.es)
        c_obj.weight = failure_aware_cycle_weight(c_obj.vs, cfg.digraph,
                                                  cfg.edge_success_prob)
        c_obj.grb_var = var
        cycle_list.append(c_obj)

    # add objective
    if not cfg.use_chains:
        obj_expr = quicksum(
            failure_aware_cycle_weight(c, cfg.digraph, cfg.edge_success_prob) *
            var for c, var in zip(cycles, cycle_vars))
    elif cfg.edge_success_prob == 1:
        obj_expr = (quicksum(
            cycle_weight(c, cfg.digraph) * var
            for c, var in zip(cycles, cycle_vars)) +
                    quicksum(e.weight * e.edge_var for ndd in cfg.ndds
                             for e in ndd.edges) +
                    quicksum(e.weight * var for e in cfg.digraph.es
                             for var in e.grb_vars))
    else:
        obj_expr = (quicksum(
            failure_aware_cycle_weight(c, cfg.digraph, cfg.edge_success_prob) *
            var for c, var in zip(cycles, cycle_vars)) +
                    quicksum(e.weight * cfg.edge_success_prob * e.edge_var
                             for ndd in cfg.ndds for e in ndd.edges) +
                    quicksum(
                        e.weight * cfg.edge_success_prob**(pos + 1) * var
                        for e in cfg.digraph.es
                        for var, pos in zip(e.grb_vars, e.grb_var_positions)))
    m.setObjective(obj_expr, GRB.MAXIMIZE)

    m.update()

    # attach the necessary objects to the optconfig
    cfg.m = m
    cfg.cycles = cycles
    cfg.cycle_vars = cycle_vars
    cfg.cycle_list = cycle_list