Exemple #1
0
def branch(instance,instance_manager):
    #branches instance problem into complementary sub problem instances following various rules
    #adds new instances that have been correctly initialised to instance_manager 
    #returns success of branching 
    log.subtitle("entering branching",instance.id)
    initial_time = time()

    branching_candidates = []
    if index_branching:
        partial_time = time()
        log.write_awaiting_answer("index branching--> ")
        candidate = index_branching_candidates(instance)
        if candidate["instances"] != None:
            branching_candidates.append(candidate)
            log.write_answer(", score: "+str(branching_candidates[-1]["score"])+", time: "+str(round(time()-partial_time,2)))
            log.write_answer(", score: "+str(branching_candidates[-1]["score"])+", time: "+str(round(time()-partial_time,2)))
    if integer_branching:
        partial_time = time()
        log.write_awaiting_answer("integer branching--> ")
        candidate = integer_branching_candidates(instance)
        if candidate["instances"] != None:
            branching_candidates.append(candidate)
            log.write(", score: "+str(branching_candidates[-1]["score"])+", time: "+str(round(time()-partial_time,2)))
    
    if cpp_constraint_branching:
        partial_time = time()
        log.write_awaiting_answer("c++ constraint branching--> ")
        candidate = cpp_constraint_branching_candidate(instance)
        if candidate["instances"] != None:
            branching_candidates.append(candidate)
            log.write(", score: "+str(branching_candidates[-1]["score"])+", time: "+str(round(time()-partial_time,2)))
        
    if branching_candidates==[]:
        return False

    #coefficients = {"integer branching":1.1,"constraint branching":0.8,"index branching":1} #the bigger the coefficient, the lyklier it will be selected
    #best_candidate = sorted( branching_candidates, key = lambda candidate : candidate["score"]*coefficients[candidate["method"]], reverse=True )[0]
    best_candidate = select_best_candidate(branching_candidates,instance)
    log.write("BEST STRATEGY: "+str(best_candidate["method"]))
    
    #adding new instances to solving queue
    for instance in best_candidate["instances"]:
        set_lower_bound(instance)
        log.write("value of objective function is " +str(round(instance.objective_value,2)) + " for new instance " +list_to_string(instance.id)+" with lower bound "+str(instance.lower_bound)[:-1])
        instance_manager.add(instance)
    
    log.end_subtitle("end of branching, method: "+best_candidate["method"]+", smallest boost: "+str(best_candidate["score"]) +", time: "+str(round(time()-initial_time,2))+" seconds")
    return True
Exemple #2
0
def column_generation_loop(instance, instance_manager):
    # a signle loop of column generation : adds all available types of cuts to the instance
    # also verifies if a feasible integer solution is found

    #0) check if solution is integer and has valid paths
    if managing.solution_is_integer(instance):
        instance0 = instance.clone()
        managing.integerize_solution(instance0)
        if paths_are_feasible(instance0):
            log.subtitle(
                "!!!!! feasible integer solution found with objective value of "
                + str(round(instance0.objective_value, 2)), instance0.id)
            assertion = " " if instance_manager.upper_bound > instance0.objective_value else " not "
            log.write(
                "new solution is" + assertion +
                "better than one previously found, this branch is dropped and its solution is"
                + assertion + "recorded")
            instance_manager.record_feasible_integer_solution(instance0)
            return True, False

    if column_input == "python":
        raise Exception(
            "hand implemented heuristics have been commented out in cap_constraint.py for more clarity in code reading, these methods are outdated in comparaison to the c++ library. If you whish to test them, uncomment them and comment this exception out"
        )
        #1) adding capacity cuts
        #success, count = cap.add_c_cap(instance)
        #log.write_awaiting_answer("capacity cuts: " +str(count))

        #2),3),4) ...
        # other types of cuts to be added

    elif column_input == "c++":
        success = cpp.generate_and_add_columns(instance)

    else:
        raise NameError("Enter a valide column input type")

    remove_inactive_constraints(instance)

    return False, success
def initialize_instance(instance):
    # takes an instsanciated pyomo concrete model and adds all the custom attributes that will be used later in the process
    # among others things it will : define the variables, add the constraint containers, delete the variables that are not usefull (we only keep the strictly inferior trianble of the X matrix),
    # delete the locations matrix for it is not necessary, we keep it as a seperate variable that will be used only by the graphing functions
    if instance.entry_type.value == "COORD":
        ''' we need to retrieve the locations and delete them from all instances for smaller size'''
        locations = to_list_locations(
            instance)  #global parameter used for graphing of solutions
        del (instance.locations)  # WORKS!

    else:  #entry type is necessarily "WEIGHT"
        ''' we need to generate random coordinates for the plotting '''
        locations = [(random() * 20, random() * 20)
                     for i in range(instance.n.value)]

    max_cost = 0
    for k in instance.costs.values():
        if k > max_cost:
            max_cost = k
    instance.max_cost = max_cost
    #managing.normalize_costs(instance)
    ''' define variable x '''
    instance.x = pyo.Var(instance.nodes, instance.nodes, bounds=set_bounds)
    #deleting uneused variables (i<=j)
    for i in instance.nodes:
        for j in instance.nodes:
            if i <= j:
                del (instance.x[i, j])
                # instance.x[i,j].deactivate()
    ''' define flow variable '''
    instance.flow = pyo.Var(instance.nodes,
                            instance.nodes,
                            bounds=set_bounds_flow)
    ''' define the objective function '''
    instance.objective = pyo.Objective(expr=sum(
        instance.costs[i, j] * instance.x[i, j] for i in instance.nodes
        for j in instance.nodes if i > j))
    ''' define degree constraints '''
    instance.c_deg = pyo.Constraint(instance.nodes, rule=rule_deg)
    ''' define flow constraints '''
    if flow_constraints:
        instance.c_flow = pyo.Constraint(instance.nodes, rule=rule_flow)
        instance.c_flow_deg = pyo.Constraint(instance.nodes,
                                             instance.nodes,
                                             rule=rule_flow_deg)
        instance.c_flow_deg_indirect = pyo.Constraint(
            instance.nodes, instance.nodes, rule=rule_flow_deg_indirect)
    ''' define capacity constraints as an empty list for now '''
    instance.c_cap = pyo.ConstraintList(
    )  # on utilise cette structure pour pouvoir ajouter et supprimer des contraintes par la suite
    instance.c_mstar = pyo.ConstraintList()
    instance.c_glm = pyo.ConstraintList()
    instance.c_fci = pyo.ConstraintList()
    instance.c_sci = pyo.ConstraintList()
    instance.c_hti = pyo.ConstraintList()

    #liste vide pour commencer
    ''' defining dual values '''
    instance.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT)
    ''' definig list of values that will be fixed by branching (used for problem reduction) '''
    ''' and the list of constraints used for branching if constraint branching is used '''
    instance.branching_indexes = []
    if cpp_constraint_branching:
        instance.c_branch = pyo.ConstraintList()
        instance.branching_sets = []
    if not (index_branching) and not (integer_branching) and not (
            cpp_constraint_branching):
        log.subtitle(
            "instance generation failed because no valid branching type is given"
        )
        raise NameError("enter a valid branching type in globals settings")
    ''' defining wether or not the instance can be subject to constraint deactivation '''
    if disable_constraint_deactivation or integer_branching or integer_simplifications:
        log.write(
            "disabling constraint deactivation because integer variables are activated (integer branching or simplifications)"
        )
        instance.disable_constraint_deactivation = True
        instance.constraints_inactivity = {}
    else:
        instance.disable_constraint_deactivation = False
        instance.constraints_inactivity = {}
    ''' defining others parameters based on those in global.py '''
    instance.reduction = 1
    instance.id = [0]
    instance.depth = 0

    if reduce_problem:
        managing.reduce_problem(instance, initial_problem_reduction)
    #managing.reduce_problem_neighboors(instance,5)
    if force_integer_to_depot:
        managing.integerize_edges_to_depot(instance)
        log.write("integerized " + str(instance.n.value) + " edges to depot " +
                  str(len(list(instance.x.keys()))) + " variables")
    if force_all_edges_integer:
        managing.integerize_all_edges(instance)
    if force_closest_neighboors_integer:
        integerized = managing.integerize_edges(
            instance, max_dist=relative_distance_closest_neighboor)
        log.write("integerized " + str(len(integerized)) +
                  " shortest edges out of " +
                  str(len(list(instance.x.keys()))) +
                  " variables with a relative distance of " +
                  str(relative_distance_closest_neighboor * 100) + '%' +
                  " of max_distance")
    '''solving the initial instance in order to initialize instance.x values '''
    opt.solve(instance)
    instance.objective_value = pyo.value(
        instance.objective
    )  #recording this as attribute to save computation time

    log.write_timed("finished constructing instance model")
    return instance, locations
Exemple #4
0
log.write("initial upper bound of cvrp problem is "+str(instance_manager.upper_bound))

''' printing initial value of objective function '''
log.write("initial value of objective function "+str(round(instance.objective_value,2))+" and is "+str(managing.integer_percent(instance))+"% integer")

'''saving initial graph  '''
full_graph(instance,locations,"initial")

''' list of old instances used for the graping of the search Tree '''
old_nodes = []

''' actual branch and cut '''
instance = instance_manager.pop()
while instance!=None and max_time_not_reached() and instance.depth<=max_depth:		
	log.subtitle("starting processing of new instance with lower_bound of "+str(instance.lower_bound)+" ( upper_bound is "+str(instance_manager.upper_bound)+") ,depth: "+ str(instance.depth) +", global time: "+str(global_time()),instance.id)
	
	if search_tree == "complete" or search_tree == "end":
		old_nodes.append(instance.clone())
	if search_tree == "complete":
		SearchTree(instance_manager,old_nodes).show()
	if queue_logging:
		queue_log.write(instance_manager)
	
	if graph_current_solution:
		full_graph(instance,locations,"current solution",True)

	if last_push_integer and (instance_manager.upper_bound - instance.objective_value)/instance_manager.upper_bound <= last_push_integer_thresh :
		instance.disable_constraint_deactivation = True
		integerized = managing.integerize_edges(instance,smart = True)
		log.subtitle("instance is close enough to upper bound "+ str(round((1-(instance_manager.upper_bound - instance.objective_value)/instance_manager.upper_bound)*100,2)) +"% : we integerize "+str(len(integerized))+" variables",instance.id)
Exemple #5
0
def column_generation(instance, instance_manager):
    # general method that will apply all available cut finding algorithms in a loop whose parameters are defined in globals.py
    log.subtitle("entering column generation", instance.id)
    initial_time, feasible_integer_found, loop_count, unmoving_count, solver_success, failure_count, obj_val_old, obj_val_old_global = round(
        time(), 2
    ), False, 1, 0, True, 0, instance.objective_value, instance.objective_value
    while instance.objective_value < instance_manager.upper_bound and loop_count <= (
            max_column_generation_count if len(instance.id) > 1 else
            max_column_generation_count_first_instance) and unmoving_count <= (
                max_unmoving_count if len(instance.id) > 1 else
                max_unmoving_count_first_instance) and not (
                    feasible_integer_found) and solver_success:
        log.write_awaiting_answer("loop " + str(loop_count) + "--> ",
                                  instance.id)
        # adding cuts
        feasible_integer_found, cuts_found = column_generation_loop(
            instance, instance_manager)
        #solving updated instance
        if cuts_found:
            solver_success = managing.solve(instance)

        #different cases are verified

        # we verify if the adding cuts method is a failure
        if not (cuts_found) and not (feasible_integer_found):
            log.write_awaiting_answer("all heurisitcs have failed")

        if not (feasible_integer_found):
            log.write_answer("improvement: " + str(
                round(
                    100 * (instance.objective_value - obj_val_old) /
                    obj_val_old, 5)) + '%' + ", objective: " +
                             str(round(instance.objective_value, 5)) + ", " +
                             str(managing.integer_percent(instance)) + '%' +
                             " integer, unmoving count:" +
                             str(unmoving_count) + ", reduction: " +
                             str(instance.reduction))

        # we verify if the objective value is increasing, if not : the cuts found have no effect
        if (instance.objective_value -
                obj_val_old) / obj_val_old < unmoving_constraint_threshold:
            unmoving_count += 1
            if unmoving_count >= (max_unmoving_count if instance.depth > 0 else
                                  max_unmoving_count_first_instance):
                log.write(
                    "no evolution in column_generation, moving on to branching",
                    instance.id)
                break
        else:
            unmoving_count = 0

        # we verifiy if the objective value is decreasing : if it is, we have probably dropped constraints that were usefull
        if (instance.objective_value - obj_val_old) / obj_val_old < -0.0001:
            log.write(
                "we are losing objective function value! useful constraints were dropped",
                instance.id)

        # we count the number of consecutive total failure
        if not (cuts_found):
            failure_count += 1
            if failure_count > max_failure_count_cuts:
                log.write(
                    "no evolution in column_generation, moving on to branching",
                    instance.id)
                break
        else:
            failure_count = 0

        obj_val_old = instance.objective_value
        loop_count += 1

    log.end_subtitle(
        "end of column generation, gain " +
        str(instance.objective_value - obj_val_old_global) + ", objective: " +
        str(instance.objective_value) + ", time: " +
        str(round(time() - initial_time, 2)), instance.id)
    return feasible_integer_found, solver_success