def test_min_conflicts_solver(): problem = Problem(MinConflictsSolver()) problem.addVariable("x", [0, 1]) problem.addVariable("y", [0, 1]) solution = problem.getSolution() possible_solutions = [ { "x": 0, "y": 0 }, { "x": 0, "y": 1 }, { "x": 1, "y": 0 }, { "x": 1, "y": 1 }, ] assert solution in possible_solutions
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()
def find_mapping(signal_patterns: Set[FrozenSet[str]]) -> Dict[str, str]: # Let’s express this as a constraint satisfaction problem problem = Problem() for signal in "abcdefg": problem.addVariable(signal, "abcdefg") # Each signal wire goes to a different segment problem.addConstraint(AllDifferentConstraint()) # Unambiguous digits based on count of lit segments for digit in {1, 4, 7, 8}: for wire in find_possible_signals_for(signal_patterns, digit): segments = DIGIT_TO_SEGMENTS[digit] problem.addConstraint(InSetConstraint(segments), wire) # Unambiguous segments based on how many times they appear in patterns for signal_wire in {"a", "b", "c", "d", "e", "f", "g"}: count = sum(1 for pattern in signal_patterns if signal_wire in pattern) if count == 4: problem.addConstraint(InSetConstraint(["e"]), signal_wire) elif count == 6: problem.addConstraint(InSetConstraint(["b"]), signal_wire) elif count == 7: problem.addConstraint(InSetConstraint(["d", "g"]), signal_wire) elif count == 8: problem.addConstraint(InSetConstraint(["a", "c"]), signal_wire) elif count == 9: problem.addConstraint(InSetConstraint(["f"]), signal_wire) else: raise ValueError return problem.getSolution()
def queens(n): problem = Problem() domain = range(n + 1)[1:] variables = LinkedList(list("X%d" % i for i in range(n + 1)[1:])) for name in variables: problem.addVariable(name, domain) constr_all(problem, variables) return problem
def _cp_step(self, state, prob): """ The constraint programming step. This is one of the more complicated steps; it divides the boundary into components that don't influence each other first, then divides each of those into areas that are equally constrained and must therefore have the same probabilities. The combinations of the number of mines in those components is computed with constraint programming. Those solutions are then combined to count the number of models in which each area has the given number of mines, from which we can calculate the average expected number of mines per square in a component if it has M mines, i.e. per component we have a mapping of {num_mines: (num_models, avg_prob)}. This information is then passed on to the combining step to form the final probabilities. :param state: The reduced state. :param prob: The already computed probabilities. :returns: The exact probability for every unknown square. """ components, num_components = self._components(state) c_counts = [] # List of model_count_by_m instances from inside the 'for c' loop. c_probs = [] # List of model_count_by_m instances from inside the 'for c' loop. m_known = self.known_mine_count() # Solve each component individually for c in range(1, num_components+1): areas, constraints = self._get_areas(state, components == c) # Create a CP problem to determine which combination of mines per area is possible. problem = Problem() # Add all variables, each one having a domain [0, num_squares]. for v in areas.values(): problem.addVariable(v, range(len(v)+1)) # Now constrain how many mines areas can have combined. for constraint in constraints: problem.addConstraint(constraint, [v for k, v in areas.items() if constraint in k]) # Add a constraint so that the maximum number of mines never exceeds the number of mines left. problem.addConstraint(MaxSumConstraint(self._total_mines - m_known), list(areas.values())) solutions = problem.getSolutions() model_count_by_m = {} # {m: #models} model_prob_by_m = {} # {m: prob of the average component model} # Now count the number of models that exist for each number of mines in that component. for solution in solutions: m = sum(solution.values()) # Number of models that match this solution. model_count = self._count_models(solution) # Increase counter for the number of models that have m mines. model_count_by_m[m] = model_count_by_m.get(m, 0) + model_count # Calculate the probability of each square in the component having a mines. model_prob = np.zeros(prob.shape) for area, m_area in solution.items(): # The area has `m_area` mines in it, evenly distributed. model_prob[tuple(zip(*area))] = m_area/len(area) # Sum up all the models, giving the expected number of mines of all models combined model_prob_by_m[m] = model_prob_by_m.get(m, np.zeros(prob.shape)) + model_count*model_prob # We've summed the probabilities of each solution, weighted by the number of models with those # probabilities, now divide out the total number of models to obtain the probability of each square of a # model with m mines having a mine. model_prob_by_m = {m: model_prob/model_count_by_m[m] for m, model_prob in model_prob_by_m.items()} c_probs.append(model_prob_by_m) c_counts.append(model_count_by_m) prob = self._combine_components(state, prob, c_probs, c_counts) return prob
def solver(domain, appointments): problem = Problem() ConstraintGraphCost = nx.Graph() variablesName = [] # for each appointment (iterate on the numerical key of the appointments) for x in appointments: dom = [] # check which elements of the generic domain are necessary for this appointment for y in domain: hour, minutes = y[1].split(".") hour = int(hour) #print(appointments[x]) #print(appointments[x]["Day"]) for a in appointments[x]["Day"]: if "Morning" == a[1] and hour < 12 and y[0] in a[0] and y[ 2] in appointments[x]["House"]: dom.append(y) if "Afternoon" == a[1] and hour > 12 and y[0] in a[0] and y[ 2] in appointments[x]["House"]: dom.append(y) #Aggiungo la variabile corrente con il domain aggiustato # print(dom) variablesName.append(x) ConstraintGraphCost.add_node(x, domain=dom) problem.addVariable(x, dom) a = itertools.combinations(variablesName, 2) for i in a: #print("Considero ", i) stop = False for domItem1 in ConstraintGraphCost.nodes[i[0]]['domain']: if (stop): break else: for domItem2 in ConstraintGraphCost.nodes[i[1]]['domain']: if domItem1[0] == domItem2[0] and domItem1[1] == domItem2[ 1] and domItem1 != "notScheduled": #print("creo edge") ConstraintGraphCost.add_edge(i[0], i[1]) problem.addConstraint(constraintFunction(), (i[0], i[1])) stop = True break start = current_milli_time() solution = problem.getSolution() end = current_milli_time() print("\n\n###########Time spent to find the first solution = ", end - start, " ms.\n\n") # pp.pprint(solution) return solution
def test_toy_python_constraint(): def func1(a): return a < 0 p = Problem() p.addVariable("a", [-1, -2, 0, 1, 2]) p.addConstraint(func1, ["a"]) result = p.getSolutions() print(result)
def solve_board(overlays, verify_just_one=False): problem = Problem() spot_constraints = defaultdict(list) overlay_constraints = [] for overlay in overlays: # the simplest case is a fully 3x3 grid if len(overlay) == 3 and len(overlay[0]) == 3: for x in range(3): for y in range(3): if overlay[x][y] is None: continue spot_constraints[(x, y)].extend( get_constraints(overlay[x][y])) else: # dealing with a grid that is smaller than 3x3 so we # need to make relative constraints - we add those # after the all different constraint so it only needs # to look at possible boards overlay_constraints.append( (FunctionConstraint(floating_overlay(overlay)), locations)) # the unspecified spots could be any piece for x in range(3): for y in range(3): if (x, y) not in spot_constraints: spot_constraints[(x, y)] = pieces for spot, values in spot_constraints.iteritems(): problem.addVariable(spot, values) problem.addConstraint(AllDifferentConstraint()) for overlay_constraint in overlay_constraints: problem.addConstraint(*overlay_constraint) solution = None if verify_just_one: solutions = problem.getSolutions() assert len(solutions) == 1, ('%d solutions but there should be 1' % len(solutions)) solution = solutions[0] else: solution = problem.getSolution() answer = [[None] * 3 for x in range(3)] for x in range(3): for y in range(3): answer[x][y] = solution[(x, y)] print('\n'.join(' '.join(_) for _ in answer)) print('') return answer
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 test_constraint(): c = Problem() c.addVariable("i", range(-100, 10)) c.addVariable("j", range(-10, 10)) c.addVariable("k", range(-10, 10)) c.addConstraint(lambda i, j, k: i <= 0 or j <= 0 or k <= 0, ("i", "j", "k")) C = CustomConstraint(c) values1 = [-1, -1, -1] values2 = [9, 8, 2] assert C.to_fitness(values1) == 9963.0 assert C.to_fitness(values2) == 12349.0
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 buildproblem(terms, total): problem = Problem() letters = sorted(list(set("".join(terms) + total))) initial = set([word[0] for word in terms + [total]]) for letter in letters: if letter in initial: problem.addVariable(letter, list(range(1, 10))) else: problem.addVariable(letter, list(range(10))) problem.addConstraint(AllDifferentConstraint()) digit_checks(problem, terms, total, letters) return problem
def buildproblem(terms, total): problem = Problem() letters = sorted(list(set("".join(terms) + total))) initial = set([word[0] for word in terms + [total]]) for letter in letters: if letter in initial: problem.addVariable(letter, list(range(1,10))) else: problem.addVariable(letter, list(range(10))) problem.addConstraint(AllDifferentConstraint()) digit_checks(problem, terms, total, letters) return problem
def team_solver(all_players, max_overall): problem = Problem() for position in all_players: problem.addVariable(position, all_players[position]) def constraint(pg, sg, sf, pf, c): team_overall = pg['player']['overall'] + sg['player']['overall'] + \ sf['player']['overall'] + pf['player']['overall'] + \ c['player']['overall'] return team_overall <= max_overall problem.addConstraint(constraint) return problem.getSolutions()
def test_least_conflicts_solver(): # another test for LeastConflictsSolver problem = Problem(LeastConflictsSolver()) result = [[('a', 1), ('b', 2), ('c', 1)], [('a', 2), ('b', 1), ('c', 1)], [('a', 2), ('b', 2), ('c', 1)]] problem.addVariables(["a", "b"], [1, 2]) problem.addVariable("c", [1]) problem.addConstraint(lambda a, b: b != a, ["a", "b"]) problem.addConstraint(lambda a, b: b != a, ["a", "c"]) problem.addConstraint(lambda a, b: b != a, ["b", "c"]) solution = problem.getSolution() assert sorted(solution.items()) in result
def solver(domain, appointments): problem = Problem() # for each appointment (iterate on the numerical key of the appointments) for x in appointments: dom = [] # check which elements of the generic domain are necessary for this appointment for y in domain: hour, minutes = y[1].split(".") hour = int(hour) if "Morning" in appointments[x]["Pref"] and hour < 12 and y[ 0] in appointments[x]["Day"] and y[2] in appointments[x][ "House"]: dom.append(y) if "Afternoon" in appointments[x]["Pref"] and hour > 12 and y[ 0] in appointments[x]["Day"] and y[2] in appointments[x][ "House"]: dom.append(y) # print("FOR APPOINTMENT:\n") # pp.pprint(x) # print("The domain is:\n") # pp.pprint(dom) # add a variable to the CSP problem with the appointment *x* and the just computed domain *dom* problem.addVariable(x, dom) # add constraints for x in appointments: for y in appointments: if (x != y): problem.addConstraint(constraintFunction(), (x, y)) start = current_milli_time() solution = problem.getSolution() end = current_milli_time() print("\n\n###########Time spent to find the first solution = ", end - start, " ms.\n\n") # pp.pprint(solution) return solution
def solve_puzzles(puzzles, solver): """ Solves an array of sudoku puzzles, recording runtime. :param puzzles: an array of 2D array boards :param solver: the CSP solver to be used :return: none """ fail_count = 0 start_time = datetime.now() # start timer (for runtime) for puzzle in puzzles: # initialize Board b = Board(puzzle) sudoku = Problem(solver) # initialize CSP with custom solver # add variables for each square, indexed 1...size^2 for index in range(b.board_size ** 2): value = b.get_value(index) if value == 0: sudoku.addVariable(index, range(1, b.board_size + 1)) else: sudoku.addVariable(index, [value]) # add uniqueness constraints to each row, column, and subsquare for i in range(b.board_size): sudoku.addConstraint(AllDifferentConstraint(), [el[0] for el in b.row(i)]) sudoku.addConstraint(AllDifferentConstraint(), [el[0] for el in b.col(i)]) sudoku.addConstraint(AllDifferentConstraint(), [el[0] for el in b.subsquare(i)]) sln = sudoku.getSolution() # solve CSP if sln: # assign solved values for index, value in sln.items(): b.set_value(index, value) else: fail_count += 1 # perform/display runtime calculation runtime = datetime.now() - start_time print("Runtime: {} seconds ({} failed)".format(runtime.total_seconds(), fail_count))
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 try_assignment(): problem = Problem() for variable in self.variables: candidates = [assignment[variable.name]] if variable.name in assignment else groups domain = list([g for g in candidates if any(st in variable.types for st in (Typing.as_legacy_type(gt) for gt in g.vector_types))]) if len(domain) == 0: return variable.name in assignment, [] problem.addVariable(variable.name, domain) for f in filters: variables = list([v.name for v in f.variables]) def c_j(ff, vv): return lambda *args: ff.test_relaxed({vv[i]: args[i] for i in range(len(args))}, solutions) problem.addConstraint(c_j(f, variables), variables) return True, list(problem.getSolutions())
def gen_disjunct_intervals_CSP(ints): p = Problem() size = len(ints) sizeL = range(size) for intnum, key in enumerate(ints): values = [[]] for i in sizeL: values += [ item for item in it.combinations(ints, i + 1) if key in item ] p.addVariable(intnum, values) for i in sizeL: p.addConstraint(check_continuity, (i, )) for i1, i2 in it.product(sizeL, sizeL): if i1 < i2: p.addConstraint(check_all, (i1, i2)) p.addConstraint(all_interval, sizeL) return p.getSolution()
def problem_2(): """Function for performing the work of problem 2.""" # Initialize the problem. problem = Problem() # Add the variables. problem.addVariable('case', CASES) problem.addVariable('motherboard', MOTHERBOARDS) problem.addVariable('memory', MEMORY) problem.addVariable('GPU', GPUS) problem.addVariable('SSD', SSDS) problem.addVariable('CPU', CPUS) # Add constraints. problem.addConstraint(motherboard_case, ['motherboard', 'case']) problem.addConstraint(motherboard_memory, ['motherboard', 'memory']) problem.addConstraint(motherboard_ssd, ['motherboard', 'SSD']) problem.addConstraint(motherboard_cpu, ['motherboard', 'CPU']) problem.addConstraint(case_gpu, ['case', 'GPU']) return problem
def gen_disjunct_intervals_CSP(ints): p = Problem() size = len(ints) sizeL = range(size) for intnum, key in enumerate(ints): values = [[]] for i in sizeL: values += [ item for item in it.combinations(ints, i + 1) if key in item ] p.addVariable(intnum, values) for i in sizeL: p.addConstraint(check_continuity, (i,)) for i1, i2 in it.product(sizeL, sizeL): if i1 < i2: p.addConstraint(check_all, (i1, i2)) p.addConstraint(all_interval, sizeL) return p.getSolution()
def player_solver(players, attribute_bounds): def create_constraint(attribute, bounds): min_value, max_value = bounds def constraint(player): return min_value <= player[attribute] return constraint # def constraint(player): # return min_value <= player[attribute] <= max_value # return constraint problem = Problem() problem.addVariable("player", players) for attribute, bounds in attribute_bounds.items(): constraint = create_constraint(attribute, bounds) problem.addConstraint(constraint, ["player"]) return problem.getSolutions()
def test_addVariable_support_domain_subclasses(): class MyCustomDomain(Domain): pass class MyConstraint(Constraint): def __call__(self, variables, domains, assignments, forwardcheck=False): assert isinstance(domains['x'], Domain) assert isinstance(domains['y'], MyCustomDomain) return True problem = Problem() problem.addVariable("x", [0, 1]) problem.addVariable("y", MyCustomDomain([0, 1])) problem.addConstraint(MyConstraint()) solution = problem.getSolution() possible_solutions = [ { "x": 0, "y": 0 }, { "x": 0, "y": 1 }, { "x": 1, "y": 0 }, { "x": 1, "y": 1 }, ] assert solution in possible_solutions
def solve_board(board, cages): problem = Problem() height = len(board) width = len(board[0]) assert width == height, 'Grid must be a square' cage_name_to_locations = defaultdict(list) for x in range(height): row_variables = [(x, ry) for ry in range(width)] column_variables = [(cx, x) for cx in range(height)] problem.addConstraint(AllDifferentConstraint(), row_variables) problem.addConstraint(AllDifferentConstraint(), column_variables) for y in range(width): if isinstance(board[x][y], basestring): # we are dealing with a function cage_name_to_locations[board[x][y]].append((x, y)) else: # we are dealing with a pre-assigned number problem.addVariable((x, y), [board[x][y]]) for cage_name, cage_locations in cage_name_to_locations.iteritems(): cage_function = cages[cage_name] all_values = product(range(1, width + 1), repeat=len(cage_locations)) possible_values = set(chain(*[values for values in all_values if cage_function(*values)])) for location in cage_locations: problem.addVariable(location, list(possible_values)) problem.addConstraint(FunctionConstraint(cage_function), cage_locations) solution = problem.getSolution() answer = [row[:] for row in board] for x in range(height): for y in range(width): answer[x][y] = solution[(x, y)] return answer
def kakuroCSP(): # Pre: variables and equations have been computed by sanityCheck # Will be run in a background thread global solutions, variables # will be accessed by main thread global solverDone problem = Problem() # Restrict the value of each white square as much as possible domains = defaultdict(set) univ = list(range(1,10)) for n in univ: for c in combinations(univ, n): domains[sum(c), n] |= set(c) candidates = {v: set(univ) for v in variables} for eq in equations: for v in eq.variables: candidates[v] &= domains[eq.clue, len(eq.variables)] # one variable for each white square, with values in range computed above for v in variables: problem.addVariable(v, tuple(candidates[v])) for eq in equations: # All the numbers in a single sum are distinct problem.addConstraint(AllDifferentConstraint(), eq.variables) # The numbers must sum to the clue problem.addConstraint(ExactSumConstraint(eq.clue), eq.variables) solutions = problem.getSolutions() solverDone = True
def Balanceo(trabajadores): global TAREA problema = Problem() variables = list() for objTrabajador in trabajadores: dominio = range(0, objTrabajador.capacidad + 1) variable = str(objTrabajador.id) variables.append(problema.addVariable(variable, dominio)) while (TAREA > 0): promedioActual = Trabajar(trabajadores) TAREA -= 1 ImprimirSalida(promedioActual, trabajadores)
def solve(sudoku_data): """ Solves the sudoku using simple constraints programming. Returns a list of solutions. Multiple solutions may be found if the sudoku wasn't parsed correctly. """ problem = Problem() # add known numbers for i in range(0, 81): problem.addVariable(i, [int(sudoku_data[i])] if sudoku_data[i] else range(1, 10)) for i in range(0, 9): # row constraint problem.addConstraint(AllDifferentConstraint(), range(i * 9, i * 9 + 9)) # column constraint problem.addConstraint(AllDifferentConstraint(), [(i + 9 * c) for c in range(0, 9)]) # box constraint problem.addConstraint(AllDifferentConstraint(), [(i * 3) + (i / 3 * 18) + (9 * j) + k for j in range(0, 3) for k in range(0, 3)]) return problem.getSolutions()
def determine(state): card_played_this_round = [ card is not None and 1 or 0 for card in state.cards_played_by_player ] remaining_hand_size = 5 - sum(state.tricks_won_by_team) hand_size_by_player = [ remaining_hand_size - played for played in card_played_this_round ] cards = list( set(deal()) - set(list(chain(*state.hands))) - set(state.cards_played)) shuffle(cards) problem = Problem() for player in range(4): if state.hands[player]: for card_index, card in enumerate(state.hands[player]): problem.addVariable((player, card_index), [card]) else: voids_by_player = state.voids_by_player[player] for card_index in range(hand_size_by_player[player]): if voids_by_player: potential_cards = potential_cards_given_voids( state.trump, voids_by_player, cards) shuffle(potential_cards) problem.addVariable((player, card_index), potential_cards) else: problem.addVariable((player, card_index), cards) problem.addConstraint(AllDifferentConstraint()) cards = sorted(iteritems(problem.getSolution())) hands = [[], [], [], []] for player in range(4): hands[player] = [c[1] for c in cards[:hand_size_by_player[player]]] del cards[:hand_size_by_player[player]] state = state._replace(hands=hands) return state
def determine(state): card_played_this_round = [card is not None and 1 or 0 for card in state.cards_played_by_player] remaining_hand_size = 5 - sum(state.tricks_won_by_team) hand_size_by_player = [remaining_hand_size - played for played in card_played_this_round] cards = list(set(deal()) - set(list(chain(*state.hands))) - set(state.cards_played)) shuffle(cards) problem = Problem() for player in range(4): if state.hands[player]: for card_index, card in enumerate(state.hands[player]): problem.addVariable((player, card_index), [card]) else: voids_by_player = state.voids_by_player[player] for card_index in range(hand_size_by_player[player]): if voids_by_player: potential_cards = potential_cards_given_voids( state.trump, voids_by_player, cards) shuffle(potential_cards) problem.addVariable((player, card_index), potential_cards) else: problem.addVariable((player, card_index), cards) problem.addConstraint(AllDifferentConstraint()) cards = sorted(iteritems(problem.getSolution())) hands = [[], [], [], []] for player in range(4): hands[player] = [c[1] for c in cards[:hand_size_by_player[player]]] del cards[:hand_size_by_player[player]] state = state._replace(hands=hands) return state
class CSPSolver(Solver): """Solver reducing the task to a Constraint Satisfaction Problem. Solving this done with the python-constraint module. The variables are the quantum numbers of particles/edges, but also some composite quantum numbers which are attributed to the interaction nodes (such as angular momentum :math:`L`). The conservation rules serve as the constraints and a special wrapper class serves as an adapter. """ # pylint: disable=too-many-instance-attributes def __init__(self, allowed_intermediate_particles: List[GraphEdgePropertyMap]): self.__variables: Set[Union[_EdgeVariableInfo, _NodeVariableInfo]] = set() self.__var_string_to_data: Dict[str, Union[_EdgeVariableInfo, _NodeVariableInfo]] = {} self.__node_rules: Dict[int, Set[Rule]] = defaultdict(set) self.__non_executable_node_rules: Dict[int, Set[Rule]] = defaultdict(set) self.__edge_rules: Dict[int, Set[GraphElementRule]] = defaultdict(set) self.__non_executable_edge_rules: Dict[ int, Set[GraphElementRule]] = defaultdict(set) self.__problem = Problem(BacktrackingSolver(True)) self.__allowed_intermediate_particles = allowed_intermediate_particles self.__scoresheet = Scoresheet() def find_solutions(self, problem_set: QNProblemSet) -> QNResult: # pylint: disable=too-many-locals self.__initialize_constraints(problem_set) solutions = self.__problem.getSolutions() node_not_executed_rules = self.__non_executable_node_rules node_not_satisfied_rules: Dict[int, Set[Rule]] = defaultdict(set) edge_not_executed_rules = self.__non_executable_edge_rules edge_not_satisfied_rules: Dict[ int, Set[GraphElementRule]] = defaultdict(set) for node_id, rules in self.__node_rules.items(): for rule in rules: if self.__scoresheet.rule_calls[(node_id, rule)] == 0: node_not_executed_rules[node_id].add(rule) elif self.__scoresheet.rule_passes[(node_id, rule)] == 0: node_not_satisfied_rules[node_id].add(rule) for edge_id, edge_rules in self.__edge_rules.items(): for rule in edge_rules: if self.__scoresheet.rule_calls[(edge_id, rule)] == 0: edge_not_executed_rules[edge_id].add(rule) elif self.__scoresheet.rule_passes[(edge_id, rule)] == 0: edge_not_satisfied_rules[edge_id].add(rule) solutions = self.__convert_solution_keys(solutions) # insert particle instances if self.__node_rules or self.__edge_rules: full_particle_solutions = ( _merge_particle_candidates_with_solutions( solutions, problem_set.topology, self.__allowed_intermediate_particles, )) else: full_particle_solutions = [ QuantumNumberSolution( node_quantum_numbers=problem_set.initial_facts.node_props, edge_quantum_numbers=problem_set.initial_facts.edge_props, ) ] if full_particle_solutions and (node_not_executed_rules or edge_not_executed_rules): # rerun solver on these graphs using not executed rules # and combine results result = QNResult() for full_particle_solution in full_particle_solutions: node_props = full_particle_solution.node_quantum_numbers edge_props = full_particle_solution.edge_quantum_numbers node_props.update(problem_set.initial_facts.node_props) edge_props.update(problem_set.initial_facts.edge_props) result.extend( validate_full_solution( QNProblemSet( topology=problem_set.topology, initial_facts=GraphElementProperties( node_props=node_props, edge_props=edge_props, ), solving_settings=GraphSettings( node_settings={ i: NodeSettings(conservation_rules=rules) for i, rules in node_not_executed_rules.items() }, edge_settings={ i: EdgeSettings(conservation_rules=rules) for i, rules in edge_not_executed_rules.items() }, ), ))) return result return QNResult( full_particle_solutions, _convert_non_executed_rules_to_names(node_not_executed_rules), _convert_violated_rules_to_names(node_not_satisfied_rules), _convert_non_executed_rules_to_names(edge_not_executed_rules), _convert_violated_rules_to_names(edge_not_satisfied_rules), ) def __clear(self) -> None: self.__variables = set() self.__var_string_to_data = {} self.__node_rules = defaultdict(set) self.__edge_rules = defaultdict(set) self.__problem = Problem(BacktrackingSolver(True)) self.__scoresheet = Scoresheet() def __initialize_constraints(self, problem_set: QNProblemSet) -> None: """Initialize all of the constraints for this graph. For each interaction node a set of independent constraints/conservation laws are created. For each conservation law a new CSP wrapper is created. This wrapper needs all of the qn numbers/variables which enter or exit the node and play a role for this conservation law. Hence variables are also created within this method. """ # pylint: disable=too-many-locals self.__clear() def get_rules_by_priority( graph_element_settings: Union[NodeSettings, EdgeSettings, ] ) -> List[Rule]: # first add priorities to the entries priority_list = [ (x, graph_element_settings.rule_priorities[type(x)]) if type(x) in graph_element_settings.rule_priorities else (x, 1) for x in graph_element_settings.conservation_rules ] # then sort according to priority sorted_list = sorted(priority_list, key=lambda x: x[1], reverse=True) # and strip away the priorities again return [x[0] for x in sorted_list] arg_handler = RuleArgumentHandler() for edge_id in problem_set.topology.edges: edge_settings = problem_set.solving_settings.edge_settings[edge_id] for rule in get_rules_by_priority(edge_settings): variable_mapping = _VariableContainer() # from cons law and graph determine needed var lists edge_qns, node_qns = get_required_qns(rule) edge_vars, fixed_edge_vars = self.__create_edge_variables( [ edge_id, ], edge_qns, problem_set, ) score_callback = self.__scoresheet.register_rule(edge_id, rule) constraint = _GraphElementConstraint[EdgeQuantumNumber]( rule, # type: ignore edge_vars, fixed_edge_vars, arg_handler, score_callback, ) if edge_vars: var_strings = [ _create_variable_string(*x) for x in edge_vars ] self.__edge_rules[edge_id].add(rule) # type: ignore self.__problem.addConstraint(constraint, var_strings) else: self.__non_executable_edge_rules[edge_id].add( rule # type: ignore ) for node_id in problem_set.topology.nodes: for rule in get_rules_by_priority( problem_set.solving_settings.node_settings[node_id]): variable_mapping = _VariableContainer() # from cons law and graph determine needed var lists edge_qns, node_qns = get_required_qns(rule) in_edges = problem_set.topology.get_edge_ids_ingoing_to_node( node_id) in_edge_vars = self.__create_edge_variables( in_edges, edge_qns, problem_set) variable_mapping.ingoing_edge_variables = in_edge_vars[0] variable_mapping.fixed_ingoing_edge_variables = in_edge_vars[1] var_list: List[Union[ _EdgeVariableInfo, _NodeVariableInfo]] = list( variable_mapping.ingoing_edge_variables) out_edges = (problem_set.topology. get_edge_ids_outgoing_from_node(node_id)) out_edge_vars = self.__create_edge_variables( out_edges, edge_qns, problem_set) variable_mapping.outgoing_edge_variables = out_edge_vars[0] variable_mapping.fixed_outgoing_edge_variables = out_edge_vars[ 1] var_list.extend(list(variable_mapping.outgoing_edge_variables)) # now create variables for node/interaction qns int_node_vars = self.__create_node_variables( node_id, node_qns, problem_set, ) variable_mapping.node_variables = int_node_vars[0] variable_mapping.fixed_node_variables = int_node_vars[1] var_list.extend(list(variable_mapping.node_variables)) score_callback = self.__scoresheet.register_rule(node_id, rule) if len(inspect.signature(rule).parameters) == 1: constraint = _GraphElementConstraint[NodeQuantumNumber]( rule, # type: ignore int_node_vars[0], { node_id: int_node_vars[1] }, arg_handler, score_callback, ) else: constraint = _ConservationRuleConstraintWrapper( rule, variable_mapping, arg_handler, score_callback) if var_list: var_strings = [ _create_variable_string(*x) for x in var_list ] self.__node_rules[node_id].add(rule) self.__problem.addConstraint(constraint, var_strings) else: self.__non_executable_node_rules[node_id].add(rule) def __create_node_variables( self, node_id: int, qn_list: Set[Type[NodeQuantumNumber]], problem_set: QNProblemSet, ) -> Tuple[Set[_NodeVariableInfo], GraphNodePropertyMap]: """Create variables for the quantum numbers of the specified node. If a quantum number is already defined for a node, then a fixed variable is created, which cannot be changed by the csp solver. Otherwise the node is initialized with the specified domain of that quantum number. """ variables: Tuple[Set[_NodeVariableInfo], GraphNodePropertyMap] = ( set(), dict(), ) if node_id in problem_set.initial_facts.node_props: node_props = problem_set.initial_facts.node_props[node_id] for qn_type in qn_list: if qn_type in node_props: variables[1].update({qn_type: node_props[qn_type]}) else: node_settings = problem_set.solving_settings.node_settings[node_id] for qn_type in qn_list: var_info = (node_id, qn_type) if qn_type in node_settings.qn_domains: qn_domain = node_settings.qn_domains[qn_type] self.__add_variable(var_info, qn_domain) variables[0].add(var_info) return variables def __create_edge_variables( self, edge_ids: Sequence[int], qn_list: Set[Type[EdgeQuantumNumber]], problem_set: QNProblemSet, ) -> Tuple[Set[_EdgeVariableInfo], Dict[int, GraphEdgePropertyMap]]: """Create variables for the quantum numbers of the specified edges. If a quantum number is already defined for an edge, then a fixed variable is created, which cannot be changed by the csp solver. This is the case for initial and final state edges. Otherwise the edges are initialized with the specified domains of that quantum number. """ variables: Tuple[Set[_EdgeVariableInfo], Dict[int, GraphEdgePropertyMap], ] = ( set(), dict(), ) for edge_id in edge_ids: variables[1][edge_id] = {} if edge_id in problem_set.initial_facts.edge_props: edge_props = problem_set.initial_facts.edge_props[edge_id] for qn_type in qn_list: if qn_type in edge_props: variables[1][edge_id].update( {qn_type: edge_props[qn_type]}) else: edge_settings = problem_set.solving_settings.edge_settings[ edge_id] for qn_type in qn_list: var_info = (edge_id, qn_type) if qn_type in edge_settings.qn_domains: qn_domain = edge_settings.qn_domains[qn_type] self.__add_variable(var_info, qn_domain) variables[0].add(var_info) return variables def __add_variable( self, var_info: Union[_EdgeVariableInfo, _NodeVariableInfo], domain: List[Any], ) -> None: if var_info not in self.__variables: self.__variables.add(var_info) var_string = _create_variable_string(*var_info) self.__var_string_to_data[var_string] = var_info self.__problem.addVariable(var_string, domain) def __convert_solution_keys( self, solutions: List[Dict[str, Scalar]], ) -> List[QuantumNumberSolution]: """Convert keys of CSP solutions from string to quantum number types.""" converted_solutions = list() for solution in solutions: edge_quantum_numbers: Dict[ int, GraphEdgePropertyMap] = defaultdict(dict) node_quantum_numbers: Dict[ int, GraphNodePropertyMap] = defaultdict(dict) for var_string, value in solution.items(): ele_id, qn_type = self.__var_string_to_data[var_string] if qn_type in getattr( # noqa: B009 EdgeQuantumNumber, "__args__"): edge_quantum_numbers[ele_id].update({qn_type: value} # type: ignore ) else: node_quantum_numbers[ele_id].update({qn_type: value} # type: ignore ) converted_solutions.append( QuantumNumberSolution(node_quantum_numbers, edge_quantum_numbers)) return converted_solutions
def main(puzzle, lines): puzzle = puzzle.rstrip().splitlines() while puzzle and not puzzle[0]: del puzzle[0] # Extract horizontal words horizontal = [] word = [] predefined = {} for row in range(len(puzzle)): for col in range(len(puzzle[row])): char = puzzle[row][col] if not char.isspace(): word.append((row, col)) if char != "#": predefined[row, col] = char elif word: if len(word) > MINLEN: horizontal.append(word[:]) del word[:] if word: if len(word) > MINLEN: horizontal.append(word[:]) del word[:] # Extract vertical words vertical = [] validcol = True col = 0 while validcol: validcol = False for row in range(len(puzzle)): if col >= len(puzzle[row]): if word: if len(word) > MINLEN: vertical.append(word[:]) del word[:] else: validcol = True char = puzzle[row][col] if not char.isspace(): word.append((row, col)) if char != "#": predefined[row, col] = char elif word: if len(word) > MINLEN: vertical.append(word[:]) del word[:] if word: if len(word) > MINLEN: vertical.append(word[:]) del word[:] col += 1 # hnames = ["h%d" % i for i in range(len(horizontal))] # vnames = ["v%d" % i for i in range(len(vertical))] # problem = Problem(MinConflictsSolver()) problem = Problem() for hi, hword in enumerate(horizontal): for vi, vword in enumerate(vertical): for hchar in hword: if hchar in vword: hci = hword.index(hchar) vci = vword.index(hchar) problem.addConstraint( lambda hw, vw, hci=hci, vci=vci: hw[hci] == vw[vci], ("h%d" % hi, "v%d" % vi)) for char, letter in predefined.items(): for hi, hword in enumerate(horizontal): if char in hword: hci = hword.index(char) problem.addConstraint( lambda hw, hci=hci, letter=letter: hw[hci] == letter, ("h%d" % hi, )) for vi, vword in enumerate(vertical): if char in vword: vci = vword.index(char) problem.addConstraint( lambda vw, vci=vci, letter=letter: vw[vci] == letter, ("v%d" % vi, )) wordsbylen = {} for hword in horizontal: wordsbylen[len(hword)] = [] for vword in vertical: wordsbylen[len(vword)] = [] for line in lines: line = line.strip() l = len(line) if l in wordsbylen: wordsbylen[l].append(line.upper()) for hi, hword in enumerate(horizontal): words = wordsbylen[len(hword)] random.shuffle(words) problem.addVariable("h%d" % hi, words) for vi, vword in enumerate(vertical): words = wordsbylen[len(vword)] random.shuffle(words) problem.addVariable("v%d" % vi, words) problem.addConstraint(AllDifferentConstraint()) solution = problem.getSolution() if not solution: print("No solution found!") maxcol = 0 maxrow = 0 for hword in horizontal: for row, col in hword: if row > maxrow: maxrow = row if col > maxcol: maxcol = col for vword in vertical: for row, col in vword: if row > maxrow: maxrow = row if col > maxcol: maxcol = col matrix = [] for row in range(maxrow + 1): matrix.append([" "] * (maxcol + 1)) for variable in solution: if variable[0] == "v": word = vertical[int(variable[1:])] else: word = horizontal[int(variable[1:])] for (row, col), char in zip(word, solution[variable]): matrix[row][col] = char for row in range(maxrow + 1): for col in range(maxcol + 1): sys.stdout.write(matrix[row][col]) sys.stdout.write("\n")
def main(puzzle, lines): puzzle = puzzle.rstrip().splitlines() while puzzle and not puzzle[0]: del puzzle[0] # Extract horizontal words horizontal = [] word = [] predefined = {} for row in range(len(puzzle)): for col in range(len(puzzle[row])): char = puzzle[row][col] if not char.isspace(): word.append((row, col)) if char != "#": predefined[row, col] = char elif word: if len(word) > MINLEN: horizontal.append(word[:]) del word[:] if word: if len(word) > MINLEN: horizontal.append(word[:]) del word[:] # Extract vertical words vertical = [] validcol = True col = 0 while validcol: validcol = False for row in range(len(puzzle)): if col >= len(puzzle[row]): if word: if len(word) > MINLEN: vertical.append(word[:]) del word[:] else: validcol = True char = puzzle[row][col] if not char.isspace(): word.append((row, col)) if char != "#": predefined[row, col] = char elif word: if len(word) > MINLEN: vertical.append(word[:]) del word[:] if word: if len(word) > MINLEN: vertical.append(word[:]) del word[:] col += 1 # hnames = ["h%d" % i for i in range(len(horizontal))] # vnames = ["v%d" % i for i in range(len(vertical))] # problem = Problem(MinConflictsSolver()) problem = Problem() for hi, hword in enumerate(horizontal): for vi, vword in enumerate(vertical): for hchar in hword: if hchar in vword: hci = hword.index(hchar) vci = vword.index(hchar) problem.addConstraint(lambda hw, vw, hci=hci, vci=vci: hw[hci] == vw[vci], ("h%d" % hi, "v%d" % vi)) for char, letter in predefined.items(): for hi, hword in enumerate(horizontal): if char in hword: hci = hword.index(char) problem.addConstraint(lambda hw, hci=hci, letter=letter: hw[hci] == letter, ("h%d" % hi,)) for vi, vword in enumerate(vertical): if char in vword: vci = vword.index(char) problem.addConstraint(lambda vw, vci=vci, letter=letter: vw[vci] == letter, ("v%d" % vi,)) wordsbylen = {} for hword in horizontal: wordsbylen[len(hword)] = [] for vword in vertical: wordsbylen[len(vword)] = [] for line in lines: line = line.strip() l = len(line) if l in wordsbylen: wordsbylen[l].append(line.upper()) for hi, hword in enumerate(horizontal): words = wordsbylen[len(hword)] random.shuffle(words) problem.addVariable("h%d" % hi, words) for vi, vword in enumerate(vertical): words = wordsbylen[len(vword)] random.shuffle(words) problem.addVariable("v%d" % vi, words) problem.addConstraint(AllDifferentConstraint()) solution = problem.getSolution() if not solution: print("No solution found!") maxcol = 0 maxrow = 0 for hword in horizontal: for row, col in hword: if row > maxrow: maxrow = row if col > maxcol: maxcol = col for vword in vertical: for row, col in vword: if row > maxrow: maxrow = row if col > maxcol: maxcol = col matrix = [] for row in range(maxrow + 1): matrix.append([" "] * (maxcol + 1)) for variable in solution: if variable[0] == "v": word = vertical[int(variable[1:])] else: word = horizontal[int(variable[1:])] for (row, col), char in zip(word, solution[variable]): matrix[row][col] = char for row in range(maxrow + 1): for col in range(maxcol + 1): sys.stdout.write(matrix[row][col]) sys.stdout.write("\n")
print("|%10s| %12s | %12s | %12s | %12s | %12s | %12s | %12s |" % (horas[i], horario[7 * i], horario[7 * i + 1], horario[7 * i + 2], horario[7 * i + 3], horario[7 * i + 4], horario[7 * i + 5], horario[7 * i + 6])) print("|__________|" + "______________|" * 7) #--------------------------------------------------------------------------- from constraint import Problem, AllDifferentConstraint problema = Problem() #Primer semestre problema.addVariable("calc_1", [7]) problema.addVariable("geom", [14]) problema.addVariable("alg_sup", [21]) problema.addVariable("apren", [28]) problema.addVariable("intro_cc", [35, 39, 40]) problema.addVariable("diseño_algo", [42, 46, 47]) #Tercer semestre problema.addVariable("calc_3", [43, 44]) problema.addVariable("ec_difs", [15, 22, 29]) problema.addVariable("fluidos", [22, 15, 29]) problema.addVariable("progra_avan", [33, 40, 47]) problema.addVariable("análi_num1", [39, 32, 46]) problema.addVariable("expre_oe", [8]) #Quinto semestre
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
section_type_one = [ make_section(course_sections[0]), make_section(course_sections[1]) ] section_type_two = [ make_section(course_sections[2]), make_section(course_sections[6]), make_section(course_sections[7]) ] course_one = Course(section_type_one) course_two = Course(section_type_two) problem.addVariable('a', course_one.sections) problem.addVariable('b', course_two.sections) total_solutions = problem.getSolutions() print('Total Pairs: {}'.format(get_number_of_pairs(total_solutions))) # TODO: fix so it works when the a.end_time matches b.start_time or vice versa def no_time_conflicts(a: CourseSection, b: CourseSection) -> bool: return (a.end_time < b.start_time or b.end_time < a.start_time) and \ (a.end_time != b.end_time or a.start_time != b.start_time) problem.addConstraint(no_time_conflicts, ['a', 'b']) solution_set = problem.getSolutions()
class Scheduler(object): """ This class provides the constraint-based Scheduler. """ def __init__(self, plant, orderList): """ plant is a Plant instance to run the Scheduler on. orderList is the OrderList instance of incoming orders to the Plant. problem is a python-constraint Problem instance where solver is used as the constraint solver. """ assert plant != None assert orderList != None self.printing = True self.plant = plant self.orderList = orderList self.problem = Problem() self.endMargin = 1 self.machineMargin = 1 def createMachineQuantityVarName(self, machine): """ Creates and returns a python-constraint Variable name from a Machine instance. """ assert type(machine) != str or type(machine) != unicode return str(machine.name) + "-quantity" def createEnterTimeVarName(self, order, machine): """ Creates and returns a python-constraint Variable name from an Order instance and a Machine instance. """ if type(machine) == str or type(machine) == unicode: machineName = machine else: machineName = machine.name return str(str(order.id) + "-enter-" + machineName) def createTimeAtMachineVarName(self, order, machine): """ Creates and returns a python-constraint Variable name from an Order instance and a Machine instance. """ if type(machine) == str or type(machine) == unicode: machineName = machine else: machineName = machine.name return str(str(order.id) + "-spend-" + machineName) def addPrecedenceConstraint(self, enterVar, order, machineIndex): """ Adds a python-constraint Variable and Constraint to an order for the precedence of Machine instances. Meaning that an order cannot get into Machine 2 before getting into Machine 1. The sequence is determined by the Plant instance. """ prevMachine = self.plant.machines[machineIndex - 1] enterVar2 = self.createEnterTimeVarName(order, prevMachine) spendVar2 = self.createTimeAtMachineVarName(order, prevMachine) if order.recipe[prevMachine.name] != 0: if prevMachine.quantity <= \ self.plant.machines[machineIndex].quantity \ and prevMachine.canUnhook == False: self.problem.addConstraint(lambda x, y, yt: x == y + yt + \ self.plant.craneMoveTime, [enterVar, enterVar2, spendVar2]) else: self.problem.addConstraint(lambda x, y, yt: x >= y + yt + \ self.plant.craneMoveTime, [enterVar, enterVar2, spendVar2]) def addFinishTimeVar(self, order): """ Adds a python-constraint Variable and Constraint to an order for the finish time on the Plant. """ var = str(order.id) + "-finish" lastMachine = self.plant.machines[-1] self.problem.addVariable( var, range(order.deadline - self.endMargin, order.deadline + self.endMargin)) self.problem.addConstraint(lambda x, y, yt: x == y + yt, [ var, self.createEnterTimeVarName(order, lastMachine), self.createTimeAtMachineVarName(order, lastMachine) ]) def addOrderEnterTimeAtMachineVar(self, order, machineName, machineIndex): """ Adds a python-constraint Variable and Constraint to an order for the entrance time at a Machine instance. """ var = self.createEnterTimeVarName(order, machineName) if order.recipe[machineName] != 0: machineStart = (order.deadline + self.endMargin) - \ order.recipe.calcMinProcTime(self.plant, machineName) - \ self.machineMargin machineEnd = machineStart + self.machineMargin + \ min(self.endMargin, self.machineMargin) variableRange = range(max(machineStart, 0), machineEnd) else: variableRange = range(0, 1) self.problem.addVariable(var, variableRange) if machineIndex != 0: self.addPrecedenceConstraint(var, order, machineIndex) def machineQuantityConstraintFunc(self, *args): quantity = args[0] argsMiddle = (len(args) - 1) / 2 enterTimes = args[1:argsMiddle + 1] spendTimes = args[argsMiddle + 1:] assert len(enterTimes) == len(spendTimes) numberOfCommons = 0 for i, et in enumerate(enterTimes): range1 = range(et, et + spendTimes[i]) numberOfCommons = 0 for j, et2 in enumerate(enterTimes): if i != j: range2 = range(et2, et2 + spendTimes[j]) for v1 in range1: if v1 in range2: numberOfCommons += 1 break return not (numberOfCommons >= quantity) def addMachineQuantityConstraint(self, machine): enterVars = [] spendVars = [] for order in self.orderList.orders: enterVars.append(self.createEnterTimeVarName(order, machine)) spendVars.append(self.createTimeAtMachineVarName(order, machine)) vars = [self.createMachineQuantityVarName(machine)] + \ enterVars + spendVars self.problem.addConstraint(self.machineQuantityConstraintFunc, vars) def machineCapacityConstraintFunc(self, *args): argsMiddle = len(args) / 2 enterTimes = args[0:argsMiddle] nextEnterTimes = args[argsMiddle:] for i, et in enumerate(enterTimes): for j, et2 in enumerate(enterTimes): if i != j: if et > et2 and nextEnterTimes[i] < nextEnterTimes[j]: return False return True def addCapacityConstraint(self, machine, machineIndex): enterVars = [] nextEnterVars = [] nextMachine = self.plant.machines[machineIndex + 1] for order in self.orderList.orders: enterVars.append(self.createEnterTimeVarName(order, machine)) nextEnterVars.append( self.createEnterTimeVarName(order, nextMachine)) self.problem.addConstraint(self.machineCapacityConstraintFunc, enterVars + nextEnterVars) def run(self): """ Runs the main Scheduler logic. """ for machine in self.plant.machines: var = self.createMachineQuantityVarName(machine) self.problem.addVariable(var, [machine.quantity]) for machine in self.plant.machines: for order in self.orderList.orders: var = self.createTimeAtMachineVarName(order, machine) self.problem.addVariable(var, [order.recipe[machine.name]]) for machineIndex, machine in enumerate(self.plant.machines): for order in self.orderList.orders: self.addOrderEnterTimeAtMachineVar(order, machine.name, machineIndex) for machineIndex, machine in enumerate(self.plant.machines): if machine.precedence == True and \ machineIndex != len(self.plant.machines) - 1: self.addCapacityConstraint(machine, machineIndex) self.addMachineQuantityConstraint(machine) for machineIndex, machine in enumerate(self.plant.machines): if len(machine.setOfBreaks()) != 0: for order in self.orderList.orders: enterVar = self.createEnterTimeVarName(order, machine) self.problem.addConstraint( MachineBreaksConstraint(order, machine), [enterVar]) for order in self.orderList.orders: self.addFinishTimeVar(order) pprint("SCHED Computing solutions...", BLUE, self.printing) solutions = self.problem.getSolutions() return solutions, len(solutions) def start(self, endMarginLimit=10, machineMarginLimit=10): pprint("SCHED Started...", BLUE, self.printing) self.endMargin = 1 while self.endMargin <= endMarginLimit: self.machineMargin = 1 while self.machineMargin <= machineMarginLimit: try: pprint("SCHED End Margin: %d, Machine Margin: %d" % \ (self.endMargin, self.machineMargin), YELLOW, self.printing) self.problem.reset() solutions, numberOfSolutions = self.run() if numberOfSolutions > 0: return solutions except Exception as e: pprint("SCHED Exception " + str(e), RED) pprint("SCHED Trying new value for End Margin.", RED) endMarginLimit += 1 self.machineMargin += 1 self.endMargin += 1 pprint("SCHED No solutions found.", RED, self.printing) return None
from constraint import Problem # Define variables for the CSP problem problem = Problem() problem.addVariable("f", [1]) problem.addVariable("t", [1, 2, 3, 4, 5, 6, 7, 8, 9]) problem.addVariable("u", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) problem.addVariable("w", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) problem.addVariable("r", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) problem.addVariable("o", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) problem.addVariable("c10", [0, 1]) problem.addVariable("c100", [0, 1]) problem.addVariable("c1000", [0, 1]) # Define the constraints for the CSP problem problem.addConstraint(lambda o, r, c10: o + o == r + (10 * c10), ("o", "r", "c10")) problem.addConstraint(lambda w, u, c10, c100: c10 + w + w == u + (10 * c100), ("w", "u", "c10", "c100")) problem.addConstraint( lambda o, t, c100, c1000: c100 + t + t == o + (10 * c1000), ("o", "t", "c100", "c1000")) problem.addConstraint(lambda f, c1000: f == c1000, ("f", "c1000")) # All diff problem.addConstraint( lambda f, t, u, w, r, o: len(set([f, t, u, w, r, o])) == 6, ("f", "t", "u", "w", "r", "o")) # Solve the CSP problem solutions = problem.getSolutions()
#!/usr/bin/env python # encoding=utf-8 (pep 0263) # je zapotrebi nainstaloval balicek python-constraint # <https://github.com/python-constraint/python-constraint> from constraint import Problem, AllDifferentConstraint problem = Problem() domain = range(10) variables = ("S", "E", "N", "D", "M", "O", "R", "Y") for name in variables: problem.addVariable(name, domain) problem.addConstraint(lambda s: s > 0, ("S")) problem.addConstraint(lambda m: m > 0, ("M")) problem.addConstraint(AllDifferentConstraint()) problem.addConstraint(lambda s, e, n, d, m, o, r, y: \ 1000 * s + 100 * e + 10 * n + d + 1000 * m + 100 * o + 10 * r + e == 10000 * m + 1000 * o + 100 * n + 10 * e + y, variables) # demonstracni vypis if __name__ == "__main__": print("CLP - Algebrogram\n") print(" S E N D") print("+ M O R E") print("---------") print("M O N E Y\n") print("Vysledek volani problem.getSolutions():") solution = problem.getSolutions()[0]
def solve(): problem = Problem() for i in range(1, 6): problem.addVariable("color%d" % i, ["red", "white", "green", "yellow", "blue"]) problem.addVariable("nationality%d" % i, ["brit", "swede", "dane", "norwegian", "german"]) problem.addVariable("drink%d" % i, ["tea", "coffee", "milk", "beer", "water"]) problem.addVariable("smoke%d" % i, ["pallmall", "dunhill", "blends", "bluemaster", "prince"]) problem.addVariable("pet%d" % i, ["dogs", "birds", "cats", "horses", "fish"]) problem.addConstraint(AllDifferentConstraint(), ["color%d" % i for i in range(1, 6)]) problem.addConstraint(AllDifferentConstraint(), ["nationality%d" % i for i in range(1, 6)]) problem.addConstraint(AllDifferentConstraint(), ["drink%d" % i for i in range(1, 6)]) problem.addConstraint(AllDifferentConstraint(), ["smoke%d" % i for i in range(1, 6)]) problem.addConstraint(AllDifferentConstraint(), ["pet%d" % i for i in range(1, 6)]) for i in range(1, 6): # Hint 1 problem.addConstraint(lambda nationality, color: nationality != "brit" or color == "red", ("nationality%d" % i, "color%d" % i)) # Hint 2 problem.addConstraint(lambda nationality, pet: nationality != "swede" or pet == "dogs", ("nationality%d" % i, "pet%d" % i)) # Hint 3 problem.addConstraint(lambda nationality, drink: nationality != "dane" or drink == "tea", ("nationality%d" % i, "drink%d" % i)) # Hint 4 if i < 5: problem.addConstraint(lambda colora, colorb: colora != "green" or colorb == "white", ("color%d" % i, "color%d" % (i + 1))) else: problem.addConstraint(lambda color: color != "green", ("color%d" % i,)) # Hint 5 problem.addConstraint(lambda color, drink: color != "green" or drink == "coffee", ("color%d" % i, "drink%d" % i)) # Hint 6 problem.addConstraint(lambda smoke, pet: smoke != "pallmall" or pet == "birds", ("smoke%d" % i, "pet%d" % i)) # Hint 7 problem.addConstraint(lambda color, smoke: color != "yellow" or smoke == "dunhill", ("color%d" % i, "smoke%d" % i)) # Hint 8 if i == 3: problem.addConstraint(lambda drink: drink == "milk", ("drink%d" % i,)) # Hint 9 if i == 1: problem.addConstraint(lambda nationality: nationality == "norwegian", ("nationality%d" % i,)) # Hint 10 if 1 < i < 5: problem.addConstraint(lambda smoke, peta, petb: smoke != "blends" or peta == "cats" or petb == "cats", ("smoke%d" % i, "pet%d" % (i - 1), "pet%d" % (i + 1))) else: problem.addConstraint(lambda smoke, pet: smoke != "blends" or pet == "cats", ("smoke%d" % i, "pet%d" % (i == 1 and 2 or 4))) # Hint 11 if 1 < i < 5: problem.addConstraint(lambda pet, smokea, smokeb: pet != "horses" or smokea == "dunhill" or smokeb == "dunhill", ("pet%d" % i, "smoke%d" % (i - 1), "smoke%d" % (i + 1))) else: problem.addConstraint(lambda pet, smoke: pet != "horses" or smoke == "dunhill", ("pet%d" % i, "smoke%d" % (i == 1 and 2 or 4))) # Hint 12 problem.addConstraint(lambda smoke, drink: smoke != "bluemaster" or drink == "beer", ("smoke%d" % i, "drink%d" % i)) # Hint 13 problem.addConstraint(lambda nationality, smoke: nationality != "german" or smoke == "prince", ("nationality%d" % i, "smoke%d" % i)) # Hint 14 if 1 < i < 5: problem.addConstraint(lambda nationality, colora, colorb: nationality != "norwegian" or colora == "blue" or colorb == "blue", ("nationality%d" % i, "color%d" % (i - 1), "color%d" % (i + 1))) else: problem.addConstraint(lambda nationality, color: nationality != "norwegian" or color == "blue", ("nationality%d" % i, "color%d" % (i == 1 and 2 or 4))) # Hint 15 if 1 < i < 5: problem.addConstraint(lambda smoke, drinka, drinkb: smoke != "blends" or drinka == "water" or drinkb == "water", ("smoke%d" % i, "drink%d" % (i - 1), "drink%d" % (i + 1))) else: problem.addConstraint(lambda smoke, drink: smoke != "blends" or drink == "water", ("smoke%d" % i, "drink%d" % (i == 1 and 2 or 4))) solutions = problem.getSolutions() return solutions