Esempio n. 1
0
def step(g, loops, feas_elims, ub, stats):
    # Solve relaxation with the cycle matrix of loops. This solution
    # MUST BE rigorous.
    relax_elims, lb = solve_relaxation(g, loops, stats)
    assert lb <= ub
    if lb == ub:
        log('***  Optimal solution found  ***')
        return None, None, feas_elims, ub
    #
    g_ruins = g.copy()
    for e in relax_elims:
        g_ruins.remove_edge(*e)
    #
    if nx.is_directed_acyclic_graph(g_ruins):
        log('***  Relaxation became feasible  ***')
        return None, None, relax_elims, lb,
    #
    log()
    log('Remaining graph')
    info(g_ruins, log=log)
    #
    # Get the missed edges and the ruins of the relaxation: We need new
    # candidate loops. The missed_edges does not have to be rigorous. We may
    # also improve the currently best feasible solution.
    missed = missed_edges(g_ruins)
    assert missed
    new_feas_elims = relax_elims + missed
    new_ub = sum(g[u][v]['weight'] for u, v in new_feas_elims)
    assert new_ub > lb
    if new_ub < ub:
        log('Improved UB: {} -> {}'.format(ub, new_ub))
        ub, feas_elims = new_ub, new_feas_elims
        double_check(g, ub, feas_elims, is_labeled=True, log=log)
    return missed, g_ruins, feas_elims, ub
Esempio n. 2
0
def solve_triangle_inequalities(g, stats, feasible_sol):
    iteratively_remove_runs_and_bypasses(g)
    # The above simplification removes edges, but the d['orig_edges'] still
    # refers to these edges. As a result, the double_check calls in this module
    # and in mfes will fail in the cost calculation, when trying to access
    # those not present edges to figure out their edge weights. So we have to
    # replace d['orig_edges'] appropriately and undo this at the end of this
    # function. Additionally, as runs are removed, new edges are introduced
    # that are not in the original graph. It is much simpler to just relabel
    # the graph and then undo it on the elimination order.
    origedges_map = get_orig_edges_map(g)
    #
    model, y = build_lp(g, feasible_sol)
    start = time()
    success = solve_ilp(model, stats)
    end = time()
    print('Overall solution time: {0:0.1f} s'.format(end - start))
    assert success, 'Solver failures are not handled at the moment...'
    cost = int(round(model.getObjective().getValue()))
    elim_order = recover_order(model, y, len(g))
    elims = get_torn_edges(g, elim_order)
    # Done. Now undo the  d['orig_edges'] mess.
    final_elims = []
    for edge in elims:
        final_elims.extend(origedges_map[edge])
    double_check(g, cost, elims, is_labeled=True)
    return final_elims, cost
Esempio n. 3
0
def simplify(g):
    print('Nodes:', g.number_of_nodes())
    print('Edges:', g.number_of_edges())
    cycles = list(nx.simple_cycles(g)) # <-
    print('Loops:', len(cycles))       # <- 
    orig_input = deepcopy(g)
    sccs = split_to_nontrivial_sccs(g)
    # Clean-up the new smaller SCCs without splitting them
    for sc in sccs:
        iteratively_remove_runs_and_bypasses(sc)
    #
    running_cost, elims = 0, [ ]
    info_after_cleanup(sccs, running_cost)
    #
    clean_sccs = [ ]
    for scc in sccs:
        plot(scc, prog='sfdp')
        new_sccs, running_cost = try_neighborhood(scc, running_cost, elims)
        clean_sccs.extend( new_sccs )
    # check what remains
    if clean_sccs:
        info_after_cleanup(clean_sccs, running_cost)
        print(sorted(n for n in clean_sccs[0]))
        dbg_dump_as_edgelist(clean_sccs[0])
    else:
        print('The simplifier eliminated the whole input at cost', running_cost)
        double_check(orig_input, running_cost, elims)
        print('Chosen:', elims)
Esempio n. 4
0
def solve_with_pcm(g, stats, feasible_sol):
    g_orig = g.copy()

    iteratively_remove_runs_and_bypasses(g)  # d['orig_edges'] mess,
    origedges_map = get_orig_edges_map(g)  # see explanation in grb_pcm.py
    #
    # FIXME It assumes that we only have a single SCC
    elims, ub = feasible_solution(g) if feasible_sol is None else feasible_sol
    # A shortest path around each edge, see grb_pcm.py for possible improvements
    loops = initial_loop_set(g)
    print('Initial cycle matrix size:', len(loops))
    # Build the model, set the elims as initial solution
    m, vrs = build_ilp(g, loops, elims)
    # Put into the model dict everything we need in the callback
    m._vrs, m._g, m._loops, m._elims, m._ub = vrs, g, loops, elims, ub
    #
    solve(m, stats)
    #
    loops, elims, ub = m._loops, m._elims, m._ub
    print('Final cycle matrix size:', len(loops))
    #simplify(g, loops, elims, ub)

    #run_IIS(g, loops, elims, ub)
    # Done. Now undo the  d['orig_edges'] mess.
    final_elims = []
    for edge in elims:
        final_elims.extend(origedges_map[edge])

    double_check(g_orig, ub, final_elims, is_labeled=True, log=print)
    return final_elims, ub, loops
Esempio n. 5
0
def rigorous_mfes(subgraph, cutoff):
    # It is just a convenience wrapper around solve_cm. Returns: 
    # (error msg, elims, obj, ncyc). The error message is empty iff successful.
    success, edges_per_cycle = get_all_cycles(subgraph, cutoff)
    if not success:
        return 'Too many simple cycles in relaxed SCC', None, None, None
    elims, objective = solve_cm(subgraph, edges_per_cycle)
    if elims is None:
        return 'Solver failure, giving up', None, None, None
    assert objective > 0 # in an SCC at least...
    double_check(subgraph, objective, elims)
    return '', elims, objective, len(edges_per_cycle) 
Esempio n. 6
0
def solve_problem(g_orig, stats=None):
    'Returns: [torn edges], cost.'
    elims, cost = [], 0
    for sc in noncopy_split_to_nontrivial_sccs(
            g_orig.copy()):  # <- Copy passed!
        partial_elims, partial_cost = solve_with_pcm(sc, stats)
        elims.extend(partial_elims)
        cost += partial_cost
    double_check(g_orig, cost, elims, is_labeled=True, log=log)
    log('Input graph')
    info(g_orig, log=log)
    return elims, cost
Esempio n. 7
0
def solve_problem(g_orig, stats=None, feasible_sol=None):
    'Returns: [torn edges], cost.'
    elims, cost = [], 0
    for sc in noncopy_split_to_nontrivial_sccs(
            g_orig.copy()):  # <- Copy passed!
        partial_elims, partial_cost = \
                            solve_triangle_inequalities(sc, stats, feasible_sol)
        elims.extend(partial_elims)
        cost += partial_cost
    double_check(g_orig, cost, elims, is_labeled=True)
    print('Input graph')
    info_short(g_orig)
    return elims, cost
Esempio n. 8
0
def solve_problem(g_orig, stats, feasible_sol=None):
    'Returns: [torn edges], cost.'
    elims, cost = [], 0
    g2 = g_orig.copy()
    # Remove self-loops
    for u, v, d in g_orig.selfloop_edges(data=True):
        g2.remove_edge(u, v)
        elims.append((u, v))
        cost += d['weight']
    cycle_matrix = []
    # Make each SCC acyclic
    for sc in noncopy_split_to_nontrivial_sccs(g2):
        partial_elims, partial_cost, loops = solve_with_pcm(
            sc, stats, feasible_sol)
        elims.extend(partial_elims)
        cost += partial_cost
        cycle_matrix.extend(loops)
    double_check(g_orig, cost, elims, is_labeled=True, log=log)
    log('Input graph')
    info(g_orig, log=log)
    return elims, cost, cycle_matrix
Esempio n. 9
0
def run_mfes_heuristic(g_input, try_one_cut=False, is_labeled=False):
    # Set the edge attributes 'weight' and 'orig_edges' if necessary
    g, copy_g = label_edges(g_input, is_labeled)
    #
    greedy_choice = no_lookahead if try_one_cut else with_lookahead
    #
    running_cost, elims = 0, []
    sccs, running_cost = iterate_cleanup(g, running_cost, elims, copy_g)
    info_after_cleanup(sccs, running_cost)
    while True:
        sccs_to_process = []
        for sc in sccs:
            log('-----------------------------------------------------------')
            #distributions(sc)
            new_state = greedy_choice(sc, running_cost, elims)
            new_sccs, elims, running_cost = new_state
            sccs_to_process.extend(new_sccs)
        sccs = sccs_to_process
        if len(sccs) == 0:
            # Make sure that what we return is at least consistent
            double_check(g_input, running_cost, elims, is_labeled, log=log)
            return running_cost, elims
Esempio n. 10
0
def extend_cm(m):
    g, ub, ub2 = m._g, m._ub, int(round(m.cbGet(GRB.Callback.MIPSOL_OBJ)))
    #if ub2 >= ub:
    #    return
    log('Callback with obj: ', ub2)
    #
    relax_elims, new_feas_elims, missed_loops = get_solution(m)
    if not missed_loops:
        if ub2 < ub:
            log('Relaxation became feasible and improved UB {} -> {}'.format(
                ub, ub2))
            m._elims, m._ub = relax_elims, ub2
            double_check(g, ub2, relax_elims, is_labeled=True, log=log)
        return
    #
    new_ub = sum(g[u][v]['weight'] for u, v in new_feas_elims)
    if new_ub < ub:
        log('Improved UB: {} -> {}'.format(ub, new_ub))
        double_check(g, new_ub, new_feas_elims, is_labeled=True, log=log)
        m._ub, m._elims = new_ub, new_feas_elims
    #
    extend_the_cycle_matrix(m, missed_loops)