def test_SCPInstances(data_dir='../data', scp_instances_dir='scp_instances'):
    # Get file list
    scp_instances_list = os.listdir(os.path.join(data_dir, scp_instances_dir))
    scp_instances_list = [
        i for i in scp_instances_list if not i.startswith('.')
    ]

    # Go trough list
    for i, scp_instance in enumerate(scp_instances_list):
        scp_instance = SCPInstance(
            os.path.join(data_dir, scp_instances_dir, scp_instance))

        if scp_instance.scp_number_of_columns != scp_instance.scp_instance_column_costs.shape[
                0]:
            print("Error in number of attributes in SCPInstance {}.".format(
                scp_instance.scp_instance_filename))

            return 1

        elif len(scp_instance.scp_instance_all_rows
                 ) != scp_instance.scp_number_of_rows:
            print("Error in number of subsets in SCPInstance {}.".format(
                scp_instance.scp_instance_filename))

            return 2

    return 0
Пример #2
0
    for ch_idx, ch in enumerate([ch1, ch2, ch3, ch4, ch5]):
        print("Current: CH{}".format(ch_idx + 1))
        # We create a results array to append all the results
        results_array = np.zeros(shape=(len(scp_instances_list) + 1, 5),
                                 dtype='object')

        # We add the columns meaning to the 1st line
        results_array[0, 0] = 'SCP Instance Filename'
        results_array[0, 1] = 'Solution'
        results_array[0, 2] = 'Cost'
        results_array[0, 3] = 'Processed Solution'
        results_array[0, 4] = 'Processed Cost'

        # We go through all the SCP Instances available
        for scp_idx, scp_instance_filename in enumerate(scp_instances_list):
            scp_instance = SCPInstance(
                os.path.join(scp_instances_dir, scp_instance_filename))

            # We add the filename to the results array
            results_array[scp_idx + 1, 0] = scp_instance_filename

            # We obtain the results
            final_solution, final_cost, final_processed_solution, final_processed_cost = ch(
                set_covering_problem_instance=scp_instance,
                post_processing=True,
                random_seed=42)

            # We add the results to the array
            results_array[scp_idx + 1, 1] = final_solution
            results_array[scp_idx + 1, 2] = final_cost
            results_array[scp_idx + 1, 3] = final_processed_solution
            results_array[scp_idx + 1, 4] = final_processed_cost
def lsh2(ih_results_array,
         scp_instances_dir,
         random_seed=42,
         k_max=10,
         l_max=10):

    # Set Numpy random seed
    np.random.seed(seed=random_seed)

    # Read the array information
    # SCP Instance Filename is at index 0
    scp_instance_filename = ih_results_array[0]

    # Processed Solution is at index 3
    initial_solution = ih_results_array[3]

    # Processed Cost is at index 4
    initial_cost = ih_results_array[4]

    # Get the SCP Instance
    scp_instance_path = os.path.join(scp_instances_dir, scp_instance_filename)

    # Load the SCP Instance Object
    scp_instance = SCPInstance(scp_instance_filename=scp_instance_path)

    # Build Row X Column Matrix
    problem_matrix = np.zeros(
        (scp_instance.scp_number_of_rows, scp_instance.scp_number_of_columns),
        dtype=int)
    # Fill Problem Matrix
    for row_idx, row in enumerate(scp_instance.scp_instance_all_rows):
        for column in row:
            problem_matrix[row_idx, column - 1] = 1

    # Variables in Memory: We create several memory variables that will be useful through the algorithm
    # Columns Availability: Variable To Use in Flips/Swaps, 1 if available, 0 if not available
    columns_availability = [1 for i in range(problem_matrix.shape[1])]
    for col in initial_solution:
        columns_availability[col] = 0

    # Rows Availability: Variable to check for rows that are covered/not covered 1 if covered, 0 if not covered
    rows_availability = [1 for i in range(problem_matrix.shape[0])]
    for row_idx, _ in enumerate(rows_availability):
        if np.sum(problem_matrix[row_idx, :]) == 0:
            rows_availability[row_idx] = 0

    # Rows Frequency Problem: Number of Times Each Row Appears in the Problem Matrix
    rows_freq_problem = [0 for i in range(problem_matrix.shape[0])]
    for row_idx, _ in enumerate(rows_freq_problem):
        rows_freq_problem[row_idx] = np.sum(problem_matrix[row_idx, :])

    # Rows Frequency Solution: Number of Times Each Row Appears in the Solution
    rows_freq_solution = [0 for i in range(problem_matrix.shape[0])]
    for col in initial_solution:
        for row_idx, _ in enumerate(rows_freq_solution):
            rows_freq_solution[row_idx] += problem_matrix[row_idx, col]

    # Column Frequency: Number of Times Each Column Appears in the Problem Matrix
    column_freq_problem = [0 for i in range(problem_matrix.shape[1])]
    for col_idx, _ in enumerate(column_freq_problem):
        column_freq_problem[col_idx] = np.sum(problem_matrix[:, col_idx])

    # Initialise variables
    # Current solution
    current_solution = initial_solution.copy()

    # Current cost
    current_cost = 0
    for col in current_solution:
        current_cost += scp_instance.scp_instance_column_costs[col]

    # If current cost is different from the processed cost, we will take this last into account
    if current_cost != initial_cost:
        initial_cost = current_cost

    # Initialise best solution and best cost variables
    best_solution = initial_solution.copy()
    best_cost = initial_cost.copy()

    # Initialise number of iterations
    iteration = 1

    # History: Save the Iteration and the Cost Value in that iteration to obtain good plots
    history = list()
    history.append([iteration, initial_cost])

    # Begin algorithm
    # Iterate through Nk, k in [1, ..., k_max]
    k = 0
    while k < k_max:
        print("Current k: {} | k_max: {}".format(k, k_max))
        # Generate Nk neighbourhood
        Nk_neighbourhood = list()
        while len(Nk_neighbourhood) < (3 * problem_matrix.shape[1]):
            # We generate a possible candidate neighbour based on a swap
            swap_column = np.random.choice(a=current_solution)

            # Neighbours
            # Should we "swap or remove" to find a new neighbour?
            swap_or_remove_or_insert = np.random.choice(a=[0, 1, 2])

            # Option 1: All rows are covered and our previous redundancy routines had bugs or were not effective
            if swap_or_remove_or_insert == 0:
                # If all the rows are covered this column is redundant!
                candidate_neighbour = current_solution.copy()
                candidate_neighbour.remove(swap_column)
                # Therefore, we found a valid neighbour solution
                # print("removed column")
                Nk_neighbourhood.append(candidate_neighbour)

            # Option 2: It's a swap!
            elif swap_or_remove_or_insert == 1:
                # Check availability
                candidate_neighbour_columns = list()
                for col, col_avail in enumerate(columns_availability):
                    if col_avail == 1:
                        candidate_neighbour_columns.append(col)

                # Create a procedure to find a proper candidate column
                candidate_column = np.random.choice(
                    a=candidate_neighbour_columns)

                # Generate candidate neighbour
                candidate_neighbour = current_solution.copy()
                candidate_neighbour.remove(swap_column)
                candidate_neighbour.append(candidate_column)
                Nk_neighbourhood.append(candidate_neighbour)

            # Option 3: It's an insert!
            elif swap_or_remove_or_insert == 2:
                # Check availability
                candidate_neighbour_columns = list()
                for col, col_avail in enumerate(columns_availability):
                    if col_avail == 1:
                        candidate_neighbour_columns.append(col)

                # Create a procedure to find a proper candidate column
                candidate_column = np.random.choice(
                    a=candidate_neighbour_columns)

                # Generate candidate neighbour
                candidate_neighbour = current_solution.copy()
                # candidate_neighbour.remove(swap_column)
                candidate_neighbour.append(candidate_column)
                Nk_neighbourhood.append(candidate_neighbour)

        # First check if all neighbours keep universitality
        Nk_valid_neighbourhood = list()
        for _, neigh in enumerate(Nk_neighbourhood):
            # print(neigh)
            rows_covered_by_neighbour = [
                0 for i in range(problem_matrix.shape[0])
            ]
            for row, _ in enumerate(rows_covered_by_neighbour):
                for col in neigh:
                    if problem_matrix[row, col] == 1:
                        rows_covered_by_neighbour[row] = 1
            if int(np.sum(rows_covered_by_neighbour)) == int(
                    problem_matrix.shape[0]):
                Nk_valid_neighbourhood.append(list(neigh))

        # Choose a random neighbour
        Nk_indices = [i for i in range(len(Nk_valid_neighbourhood))]
        Nk_index = np.random.choice(a=Nk_indices)

        # Assign variables solution and costs
        Nk_solution = Nk_valid_neighbourhood[Nk_index]
        Nk_cost = np.sum(
            [scp_instance.scp_instance_column_costs[c] for c in Nk_solution])

        # Generate Nl neighbourhood
        l = 0
        while l < l_max:
            print("Current l: {} | l_max: {}".format(l, l_max))
            Nl_neighbourhood = list()
            # Let's create the Nl_neighbourhood
            while len(Nl_neighbourhood) < (3 * problem_matrix.shape[1]):
                # We generate a possible candidate neighbour based on a swap (it's the neighbourhood of Nk_solution!)
                swap_column = np.random.choice(a=Nk_solution)

                # Neighbours
                # Should we "swap or remove" to find a new neighbour?
                swap_or_remove_or_insert = np.random.choice(a=[0, 1, 2])

                # Option 1: All rows are covered and our previous redundancy routines had bugs or were not effective
                if swap_or_remove_or_insert == 0:
                    # If all the rows are covered this column is redundant!
                    candidate_neighbour = Nk_solution.copy()
                    candidate_neighbour.remove(swap_column)
                    # Therefore, we found a valid neighbour solution
                    # print("removed column")
                    Nl_neighbourhood.append(candidate_neighbour)

                # Option 2: It's a swap!
                elif swap_or_remove_or_insert == 1:
                    # Check availability
                    candidate_neighbour_columns = list()
                    for col in range(problem_matrix.shape[1]):
                        if col not in Nk_solution:
                            candidate_neighbour_columns.append(col)

                    # Create a procedure to find a proper candidate column
                    candidate_column = np.random.choice(
                        a=candidate_neighbour_columns)

                    # Generate candidate neighbour
                    candidate_neighbour = Nk_solution.copy()
                    candidate_neighbour.remove(swap_column)
                    candidate_neighbour.append(candidate_column)
                    Nl_neighbourhood.append(candidate_neighbour)

                # Option 3: It's an insert!
                elif swap_or_remove_or_insert == 2:
                    # Check availability
                    candidate_neighbour_columns = list()
                    for col in range(problem_matrix.shape[1]):
                        if col not in Nk_solution:
                            candidate_neighbour_columns.append(col)

                    # Create a procedure to find a proper candidate column
                    candidate_column = np.random.choice(
                        a=candidate_neighbour_columns)

                    # Generate candidate neighbour
                    candidate_neighbour = Nk_solution.copy()
                    # candidate_neighbour.remove(swap_column)
                    candidate_neighbour.append(candidate_column)
                    Nl_neighbourhood.append(candidate_neighbour)

            # First check if all neighbours keep universitality
            Nl_valid_neighbourhood = list()
            for _, neigh in enumerate(Nl_neighbourhood):
                # print(neigh)
                rows_covered_by_neighbour = [
                    0 for i in range(problem_matrix.shape[0])
                ]
                for row, _ in enumerate(rows_covered_by_neighbour):
                    for col in neigh:
                        if problem_matrix[row, col] == 1:
                            rows_covered_by_neighbour[row] = 1
                if int(np.sum(rows_covered_by_neighbour)) == int(
                        problem_matrix.shape[0]):
                    Nl_valid_neighbourhood.append(list(neigh))

            # Choose a the best neighbour
            Nl_valid_neighbourhood_costs = list()
            for Nl in Nl_valid_neighbourhood:
                Nl_valid_neighbourhood_costs.append(
                    np.sum([
                        scp_instance.scp_instance_column_costs[c] for c in Nl
                    ]))

            Nl_cost = Nl_valid_neighbourhood_costs[np.argmin(
                Nl_valid_neighbourhood_costs)]
            Nl_solution = Nl_valid_neighbourhood[np.argmin(
                Nl_valid_neighbourhood_costs)]

            # Compare Nk_solution vs Nl_solution
            if Nl_cost < Nk_cost:
                Nk_solution = Nl_solution.copy()
                Nk_cost = Nl_cost.copy()
                l = 0

            else:
                l += 1

        # Now compare the current Nk_solution against the current solution
        if Nl_cost < current_cost:
            current_solution = Nl_solution.copy()
            current_cost = Nl_cost.copy()

            if current_cost < best_cost:
                best_solution = current_solution.copy()
                best_cost = current_cost.copy()

        else:
            k += 1

        # Updates
        history.append([iteration, current_cost])
        iteration += 1
        print("Initial Cost: {} | Current Cost: {} | Best Cost: {}".format(
            initial_cost, current_cost, best_cost))

    # Final Solutions
    final_solution = best_solution.copy()
    final_cost = best_cost.copy()

    return initial_solution, initial_cost, final_solution, final_cost, history
def lsh1(ih_results_array,
         scp_instances_dir,
         random_seed=42,
         initial_temperature=10,
         final_temperature=0.01,
         cooling_ratio_alpha=0.99,
         tabu_thr=10,
         patience=30):

    # Set Numpy random seed
    np.random.seed(seed=random_seed)

    # Read the array information
    # SCP Instance Filename is at index 0
    scp_instance_filename = ih_results_array[0]

    # Processed Solution is at index 3
    initial_solution = ih_results_array[3]

    # Processed Cost is at index 4
    initial_cost = ih_results_array[4]

    # Get the SCP Instance
    scp_instance_path = os.path.join(scp_instances_dir, scp_instance_filename)

    # Load the SCP Instance Object
    scp_instance = SCPInstance(scp_instance_filename=scp_instance_path)

    # Build Row X Column Matrix
    problem_matrix = np.zeros(
        (scp_instance.scp_number_of_rows, scp_instance.scp_number_of_columns),
        dtype=int)
    # Fill Problem Matrix
    for row_idx, row in enumerate(scp_instance.scp_instance_all_rows):
        for column in row:
            problem_matrix[row_idx, column - 1] = 1

    # Variables in Memory: We create several memory variables that will be useful through the algorithm
    # Columns Availability: Variable To Use in Flips/Swaps, 1 if available, 0 if not available
    columns_availability = [1 for i in range(problem_matrix.shape[1])]
    for col in initial_solution:
        columns_availability[col] = 0

    # Rows Availability: Variable to check for rows that are covered/not covered 1 if covered, 0 if not covered
    rows_availability = [1 for i in range(problem_matrix.shape[0])]
    for row_idx, _ in enumerate(rows_availability):
        if np.sum(problem_matrix[row_idx, :]) == 0:
            rows_availability[row_idx] = 0

    # Rows Frequency Problem: Number of Times Each Row Appears in the Problem Matrix
    rows_freq_problem = [0 for i in range(problem_matrix.shape[0])]
    for row_idx, _ in enumerate(rows_freq_problem):
        rows_freq_problem[row_idx] = np.sum(problem_matrix[row_idx, :])

    # Rows Frequency Solution: Number of Times Each Row Appears in the Solution
    rows_freq_solution = [0 for i in range(problem_matrix.shape[0])]
    for col in initial_solution:
        for row_idx, _ in enumerate(rows_freq_solution):
            rows_freq_solution[row_idx] += problem_matrix[row_idx, col]

    # Column Frequency: Number of Times Each Column Appears in the Problem Matrix
    column_freq_problem = [0 for i in range(problem_matrix.shape[1])]
    for col_idx, _ in enumerate(column_freq_problem):
        column_freq_problem[col_idx] = np.sum(problem_matrix[:, col_idx])

    # Tabu Search for Columnns: Columns in the solution begin with value -1, the rest with 0; until the value of tabu_thr the column is usable
    tabu_columns = [0 for i in range(problem_matrix.shape[1])]
    for col_idx, _ in enumerate(tabu_columns):
        if col_idx in initial_solution:
            tabu_columns[col_idx] = -1

    # Initialise variables
    # Current solution
    current_solution = initial_solution.copy()

    # Current cost
    current_cost = 0
    for col in current_solution:
        current_cost += scp_instance.scp_instance_column_costs[col]

    # If current cost is different from the processed cost, we will take this last into account
    if current_cost != initial_cost:
        initial_cost = current_cost

    # Initialise best solution and best cost
    best_solution = initial_solution.copy()
    best_cost = initial_cost.copy()

    # Initialise number of iterations
    iteration = 1

    # History: Save the Iteration and the Cost Value in that iteration to obtain good plots
    history = list()
    history.append([iteration, initial_cost, initial_temperature])

    # Current temperature
    current_temperature = initial_temperature
    current_patience = 0

    # Begin algorithm
    while (current_temperature > final_temperature) and (current_patience <
                                                         patience):
        # Select a random neighbour
        # Valid neighbour finding success variable
        valid_neighbour = list()

        # Let's generate more neighbours at a time
        while len(valid_neighbour) < (3 * problem_matrix.shape[1]):
            # We generate a possible candidate neighbour based on a swap
            swap_column = np.random.choice(a=current_solution)

            # Neighbours
            # Should we "swap or remove" to find a new neighbour?
            swap_or_remove_or_insert = np.random.choice(a=[0, 1, 2])

            # Option 1: All rows are covered and our previous redundancy routines had bugs or were not effective
            if swap_or_remove_or_insert == 0:
                # If all the rows are covered this column is redundant!
                candidate_neighbour = current_solution.copy()
                candidate_neighbour.remove(swap_column)
                # Therefore, we found a valid neighbour solution
                # print("removed column")
                valid_neighbour.append(candidate_neighbour)

            # Option 2: It's a swap!
            elif swap_or_remove_or_insert == 1:
                # Check availability
                candidate_neighbour_columns = list()
                for col, col_avail in enumerate(columns_availability):
                    if col_avail == 1:
                        candidate_neighbour_columns.append(col)

                # Create a procedure to find a proper candidate column
                candidate_column_found = False
                while candidate_column_found != True:
                    candidate_column = np.random.choice(
                        a=candidate_neighbour_columns)
                    if (tabu_columns[candidate_column] >=
                            0) and (tabu_columns[candidate_column] <= 10):
                        candidate_column_found = True

                # Generate candidate neighbour
                candidate_neighbour = current_solution.copy()
                candidate_neighbour.remove(swap_column)
                candidate_neighbour.append(candidate_column)
                valid_neighbour.append(candidate_neighbour)

            # Option 3: It's an insert!
            elif swap_or_remove_or_insert == 2:
                # Check availability
                candidate_neighbour_columns = list()
                for col, col_avail in enumerate(columns_availability):
                    if col_avail == 1:
                        candidate_neighbour_columns.append(col)

                # Create a procedure to find a proper candidate column
                candidate_column_found = False
                while candidate_column_found != True:
                    candidate_column = np.random.choice(
                        a=candidate_neighbour_columns)
                    if (tabu_columns[candidate_column] >=
                            0) and (tabu_columns[candidate_column] <= 10):
                        candidate_column_found = True

            # Generate candidate neighbour
                candidate_neighbour = current_solution.copy()
                # candidate_neighbour.remove(swap_column)
                candidate_neighbour.append(candidate_column)
                valid_neighbour.append(candidate_neighbour)

        # First check if all neighbours keep universitality
        possible_neighbours = list()
        for _, neigh in enumerate(valid_neighbour):
            # print(neigh)
            rows_covered_by_neighbour = [
                0 for i in range(problem_matrix.shape[0])
            ]
            for row, _ in enumerate(rows_covered_by_neighbour):
                for col in neigh:
                    if problem_matrix[row, col] == 1:
                        rows_covered_by_neighbour[row] = 1
            if int(np.sum(rows_covered_by_neighbour)) == int(
                    problem_matrix.shape[0]):
                possible_neighbours.append(list(neigh))

        # We have possible neighbours! (check the universitality of the solution)
        if len(possible_neighbours) > 0:
            # print(possible_neighbours, len(possible_neighbours))
            possible_neighbours_costs = list()
            for _, neighbour in enumerate(possible_neighbours):
                # print(len(neigh))
                neigh_cost = list()
                # print(n)
                if isinstance(neighbour, list):
                    for c in neighbour:
                        # print(c)
                        neigh_cost.append(
                            scp_instance.scp_instance_column_costs[c])
                    neigh_cost = np.sum(neigh_cost)
                    # print(neigh_cost)
                    possible_neighbours_costs.append(neigh_cost)

            # We choose the best neighbour
            best_neighbour = possible_neighbours[np.argmin(
                possible_neighbours_costs)]
            best_neighbour_cost = possible_neighbours_costs[np.argmin(
                possible_neighbours_costs)]

            cost_difference = current_cost - best_neighbour_cost

            # Now, evaluate costs
            if cost_difference > 0:
                current_solution = best_neighbour.copy()
                current_cost = best_neighbour_cost

            else:
                # Probability threshold
                if random.uniform(0, 1) < math.exp(
                        cost_difference / current_temperature):
                    current_solution = best_neighbour.copy()
                    current_cost = best_neighbour_cost

        # Update Best Solution Found
        if current_cost < best_cost:
            best_cost = current_cost.copy()
            best_solution = current_solution.copy()
            current_patience = 0

        else:
            current_patience += 1

        # Temperature update
        print("Temperature decreased from {} to {}.".format(
            current_temperature, current_temperature * cooling_ratio_alpha))
        current_temperature *= cooling_ratio_alpha
        print("Initial Cost: {} | Current Cost: {} | Best Cost: {}".format(
            initial_cost, current_cost, best_cost))
        print("Current Patience: {} | Max Patience: {}".format(
            current_patience, patience))

        # Updates
        # Columns Availability
        for col, _ in enumerate(columns_availability):
            if col in current_solution:
                columns_availability[col] = 0
            else:
                columns_availability[col] = 1

        # Tabu Search
        for col, col_tabu in enumerate(tabu_columns):
            if col in current_solution:
                tabu_columns[col] = -1
            elif col in best_neighbour and col not in current_solution:
                tabu_columns[col] += 1
                if tabu_columns[col] == 2 * tabu_thr:
                    tabu_columns[col] = 0

        # Rows Frequency Solution
        rows_freq_solution = [0 for i in range(problem_matrix.shape[0])]
        for col in current_solution:
            for row_idx, _ in enumerate(rows_freq_solution):
                rows_freq_solution[row_idx] += problem_matrix[row_idx, col]

        # Iterations
        iteration += 1

        # History
        history.append([iteration, current_cost, current_temperature])

    # Final
    final_solution = best_solution.copy()
    final_cost = best_cost.copy()
    print("Initial Cost: {} | Final Cost: {}".format(initial_cost, final_cost))

    return initial_solution, initial_cost, final_solution, final_cost, history
Пример #5
0
def ih4(ch_results_array,
        scp_instances_dir,
        use_processed_solution=True,
        random_seed=42,
        set_minimization_repetition_factor=5000,
        hill_climbing_repetition_factor=1000):
    # Set Numpy random seed
    np.random.seed(seed=random_seed)

    # Read the array information
    # SCP Instance Filename is at index 0
    scp_instance_filename = ch_results_array[0]

    if use_processed_solution:
        # Processed Solution is at index 3
        initial_solution = ch_results_array[3]

        # Processed Cost is at index 4
        initial_cost = ch_results_array[4]

    else:
        # Initial Solution is at index 1
        initial_solution = ch_results_array[1]

        # Initial Cost is at index 2
        initial_cost = ch_results_array[2]

    # Get the SCP Instance
    scp_instance_path = os.path.join(scp_instances_dir, scp_instance_filename)

    # Load the SCP Instance Object
    scp_instance = SCPInstance(scp_instance_filename=scp_instance_path)

    # Build Row X Column Matrix
    problem_matrix = np.zeros(
        (scp_instance.scp_number_of_rows, scp_instance.scp_number_of_columns),
        dtype=int)
    # Fill Problem Matrix
    for row_idx, row in enumerate(scp_instance.scp_instance_all_rows):
        for column in row:
            problem_matrix[row_idx, column - 1] = 1

    # Variables in Memory: We create several memory variables that will be useful through the algorithm
    # Columns Availability: Variable To Use in Flips/Swaps, 1 if available, 0 if not available
    columns_availability = [1 for i in range(problem_matrix.shape[1])]
    for col in initial_solution:
        columns_availability[col] = 0

    # Rows Availability: Variable to check for rows that are covered/not covered 1 if covered, 0 if not covered
    rows_availability = [1 for i in range(problem_matrix.shape[0])]
    for row_idx, _ in enumerate(rows_availability):
        if np.sum(problem_matrix[row_idx, :]) == 0:
            rows_availability[row_idx] = 0

    # Rows Frequency Problem: Number of Times Each Row Appears in the Problem Matrix
    rows_freq_problem = [0 for i in range(problem_matrix.shape[0])]
    for row_idx, _ in enumerate(rows_freq_problem):
        rows_freq_problem[row_idx] = np.sum(problem_matrix[row_idx, :])

    # Rows Frequency Solution: Number of Times Each Row Appears in the Solution
    rows_freq_solution = [0 for i in range(problem_matrix.shape[0])]
    for col in initial_solution:
        for row_idx, _ in enumerate(rows_freq_solution):
            rows_freq_solution[row_idx] += problem_matrix[row_idx, col]

    # Column Frequency: Number of Times Each Column Appears in the Problem Matrix
    column_freq_problem = [0 for i in range(problem_matrix.shape[1])]
    for col_idx, _ in enumerate(column_freq_problem):
        column_freq_problem[col_idx] = np.sum(problem_matrix[:, col_idx])

    # Tabu Search for Columnns: Columns in the solution begin with value -1, the rest with 0; until the value of tabu_thr the column is usable
    tabu_columns = [0 for i in range(problem_matrix.shape[1])]
    for col_idx, _ in enumerate(tabu_columns):
        if col_idx in initial_solution:
            tabu_columns[col_idx] = -1

    # Initialise variables
    # Current solution
    current_solution = initial_solution.copy()

    # Current cost
    current_cost = 0
    for col in current_solution:
        current_cost += scp_instance.scp_instance_column_costs[col]

    # If current cost is different from the processed cost, we will take this last into account
    if current_cost != initial_cost:
        initial_cost = current_cost

    # Compute the R value from the paper
    R = problem_matrix.shape[0] * problem_matrix.shape[1]

    # History
    history = list()
    history.append(initial_cost)

    # Begin algorithm
    # First Part: Set Redundancy Elimination
    for _ in range(int(set_minimization_repetition_factor)):
        # Randomly select a set X* from the selected sets.
        candidate_redundant_set = np.random.choice(a=current_solution)

        # Mark this set X as Unselected Set.
        new_solution = current_solution.copy()
        new_solution.remove(candidate_redundant_set)

        # Check whether the universality constraint holds
        temp_row_availability = [0 for i in range(problem_matrix.shape[0])]
        for col in new_solution:
            for row, row_value in enumerate(problem_matrix[:, col]):
                if row_value == 1:
                    if temp_row_availability[row] == 0:
                        temp_row_availability[row] = 1
                    else:
                        temp_row_availability[row] = 1

        # The total_availability must be equal to the number of rows to cover, then we have a new solution
        if int(np.sum(temp_row_availability)) == (problem_matrix.shape[0]):
            new_cost = [
                scp_instance.scp_instance_column_costs[c] for c in new_solution
            ]
            # Stay with this state and find the cost, Cnew.
            new_cost = np.sum(new_cost)
            # Replace the best found cost C, with the current cost, Cnew.
            current_cost = new_cost
            # Remove set X from the selected sets, X.
            current_solution = new_solution.copy()

            # Update column availability
            columns_availability[candidate_redundant_set] = 1

            # History
            history.append(new_cost)

    # Second Part: Hill Climbing Algorithm
    for _ in range(int(hill_climbing_repetition_factor)):
        # Randomly select a set Y from the unselected sets, S-X
        available_sets = [
            c for c, c_avail in enumerate(columns_availability) if c_avail == 1
        ]

        # Mark this set as Selected.
        candidate_added_set = np.random.choice(a=available_sets)
        new_solution = current_solution.copy()

        # Check whether the universality constraint holds
        new_solution.append(candidate_added_set)

        # Check whether the universality constraint holds
        temp_row_availability = [0 for i in range(problem_matrix.shape[0])]
        for col in new_solution:
            for row, row_value in enumerate(problem_matrix[:, col]):
                if row_value == 1:
                    if temp_row_availability[row] == 0:
                        temp_row_availability[row] = 1
                    else:
                        temp_row_availability[row] = 1

        # The total_availability must be equal to the number of rows to cover, then we have a new solution
        if int(np.sum(temp_row_availability)) == (problem_matrix.shape[0]):
            # Find cost Cnew of c((X - X) U ( Y )
            new_cost = np.sum([
                scp_instance.scp_instance_column_costs[c] for c in new_solution
            ])
            if new_cost <= current_cost:

                # Replace the best found cost C, with the current cost, Cnew.
                current_cost = new_cost
                current_solution = new_solution.copy()

                # Update column availability
                columns_availability[candidate_added_set] = 0

                # History
                history.append(new_cost)

    # End algorithm
    final_solution = current_solution.copy()
    final_cost = current_cost.copy()

    # History
    history.append(final_cost)

    return initial_solution, initial_cost, final_solution, final_cost, history
Пример #6
0
def ih3(ch_results_array,
        scp_instances_dir,
        use_processed_solution=True,
        random_seed=42,
        max_iterations=5000,
        patience=100,
        tabu_thr=10):
    """
    IH3: A (tentative) hybrid approach.
    """

    # Set Numpy random seed
    np.random.seed(seed=random_seed)

    # Read the array information
    # SCP Instance Filename is at index 0
    scp_instance_filename = ch_results_array[0]

    if use_processed_solution:
        # Processed Solution is at index 3
        initial_solution = ch_results_array[3]

        # Processed Cost is at index 4
        initial_cost = ch_results_array[4]

    else:
        # Initial Solution is at index 1
        initial_solution = ch_results_array[1]

        # Initial Cost is at index 2
        initial_cost = ch_results_array[2]

    # Get the SCP Instance
    scp_instance_path = os.path.join(scp_instances_dir, scp_instance_filename)

    # Load the SCP Instance Object
    scp_instance = SCPInstance(scp_instance_filename=scp_instance_path)

    # Build Row X Column Matrix
    problem_matrix = np.zeros(
        (scp_instance.scp_number_of_rows, scp_instance.scp_number_of_columns),
        dtype=int)
    # Fill Problem Matrix
    for row_idx, row in enumerate(scp_instance.scp_instance_all_rows):
        for column in row:
            problem_matrix[row_idx, column - 1] = 1

    # Variables in Memory: We create several memory variables that will be useful through the algorithm
    # Columns Availability: Variable To Use in Flips/Swaps, 1 if available, 0 if not available
    columns_availability = [1 for i in range(problem_matrix.shape[1])]
    for col in initial_solution:
        columns_availability[col] = 0

    # Rows Availability: Variable to check for rows that are covered/not covered 1 if covered, 0 if not covered
    rows_availability = [1 for i in range(problem_matrix.shape[0])]
    for row_idx, _ in enumerate(rows_availability):
        if np.sum(problem_matrix[row_idx, :]) == 0:
            rows_availability[row_idx] = 0

    # Rows Frequency Problem: Number of Times Each Row Appears in the Problem Matrix
    rows_freq_problem = [0 for i in range(problem_matrix.shape[0])]
    for row_idx, _ in enumerate(rows_freq_problem):
        rows_freq_problem[row_idx] = np.sum(problem_matrix[row_idx, :])

    # Rows Frequency Solution: Number of Times Each Row Appears in the Solution
    rows_freq_solution = [0 for i in range(problem_matrix.shape[0])]
    for col in initial_solution:
        for row_idx, _ in enumerate(rows_freq_solution):
            rows_freq_solution[row_idx] += problem_matrix[row_idx, col]

    # Column Frequency: Number of Times Each Column Appears in the Problem Matrix
    column_freq_problem = [0 for i in range(problem_matrix.shape[1])]
    for col_idx, _ in enumerate(column_freq_problem):
        column_freq_problem[col_idx] = np.sum(problem_matrix[:, col_idx])

    # Tabu Search for Columnns: Columns in the solution begin with value -1, the rest with 0; until the value of tabu_thr the column is usable
    tabu_columns = [0 for i in range(problem_matrix.shape[1])]
    for col_idx, _ in enumerate(tabu_columns):
        if col_idx in initial_solution:
            tabu_columns[col_idx] = -1

    # Initialise variables
    # Current solution
    current_solution = initial_solution.copy()

    # Current cost
    current_cost = 0
    for col in current_solution:
        current_cost += scp_instance.scp_instance_column_costs[col]

    # If current cost is different from the processed cost, we will take this last into account
    if current_cost != initial_cost:
        initial_cost = current_cost

    # Initialise number of iterations
    nr_iteration = 1

    # Initialise number of iterations in patience
    nr_patience = 1

    # History
    history = list()
    history.append(initial_cost)

    # Begin algorithm
    # We create a history list first so we do not repeat in each iteration
    swap_column_history = list()
    while (nr_iteration <= max_iterations) and (nr_patience <= patience):
        # print("Iteration: {} | Patience: {}".format(nr_iteration, nr_patience))
        # Generate a neighbour-solution

        # Create a condition that decides that we have found a proper neighbour
        valid_neighbours = False
        while valid_neighbours != True:
            # Always choose the most expensive column

            # Sort the solution: last columns will be the expensive ones
            current_solution.sort()

            # To perform the while loop
            swap_column = -1

            # Will help us to reach the last col, then the second to last, and so on...
            aux_idx = 1

            # Get the swap column
            while swap_column < 0:
                if len(current_solution) - aux_idx >= 0:
                    col_candidate = current_solution[len(current_solution) -
                                                     aux_idx]
                    if col_candidate not in swap_column_history:
                        swap_column = col_candidate
                        swap_column_history.append(swap_column)
                    else:
                        aux_idx += 1
                else:
                    swap_column_history = list()
                    aux_idx = 1

            # Neighbours
            # First we verify the rows that this column covers
            rows_covered_by_swap_column = list()
            for row, row_value in enumerate(problem_matrix[:, swap_column]):
                if row_value == 1:
                    rows_covered_by_swap_column.append(row)

            # Then we subtract 1 from the row freq column to check if there are columns that suddenly become unavailable
            rows_freq_solution_after_swap = rows_freq_solution.copy()
            for row in rows_covered_by_swap_column:
                rows_freq_solution_after_swap[row] -= 1

            # Now we have to verify if there is some row that is unavailable
            uncovered_rows_after_swap = list()
            for row, row_freq in enumerate(rows_freq_solution_after_swap):
                if row_freq <= 0:
                    uncovered_rows_after_swap.append(row)

            # We have two options:
            neighbours = list()
            # Option 1: All rows are covered
            if len(uncovered_rows_after_swap) == 0:
                # If all the rows are covered this column is redundant!
                new_solution = current_solution.copy()
                new_solution.remove(swap_column)
                # Therefore, we found a valid neighbour solution
                valid_neighbours = True

            # Option 2: We have uncovered rows
            else:
                # This way, the neighbours must contain at least the uncovered rows after swap
                # We check the available columns first
                for col, col_avail in enumerate(columns_availability):
                    # print("Column: ", col)
                    # It must respect the constraints related with the availability and tabu search
                    if (col != swap_column) and (col_avail == 1) and (
                            tabu_columns[col] >= 0) and (tabu_columns[col] <=
                                                         10):
                        # Check the rows that this col covers
                        temp_rows_col_covers = list()
                        for row, row_value in enumerate(problem_matrix[:,
                                                                       col]):
                            if row_value == 1:
                                temp_rows_col_covers.append(row)

                        # Now let's check this column covers all the uncovered rows
                        try:
                            for row in uncovered_rows_after_swap:
                                temp_rows_col_covers.remove(row)

                            if len(temp_rows_col_covers) == 0:
                                neighbours.append(col)

                        except:
                            neighbours = neighbours.copy()

                # Now we should have neighbours (or not)
                if len(neighbours) >= 0:
                    # print("more than one neighbour", len(neighbours))
                    # Small tweak to avoid loopholes
                    new_solution = current_solution.copy()
                    valid_neighbours = True

        # If we just removed a column
        if len(neighbours) == 0:
            # Let's compute the cost of the new solution
            new_cost = np.sum([
                scp_instance.scp_instance_column_costs[col]
                for col in new_solution
            ])
            # It is better, our current cost is the new cost
            if new_cost < current_cost:
                current_cost = new_cost

                # And our current solution is the new_solution
                current_solution = new_solution

                # Updates
                # Columns Availability
                for col in current_solution:
                    columns_availability[col] = 0

                # Rows Frequency Solution
                rows_freq_solution = [
                    0 for i in range(problem_matrix.shape[0])
                ]
                for col in current_solution:
                    for row_idx, _ in enumerate(rows_freq_solution):
                        rows_freq_solution[row_idx] += problem_matrix[row_idx,
                                                                      col]

                # History
                history.append(new_cost)

                # Iterations
                nr_iteration += 1
                nr_patience = 0

            else:
                # History
                history.append(new_cost)

                nr_iteration += 1
                nr_patience += 1

        # Otherwise, we have neighbours
        else:
            # Here, we choose the BEST neighbour we find
            # First compute the costs of the possible neighbours
            chosen_neighbour_costs = [
                scp_instance.scp_instance_column_costs[c] for c in neighbours
            ]
            # We add the one which grants less cost
            chosen_neighbour = neighbours[np.argmin(chosen_neighbour_costs)]

            # Let's perform the swap
            new_solution = current_solution.copy()
            new_solution.remove(swap_column)
            if chosen_neighbour not in new_solution:
                new_solution.append(chosen_neighbour)

            # Compute the new cost
            new_cost = np.sum([
                scp_instance.scp_instance_column_costs[col]
                for col in new_solution
            ])
            # It is better, our current cost is the new cost
            if new_cost < current_cost:
                current_cost = new_cost

                # And our current solution is the new_solution
                current_solution = new_solution

                # Updates
                # Columns Availability
                for col in current_solution:
                    columns_availability[col] = 0

                # Do not forget the swap column!
                columns_availability[swap_column] = 1

                # Tabu Search
                # The neighbour
                tabu_columns[chosen_neighbour] += 1
                # The swap column
                tabu_columns[swap_column] += 1
                # Check if we have to reset Tabu Search
                for col, tabu_value in enumerate(tabu_columns):
                    if tabu_value >= 20:
                        tabu_columns[col] = 0

                # Rows Frequency Solution
                rows_freq_solution = [
                    0 for i in range(problem_matrix.shape[0])
                ]
                for col in current_solution:
                    for row_idx, _ in enumerate(rows_freq_solution):
                        rows_freq_solution[row_idx] += problem_matrix[row_idx,
                                                                      col]

                # History
                history.append(new_cost)

                # Iterations
                nr_iteration += 1
                nr_patience = 0

            else:
                # History
                history.append(new_cost)

                nr_iteration += 1
                nr_patience += 1

    # Final solutions
    final_cost = current_cost
    final_solution = current_solution.copy()

    # History
    history.append(final_cost)

    return initial_solution, initial_cost, final_solution, final_cost, history