def solution(dimension, solver=None): """Generate binary puzzle solution.""" problem = Problem() if solver is not None: problem.setSolver(solver) else: problem.setSolver(BacktrackingSolver()) problem.addVariables(range(dimension**2), [0, 1]) for i in range(dimension): row_positions = range(i * dimension, (i + 1) * dimension) column_positions = range(i, dimension**2, dimension) # same number of zeros and ones in each row problem.addConstraint(ExactSumConstraint(dimension / 2), row_positions) problem.addConstraint(ExactSumConstraint(dimension / 2), column_positions) # maximum two of the same next to each other for triplet in _nwise(row_positions, 3): problem.addConstraint(MaxSumConstraint(2), triplet) problem.addConstraint(MinSumConstraint(1), triplet) for triplet in _nwise(column_positions, 3): problem.addConstraint(MaxSumConstraint(2), triplet) problem.addConstraint(MinSumConstraint(1), triplet) # unique rows and columns problem.addConstraint( FunctionConstraint(partial(_test_uniqueness, dimension=dimension)), range(dimension**2)) if isinstance(solver, RecursiveBacktrackingSolver): return problem.getSolutions() if isinstance(solver, MinConflictsSolver): return (problem.getSolution(), ) return problem.getSolutionIter()
def _generate_solutions(cls, period, doms): """ Each child gets as domain a set of ShiftAssignables, which is generated by their shift preferences; each solution selects for each child a ShiftAssignable so that all assignments are compatible. Here we configure a problem from the constraint library, then call the solver. The problem takes a mapping, doms, from child_pk to a set of assignables for that child, plus a specification, across children, of which assignables are pairwise consistent The solver yields all compatible sets of assignables """ problem = Problem() prefs = (ShiftPreference.objects.filter( period=period).select_related('shift').select_related('child')) for pref in prefs: for assignable in pref.assignables(): if assignable.is_active: doms[assignable.child.pk].append(assignable) for k, dom in doms.items(): try: problem.addVariable(k, dom) except ValueError: return {} cached_compatibility = functools.lru_cache()( lambda a1, a2: a1.is_compatible_with(a2)) for k1, k2 in itertools.combinations(doms.keys(), 2): problem.addConstraint(cached_compatibility, (k1, k2)) # call the solver return problem.getSolutionIter()
class SudokuProblem(object): def __init__(self): self._problem = Problem() def _each_once_per_line(self): for line_index in range(9): variable_names = [self._variable_name(line_index, column_index) for column_index in range(9)] self._problem.addConstraint(AllDifferentConstraint(), variable_names) def _each_once_per_column(self): for column_index in range(9): variable_names = [self._variable_name(line_index, column_index) for line_index in range(9)] self._problem.addConstraint(AllDifferentConstraint(), variable_names) def _each_once_per_square(self): for line_square_index in range(3): for column_square_index in range(3): variable_names = [] line_index_start = line_square_index * 3 for line_index in range(line_index_start, line_index_start + 3): column_index_start = column_square_index * 3 for column_index in range(column_index_start, column_index_start + 3): variable_names.append(self._variable_name(line_index, column_index)) self._problem.addConstraint(AllDifferentConstraint(), variable_names) def generate_constraints(self): self._each_once_per_line() self._each_once_per_column() self._each_once_per_square() def _variable_name(self, line_index, column_index): return '(l%d,c%d)' % (line_index, column_index) def feed_puzzle(self, values_of_colum_of_line): for line_index, values_of_column in values_of_colum_of_line.items(): for column_index, values in values_of_column.items(): variable_name = self._variable_name(line_index, column_index) self._problem.addVariable(variable_name, values) def iterate_solutions(self): return self._problem.getSolutionIter() def format_solution(self, solution_dict, indent_text): chunks = [] for line_index in range(9): values = [] for column_index in range(9): value = solution_dict[self._variable_name(line_index, column_index)] values.append(value) chunks.append(indent_text + '%d %d %d | %d %d %d | %d %d %d' % tuple(values)) if line_index % 3 == 2 and line_index < 8: chunks.append(indent_text + '------+-------+------') return '\n'.join(chunks)
def solve(): problem = Problem() total = 3.00 variables = ("0.02", "0.09", "0.13", "1.50", "2.00") values = [float(x) for x in variables] for variable, value in zip(variables, values): problem.addVariable(variable, range(int(total / value))) problem.addConstraint(ExactSumConstraint(total, values), variables) problem.addConstraint(ExactSumConstraint(100)) solutions = problem.getSolutionIter() return solutions, variables
def solve(): problem = Problem() total = 5.00 variables = ("0.01", "0.05", "0.10", "0.50", "1.00") values = [float(x) for x in variables] for variable, value in zip(variables, values): problem.addVariable(variable, range(int(total / value))) problem.addConstraint(ExactSumConstraint(total, values), variables) problem.addConstraint(ExactSumConstraint(100)) solutions = problem.getSolutionIter() return solutions, variables
def processResults(all_data): prob = Problem() for i, d in enumerate(all_data): prob.addVariable(i, d["logic"]) prob.addConstraint(MaxSumConstraint(234720)) solutions = prob.getSolutionIter() for i, s in enumerate(solutions): if i % 1000 == 0: print(i)
def compute_using_external_solver(predicate, env, varList): # TODO: import at module level # external software: # install http://labix.org/python-constraint # download and unzip python-constraint-1.1.tar.bz2 # python setup.py build # python setup.py install #from pretty_printer import pretty_print #print "predicate:", pretty_print(predicate) from constraint import Problem assert isinstance(predicate, Predicate) var_and_domain_lst = [] # get domain for idNode in varList: assert isinstance(idNode, AIdentifierExpression) atype = env.get_type_by_node(idNode) #if USE_RPYTHON_CODE: # domain = all_values_by_type_RPYTHON(atype, env, idNode) #else: # domain = all_values_by_type(atype, env, idNode) domain = all_values_by_type(atype, env, idNode) tup = (idNode.idName, domain) var_and_domain_lst.append(tup) problem = Problem() # import from "constraint" for tup in var_and_domain_lst: name = tup[0] lst = tup[1] if isinstance(lst, frozenset): lst = set_to_list(lst) problem.addVariable(name, lst) qme_nodes = [] constraint_string = pretty_print_python_style(env, varList, predicate, qme_nodes) names = [x.idName for x in varList] expr = "lambda " for n in names[0:-1]: expr += n+"," expr += varList[-1].idName+":"+constraint_string #print expr my_globales = {"qme_nodes":qme_nodes, "quick_member_eval":quick_member_eval, "env":env} lambda_func = eval(expr, my_globales) # TODO:(#ISSUE 16) not Rpython problem.addConstraint(lambda_func, names) return problem.getSolutionIter()
def calculate_solutions_and_store( problem: constraint.Problem, solution_queue: "multiprocessing.Queue[Solution]", queue_lock: "multiprocessing.synchronize.Lock", ) -> None: """ Uses the getSolutionIter interface to find solutions to the given problem one by one and add them to the given multiprocess queue. """ solution_iter = problem.getSolutionIter() while True: try: new_solution = next(solution_iter) try: queue_lock.acquire() solution_queue.put(new_solution) finally: queue_lock.release() except StopIteration: break
def getSolutions(problem: constraint.Problem, numSlots: int, maxSolutions=0, timeout=0., timeoutSearch=0.) -> list: """ Args: problem: the problem to solve, after having added all constraints numSlots: the number of variables (slots) defined for this problem maxSolutions: stop searching for solutions if this amount is reached timeout: raise Timedout if now solutions were found before this time. timeoutSearch: interrupt search after this period of time. Returns the results so far Returns: a list of solutions, where each solution is a list with the values for each slot Raises: Timedout: if timeout > 0 and no solution is found in that time """ solutions = [] if timeout: # try to get one solution try: func_timeout.func_timeout(timeout, problem.getSolution) except func_timeout.FunctionTimedOut: raise Timedout("Function timed out before first solution") t0 = time.time() for sol in problem.getSolutionIter(): if not sol: continue vals = [sol[k] for k in range(numSlots)] solutions.append(vals) if maxSolutions and len(solutions) >= maxSolutions: break if timeoutSearch > 0 and time.time() - t0 > timeoutSearch: break return solutions
def derive_depths(original_markers, additional_constraints=[]): """Use constraint programming to derive the paragraph depths associated with a list of paragraph markers. Additional constraints (e.g. expected marker types, etc.) can also be added. Such constraints are functions of two parameters, the constraint function (problem.addConstraint) and a list of all variables""" if not original_markers: return [] problem = Problem() marker_list = _compress_markerless(original_markers) # Depth in the tree, with an arbitrary limit of 10 problem.addVariables(["depth" + str(i) for i in range(len(marker_list))], range(10)) # Always start at depth 0 problem.addConstraint(rules.must_be(0), ("depth0",)) all_vars = [] for idx, marker in enumerate(marker_list): type_var = "type{}".format(idx) depth_var = "depth{}".format(idx) # Index within the marker list. Though this variable is redundant, it # makes the code easier to understand and doesn't have a significant # performance penalty idx_var = "idx{}".format(idx) typ_opts = [t for t in markers.types if marker in t] idx_opts = [i for t in typ_opts for i in range(len(t)) if t[i] == marker] problem.addVariable(type_var, typ_opts) problem.addVariable(idx_var, idx_opts) problem.addConstraint(rules.type_match(marker), [type_var, idx_var]) all_vars.extend([type_var, idx_var, depth_var]) if idx > 0: pairs = all_vars[3*(idx-1):] problem.addConstraint(rules.depth_check, pairs) if idx > 1: pairs = all_vars[3*(idx-2):] problem.addConstraint(rules.markerless_sandwich, pairs) problem.addConstraint(rules.star_sandwich, pairs) # separate loop so that the simpler checks run first for idx in range(1, len(marker_list)): # start with the current idx params = all_vars[3*idx:3*(idx+1)] # then add on all previous params += all_vars[:3*idx] problem.addConstraint(rules.sequence, params) # @todo: There's probably efficiency gains to making these rules over # prefixes (see above) rather than over the whole collection at once problem.addConstraint(rules.same_parent_same_type, all_vars) problem.addConstraint(rules.stars_occupy_space, all_vars) for constraint in additional_constraints: constraint(problem.addConstraint, all_vars) solutions = [] for assignment in problem.getSolutionIter(): assignment = _decompress_markerless(assignment, original_markers) solutions.append(Solution(assignment)) return solutions
def derive_depths(original_markers, additional_constraints=[]): """Use constraint programming to derive the paragraph depths associated with a list of paragraph markers. Additional constraints (e.g. expected marker types, etc.) can also be added. Such constraints are functions of two parameters, the constraint function (problem.addConstraint) and a list of all variables""" if not original_markers: return [] problem = Problem() marker_list = _compress_markerless(original_markers) # Depth in the tree, with an arbitrary limit of 10 problem.addVariables(["depth" + str(i) for i in range(len(marker_list))], range(10)) # Always start at depth 0 problem.addConstraint(rules.must_be(0), ("depth0", )) all_vars = [] for idx, marker in enumerate(marker_list): type_var = "type{}".format(idx) depth_var = "depth{}".format(idx) # Index within the marker list. Though this variable is redundant, it # makes the code easier to understand and doesn't have a significant # performance penalty idx_var = "idx{}".format(idx) typ_opts = [t for t in markers.types if marker in t] idx_opts = [ i for t in typ_opts for i in range(len(t)) if t[i] == marker ] problem.addVariable(type_var, typ_opts) problem.addVariable(idx_var, idx_opts) problem.addConstraint(rules.type_match(marker), [type_var, idx_var]) all_vars.extend([type_var, idx_var, depth_var]) if idx > 0: pairs = all_vars[3 * (idx - 1):] problem.addConstraint(pair_rules, pairs) if idx > 1: pairs = all_vars[3 * (idx - 2):] problem.addConstraint(rules.triplet_tests, pairs) # separate loop so that the simpler checks run first for idx in range(1, len(marker_list)): # start with the current idx params = all_vars[3 * idx:3 * (idx + 1)] # then add on all previous params += all_vars[:3 * idx] problem.addConstraint(rules.continue_previous_seq, params) # @todo: There's probably efficiency gains to making these rules over # prefixes (see above) rather than over the whole collection at once problem.addConstraint(rules.same_parent_same_type, all_vars) problem.addConstraint(rules.stars_occupy_space, all_vars) for constraint in additional_constraints: constraint(problem.addConstraint, all_vars) solutions = [] for assignment in problem.getSolutionIter(): assignment = _decompress_markerless(assignment, original_markers) solutions.append(Solution(assignment)) return solutions