示例#1
0
def optimize_picef(cfg, check_edge_success=False):
    """create and solve a picef model, and return the solution"""

    if cfg.m is None:
        create_picef_model(cfg, check_edge_success=check_edge_success)

    optimize(cfg.m)

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

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

    sol = OptSolution(
        ip_model=cfg.m,
        cycles=cycles_used,
        cycle_obj=cycle_obj,
        chains=matching_chains,
        digraph=cfg.digraph,
        edge_success_prob=cfg.edge_success_prob,
        cycle_cap=cfg.max_chain,
        chain_cap=cfg.max_cycle,
    )
    sol.add_matching_edges(cfg.ndds)
    kidney_utils.check_validity(sol, cfg.digraph, cfg.ndds, cfg.max_cycle,
                                cfg.max_chain)
    return cycle_obj, matching_chains, sol
def solve_kep(cfg, formulation, use_relabelled=True):
    formulations = {
        "uef": ("Uncapped edge formulation", kidney_ip.optimise_uuef),
        "eef": ("EEF", kidney_ip.optimise_eef),
        "eef_full_red": ("EEF with full reduction by cycle generation",
                         kidney_ip.optimise_eef_full_red),
        "hpief_prime": ("HPIEF'", kidney_ip.optimise_hpief_prime),
        "hpief_prime_full_red":
        ("HPIEF' with full reduction by cycle generation",
         kidney_ip.optimise_hpief_prime_full_red),
        "hpief_2prime": ("HPIEF''", kidney_ip.optimise_hpief_2prime),
        "hpief_2prime_full_red":
        ("HPIEF'' with full reduction by cycle generation",
         kidney_ip.optimise_hpief_2prime_full_red),
        "picef": ("PICEF", kidney_ip.optimise_picef),
        "cf": ("Cycle formulation", kidney_ip.optimise_ccf)
    }

    if formulation in formulations:
        formulation_name, formulation_fun = formulations[formulation]
        if use_relabelled:
            opt_result = kidney_ip.optimise_relabelled(formulation_fun, cfg)
        else:
            opt_result = formulation_fun(cfg)
        if cfg.multi > 1:  # added by Duncan
            for sol in opt_result.solutions:
                kidney_utils.check_validity(sol, cfg.digraph, cfg.ndds,
                                            cfg.max_cycle, cfg.max_chain)
        else:
            kidney_utils.check_validity(opt_result, cfg.digraph, cfg.ndds,
                                        cfg.max_cycle, cfg.max_chain)
        opt_result.formulation_name = formulation_name
        return opt_result
    else:
        raise ValueError("Unrecognised IP formulation name")
def solve_kep(cfg, formulation, use_relabelled=True):

    formulations = {
        "uef":  ("Uncapped edge formulation", kidney_ip.optimise_uuef),
        "eef": ("EEF", kidney_ip.optimise_eef),
        "eef_full_red": ("EEF with full reduction by cycle generation", kidney_ip.optimise_eef_full_red),
        "hpief_prime": ("HPIEF'", kidney_ip.optimise_hpief_prime),
        "hpief_prime_full_red": ("HPIEF' with full reduction by cycle generation", kidney_ip.optimise_hpief_prime_full_red),
        "hpief_2prime": ("HPIEF''", kidney_ip.optimise_hpief_2prime),
        "hpief_2prime_full_red": ("HPIEF'' with full reduction by cycle generation", kidney_ip.optimise_hpief_2prime_full_red),
        "picef": ("PICEF", kidney_ip.optimise_picef),
        "cf":   ("Cycle formulation",
                  kidney_ip.optimise_ccf)
    }
    
    if formulation in formulations:
        formulation_name, formulation_fun = formulations[formulation]
        if use_relabelled:
            opt_result = kidney_ip.optimise_relabelled(formulation_fun, cfg)
        else:
            opt_result = formulation_fun(cfg)
        kidney_utils.check_validity(opt_result, cfg.digraph, cfg.ndds, cfg.max_cycle, cfg.max_chain)
        opt_result.formulation_name = formulation_name
        return opt_result
    else:
        raise ValueError("Unrecognised IP formulation name")
示例#4
0
def solve_picef_model(cfg, remove_edges=[]):
    """
    solve a picef model using a config object, and return the solution

    if remove_edges is provided, disallow these edges from being used.
    """

    for e in remove_edges:
        e.used_var.setAttr(GRB.Attr.UB, 0.0)

    optimize(cfg.m)

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

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

    sol = OptSolution(
        ip_model=cfg.m,
        cycles=cycles_used,
        cycle_obj=cycle_obj,
        chains=matching_chains,
        digraph=cfg.digraph,
        edge_success_prob=cfg.edge_success_prob,
        cycle_cap=cfg.max_chain,
        chain_cap=cfg.max_cycle,
    )
    sol.add_matching_edges(cfg.ndds)
    kidney_utils.check_validity(sol, cfg.digraph, cfg.ndds, cfg.max_cycle,
                                cfg.max_chain)

    # allow removed edges to be used again
    for e in remove_edges:
        e.used_var.setAttr(GRB.Attr.UB, 1.0)

    return sol
def solve_kep(cfg, formulation, use_relabelled=True):
    formulations = {
        "picef": ("PICEF", kidney_ip.optimise_picef),
        "pctsp": ("PC-TSP", kidney_ip.optimise_pctsp),
        "pitsp": ("PI-TSP", kidney_ip.optimize_pitsp),
        "rob_picef": ("ROBUST PICEF", kidney_ip.optimise_robust_picef),
        "edge_weight_uncertainty": ("var. budget edge weight uncertainty", kidney_ip.solve_edge_weight_uncertainty),
        "rob_pctsp": ("ROBUST PC-TSP", kidney_ip.optimize_robust_pctsp)
    }

    if formulation in formulations:
        formulation_name, formulation_fun = formulations[formulation]
        if use_relabelled:
            opt_result = kidney_ip.optimise_relabelled(formulation_fun, cfg)
        else:
            opt_result = formulation_fun(cfg)

        kidney_utils.check_validity(opt_result, cfg.digraph, cfg.ndds, cfg.max_cycle, cfg.max_chain)
        opt_result.formulation_name = formulation_name
        return opt_result
    else:
        raise ValueError("Unrecognised IP formulation name")
示例#6
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
示例#7
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
示例#8
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
示例#9
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
示例#10
0
def optimize_dro_saa_edge_existence(cfg, num_measurements, gamma, alpha):
    """
    solve the PICEF model with DRO-SAA objective for edge existence uncertainty

    this requires that each edge has the property e.realizations: a binary vector of length num_measurements
    """
    m, cycles, cycle_vars, _ = create_picef_model(
        cfg, add_o_vars=True, num_o_vars=num_measurements
    )

    # 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 = expected_cycle_weight(c_obj)
        c_obj.grb_var = var
        # for DRO-SAA - keep track of cycle realizations
        c_obj.realizations = [
            min(e.realizations[n] for e in c_obj.edges) for n in range(num_measurements)
        ]
        cycle_list.append(c_obj)

    # add weight variables for each realization
    w_vars = m.addVars(num_measurements, lb=-GRB.INFINITY, ub=GRB.INFINITY)

    for n in range(num_measurements):
        # objective for the n^th realization
        m.addConstr(
            w_vars[n]
            == (
                -quicksum(
                    e.weight * big_o_var
                    for i in range(cfg.max_chain - 1)
                    for v in cfg.digraph.vs
                    for e, big_o_var in zip(v.edges_in[i], v.big_o_vars_in[i][n])
                )
                - quicksum(c.weight * c.realizations[n] * c.grb_var for c in cycle_list)
            )
        )

    d_var = m.addVar(lb=-GRB.INFINITY, ub=GRB.INFINITY)

    # define pi variables
    pi_vars = m.addVars(num_measurements, lb=0, ub=GRB.INFINITY)
    for n in range(num_measurements):
        m.addConstr(pi_vars[n] >= w_vars[n] - d_var)

    # define objective
    obj_expr = (1.0 / float(num_measurements)) * quicksum(w_vars) + gamma * (
        d_var + (1.0 / (alpha * float(num_measurements))) * quicksum(pi_vars)
    )

    m.setObjective(obj_expr, GRB.MINIMIZE)

    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 cycle_obj, matching_chains
示例#11
0
def optimize_picef_heterogeneous_edge_prob(cfg):
    """
    solve the PICEF model with heterogeneous edge success probabilities of Ren, McElfresh, Bidkhori, Dickerson (2020)

    this requires that each edge has the property edge.success_prob
    """
    m, cycles, cycle_vars, _ = create_picef_model(cfg, add_o_vars=True)

    # 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 = expected_cycle_weight(c_obj)
        c_obj.grb_var = var
        cycle_list.append(c_obj)

    # add objective
    chain_weight = quicksum(
        e.weight * big_o_var
        for i in range(cfg.max_chain - 1)
        for v in cfg.digraph.vs
        for e, big_o_var in zip(v.edges_in[i], v.big_o_vars_in[i])
    )

    obj_expr = chain_weight + quicksum(c.weight * c.grb_var for c in cycle_list)

    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 cycle_obj, matching_chains