Beispiel #1
0
def ba_proc(rows, cols, chrom_seq, pnum, counter, permutations):
    """
    BA Algorithm function for use in a multiprocessor process.
	:param rows:         number of rows on the board.
	:param cols:         number of columns on the board.
	:param chrom_seq:    pre-initialised chromosome.
	:param pnum:         number of processors.
	:param permutations: permutations calculation flag.
	:param counter:      reference to counter object.
    :return: n/a
    """
    #
    # Plot the domino grid solutions; saving to a each to a file.
    #
    print("Saving resultant board plots.")
    # Retrieve the expected number of holes and dominoes
    (holes, dominoes) = cmn.calc_solution_holes(rows, cols)
    dplobj = dpl.CPlot(rows, cols, "BA", holes, dominoes)
    dplobj.set_proc_num(pnum)
    #
    # Create the backtracking algorithm object with the specified board size.
    #
    baobj = CBacktracking(rows, cols, dplobj, counter, permutations)
    # Display the backtracking algorithm's properties
    if pnum == 0:
        print(baobj)  # Only print it the once.
    # Now ready to solve the problem using the chromosome.
    print(
        "Executing the backtracking algorithm on processor[{}].".format(pnum))
    baobj.runp(chrom_seq)
Beispiel #2
0
def main():
    """
    Main function entry point to calculating all the solutions to the (8 x 10)
    board, by using the solutions from lower dimensions. These being 2x slices
    of (3 x 8) solutions, plus 1x slice of (4 x 8) solutions.
    :param:  n/a
    :return: n/a
    """
    #
    # Check that the solution folders are present, no point continuing if
    # they are not there.
    #
    ok_to_proceed = True
    soln_folders = ["3x8__8_holes-8_dominoes", "4x8__10_holes-11_dominoes"]
    for folder in soln_folders:
        if not os.path.exists(os.path.join(cmn.BA_FOLDER, folder)):
            print("Error: {} doesn't exist.".format(folder))
            ok_to_proceed = False
        else:
            print("Found solution folder '{}'".format(folder))
    if not ok_to_proceed:
        print("Missing solution folder(s) so quitting.")
        sys.exit(-1)
    #
    # Load all the solutions into memory for speed and ease of processing.
    #
    solns = {"3x8": [], "4x8": []}
    for folder in soln_folders:
        path_str = os.path.join(cmn.BA_FOLDER, folder)
        for fname in os.listdir(path_str):
            # Identify a solution file and load it.
            if fname.endswith(".npy"):
                board = np.load(os.path.join(path_str, fname))
                if folder == "3x8__8_holes-8_dominoes":
                    solns["3x8"].append(board)
                else:
                    solns["4x8"].append(board)

    # Need 3x nested for loops representing each of the 3 slices.
    # As the seed board solutions are different we need to execute the nested
    # loop three times to get full coverage, as follows:
    all_solns = []
    # 1) slice1: 3x8, slice2: 3x8, slice3: 4x8
    all_solns.extend(get_solns(solns["3x8"], solns["3x8"], solns["4x8"]))
    # 2) slice1: 3x8, slice2: 4x8, slice3: 3x8
    all_solns.extend(get_solns(solns["3x8"], solns["4x8"], solns["3x8"]))
    # 3) slice1: 4x8, slice2: 3x8, slice3: 3x8
    all_solns.extend(get_solns(solns["4x8"], solns["3x8"], solns["3x8"]))

    # Plot the domino board solutions; saving to a each to a file.
    # Note we need to pass in a numpy 2D array!
    max_holes, min_dominoes = cmn.calc_solution_holes(8, 10)
    dplobj = dpl.CPlot(8, 10, "pattern", max_holes, min_dominoes)
    for result in all_solns:
        dplobj.write_soln_to_file(result)
    dplobj.classify_solns()
    dplobj.plot_all()
Beispiel #3
0
def main(grid, algo):
    """
    Main reclassification script.
    :param grid: grid dimensions in rows and columns.
    :param algo: algorithm to be reclassified.
    :return: execution time
    """
    rows = int(grid[0])
    cols = int(grid[1])
    # Calculate solution maximum number of holes.
    (max_holes, max_dominoes) = cmn.calc_solution_holes(rows, cols)
    #
    # Instantiate the plotting object.
    #
    dplobj = dpl.CPlot(rows, cols, algo, max_holes, max_dominoes)
    #
    # Scan the results folder solutions and update the class members
    #
    print("Scanning the previous results.")
    dplobj.load_soln_info()
#    print(dplobj)  # debug output
    #
    # Scan the results folder for image files and remove any it finds
    #
    print("Removing old image solution files.")
    for fname in os.listdir(dplobj.path_str):
        if fname.endswith(".png"):
            os.remove(os.path.join(dplobj.path_str, fname))
    #
    # Parse the solution files; classify the solutions and plot them.
    #
    print("Re-classfying the results.")
    dplobj.classify_solns()
    print("Plotting all the results.")
    dplobj.plot_all()
    print("Found {} total solutions and {} fundamental solutions" \
          .format(dplobj.total_all_solns, dplobj.total_fundamental))
    #
    # Update the test information file with the correct number of fundamental solutions.
    #
    fname = os.path.join(dplobj.path_str, cmn.RESULTS_FILE)
    if os.path.exists(fname):
        print("Updating the number of fundamental solutions in {}".format(fname))
        # The file exists so read in the entire contents
        with open(fname, "r") as fin:
            contents = fin.read()
        # Update the number of fundamental solutions
        repl = "Fundamental solutions: {}".format(dplobj.total_fundamental)
        # Had to use regex '(?!...) look behind so that regex it didn't consume
        # the 'Fundamental solutions: ' string.
        contents = re.sub(r"(Fundamental solutions:\s*[0-9]+)", repl, contents, re.IGNORECASE)
        # Write the file back again.
        with open(fname, "w") as fout:
            fout.write(contents)
Beispiel #4
0
def main(grid, calc_permutations, timed_execution):
    """
    Main Bracktracking Algorithm implementation.
    :param grid: grid dimensions in rows and columns.
    :param timed_execution: flag to time algorithm execution.
    :return: execution time
    """
    start = time.time()  # Used to time script execution.
    rows = int(grid[0])
    cols = int(grid[1])

    # Set the number of parallel processes we want to run.
    # This is dependent on the number of processors available and the
    # initialisation values available and the board dimensions.
    num_processors = mp.cpu_count()
    print("Number of processors available: {}".format(num_processors))
    # Create the chromosome initialisation list.
    if calc_permutations and num_processors >= 9 and rows > 4 and cols > 4:
        num_processors = 9  # Only use 9 processors
        # Note: when calculating the permutations we need ALL combinations!
        chrom_init_seq = [[cmn.CELL_SPACE, cmn.CELL_HDOMINO],
                          [cmn.CELL_SPACE, cmn.CELL_VDOMINO],
                          [cmn.CELL_SPACE, cmn.CELL_SPACE],
                          [cmn.CELL_HDOMINO, cmn.CELL_SPACE],
                          [cmn.CELL_HDOMINO, cmn.CELL_HDOMINO],
                          [cmn.CELL_HDOMINO, cmn.CELL_VDOMINO],
                          [cmn.CELL_VDOMINO, cmn.CELL_SPACE],
                          [cmn.CELL_VDOMINO, cmn.CELL_HDOMINO],
                          [cmn.CELL_VDOMINO, cmn.CELL_VDOMINO]]
    if not calc_permutations and num_processors >= 8 and rows > 4 and cols > 4:
        num_processors = 8  # Only use 8 processors
        chrom_init_seq = [[cmn.CELL_SPACE, cmn.CELL_HDOMINO],
                          [cmn.CELL_SPACE, cmn.CELL_VDOMINO],
                          [cmn.CELL_HDOMINO, cmn.CELL_SPACE],
                          [cmn.CELL_HDOMINO, cmn.CELL_HDOMINO],
                          [cmn.CELL_HDOMINO, cmn.CELL_VDOMINO],
                          [cmn.CELL_VDOMINO, cmn.CELL_SPACE],
                          [cmn.CELL_VDOMINO, cmn.CELL_HDOMINO],
                          [cmn.CELL_VDOMINO, cmn.CELL_VDOMINO]]
    elif not calc_permutations and num_processors >= 6 and rows >= 4 and cols >= 4:
        num_processors = 6  # Only use 6 processors
        chrom_init_seq = [[cmn.CELL_SPACE, cmn.CELL_HDOMINO],
                          [cmn.CELL_SPACE,
                           cmn.CELL_VDOMINO], [cmn.CELL_HDOMINO],
                          [cmn.CELL_VDOMINO, cmn.CELL_SPACE],
                          [cmn.CELL_VDOMINO, cmn.CELL_HDOMINO],
                          [cmn.CELL_VDOMINO, cmn.CELL_VDOMINO]]
    elif num_processors >= 3 and rows >= 3 and cols >= 3:
        num_processors = 3  # Only use 3 processors
        chrom_init_seq = [[cmn.CELL_SPACE], [cmn.CELL_HDOMINO],
                          [cmn.CELL_VDOMINO]]
    else:
        num_processors = 1  # Only use 1 processor
        chrom_init_seq = []

    # Initialise our permutations counter object.
    counter = Counter(0)
    # Setup the BA algorithm to run on num_processors value, if possible.
    if num_processors == 1:
        print("System not sufficient to run parallel processes.")
        ba_proc(rows, cols, chrom_init_seq, 0, counter, calc_permutations)
    else:
        print("Running {} parallel processes.".format(num_processors))
        # Seeding each BA object with all the different possible combinations
        # for the first board squares.
        procs = []
        for i in range(num_processors):
            proc = Process(target=ba_proc,
                           args=(rows, cols, chrom_init_seq[i], i, counter,
                                 calc_permutations))
            procs.append(proc)
            proc.start()
        # Block until all cores have finished.
        for proc in procs:
            proc.join()

    execution_time = time.time() - start
    #
    # Now display the number of permutations found.
    #
    if calc_permutations:
        msg = "\n\nNumber of permutations = {}\n".format(counter.value())
        print(msg)
        fname = "{}x{}-permutations.txt".format(rows, cols)
        fname = os.path.join(cmn.PERMUTATIONS_FOLDER, fname)
        with open(fname, "w") as fout:
            fout.write(msg)
    #
    # Parse the solution files; classify the solutions and plot them.
    #
    else:
        # Retrieve the expected number of holes and dominoes
        (holes, dominoes) = cmn.calc_solution_holes(rows, cols)
        dplobj = dpl.CPlot(rows, cols, "BA", holes, dominoes)
        dplobj.load_soln_info()
        dplobj.classify_solns()
        dplobj.plot_all()
        #
        # Write the version information to the results folder.
        # - Python and script version information is recorded.
        # - this includes the execution time in seconds.
        #
        verstr = cmn.get_version_str(SCRIPTINFO)
        if dplobj.get_total_solns():
            time_1st = float(execution_time / dplobj.get_total_solns())
        else:
            # protect against divide by zero
            time_1st = execution_time
        dplobj.write_test_info(verstr, execution_time, time_1st)
    # Display the time taken if requested.
    if timed_execution:
        print("Script execution time:", execution_time, "seconds")
    # Return the execution time.
    return execution_time
Beispiel #5
0
def main(grid, timed_execution):
    """
    Main Genetic Algorithm implementation.
    :param grid: the board dimensions in rows and columns.
    :param timed_execution: flag to time algorithm execution.
    :return: execution time and total number of solutions found.
    """
    # Setup the GA configuration and show the user
    rows = int(grid[0])
    cols = int(grid[1])
    print('Running {} with board ({} x {}):'.format(SCRIPTNAME, rows, cols))
    #
    # Calculate solution maximum number of holes.
    #
    (max_holes, max_dominoes) = cmn.calc_solution_holes(rows, cols)
    print("Maximum number of holes for {} x {} board is {}.".format(
        rows, cols, max_holes))
    print("Maximum number of dominoes for {} x {} board is {}.".format(
        rows, cols, max_dominoes))
    # Determine the chromosome size 'csize' from the maximum number of rows and
    # columns that are possible; plus add some padding.
    csize = int(max_holes + max_dominoes + 4)
    print("Chromosome size = {}".format(csize))
    print(
        "Generations={}, Population={}, Crossover Probability={}, Mutation Rate={}"
        .format(GENERATIONS_MAX, POPULATION_SIZE, CX_PROBABILITY,
                MUTATION_RATE))

    # Initialise the DEAP structure and evaluation object
    toolbox, pop, fits = init_DEAP_structures(csize, cols, rows, max_holes,
                                              max_dominoes)

    #
    # Run the evolution:
    # Stop if we find a solution or we reach the maximum number of generations.
    #
    # Variable keeping track of the number of generations
    generation = 0
    start = time.time()  # Used to time script execution.
    while max(fits) < 100 and generation < GENERATIONS_MAX:
        # Display the header at the regular intervals.
        if not generation % 50:
            print("\n {:<10}{:<10}{:<8}{:<8}{:<8}{:<8}".format(
                "Gen", "Eval", "Min", "Max", "Avg", "Std"))
            print("-" * 60)
        # A new generation
        random.seed(time.time())  # MUST ENSURE RANDOM SEED EACH RUN
        generation = generation + 1

        # Select the next generation individuals
        offspring = toolbox.select(pop, len(pop))
        # Clone the selected individuals
        offspring = list(map(toolbox.clone, offspring))

        for mutant in offspring:
            # mutate an individual with probability MUTATION_RATE
            # range [0.0, 1.0)
            if random.random() < MUTATION_RATE:
                toolbox.mutate(mutant)
                del mutant.fitness.values

        # Evaluate the individuals with an invalid fitness
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = map(toolbox.evaluate, invalid_ind)
        for ind, fit in zip(invalid_ind, fitnesses):
            ind.fitness.values = fit

        # The population is entirely replaced by the offspring
        pop[:] = offspring

        # Gather all the fitnesses in one list and print the stats
        fits = [ind.fitness.values[0] for ind in pop]
        # Calculate and print out the statistics
        length = len(pop)
        mean = sum(fits) / length
        sum2 = sum(x * x for x in fits)
        std = abs(sum2 / length - mean**2)**0.5

        print(" {:<10}{:<10}{:<8.2f}{:<8.2f}{:<8.2f}{:<8.2f}".format(
            generation, len(invalid_ind), min(fits), max(fits), mean, std))

    # Had to add time.sleep() to get around a weird bug, where times less than
    # 1 second are not recorded/recognised. Simple 1 second delay and then we
    # have to subtract the 1 second to get the actual time.
    time.sleep(1)
    execution_time = time.time() - start - 1
    total_solns = int(0)
    best = tools.selBest(pop, 1)[0]
    #    print("Best individual is %s, %s" % (best, best.fitness.values))   # debug

    # Let the user know the result.
    if best.fitness.values[0] >= 100:
        print("\nSolution(s) Found.")
        print("Saving resultant board plots.")
        dplobj = dpl.CPlot(rows, cols, "GA", max_holes, max_dominoes)
        solns = []
        # Find all the solutions in the population as there may be more than 1
        for ind in pop:
            if ind.fitness.values[0] == 100:
                result = cmn.convert2board(ind, rows, cols)
                # Only add if the solution is unique!
                unique = True
                for soln in solns:
                    if np.array_equal(soln, result):
                        unique = False
                        break
                if unique:
                    #                    print("ind={}".format(ind)) # debug
                    solns.append(result)
                    # Plot the domino board solutions; saving to a each to a file.
                    # Note we need to pass in a numpy 2D array!
                    dplobj.write_soln_to_file(result)
        dplobj.classify_solns()
        dplobj.plot_all()
        #
        # Write the version information to the results folder.
        # - Python and script version information is recorded.
        # - this includes the execution time in seconds.
        #
        total_solns = len(solns)
        verstr = cmn.get_version_str(SCRIPTINFO)
        time1st = execution_time / total_solns
        dplobj.write_test_info(verstr, execution_time, time1st)
    else:
        print("\nFailed to find a solution.")

    # Display the time taken if requested.
    if timed_execution:
        print("Script execution time:", execution_time, "seconds")
    # Return the execution time.
    return execution_time, total_solns