Exemplo n.º 1
0
def string_from_file(file_name, indent=0):
    """Read file into string"""
    print_message('Reading file "' + file_name + '" ...', 3, indent=indent)
    with open(file_name, "r") as pattern_file:
        input_string = pattern_file.read()
    print_message('Done\n', 3, indent=indent)
    return input_string
Exemplo n.º 2
0
 def make_string(self, indent=0):
     print_message('Writing clauses into DIMACS format ...',
                   3,
                   indent=indent)
     return "p cnf " + str(self.number_of_variables) + " " + str(
         len(self.clause_set)) + "\n" + "".join(self.clause_set)
     print_message('Done\n', 3, indent=indent)
Exemplo n.º 3
0
def object_from_file(file_name, indent=0):
    """Load object from file"""
    print_message('Reading file "' + file_name + '" ...', 3, indent=indent)
    with open(file_name, "rb") as object_file:
        input_object = pickle.load(object_file)
    print_message('Done\n', 3, indent=indent)
    return input_object
 def force_population_exactly(self, constraint, indent=0):
     (times, population) = constraint
     print_message("Forcing the population in generation" + ("s" if len(times) > 1 else "") + " " + ", ".join(
         str(t) for t in times) + " to be exactly " + str(population), 3, indent=indent)
     literals = [cell for t in times for row in self.grid[t] for cell in row]
     self.force_exactly(literals, population, indent=indent + 1)
     print_message("Done\n", 3, indent=indent)
Exemplo n.º 5
0
def make_csv(grid,
             ignore_transition=None,
             background_grid=None,
             background_ignore_transition=None,
             rule=None,
             determined=None,
             show_background=None,
             indent=0):
    """Turn a search pattern in list form into nicely formatted csv string"""

    print_message('Format: csv', 3, indent=indent)

    grid = space_evenly(grid, ignore_transition)

    csv_string = ""

    if rule is not None:
        csv_string += "Rule = " + rulestring_from_rule(rule) + "\n"

    csv_string += "\n".join(",".join(line) for line in grid[0]) + "\n"

    if not determined:
        csv_string += "\n" + "\n\n".join("\n".join(",".join(line)
                                                   for line in generation)
                                         for generation in grid[1:]) + "\n"

    if show_background:
        csv_string += "\nBackground:\n"
        background_grid = space_evenly(background_grid,
                                       background_ignore_transition)
        csv_string += "\n" + "\n\n".join(
            "\n".join(",".join(line) for line in generation)
            for generation in background_grid) + "\n"

    return csv_string
    def remove_redundancies(self, indent=0):
        print_message("Removing redundant transitions...", 3, indent=indent)
        parents_dict = {}
        to_force_equal = []
        background_duration = len(self.background_grid)
        for t, generation in enumerate(self.background_grid):
            for y, row in enumerate(generation):
                for x, cell in enumerate(row):
                    predecessor_cell = self.background_grid[(t - 1) % background_duration][y][x]
                    neighbours = neighbours_from_coordinates(self.background_grid, x, y, t,
                                                             background_grid=self.background_grid)
                    if not self.background_ignore_transition[t][y][x]:
                        parents = [predecessor_cell] + list(src.rules.sort_neighbours(neighbours))
                        parents_string = str(parents)
                        if parents_string in parents_dict:
                            self.background_grid[t][y][x] = parents_dict[parents_string]
                            to_force_equal.append((parents_dict[parents_string], cell))
                            self.background_ignore_transition[t][y][x] = True
                        elif all(parent in ["0", "1"] for parent in parents):
                            bs_letter = ["B", "S"][["0", "1"].index(predecessor_cell)]
                            transition = src.rules.transition_from_cells(neighbours)
                            child = self.rule[bs_letter + transition]
                            if cell not in ["0", "1"]:
                                self.background_grid[t][y][x] = child
                            to_force_equal.append((cell, child))
                            self.background_ignore_transition[t][y][x] = True
                            parents_dict[parents_string] = self.background_grid[t][y][x]
                        else:
                            parents_dict[parents_string] = cell
        self.force_equal(to_force_equal)
        to_force_equal = []
        for t, generation in enumerate(self.grid):
            if t > 0:
                for y, row in enumerate(generation):
                    for x, cell in enumerate(row):
                        predecessor_cell = self.grid[t - 1][y][x]
                        neighbours = neighbours_from_coordinates(self.grid, x, y, t,
                                                                 background_grid=self.background_grid)

                        if not self.ignore_transition[t][y][x]:
                            parents = [predecessor_cell] + list(src.rules.sort_neighbours(neighbours))
                            parents_string = str(parents)
                            if parents_string in parents_dict:
                                self.grid[t][y][x] = parents_dict[parents_string]
                                to_force_equal.append((parents_dict[parents_string], cell))
                                self.ignore_transition[t][y][x] = True
                            elif all(parent in ["0", "1"] for parent in parents):
                                bs_letter = ["B", "S"][["0", "1"].index(predecessor_cell)]
                                transition = src.rules.transition_from_cells(neighbours)
                                child = self.rule[bs_letter + transition]
                                if cell not in ["0", "1"]:
                                    self.grid[t][y][x] = child
                                to_force_equal.append((cell, child))
                                self.ignore_transition[t][y][x] = True
                                parents_dict[parents_string] = self.grid[t][y][x]
                            else:
                                parents_dict[parents_string] = cell
        self.force_equal(to_force_equal)
        print_message("Done\n", 3, indent=indent)
    def force_at_least(self, literals, amount, indent=0):
        """Adds clauses forcing at least the given amount of literals to be true"""

        starting_number_of_clauses = len(self.clauses.clause_set)
        name = self.define_cardinality_variable(literals, amount)
        self.clauses.append([name])
        print_message("Number of clauses used: " + str(len(self.clauses.clause_set) - starting_number_of_clauses), 3,
                      indent=indent)
Exemplo n.º 8
0
def parse_input_string(input_string, indent=0):
    """Parses a search pattern given as a string"""

    print_message("Parsing input pattern...", 3, indent=indent)

    input_string = format_carriage_returns(input_string)

    # Remove any comments
    input_string = re.sub('#.*', '', input_string)

    # Remove any trailing or leading whitespace and commas
    input_string = input_string.strip(" ,\t\n")

    # Break down string into list-of-lists-of-lists
    split_by_generation = re.split(
        r"[ ,\t]*\n(?:[ ,\t]*\n)+[ ,\t]*",  # Split on at least two newlines and any spaces, commas or tabs
        input_string)
    split_by_line = [
        re.split(
            r"[ ,\t]*\n[ ,\t]*",  # Split on single newline and any amount of commas or spaces
            generation) for generation in split_by_generation
    ]
    grid = [
        [
            re.split(
                r"[ ,\t]+",  # Split on any amount of commas or spaces
                line) for line in generation
        ] for generation in split_by_line
    ]

    assert (all(
        len(generation) == len(grid[0])
        for generation in grid)
            and all(all(
                len(line) == len(grid[0][0])
                for line in generation) for generation in grid)), \
        "Search pattern is not cuboidal"

    # Tidy up any weird inputs
    grid = [[[standard_form_literal(cell) for cell in row]
             for row in generation] for generation in grid]

    # Create array which says when a "'" means that a transition should be ignored
    ignore_transition = [[[(cell[-1] in "'’") for cell in row]
                          for row in generation] for generation in grid]
    grid = [
        [
            [
                cell.rstrip("'’")  # The "'"s are now unnecessary
                for cell in row
            ] for row in generation
        ] for generation in grid
    ]

    print_message("Done\n", 3, indent=indent)

    return grid, ignore_transition
def search_pattern_from_string(input_string, indent=0):
    """Create the grid and ignore_transition of a search pattern from the given string"""
    grid, ignore_transition = src.formatting.parse_input_string(input_string, indent=indent)

    print_message("Pattern parsed as:\n" + src.formatting.make_csv(grid, ignore_transition) + "\n", 3, indent=indent)

    for t, generation in enumerate(grid):
        for y, row in enumerate(generation):
            for x, cell in enumerate(row):
                if cell not in ["0", "1", "*"]:
                    variable, negated = variable_from_literal(cell)
                    grid[t][y][x] = negate("user_input_" + variable, negated)

    return grid, ignore_transition
Exemplo n.º 10
0
    def standardise_variables_names(self, indent=0):
        print_message("Standardising variable names...", 3, indent=indent)

        # Give variables standard names and replace stars with new variable names
        standard_variables_from_input_variables = {}
        current_variable_number = 0

        for t, generation in enumerate(self.background_grid):
            for y, row in enumerate(generation):
                for x, cell in enumerate(row):
                    if cell == "*":
                        self.background_grid[t][y][x] = "c" + str(current_variable_number)
                        current_variable_number += 1
                    elif cell not in ["0", "1"]:
                        (variable, negated) = variable_from_literal(cell)
                        if variable not in standard_variables_from_input_variables:
                            standard_variables_from_input_variables[variable] = negate(
                                "c" + str(current_variable_number), negated)
                            current_variable_number += 1
                        self.background_grid[t][y][x] = negate(standard_variables_from_input_variables[variable],
                                                               negated)
        for t, generation in enumerate(self.grid):
            for y, row in enumerate(generation):
                for x, cell in enumerate(row):
                    if cell == "*":
                        self.grid[t][y][x] = "c" + str(current_variable_number)
                        current_variable_number += 1
                    elif cell not in ["0", "1"]:
                        (variable, negated) = variable_from_literal(cell)
                        if variable not in standard_variables_from_input_variables:
                            standard_variables_from_input_variables[variable] = negate(
                                "c" + str(current_variable_number), negated)
                            current_variable_number += 1
                        self.grid[t][y][x] = negate(standard_variables_from_input_variables[variable], negated)
        # Rename any literals in rule
        for transition, literal in self.rule.items():
            if literal not in ["0", "1"]:
                (variable, negated) = variable_from_literal(literal)
                if variable not in standard_variables_from_input_variables:
                    standard_variables_from_input_variables[variable] = negate("c" + str(current_variable_number),
                                                                               negated)
                    current_variable_number += 1
                self.rule[transition] = negate(standard_variables_from_input_variables[variable], negated)

        print_message("Done\n", 3, indent=indent)
Exemplo n.º 11
0
def sat_solve(search_pattern, solver=None, parameters=None, timeout=None, save_dimacs=None, dry_run=None, indent=0):
    """Solve the given DIMACS problem, using the specified SAT solver"""

    print_message('Solving...', indent=indent)

    if solver is None:
        solver = src.defaults.solver
    if solver not in src.defaults.supported_solvers:
        raise ValueError

    if save_dimacs is not None:
        if not isinstance(save_dimacs, str):
            file_number = 0
            while True:
                save_dimacs = "lls_dimacs" + str(file_number) + ".cnf"
                file_number += 1
                if not os.path.isfile(save_dimacs):
                    break
        search_pattern.clauses.make_file(save_dimacs, indent=indent + 1)

    dimacs_string = search_pattern.clauses.make_string(indent=indent + 1)

    if not dry_run:
        solution, time_taken = use_solver(solver, dimacs_string, parameters=parameters, timeout=timeout, indent=indent+1)
    else:
        solution = "DRYRUN\n"
        time_taken = None

    if solution not in ["UNSAT\n", "TIMEOUT\n", "DRYRUN\n"]:
        sat = "SAT"
        solution = search_pattern.substitute_solution(
            solution,
            indent=indent + 1
        )
    else:
        sat = solution[:-1]
        solution = None
    print_message('Done\n', indent=indent)
    return solution, sat, time_taken
Exemplo n.º 12
0
    def make_string(self, pattern_output_format=None, determined=None, show_background=None, indent=0):
        if pattern_output_format is None:
            pattern_output_format = src.defaults.pattern_output_format

        print_message('Formatting output...', 3, indent=indent)

        assert pattern_output_format in ["rle", "csv"], "Format not recognised"

        background_grid = copy.deepcopy(self.background_grid)
        src.literal_manipulation.offset_background(background_grid, -1, -1, 0)
        background_ignore_transition = copy.deepcopy(self.background_ignore_transition)
        src.literal_manipulation.offset_background(background_ignore_transition, -1, -1, 0)

        if pattern_output_format == "rle":
            output_string = src.formatting.make_rle(
                self.grid,
                background_grid=background_grid,
                rule=self.rule,
                determined=determined,
                show_background=show_background,
                indent=indent + 1
            )
        elif pattern_output_format == "csv":
            output_string = src.formatting.make_csv(
                self.grid,
                ignore_transition=self.ignore_transition,
                background_grid=background_grid,
                background_ignore_transition=background_ignore_transition,
                rule=self.rule,
                determined=determined,
                show_background=show_background,
                indent=indent + 1
            )
        else:
            raise Exception

        print_message('Done\n', 3, indent=indent)

        return output_string
def blank_search_pattern(width, height, duration, indent=0):
    print_message('Creating spaceship search pattern...', 3, indent=indent)

    grid = make_grid('*', width, height, duration)

    print_message("Pattern created:\n" + src.formatting.make_csv(grid) + "\n", 3, indent=indent + 1)
    print_message('Done\n', 3, indent=indent)
    return grid
Exemplo n.º 14
0
    def deterministic(self, indent=0):
        print_message("Checking if pattern is deterministic...", 3, indent=indent)
        determined = make_grid(False, template=self.grid)
        determined_variables = set()
        width = len(self.grid[0][0])
        height = len(self.grid[0])

        while True:
            determined_copy = copy.deepcopy(determined)
            for t, generation in enumerate(self.grid):
                for y, row in enumerate(generation):
                    for x, cell in enumerate(row):
                        if not determined[t][y][x]:
                            if cell in ["0", "1"]:
                                determined[t][y][x] = True
                            else:
                                variable, negated = variable_from_literal(cell)
                                if t == 0:
                                    determined[t][y][x] = True
                                    determined_variables.add(variable)
                                elif variable in determined_variables:
                                    determined[t][y][x] = True
                                elif all(determined[t - 1][y + y_offset][x + x_offset] for x_offset in range(2) for
                                         y_offset in range(2) if
                                         x + x_offset in range(width) and y + y_offset in range(height)) and not \
                                        self.ignore_transition[t][y][x]:
                                    determined[t][y][x] = True
                                    determined_variables.add(variable)
            if determined == determined_copy:
                break

        print_message("Done\n", 3, indent=indent)
        if all(flag for generation in determined for row in generation for flag in row):
            return True
        else:
            return False
Exemplo n.º 15
0
    def force_evolution(self, method=None, indent=0):
        """Adds clauses that force the search pattern to obey the transition rule"""

        # Methods:
        # 0. An implementation of the scheme Knuth describes in TAOCP Volume 4, Fascicle 6, solution to exercise 65b
        # (57 clauses and 13 auxiliary variables per cell)
        # 1. An implementation of the naive scheme Knuth gives in the solution to exercise 65a
        # (190 clauses and 0 auxiliary variables per cell)
        # 2. A very naive scheme just listing all possible predecessor neighbourhoods
        # (512 clauses and 0 auxiliary variables per cell)

        print_message("Enforcing evolution rule...", 3, indent=indent)

        if method is None:
            if src.rules.rulestring_from_rule(self.rule) == "B3/S23":
                method = src.defaults.life_encoding_method
            else:
                method = 2  # Default method
        assert method in range(3), "Method not found"
        assert method == 2 or src.rules.rulestring_from_rule(
            self.rule) == "B3/S23", "Rules other than Life can only use method 2"

        print_message("Method: " + str(method), 3, indent=indent + 1)
        starting_number_of_clauses = len(self.clauses.clause_set)
        # Iterate over all cells not in the first generation
        for t, generation in enumerate(self.grid):
            if t > 0:
                for y, row in enumerate(generation):
                    for x, cell in enumerate(row):
                        if not self.ignore_transition[t][y][x]:
                            self.force_transition(self.grid, x, y, t, method)

        # Iterate over all background cells
        for t, generation in enumerate(self.background_grid):
            for y, row in enumerate(generation):
                for x, cell in enumerate(row):
                    if not self.background_ignore_transition[t][y][x]:
                        self.force_transition(self.background_grid, x, y, t, method)

        print_message("Number of clauses used: " + str(len(self.clauses.clause_set) - starting_number_of_clauses), 3,
                      indent=indent + 1)
        print_message("Done\n", 3, indent=indent)
Exemplo n.º 16
0
 def force_max_growth(self, max_growth, indent=0):
     print_message("Forcing the pattern to never grow by more than " + str(max_growth) + " cells", 3, indent=indent)
     width = len(self.grid[0][0])
     height = len(self.grid[0])
     duration = len(self.grid)
     for t in range(1, duration):
         literals = []
         for x in range(width):
             for y in range(height):
                 literal = str(t) + "_" + str(x) + "_" + str(y) + "_grows"
                 self.clauses.append(implies([self.grid[t][y][x], negate(self.grid[0][y][x])], literal))
                 literals.append(literal)
         print_message("Generation " + str(t), 3, indent=indent + 1)
         self.force_at_most(literals, max_growth, indent=indent + 2)
     print_message("Done\n", 3, indent=indent)
Exemplo n.º 17
0
    def force_change(self, times, indent=0):
        """Adds clauses forcing at least one cell to change between specified generations"""

        (t_0, t_1) = times
        print_message(
            "Forcing at least one cell to change between generations " + str(t_0) + " and " + str(t_1) + " ...", 3,
            indent=indent)

        starting_number_of_clauses = len(self.clauses.clause_set)

        width = len(self.grid[0][0])
        height = len(self.grid[0])

        self.force_unequal([(self.grid[t_0][y][x], self.grid[t_1][y][x]) for x in range(width) for y in range(height)])

        print_message("Number of clauses used: " + str(len(self.clauses.clause_set) - starting_number_of_clauses), 3,
                      indent=indent + 1)
        print_message("Done\n", 3, indent=indent)
Exemplo n.º 18
0
    def force_distinct(self, solution, determined=False, indent=0):
        """Force search_pattern to have at least one difference from given solution"""

        print_message("Forcing pattern to be different from solution...", 3, indent=indent)

        clause = []

        for t, generation in enumerate(self.grid):
            if t == 0 or not determined:
                for y, row in enumerate(generation):
                    for x, cell in enumerate(row):
                        other_cell = solution.grid[t][y][x]
                        assert other_cell in ["0", "1"], "Only use force_distinct against solved patterns"
                        if other_cell == "0":
                            clause.append(cell)
                        else:
                            clause.append(negate(cell))

        for t, generation in enumerate(self.background_grid):
            for y, row in enumerate(generation):
                for x, cell in enumerate(row):
                    other_cell = solution.background_grid[t][y][x]
                    assert other_cell in ["0", "1"], "Only use force_distinct against solved patterns"
                    if other_cell == "0":
                        clause.append(cell)
                    else:
                        clause.append(negate(cell))

        for transition, literal in self.rule.items():
            other_literal = solution.rule[transition]
            assert other_literal in ["0", "1"], "Only use force_distinct against solved patterns"
            if other_literal == "0":
                clause.append(literal)
            else:
                clause.append(negate(literal))
        self.clauses.append(clause)
        print_message("Number of clauses used: 1", 3, indent=indent + 1)
        print_message("Done\n", 3, indent=indent)
Exemplo n.º 19
0
    def substitute_solution(self, solution, indent=0):
        """Return a copy of the search_pattern with the solution substituted back into it"""
        grid = copy.deepcopy(self.grid)
        rule = copy.deepcopy(self.rule)
        background_grid = copy.deepcopy(self.background_grid)

        print_message('Substituting solution back into search grid...', 3, indent=indent)

        # Remove the first line that just says "SAT", and split into a list of literals
        solution = set(solution.split("\n")[1].split())

        for t, generation in enumerate(grid):
            for y, row in enumerate(generation):
                for x, cell in enumerate(row):
                    if cell in ["0", "1"]:
                        pass
                    else:
                        (CNF_variable, negated) = variable_from_literal(cell)
                        if CNF_variable in self.clauses.dimacs_literal_from_variable:
                            dimacs_variable = self.clauses.dimacs_literal_from_variable[CNF_variable]

                            dimacs_literal = negate(dimacs_variable, negated, dimacs=True)

                            if dimacs_literal in solution:
                                grid[t][y][x] = "1"
                            else:
                                grid[t][y][x] = "0"
                        else:
                            grid[t][y][x] = "0"

        for t, generation in enumerate(background_grid):
            for y, row in enumerate(generation):
                for x, cell in enumerate(row):
                    if cell in ["0", "1"]:
                        pass
                    else:
                        (CNF_variable, negated) = variable_from_literal(cell)
                        if CNF_variable in self.clauses.dimacs_literal_from_variable:
                            dimacs_variable = self.clauses.dimacs_literal_from_variable[CNF_variable]

                            dimacs_literal = negate(dimacs_variable, negated, dimacs=True)

                            if dimacs_literal in solution:
                                background_grid[t][y][x] = "1"
                            else:
                                background_grid[t][y][x] = "0"
                        else:
                            background_grid[t][y][x] = "0"

        for transition, literal in rule.items():
            if literal in ["0", "1"]:
                pass
            else:
                (CNF_variable, negated) = variable_from_literal(literal)
                if CNF_variable in self.clauses.dimacs_literal_from_variable:
                    dimacs_variable = self.clauses.dimacs_literal_from_variable[CNF_variable]

                    dimacs_literal = negate(dimacs_variable, negated, dimacs=True)

                    if dimacs_literal in solution:
                        rule[transition] = "1"
                    else:
                        rule[transition] = "0"
                else:
                    rule[transition] = "0"
        print_message('Done\n', 3, indent=indent)

        return SearchPattern(grid, background_grid=background_grid, rule=rule, add_border=False)
Exemplo n.º 20
0
def make_rle(grid,
             background_grid=None,
             rule=None,
             determined=None,
             show_background=None,
             indent=0):
    """Turn a search pattern into nicely formatted string form"""

    print_message('Format: RLE', 3, indent=indent)

    grid = copy.deepcopy(grid)

    width = len(grid[0][0])
    height = len(grid[0])

    for t, generation in enumerate(grid):
        for y, row in enumerate(generation):
            for x, cell in enumerate(row):
                assert cell in ["0",
                                "1"], "Cell not equal to 0 or 1 in RLE format"
                if cell == "0":
                    grid[t][y][x] = "b"
                elif cell == "1":
                    grid[t][y][x] = "o"

    rle_string = "x = " + str(width) + ", y = " + str(height)

    if rule is not None:
        rle_string += ", rule = " + rulestring_from_rule(rule)

    rle_string += "\n"

    rle_string += "$\n".join("".join(line) for line in grid[0])

    rle_string += "!\n"

    if not determined:
        rle_string += "\nOther generations:\n"
        rle_string += "\n\n".join("$\n".join("".join(line)
                                             for line in generation)
                                  for generation in grid[1:]) + "\n"

    if show_background:
        rle_string += "\nBackground:\n"
        background_grid = copy.deepcopy(background_grid)

        for t, generation in enumerate(background_grid):
            for y, row in enumerate(generation):
                for x, cell in enumerate(row):
                    assert cell in ["0", "1"
                                    ], "Cell not equal to 0 or 1 in RLE format"
                    if cell == "0":
                        background_grid[t][y][x] = "b"
                    elif cell == "1":
                        background_grid[t][y][x] = "o"

        rle_string += "\n\n".join("$\n".join("".join(line)
                                             for line in generation)
                                  for generation in background_grid) + "\n"

    return rle_string
Exemplo n.º 21
0
 def make_file(self, file_name, indent=0):
     output_string = self.make_string()
     print_message('Writing file "' + file_name + '" ...', 3, indent=indent)
     with open(file_name, "w") as output_file:
         output_file.write(output_string)
     print_message('Done\n', 3, indent=indent)
Exemplo n.º 22
0
def append_to_file_from_string(file_name, input_string, indent=0):
    """Append string to file"""
    print_message('Writing to file "' + file_name + '" ...', 3, indent=indent)
    with open(file_name, "a+") as output_file:
        output_file.write(input_string)
    print_message('Done\n', 3, indent=indent)
Exemplo n.º 23
0
def file_from_object(file_name, input_object, indent=0):
    """Write object to file"""
    print_message('Writing file "' + file_name + '" ...', 3, indent=indent)
    with open(file_name, "wb") as output_file:
        pickle.dump(input_object, output_file)
    print_message('Done\n', 3, indent=indent)
Exemplo n.º 24
0
def preprocess_and_solve(search_pattern,
                         symmetries=None,
                         asymmetries=None,
                         population_at_most=None,
                         population_at_least=None,
                         population_exactly=None,
                         max_change=None,
                         max_decay=None,
                         max_growth=None,
                         force_change=None,
                         solver=None,
                         parameters=None,
                         timeout=None,
                         save_dimacs=None,
                         save_state=None,
                         method=None,
                         dry_run=False,
                         indent=0
                         ):
    """Preprocess and solve the search pattern"""
    if force_change is None:
        force_change = []
    if population_exactly is None:
        population_exactly = []
    if population_at_least is None:
        population_at_least = []
    if population_at_most is None:
        population_at_most = []
    if asymmetries is None:
        asymmetries = []
    if symmetries is None:
        symmetries = []
    try:
        preprocess(
            search_pattern,
            symmetries=symmetries,
            asymmetries=asymmetries,
            population_at_most=population_at_most,
            population_at_least=population_at_least,
            population_exactly=population_exactly,
            max_change=max_change,
            max_decay=max_decay,
            max_growth=max_growth,
            force_change=force_change,
            method=method,
            indent=indent
        )
        if save_state:
            if isinstance(save_state, str):
                state_file = save_state
            else:
                state_file = "lls_state.pkl"
                file_number = 0
                while os.path.isfile(state_file):
                    file_number += 1
                    state_file = "lls_state" + str(file_number) + ".pkl"
            print_message("Saving state...", 3, indent=indent + 1)
            src.files.file_from_object(
                state_file,
                (search_pattern.grid, search_pattern.ignore_transition, search_pattern.background_grid,
                 search_pattern.background_ignore_transition, search_pattern.rule,
                 search_pattern.clauses.DIMACS_literal_from_variable),
                indent=indent + 2
            )
            print_message("Done\n", 3, indent=indent + 1)
        # Problem statistics
        width = len(search_pattern.grid[0][0])
        height = len(search_pattern.grid[0])
        duration = len(search_pattern.grid)
        active_width = sum(
            any(
                any(
                    (search_pattern.grid[t][y][x] not in ["0", "1"])
                    for y in range(height))
                for t in range(duration))
            for x in range(width)
        )
        active_height = sum(
            any(
                any(
                    (search_pattern.grid[t][y][x] not in ["0", "1"])
                    for t in range(duration))
                for x in range(width))
            for y in range(height)
        )
        active_duration = sum(
            any(
                any(
                    (search_pattern.grid[t][y][x] not in ["0", "1"])
                    for x in range(width))
                for y in range(height))
            for t in range(duration)
        )
        number_of_cells = search_pattern.number_of_cells()
        number_of_variables = search_pattern.clauses.number_of_variables
        number_of_clauses = len(search_pattern.clauses.clause_set)
        print_message('Number of undetermined cells: ' + str(number_of_cells), indent=indent)
        print_message('Number of variables: ' + str(number_of_variables), indent=indent)
        print_message('Number of clauses: ' + str(number_of_clauses) + "\n", indent=indent)
        print_message('Active width: ' + str(active_width), indent=indent)
        print_message('Active height: ' + str(active_height), indent=indent)
        print_message('Active duration: ' + str(active_duration) + "\n", indent=indent)
    except UnsatInPreprocessing:
        (
            solution,
            sat,
            number_of_cells,
            number_of_variables,
            number_of_clauses,
            active_width,
            active_height,
            active_duration,
            time_taken
        ) = (
            None,
            "UNSAT",
            None,
            None,
            None,
            None,
            None,
            None,
            0
        )
        print_message("Unsatisfiability proved in preprocessing", indent=indent + 1)
        print_message('Done\n', indent=indent)
    else:
        (
            solution,
            sat,
            time_taken
        ) = src.sat_solvers.sat_solve(
            search_pattern,
            solver=solver,
            parameters=parameters,
            timeout=timeout,
            save_dimacs=save_dimacs,
            dry_run=dry_run,
            indent=indent
        )
    if not dry_run:
        print_message('Time taken: ' + str(time_taken) + " seconds\n", indent=indent)
    return \
        solution,\
        sat, \
        number_of_cells,\
        number_of_variables, \
        number_of_clauses, \
        active_width, \
        active_height, \
        active_duration, \
        time_taken
Exemplo n.º 25
0
def lls(
        search_pattern,
        symmetries=None,
        asymmetries=None,
        population_at_most=None,
        population_at_least=None,
        population_exactly=None,
        max_change=None,
        max_decay=None,
        max_growth=None,
        force_change=None,
        solver=None,
        parameters=None,
        timeout=None,
        save_dimacs=None,
        save_state=None,
        method=None,
        dry_run=False,
        number_of_solutions=None,
        pattern_output_format=None,
        output_file_name=None,
        indent=0
):
    """The central part of LLS. Controls the flow of the program"""

    if force_change is None:
        force_change = []
    if population_exactly is None:
        population_exactly = []
    if population_at_least is None:
        population_at_least = []
    if population_at_most is None:
        population_at_most = []
    if asymmetries is None:
        asymmetries = []
    if symmetries is None:
        symmetries = []
    (
        solution,
        sat,
        number_of_cells,
        number_of_variables,
        number_of_clauses,
        active_width,
        active_height,
        active_duration,
        time_taken
    ) = preprocess_and_solve(
        search_pattern,
        symmetries=symmetries,
        asymmetries=asymmetries,
        population_at_most=population_at_most,
        population_at_least=population_at_least,
        population_exactly=population_exactly,
        max_change=max_change,
        max_decay=max_decay,
        max_growth=max_growth,
        force_change=force_change,
        solver=solver,
        parameters=parameters,
        timeout=timeout,
        save_dimacs=save_dimacs,
        save_state=save_state,
        method=method,
        dry_run=dry_run,
        indent=indent
    )

    # Check if the first generation of pattern determines the others

    solutions = []

    if sat == "SAT":
        determined = search_pattern.deterministic(indent=indent)
        show_background = search_pattern.background_nontrivial()
        solutions.append(solution)
        output_string = solution.make_string(
            pattern_output_format=pattern_output_format,
            determined=determined,
            show_background=show_background,
            indent=indent
        )
    else:
        output_string = ["Unsatisfiable", "Timed Out", "Dry run"][["UNSAT", "TIMEOUT", "DRYRUN"].index(sat)]
    print_message(output_string + "\n", 1, indent=indent)
    if output_file_name:
        print_message('Writing to output file...', indent=indent)
        src.files.append_to_file_from_string(output_file_name, output_string, indent=indent + 1)
        print_message('Done\n', indent=indent)

    # Deal with the case where we need more than one solution
    if number_of_solutions and sat == "SAT" and not dry_run:
        if number_of_solutions != "Infinity":
            number_of_solutions = int(number_of_solutions)
            enough_solutions = (len(solutions) >= number_of_solutions)
        else:
            enough_solutions = False

        while sat == "SAT" and not enough_solutions:
            # Force the new solution to be different
            search_pattern.force_distinct(solution, determined=determined)
            # No need to apply the constraints again
            (
                solution,
                sat,
                _,
                _,
                _,
                _,
                _,
                _,
                extra_time_taken
            ) = preprocess_and_solve(
                search_pattern,
                solver=solver,
                parameters=parameters,
                timeout=timeout,
                method=method,
                indent=indent
            )
            time_taken += extra_time_taken
            if sat == "SAT":
                solutions.append(solution)
                output_string = solution.make_string(pattern_output_format=pattern_output_format, determined=determined,
                                                     show_background=show_background, indent=indent)
            else:
                output_string = ["Unsatisfiable", "Timed Out", "Dry run"][["UNSAT", "TIMEOUT", None].index(sat)]
            print_message(output_string + "\n", 1, indent=indent)
            if output_file_name:
                print_message('Writing output file...', indent=indent)
                src.files.append_to_file_from_string(output_file_name, output_string, indent=indent + 1)
                print_message('Done\n', indent=indent)

            if number_of_solutions != "Infinity":
                enough_solutions = (len(solutions) >= number_of_solutions)
        sat = "SAT"
        print_message('Total solver time: ' + str(time_taken), indent=indent)

    return solutions, \
        sat, \
        number_of_cells, \
        number_of_variables, \
        number_of_clauses, \
        active_width, \
        active_height, \
        active_duration, \
        time_taken
Exemplo n.º 26
0
def preprocess(
        search_pattern,
        symmetries=None,
        asymmetries=None,
        population_at_most=None,
        population_at_least=None,
        population_exactly=None,
        max_change=None,
        max_decay=None,
        max_growth=None,
        force_change=None,
        force_evolution=True,
        method=None,
        indent=0
):
    """Apply constraints and create SAT problem"""
    if force_change is None:
        force_change = []
    if population_exactly is None:
        population_exactly = []
    if population_at_least is None:
        population_at_least = []
    if asymmetries is None:
        asymmetries = []
    if population_at_most is None:
        population_at_most = []
    if symmetries is None:
        symmetries = []
    print_message('Preprocessing...', indent=indent)

    # Constraints that change the grid
    search_pattern.standardise_variables_names(indent=indent + 1)
    for symmetry in symmetries:
        search_pattern.force_symmetry(symmetry)

    search_pattern.remove_redundancies(indent=indent + 1)
    search_pattern.standardise_variables_names(indent=indent + 1)

    print_message("Search grid:\n", 3, indent=indent + 1)
    print_message(search_pattern.make_string(pattern_output_format="csv", show_background=True), 3, indent=indent + 2)

    # Constraints that are enforced by clauses
    for asymmetry in asymmetries:
        search_pattern.force_asymmetry(asymmetry)
    for constraint in population_at_most:
        search_pattern.force_population_at_most(constraint, indent=indent + 1)
    for constraint in population_at_least:
        search_pattern.force_population_at_least(constraint, indent=indent + 1)
    for constraint in population_exactly:
        search_pattern.force_population_exactly(constraint, indent=indent + 1)
    if max_change is not None:
        search_pattern.force_max_change(max_change, indent=indent + 1)
    if max_decay is not None:
        search_pattern.force_max_decay(max_decay, indent=indent + 1)
    if max_growth is not None:
        search_pattern.force_max_growth(max_growth, indent=indent + 1)
    for times in force_change:
        search_pattern.force_change(times, indent=indent + 1)

    if force_evolution:
        # The most important bit. Enforces the evolution rules
        search_pattern.force_evolution(method=method, indent=indent + 1)
    print_message('Done\n', indent=indent)
Exemplo n.º 27
0
def use_solver(solver, dimacs_string, parameters=None, timeout=None, indent=0):
    if parameters is not None:
        parameter_list = parameters.strip(" ").split(" ")
    else:
        parameter_list = []

    solver_path = sys.path[0] + "/solvers/" + solver

    command = [solver_path] + parameter_list

    solver_process = subprocess.Popen(
        command,
        stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)

    timeout_flag = [False]  # We want the flag to be mutable, so we put it into a little box.

    def timeout_function(process, flag):
        process.kill()
        flag[0] = "TIMEOUT"

    timeout_timer = threading.Timer(timeout, timeout_function, [solver_process, timeout_flag])

    print_message('Solving with "' + solver + '" ... (Start time: ' + time.ctime() + ")", 3, indent=indent)

    start_time = time.time()
    try:
        timeout_timer.start()
        out, error = solver_process.communicate(dimacs_string.encode())
    except KeyboardInterrupt:
        solver_process.kill()
        timeout_flag[0] = "SIGINT"
    finally:
        out = out.decode("utf-8")
        error = error.decode("utf-8")
        timeout_timer.cancel()
        time_taken = time.time() - start_time

    if not timeout_flag[0]:
        print_message('Done\n', 3, indent=indent)

        print_message('Formatting SAT solver output...', 3, indent=indent)

        solution = str(out)
        solution = solution.split("\ns ")
        solution = solution[-1]
        solution = solution.split("\nc")
        solution = solution[0]
        solution = solution.split("\nv ")
        solution = solution[0] + "\n" + " ".join(solution[1:])

        if "UNSAT" in solution.upper():
            solution = "UNSAT\n"

        print_message("SAT solver output:", 3, indent=indent + 1)
        print_message(out, 3, indent=indent + 2)
        print_message('Error (if any): "' + error + '"', 3, indent=indent + 1)
        print_message('Time taken: ' + str(time_taken), 3, indent=indent + 1)
        print_message('Done\n', 3, indent=indent)

    else:
        print_message('Timed out\n', 3, indent=indent)
        solution = "TIMEOUT\n"

    return solution, time_taken
Exemplo n.º 28
0
def rule_from_rulestring(rulestring, indent=0):
    if rulestring is None:
        return None
    else:

        rule = {}

        rulestring = rulestring.strip()
        original_rulestring = rulestring

        partial_flag = False

        if rulestring[0] == "{":
            rule_unsanitized = ast.literal_eval(rulestring)
            for BS_letter in "BS":
                for number_of_neighbours in "012345678":
                    for character in possible_transitions[number_of_neighbours]:
                        literal = standard_form_literal(
                            str(rule_unsanitized[BS_letter + number_of_neighbours + character]))
                        assert literal[-1] not in ['\xe2\x80\x99', "'"], "Can't ignore transition in rule"
                        rule[BS_letter + number_of_neighbours + character] = literal
            return rule
        elif rulestring[0] in ["p", "P"]:
            partial_flag = True
            if len(rulestring) == 1:
                rulestring = "B012345678/S012345678"
            else:
                rulestring = rulestring[1:]

        rulestring = re.sub(' ', '', rulestring.upper())

        rulestrings = re.split("/", rulestring)

        if len(rulestrings) == 1:
            assert "B" in rulestring or "S" in rulestring, 'Rule sting not recognised (no "B" or "S")'
            b_position = rulestring.find("B")
            s_position = rulestring.find("S")
            rulestring = rulestring.strip("BS")
            rulestrings = re.split("[BS]*", rulestring)
            assert len(rulestrings) < 3, "Rule sting not recognised"
            if b_position > s_position:
                birth_string = rulestrings[1] if len(rulestrings) == 2 else ""
                survival_string = rulestrings[0]
            else:
                birth_string = rulestrings[0]
                survival_string = rulestrings[1] if len(rulestrings) == 2 else ""
        else:
            assert len(rulestrings) == 2, 'Rule sting not recognised (too many "/"s)'
            if "S" in rulestrings[0] or "B" in rulestrings[1]:
                birth_string = rulestrings[1]
                survival_string = rulestrings[0]
            else:
                birth_string = rulestrings[0]
                survival_string = rulestrings[1]

        assert "S" not in birth_string and "B" not in survival_string, "Rule sting not recognised"

        birth_string = re.sub('B', '', birth_string).lower()
        survival_string = re.sub('S', '', survival_string).lower()

        assert (birth_string == "" or birth_string[0] in "012345678") and (
                    survival_string == "" or survival_string[0] in "012345678"), "Rule sting not recognised"

        if partial_flag:
            variable_number = 0

        for BS_letter, rulestring in zip(["B", "S"], [birth_string, survival_string]):
            transitions = []
            previous_number = 0

            if rulestring != "":
                for position in range(1, len(rulestring)):
                    if rulestring[position] in "012345678":
                        transitions.append(rulestring[previous_number:position])
                        previous_number = position
                transitions.append(rulestring[previous_number:])

            for transition in transitions:
                number_of_neighbours = transition[0]
                if not partial_flag:
                    if len(transition) == 1:
                        for character in possible_transitions[number_of_neighbours]:
                            rule[BS_letter + number_of_neighbours + character] = "1"
                    elif transition[1] == "-":
                        banned_characters = transition[2:]
                        assert all(character in possible_transitions[number_of_neighbours] for character in
                                   banned_characters), "Unrecognized character"
                        for character in possible_transitions[number_of_neighbours]:
                            if character in banned_characters:
                                rule[BS_letter + number_of_neighbours + character] = "0"
                            else:
                                rule[BS_letter + number_of_neighbours + character] = "1"
                    else:
                        characters = transition[1:]
                        assert all(character in possible_transitions[number_of_neighbours] for character in
                                   characters), "Unrecognized character"
                        for character in possible_transitions[number_of_neighbours]:
                            if character in characters:
                                rule[BS_letter + number_of_neighbours + character] = "1"
                            else:
                                rule[BS_letter + number_of_neighbours + character] = "0"
                else:
                    if len(transition) == 1:
                        for character in possible_transitions[number_of_neighbours]:
                            rule[BS_letter + number_of_neighbours + character] = "rule_variable_" + str(variable_number)
                            variable_number += 1
                    else:
                        characters = transition[1:]
                        if "-" in characters:
                            characters, banned_characters = re.split("-", characters)
                        else:
                            banned_characters = ""

                        for character in possible_transitions[number_of_neighbours]:
                            if character in characters:
                                rule[BS_letter + number_of_neighbours + character] = "1"
                            elif character in banned_characters:
                                rule[BS_letter + number_of_neighbours + character] = "0"
                            else:
                                rule[BS_letter + number_of_neighbours + character] = "rule_variable_" + str(
                                    variable_number)
                                variable_number += 1

            for number_of_neighbours in "012345678":
                if BS_letter + number_of_neighbours + "c" not in rule:
                    for character in possible_transitions[number_of_neighbours]:
                        rule[BS_letter + number_of_neighbours + character] = "0"

        new_rulestring = rulestring_from_rule(rule)
        if original_rulestring != new_rulestring:
            print_message("Rulestring parsed as: " + new_rulestring, 3, indent=indent)

        return rule