示例#1
0
def solve(data, recompute, display, load, callback, time, **args):
    R, C, slices, areas, overlap = preprocess(data, recompute, display)

    with localsolver.LocalSolver() as ls:
        model = ls.model

        # Variables
        x = [model.bool() for i in range(len(slices))]

        # Constraints
        for i in range(R):
            if display and i % 50 == 0: print(str(i) + '/' + str(R))
            for j in range(C):
                model.constraint(model.sum(x[s] for s in overlap[i][j]) <= 1)

        # Objective
        model.maximize(model.sum(s * area for s, area in zip(x, areas)))

        model.close()

        if callback: set_callback(ls, slices, x)
        if load: load_initial_position(load, slices, x)

        ls.create_phase().time_limit = int(time)

        ls.solve()

        solution = retrieve_solution(slices, x)

        return solution
示例#2
0
 def __init__(self, graph, options):
     self.graph = graph
     self.ls = localsolver.LocalSolver()
     self.model = self.ls.get_model()
     self.options = options
     # Constraints + starting point
     self.coarsening = []
     # Decisions
     self.node_placement = None
     # Debug
     self.edge_counters = []
     self.edge_degrees = []
     self.edge_costs = []
def solve(filename):
    with localsolver.LocalSolver() as ls:

        #
        # Reads instance data
        #

        file_it = iter(read_integers(filename))
        # Number of vertices
        n = next(file_it)
        # Number of edges
        m = next(file_it)

        # Origin of each edge
        origin = [None] * m
        # Destination of each edge
        dest = [None] * m
        # Weight of each edge
        w = [None] * m

        for e in range(m):
            origin[e] = next(file_it)
            dest[e] = next(file_it)
            w[e] = next(file_it)

        model = ls.model

        # Decision variables x[i]
        # Is true if vertex x[i] is on the right side of the cut and false if it is on the left side of the cut
        x = [model.bool() for i in range(n)]

        # incut[e] is true if its endpoints are in different class of the partition
        incut = [None] * m
        for e in range(m):
            incut[e] = model.neq(x[origin[e] - 1], x[dest[e] - 1])

        # Size of the cut
        cut_weight = model.sum(w[e] * incut[e] for e in range(m))
        model.maximize(cut_weight)

        model.close()

        #
        # Param
        #
        if len(sys.argv) >= 4: ls.param.time_limit = int(sys.argv[3])
        else: ls.param.time_limit = 10

        ls.solve()
        return ls
示例#4
0
    def solve(self, instance, time_limit=5):

        instance = np.array(instance)
        if self.maximize:
            np.fill_diagonal(instance, 0)
            instance = instance.max(
            ) - instance  # transformation function (similarity -> distance)

        instance = np.pad(
            instance,
            ((0, 1), (0, 1)),
            mode='constant',
            constant_values=0  # dummy node
        )
        np.fill_diagonal(instance, 1e7)  #instance.max()) # self loops
        num_cities = instance.shape[0]

        with localsolver.LocalSolver() as ls:
            # model
            model = ls.model
            cities = model.list(num_cities)
            model.constraint(model.count(cities) == num_cities)
            distance_array = model.array(instance.tolist())

            # minimize the total distance
            dist_selector = model.function(
                lambda i: model.at(distance_array, cities[i - 1], cities[i]))
            obj = (model.sum(model.range(1, num_cities), dist_selector) +
                   model.at(distance_array, cities[num_cities - 1], cities[0]))
            model.minimize(obj)
            model.close()

            # time limit
            ls.create_phase().time_limit = time_limit
            ls.solve()
            solution = [c for c in cities.value]

        dummy_idx = solution.index(num_cities - 1)
        self.solution = solution[dummy_idx + 1:] + solution[:dummy_idx]
        return self
示例#5
0
########## knapsack.py ##########

import localsolver
import sys

if len(sys.argv) < 2:
    print("Usage: python knapsack.py inputFile [outputFile] [timeLimit]")
    sys.exit(1)


def read_integers(filename):
    with open(filename) as f:
        return [int(elem) for elem in f.read().split()]


with localsolver.LocalSolver() as ls:

    #
    # Reads instance data
    #

    file_it = iter(read_integers(sys.argv[1]))

    # Number of items
    nb_items = next(file_it)

    # Items properties
    weights = [next(file_it) for i in range(nb_items)]
    values = [next(file_it) for i in range(nb_items)]

    # Knapsack bound
示例#6
0
def solve(data, load, callback, time, **args):
    # data = preprocess(data)
    R, C, F, N, B, T, demand = data

    with localsolver.LocalSolver() as ls:
        model = ls.model

        # Data
        times = model.array(build_times([((0, 0), (0, 0), 0, 0)] + demand))
        max_lates = model.array([e - s for _, _, s, e in demand])

        # Variables
        cars = [model.list(N) for i in range(F)]

        # Expressions
        def make_lambda(k):
            return lambda i, prev: model.max(
                0, prev + model.iif(
                    i == 0, model.at(times, 0, cars[k][0] + 1),
                    model.at(times, cars[k][i - 1] + 1, cars[k][i] + 1)))

        lates = [
            model.array(model.range(0, N), model.function(make_lambda(k)))
            for k in range(F)
        ]

        # Constraints
        model.constraint(model.disjoint(*cars))
        for late, car in zip(lates, cars):
            for i in range(N):
                model.constraint(
                    model.count(car) <= model.create_constant(i)
                    or late[i] <= model.at(max_lates, car[i]))

        # Objective
        model.maximize(model.sum([model.count(car) for car in cars]))

        model.close()

        l = cars[0].get_value()
        l.clear()
        l.add(0)

        a = cars[1].get_value()
        a.clear()
        a.add(1)
        a.add(2)

        if callback: set_callback(ls)
        if load: load_initial_position(load)

        ls.create_phase().time_limit = int(time)

        ls.solve()

        for car in cars:
            print(car.value)
        for late in lates:
            print(late.value)
        print(max_lates.value)
        print(build_times([((0, 0), (0, 0), 0, 0)] + demand))

        # solution = retrieve_solution(cars, lates, N)

        print(ls.compute_inconsistency())

        return []
示例#7
0
def main(instance_file, str_time_limit, sol_file, str_nb_trucks,
         truck_capacity, demands_for_day):
    nb_trucks = int(str_nb_trucks)
    mapIndex = {}
    #
    # Reads instance data
    #
    (nb_customers, truck_capacity, distance_matrix, distance_warehouses,
     dist_warehouses, demands, mapIndex, timePV, time_wh_to_pv,
     pv_for_time) = read_excel(instance_file, mapIndex, int(truck_capacity),
                               demands_for_day)

    if nb_trucks == 0:
        nb_trucks = get_nb_trucks(instance_file)

    with localsolver.LocalSolver() as ls:
        #
        # Declares the optimization model
        #
        model = ls.model

        # Sequence of customers visited by each truck.
        customers_sequences = [
            model.list(nb_customers) for k in range(nb_trucks)
        ]

        # All customers must be visited by  the trucks
        model.constraint(model.partition(customers_sequences))

        # Create demands as an array to be able to access it with an "at" operator
        demands_array = model.array(demands)

        # Create distance as an array to be able to acces it with an "at" operator
        distance_array = model.array()

        for n in range(nb_customers):
            distance_array.add_operand(model.array(distance_matrix[n]))

        distance_warehouse_array = model.array(distance_warehouses)

        route_distances = [None for n in range(nb_trucks)]  #

        # A truck is used if it visits at least one customer
        trucks_used = [(model.count(customers_sequences[k]) > 0)
                       for k in range(nb_trucks)]
        nb_trucks_used = model.sum(trucks_used)

        #for k in range(nb_trucks):
        for k in range(nb_trucks):
            sequence = customers_sequences[k]
            c = model.count(sequence)

            # Quantity in each truck
            demand_selector = model.function(
                lambda i: model.at(demands_array, sequence[i]))
            route_quantity = model.sum(model.range(0, c), demand_selector)
            model.constraint(route_quantity <= truck_capacity)

            # Distance traveled by each truck
            dist_selector = model.function(lambda i: model.at(
                distance_array, sequence[i - 1], sequence[i]))
            route_distances[k] = model.sum(model.range(1,c), dist_selector) + \
                 model.iif(c > 0, model.at(distance_warehouse_array, sequence[0]) + model.at(distance_warehouse_array, sequence[c-1]),0)

        # Total distance travelled
        total_distance = model.sum(route_distances)
        #print(route_distances.get_value())

        # Objective: minimize the number of trucks used, then minimize the distance travelled
        model.minimize(nb_trucks_used)
        model.minimize(total_distance)

        model.close()

        #
        # Parameterizes the solver
        #
        ls.param.time_limit = int(str_time_limit)

        ls.solve()

        #
        # Writes the solution in a file with the following format:
        #  - number of routes and total distance
        #  - for each routes the nodes visited (omitting the start/end at the depot)
        #

        if len(sys.argv) >= 3:
            with open("../results/" + sol_file, 'w') as f:
                f.write("%d %d\n" %
                        (nb_trucks_used.value, total_distance.value))
                for k in range(nb_trucks):
                    if (trucks_used[k].value != 1): continue
                    # Values in sequence are in [0..nbCustomers-1]. +2 is to put it back in [2..nbCustomers+1]
                    # as in the data files (1 being the depot)
                    for customer in customers_sequences[k].value:
                        f.write("%d " % (customer + 2))
                    f.write("\n")

    add_time = []
    add_time_temp = []
    sum_time = 0
    sum_time_route = []
    sum_dist = 0
    sum_dist_route = []
    sumQTA = 0
    sum_QTA_route = []

    with open("../results/" + sol_file, "r") as fd:
        for line in fd:
            add_time.append(line)
        fd.close()

    for n, el in enumerate(add_time):
        if (n != 0):
            add_time_temp = el.split(" ")
            for k, elem in enumerate(add_time_temp):
                if (elem != "\n"):
                    if (k > 0):
                        #print(add_time_temp[k-1])
                        #print(elem)
                        #print(distance_matrix[ int(add_time_temp[k-1]) -2][int(elem) -2])
                        sum_time += timePV[int(add_time_temp[k - 1]) -
                                           2][int(elem) - 2]
                        sum_dist += distance_matrix[int(add_time_temp[k - 1]) -
                                                    2][int(elem) - 2]
                    sumQTA += demands[int(add_time_temp[k]) - 2]
            sum_time_route.append(sum_time)
            sum_dist_route.append(sum_dist)
            sum_QTA_route.append(sumQTA)
            sum_time = 0
            sum_dist = 0
            sumQTA = 0

    # Write solution as pv id.
    write_results(mapIndex, "../results/" + sol_file)

    file_sol = []
    with open("../results/" + sol_file, "r") as fd:
        for line in fd:
            file_sol.append(line)
        fd.close()

    tempList = []

    for n, el in enumerate(file_sol):
        if (n != 0):
            tempList = el.split(" ")
            initNode = tempList[0]
            endNode = tempList[-2]
            # Time
            sum_time_route[n - 1] += time_wh_to_pv[pv_for_time.index(
                int(initNode))]
            sum_time_route[n - 1] += time_wh_to_pv[pv_for_time.index(
                int(endNode))]
            # Distance from/to depot
            #print(pv_for_time.index(int(initNode)))
            #print(dist_warehouses[pv_for_time.index(int(initNode))])
            sum_dist_route[n - 1] += dist_warehouses[pv_for_time.index(
                int(initNode))]
            sum_dist_route[n - 1] += dist_warehouses[pv_for_time.index(
                int(endNode))]

    # Draw graph of track's route
    draw_graph("../results/" + sol_file,
               sum_time_route,
               sum_dist_route,
               sum_QTA_route,
               truck_capacity,
               warehouse="434")
示例#8
0
def main(instance_file, str_time_limit, sol_file, str_nb_trucks):
    nb_trucks = int(str_nb_trucks)

    #
    # Reads instance data
    #
    (nb_customers, truck_capacity, distance_matrix, distance_warehouses,
     demands) = read_input_cvrp(instance_file)

    # The number of trucks is usually given in the name of the file
    # nb_trucks can also be given in command line
    if nb_trucks == 0:
        nb_trucks = get_nb_trucks(instance_file)

    with localsolver.LocalSolver() as ls:
        #
        # Declares the optimization model
        #
        model = ls.model

        # Sequence of customers visited by each truck.
        customers_sequences = [
            model.list(nb_customers) for k in range(nb_trucks)
        ]

        # All customers must be visited by the trucks
        model.constraint(model.partition(customers_sequences))

        # Create demands as an array to be able to access it with an "at" operator
        demands_array = model.array(demands)

        # Create distance as an array to be able to acces it with an "at" operator
        distance_array = model.array()
        for n in range(nb_customers):
            distance_array.add_operand(model.array(distance_matrix[n]))

        distance_warehouse_array = model.array(distance_warehouses)

        route_distances = [None] * nb_trucks

        # A truck is used if it visits at least one customer
        trucks_used = [(model.count(customers_sequences[k]) > 0)
                       for k in range(nb_trucks)]
        nb_trucks_used = model.sum(trucks_used)

        for k in range(nb_trucks):
            sequence = customers_sequences[k]
            c = model.count(sequence)

            # Quantity in each truck
            demand_selector = model.lambda_function(
                lambda i: demands_array[sequence[i]])
            route_quantity = model.sum(model.range(0, c), demand_selector)
            model.constraint(route_quantity <= truck_capacity)

            # Distance traveled by each truck
            dist_selector = model.lambda_function(lambda i: model.at(
                distance_array, sequence[i - 1], sequence[i]))
            route_distances[k] = model.sum(model.range(1, c), dist_selector) + \
                 model.iif(c > 0, distance_warehouse_array[sequence[0]] + distance_warehouse_array[sequence[c-1]], 0)

        # Total distance traveled
        total_distance = model.sum(route_distances)

        # Objective: minimize the number of trucks used, then minimize the distance traveled
        model.minimize(nb_trucks_used)
        model.minimize(total_distance)

        model.close()

        #
        # Parameterizes the solver
        #
        ls.param.time_limit = int(str_time_limit)

        ls.solve()

        #
        # Writes the solution in a file with the following format:
        #  - number of trucks used and total distance
        #  - for each truck the nodes visited (omitting the start/end at the depot)
        #
        if len(sys.argv) >= 3:
            with open(sol_file, 'w') as f:
                f.write("%d %d\n" %
                        (nb_trucks_used.value, total_distance.value))
                for k in range(nb_trucks):
                    if trucks_used[k].value != 1: continue
                    # Values in sequence are in [0..nbCustomers-1]. +2 is to put it back in [2..nbCustomers+1]
                    # as in the data files (1 being the depot)
                    for customer in customers_sequences[k].value:
                        f.write("%d " % (customer + 2))
                    f.write("\n")
示例#9
0
    def solve(self):
        """
        Solves the instance.
        :return:
        """
        '''
        #calculate a new equivalent non directed exchanges,
        # (in order to remove the cases where we have the double arrow source -> target and target -> source

        exchanges2 = {}
        for source in self.instance.get_all_sources():
            exchanges_s =  self.instance.exchanges[source]
            for target,v  in exchanges_s.items():
                s = source
                t = target
                if source > target:
                   s,t = t,s

                if s not in exchanges2:
                    exchanges2[s] = {}
                exchanges2_s = exchanges2[s]
                if t not in exchanges2_s:
                    exchanges2_s[t] = v
                else:
                    exchanges2_s[t] += v
        '''
        exchanges2 = self.instance.exchanges
        # Maximum number of clusters, to be set to number of OBEs
        nb_max_cluster = len(self.instance.get_all_nodes())

        with localsolver.LocalSolver() as ls:
            # Declares the optimization model
            self.model = ls.model
            # Set decisions: cluster_list[k] represents the OBEs in cluster k

            clusters_list = [
                self.model.set(self.nb_nodes) for k in range(self.nb_clusters)
            ]

            # Each OBE must be in one cluster and one cluster only
            self.model.constraint(self.model.partition(clusters_list))

            # translation int to OBE name:
            obeToInt = {}
            intToObe = []

            for count, val in enumerate(self.instance.get_all_nodes()):
                obeToInt[val] = count
                intToObe.append(val)

            #x[n][k]: is n in cluster #k
            x = {}
            for n in self.instance.get_all_nodes():
                x[n] = []
                for k in clusters_list:
                    x[n].append(self.model.contains(k, obeToInt[n]))

            #y[k][s][t]: (cluster, source name, target name): is the arrow s->t inner to the cluster k
            #z[s][t] (not used): indexed by OBE names (source and target): is the arrow s->t inner to the same cluster
            y = {}
            z = {}
            for source in exchanges2:
                z[source] = {}
                z_s = z[source]
                for target in exchanges2[source]:
                    z_s_t_k = []
                    for k in range(0, self.nb_clusters):
                        if k not in y:
                            y[k] = {}
                        y_k = y[k]
                        if source not in y_k:
                            y_k[source] = {}
                        y_k_s = y_k[source]
                        a = self.model.and_(x[source][k], x[target][k])
                        y_k_s[target] = a
                        z_s_t_k.append(a)
                    z_s[target] = self.model.or_(z_s_t_k)
            '''
            #internal traffic: definition using z (not used)
            internal_traffic = []
          
            for source in exchanges2:
                for target,exchange in exchanges2[source].items():
                    internal_traffic.append(
                        self.model.iif(z[source][target],
                            exchange,
                            0))
            total_traffic = self.model.sum(internal_traffic)
            '''

            #cluster sizes:
            cluster_sizes = []
            for k in clusters_list:
                cluster_sizes.append(self.model.count(k))

            #internal traffic per cluster:
            total_traffic_k = []

            for k in range(0, len(clusters_list)):
                weights_in_cluster = []
                for source in exchanges2:
                    for target, exchange in exchanges2[source].items():
                        weights_in_cluster.append(
                            self.model.iif(y[k][source][target], exchange, 0))
                total_traffic_k.append(self.model.sum(weights_in_cluster))

            #internal traffic
            total_traffic = self.model.sum(total_traffic_k)

            #nb elmt in cluster
            cluster_count = [self.model.count(k) for k in clusters_list]

            #min_cluster_size (if cluster is not empty)
            min_cluster_size = self.min_cluster_size

            if self.min_cluster_size is None and self.force_nb_clusters:
                min_cluster_size = 1
            if min_cluster_size is not None and min_cluster_size > 0:
                for k_idx, k in enumerate(clusters_list):
                    if self.force_nb_clusters:
                        self.model.add_constraint(
                            cluster_count[k_idx] >= min_cluster_size)
                    else:
                        self.model.add_constraint(
                            self.model.or_(
                                cluster_count[k_idx] >= min_cluster_size,
                                cluster_count[k_idx] == 0))

            #max_cluster_size
            if self.max_cluster_size is not None:
                for k in clusters_list:
                    self.model.add_constraint(
                        self.model.count(k) <= self.max_cluster_size)

            #min_cluster_weight (if cluster is not empty)
            if self.min_cluster_weight is not None and self.min_cluster_weight > 0:
                for k_idx, k in enumerate(total_traffic_k):
                    if self.force_nb_clusters:
                        self.model.add_constraint(k >= self.min_cluster_weight)
                    else:
                        self.model.add_constraint(
                            self.model.or_(k >= self.min_cluster_weight,
                                           cluster_count[k_idx] == 0))

            for count, node in enumerate(self.separated_nodes):
                self.model.add_constraint(
                    self.model.contains(clusters_list[count], obeToInt[node]))

            self.model.maximize(total_traffic)

            # heuristic: we add an equilibrium score in order to have clusters with similar weights
            # let's try to optimize the sum -ln(traffic_proportion_in_cluster)*cluster_traffic
            '''
            equilibrum_score = 1-4*self.model.sum([k*self.model.log((k+0.000000001)/total_traffic)
                                                   for k in total_traffic_k])/(total_traffic*math.log(self.nb_clusters))
            heuristic_total_traffics = total_traffic*equilibrum_score

            self.model.maximize(heuristic_total_traffics)
            '''

            if self.time_limit is not None:
                phase = ls.create_phase()
                phase.set_optimized_objective(0)
                phase.set_time_limit(self.time_limit)

            # close the model
            self.model.close()

            # Parameterizes the solver

            # solve model
            start = time.time()
            ls.solve()
            end = time.time()

            self.running_time = end - start
            logging.info("Running time (sec.) = {}".format(self.running_time))

            status = ls.solution.get_status()
            if status in (localsolver.LSSolutionStatus.FEASIBLE,
                          localsolver.LSSolutionStatus.OPTIMAL):
                logging.info("sum internal traffic = {}".format(
                    total_traffic.value))
                #logging.info("equilibrium score " + str(equilibrum_score.value))
                for node in self.instance.get_all_nodes():
                    for k in range(self.nb_clusters):
                        if x[node][k].value == 1:
                            self.solution[node] = k
                            break
                self.instance.set_solution(self.solution)

                # Writes the solution in a file
                with open('./LS_solution.txt', 'w') as f:
                    f.write("%d\n" % total_traffic.value)
                    for cluster in range(self.nb_clusters):
                        for obe in self.instance.get_all_nodes():
                            f.write("%s " % x[obe][cluster].value)
                        f.write("\n")
            else:
                self.infeasible = True
                logging.warning("Problem has no solution")
示例#10
0
    def ls_problem(self,
                   liste_tic=False,
                   liste_job=False,
                   instance=0,
                   no_pen=False,
                   affectation={},
                   dual=-1,
                   time_limit=10):
        ###############################################################################
        ###################    initialisation variables   #############################
        ###############################################################################
        res = ls_prob()
        if (liste_tic == False):
            TICS = copy.deepcopy(constantes.TICS)
##restriction du problème à un sous ensemble de tic
        else:
            TICS = copy.deepcopy(constantes.TICS)
            TICS = [t for t in TICS if t.id in liste_tic]

        if (liste_job == False):
            JOBS = copy.deepcopy(constantes.JOBS)
##restriction du problème à un sous ensemble de tic et de job
        else:
            JOBS = copy.deepcopy(constantes.JOBS)
            JOBS = [j for j in JOBS if j.id in liste_job]

        res.JOBS = JOBS
        res.TICS = TICS
        ind_jobs = [j.id for j in JOBS]
        ind_jobs.insert(0, 0)
        ind_tics = [t.id for t in TICS]
        ind_tics_2 = [t.id for t in TICS]
        ind_tics_2.insert(0, 0)

        ####creation des données initiales pourlocalsolver
        nb_job = len(res.JOBS)
        nb_tic = len(res.TICS)
        t_min = [j.t_min for j in res.JOBS]
        t_max = [j.t_max for j in res.JOBS]
        t_start = [t.t_start for t in res.TICS]
        t_end = [t.t_end for t in res.TICS]
        dur = [j.dur for j in res.JOBS]

        ###############################################################################
        ########################    localsolver   #####################################
        ###############################################################################
        with localsolver.LocalSolver() as ls:
            #
            # Declares the optimization model
            #
            model = ls.model

            # Sequence of customers visited by each truck.
            jobs_sequences = [model.list(nb_job) for k in range(nb_tic)]
            ##ajout des contraintes de compétences
            #localsolver.LSOperator.INDEXOF(jobs_sequences[1],1)==-1
            # All customers must be visited by  the trucks
            ##TODO creer technicien fictif
            for k in range(nb_tic):
                cmp_list = TICS[k].cmp_list
                #cmp_list=[t for t in TICS if t.id==k+1][0].cmp_list
                for j in range(nb_job):
                    comp = JOBS[j].cmp
                    #comp=[jj for jj in JOBS if jj.id==j+1][0].cmp
                    if comp not in cmp_list:
                        model.constraint(
                            model.index(jobs_sequences[k], j) == -1)
            model.constraint(model.partition(jobs_sequences))

            # Create demands, earliest, latest and service as arrays to be able to access it with an "at" operator
            t_min_array = model.array(t_min)
            t_max_array = model.array(t_max)
            t_start_array = model.array(t_start)
            t_end_array = model.array(t_end)
            dur_array = model.array(dur)
            # Create distance as an array to be able to acces it with an "at" operator
            distance_array = model.array()
            temps_array = model.array()
            for j1 in res.JOBS:
                distance_matrix = []
                temps_matrix = []
                for j2 in res.JOBS:
                    distance_matrix.append(job.distance(j1, j2))
                    temps_matrix.append(job.temps(j1, j2))
                distance_array.add_operand(model.array(distance_matrix))
                temps_array.add_operand(model.array(temps_matrix))

            distance_warehouse_array = model.array()
            temps_warehouse_array = model.array()
            for t in res.TICS:
                distance_warehouse_matrix = []
                temps_warehouse_matrix = []
                for j1 in res.JOBS:
                    distance_warehouse_matrix.append(job.distance(t, j1))
                    temps_warehouse_matrix.append(job.temps(t, j1))
                distance_warehouse_array.add_operand(
                    model.array(distance_warehouse_matrix))
                temps_warehouse_array.add_operand(
                    model.array(temps_warehouse_matrix))

            route_distances = [None for n in res.TICS]
            end_time = [None for n in res.TICS]
            test_temp = [None for n in res.TICS]
            home_lateness = [None for n in res.TICS]
            lateness = [None for n in res.TICS]

            # A truck is used if it visits at least one customer
            trucks_used = [(model.count(jobs_sequences[k]) > 0)
                           for k in range(nb_tic)]
            #nb_trucks_used = model.sum(trucks_used)

            for k in range(nb_tic):
                sequence = jobs_sequences[k]
                c = model.count(sequence)

                # Distance traveled by each truck
                dist_selector = model.function(lambda i: model.at(
                    distance_array, sequence[i - 1], sequence[i]))
                route_distances[k] = model.sum(model.range(1,c), dist_selector) + \
                     model.iif(c > 0, model.at(distance_warehouse_array,k,sequence[0]) + model.at(distance_warehouse_array,k, sequence[c-1]),0)

                # End of each visit
                ##TODO a modifier pourauthoriser un temps d'attente?
                end_selector = model.function(lambda i, prev: model.max(t_min_array[sequence[i]],
                            model.iif(i == 0,model.at(t_start_array,k)+model.at(temps_warehouse_array,k,sequence[0]), \
                                      prev + model.at(temps_array,sequence[i-1],sequence[i]))) + \
                            model.at(dur_array, sequence[i]))
                end_time[k] = model.array(model.range(0, c), end_selector)

                #test_temp[k] = end_time[k][0]

                # Arriving home after max_horizon
                home_lateness[k] = model.iif(trucks_used[k], \
                   model.max(0,model.at(end_time[k],c-1) + model.at(temps_warehouse_array,k,sequence[c-1])-t_end[k]), \
                    0)

                # completing visit after latest_end
                ##TODO retirer dur du job
                late_selector = model.function(lambda i: model.max(
                    0,
                    model.at(end_time[k], i) - model.at(
                        dur_array, sequence[i]) - model.at(
                            t_max_array, sequence[i])))
                lateness[k] = home_lateness[k] + model.sum(
                    model.range(0, c), late_selector)

            # Total lateness
            total_lateness = model.sum(lateness)

            # Total distance travelled
            total_distance = model.sum(route_distances)

            # Objective: minimize the number of trucks used, then minimize the distance travelled
            model.minimize(total_lateness)
            model.minimize(total_distance)

            model.close()

            #
            # Parameterizes the solver
            #

            ls.create_phase().time_limit = time_limit
            ls.solve()

            #
            # Writes the solution in a file with the following format :
            #  - number of trucks used and total distance
            #  - for each truck the nodes visited (omitting the start/end at the depot)
            #
            #print("%d %d\n" % (total_lateness.value,total_distance.value))
            print(constantes.INSTANCE)
            print(total_distance.value)
            return total_distance.value + 100000 * total_lateness.value