コード例 #1
0
def main():
    parser = argparse.ArgumentParser(
        description=
        'A Multi-threaded Python Application that Plays the Game of Life. '
        'Must specify either random input data or input from file.',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-e',
                        '--evolutions',
                        type=int,
                        default=1,
                        help='Number of evolutions of game.')
    parser.add_argument(
        '--hosts',
        type=str,
        default=None,
        help='Specifies the hosts and number of threads on each host')
    parser.add_argument(
        '-i',
        '--input',
        type=str,
        default=None,
        help=
        'filename for input with either image data, binary data, or space separated text'
    )
    parser.add_argument(
        '-c',
        '--cutoff',
        type=int,
        default=128,
        help=
        'where to threshold image values if input is an image (higher the cutoff, the darker the initial image)'
    )
    parser.add_argument(
        '-r',
        '--random',
        type=str,
        default=None,
        help='initialize a random start matrix. Format: ROWSxCOLUMNS')
    parser.add_argument('-p',
                        '--plot',
                        action='store_true',
                        help='shows each evolution in a matplotlib window')
    parser.add_argument(
        '-l',
        '--log',
        type=str,
        default=None,
        help=
        'if specified, saves each evolution of the input board to the specified directory'
    )
    parser.add_argument(
        '-f',
        '--format',
        type=str,
        default='bmp',
        help=
        f'if specified, the program will save each evolution as this format. Only has effect if --log is also set. Independent of the actual output file format. Options={supported_formats}'
    )
    parser.add_argument(
        '-o',
        '--output',
        type=str,
        default='life_output.bmp',
        help='filename for output to either image data, or space separated text'
    )
    parser.add_argument(
        '-u',
        '--unit',
        action='store_true',
        help=
        'if specified, will run a unit test to ensure the life function is working properly. Program will exit afterward'
    )

    # User has to specify some args
    args = parser.parse_args()

    if args.unit:
        print(
            "Unit testing the life() function. Will raise AssertionError if unit test fails."
        )
        unit_test_life()
        sys.exit(0)

    # Can only specify input or random
    if (args.input is None
            and args.random is None) or (args.input is not None
                                         and args.random is not None):
        parser.print_help()
        print("\nERROR: need to specify either --input OR --random, not both",
              file=sys.stderr)
        sys.exit(1)
    elif args.input:
        if '.' in args.input and args.input.split('.')[1] in binary_formats:
            image_bytes = np.array(Image.open(args.input).convert('L'))
            input_bytes = (image_bytes < args.cutoff)
        else:
            with open(args.input, 'r') as f:
                input_bytes = np.loadtxt(f, delimiter=' ').astype('uint8')
    elif args.random:
        print('Generating input matrix...')
        input_bytes = fast_random_bool(
            tuple([int(el) for el in args.random.lower().split('x')][0:2]))
        print('Writing input matrix to file...')
        write_to_file(input_bytes, Path('./'),
                      f"input_{datetime.now().strftime('%y-%m-%d-%H%M%S')}",
                      args.format)

    if args.format not in supported_formats:
        parser.print_help()
        print(
            f"\nERROR: illegal format '{args.format}' specified, "
            f"please choose from {supported_formats}",
            file=sys.stderr)
        sys.exit(1)

    if args.hosts is not None:
        broke = Broker(args.hosts)
    else:
        parser.print_help()
        print(f"\nERROR: did not specify hosts file", file=sys.stderr)
        sys.exit(1)

    args.evolutions = max(1, int(args.evolutions))
    (num_threads_r, num_threads_c) = factorize(broke.total_threads)
    # we want more threads in the row direction if odd total because this is
    # more efficient in terms of memory caching
    num_rows = input_bytes.shape[0]
    num_cols = input_bytes.shape[1]
    block_size_r = int(np.ceil(num_rows / num_threads_r))
    block_size_c = int(np.ceil(num_cols / num_threads_c))

    padded = np.zeros((num_rows + 2, num_cols + 2), dtype='uint8')
    padded[1:-1, 1:-1] = input_bytes

    if args.plot:
        plt.figure(figsize=(24, 24))
        img = plt.imshow(padded[1:-1, 1:-1], cmap='Greys')
        plt.pause(0.001)

    # save the initial matrix to file.
    # for ease of scripting we 0 pad the numbers with the needed number of digits
    fname_padding = int(np.log10(args.evolutions)) + 1
    if args.log:
        write_to_file(
            padded[1:-1, 1:-1], Path(args.log),
            "{header}_evo_{evo:0{padding}d}_{curtime}".format(
                header=(args.input.split('.')[0]
                        if args.input else args.random),
                evo=0,
                padding=fname_padding,
                curtime=datetime.now().strftime('%y-%m-%d-%H%M%S'),
                fmt=args.format), args.format)
    '''
    for each evolution,
    split the padded matrix up into the number of parts
    append it to a list
    and use map to distribute this list to the pool of processes.
    
    we catch keyboard interrupts so the user can stop the plotting
    '''

    oldtime = datetime.now()

    # pool = Pool(broke.local_threads)
    # print(f"Started {broke.local_threads} threads")

    for evolution in range(1, args.evolutions + 1):
        try:
            if args.plot:
                img.set_data(padded[1:-1, 1:-1])
                plt.pause(0.0001)

            results = []
            for i in range(num_threads_r):
                for j in range(num_threads_c):
                    x = get_padded_slice(num_rows, block_size_r, i)
                    y = get_padded_slice(num_cols, block_size_c, j)
                    results.append(padded[x, y])

            newtime = datetime.now()
            if evolution == 1:
                print(
                    f"Starting Evolution {evolution}, Time elapsed since last evolution N/A"
                )
            else:
                print(
                    f"Starting Evolution {evolution}, Time elapsed since last evolution {(newtime - oldtime)}"
                )
            oldtime = newtime

            broke.send_off(results[broke.local_threads:])
            print(f'Sent to all workers!')
            results = broke.receive()
            print(f'Received from all workers!')

            counter = 0
            for i in range(num_threads_r):
                for j in range(num_threads_c):
                    padded[get_unpadded_slice(num_rows, block_size_r, i),
                           get_unpadded_slice(num_cols, block_size_c, j
                                              )] = results[counter][1:-1, 1:-1]
                    counter += 1

            if args.log:
                write_to_file(
                    padded[1:-1, 1:-1], Path(args.log),
                    "{header}_evo_{evo:0{padding}d}_{curtime}".format(
                        header=(args.input.split('.')[0]
                                if args.input else args.random),
                        evo=evolution,
                        padding=fname_padding,
                        curtime=datetime.now().strftime('%y-%m-%d-%H%M%S'),
                        fmt=args.format), args.format)

        except KeyboardInterrupt:
            print("Interrupt received! Ending now.", file=sys.stderr)
            break

    broke.close()

    final_result = padded[1:-1, 1:-1]

    # plot the start and stop states
    if not args.random and '.' in args.input and args.input.split(
            '.')[1] in binary_formats:
        fig, (ax1, ax2, ax3) = plt.subplots(nrows=1, ncols=3, dpi=1000)
        ax1.imshow(255 - image_bytes, cmap='Greys')
        ax2.imshow(input_bytes, cmap='Greys')
        ax3.imshow(final_result, cmap='Greys')
    else:
        fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, dpi=1000)
        ax1.imshow(input_bytes, cmap='Greys')
        ax2.imshow(final_result, cmap='Greys')

    # fig.tight_layout()

    if '.' in args.output:
        out_split = args.output.split('.')
        write_to_file(final_result, Path('.'), out_split[-2], out_split[-1])
        plt.savefig(f"{out_split[-2]}_comparison_plot.png")
    else:
        write_to_file(final_result, Path('.'), args.output, args.format)