def solve(): problem = Problem() problem.addVariables("abc", range(1, 10)) problem.getSolutions() minvalue = 999 / (9 * 3) minsolution = {} for solution in problem.getSolutions(): a = solution["a"] b = solution["b"] c = solution["c"] value = (a * 100 + b * 10 + c) / (a + b + c) if value < minvalue: minsolution = solution return minvalue, minsolution
def solve(): problem = Problem() problem.addVariables("sendmory", range(10)) problem.addConstraint(lambda d, e, y: (d + e) % 10 == y, "dey") problem.addConstraint( lambda n, d, r, e, y: (n * 10 + d + r * 10 + e) % 100 == e * 10 + y, "ndrey" ) problem.addConstraint( lambda e, n, d, o, r, y: (e * 100 + n * 10 + d + o * 100 + r * 10 + e) % 1000 == n * 100 + e * 10 + y, "endory", ) 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, "sendmory", ) problem.addConstraint(NotInSetConstraint([0]), "sm") problem.addConstraint(AllDifferentConstraint()) solutions = problem.getSolutions() return solutions
def generateSchedule(self, assignments, scoring_function): """ Generate schedule for a user based on scoring_function and user's existing schedule """ problem = Problem() solution = {} print("Name: {self.name}") print("Existing schedule: {self.schedule}") print( f"Possible Rotations: {self.possibleRotations}\nOrder: {self.order}" ) print() if len(self.possibleRotations) > 0: problem.addVariables(self.possibleRotations, self.order) # HARD CONSTRAINT 1 - All rotations must be unique in the period of the program problem.addConstraint(AllDifferentConstraint(), self.possibleRotations) solutions = problem.getSolutions() # Score the options print(f"Scoring {len(solutions)} found") scores = np.zeros(len(solutions)) for sidx, solution in enumerate(solutions): scores[sidx] = scoring_function( assignments, sorted(solution.items(), key=lambda x: x[1]), self.earliestAvailableDate()) chosen = np.argsort(scores) solution = solutions[chosen[0]] print(f"Best solution: {solution}") return solution
def solve(): problem = Problem() size = matrixOfStrimko.size + 1 # Define the variables: rows of x variables rangin in 1...x # x = tam of the matrix for i in range(1, size): problem.addVariables(range(i * 10 + 1, i * 10 + size), range(1, size)) #Each row has different values for i in range(1, size): problem.addConstraint(AllDifferentConstraint(), range(i * 10 + 1, i * 10 + size)) # Each colum has different values for i in range(1, size): problem.addConstraint(AllDifferentConstraint(), range(10 + i, 10 * size + i, 10)) #Each route has different values for i in range(0, size - 1): problem.addConstraint(AllDifferentConstraint(), matrixOfStrimko.routes[i].elements) return problem.getSolutions()
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 _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 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(): problem = Problem() # Define the variables: 9 rows of 9 variables rangin in 1...9 for i in range(1, 10): problem.addVariables(range(i * 10 + 1, i * 10 + 10), range(1, 10)) # Each row has different values for i in range(1, 10): problem.addConstraint(AllDifferentConstraint(), range(i * 10 + 1, i * 10 + 10)) # Each colum has different values for i in range(1, 10): problem.addConstraint(AllDifferentConstraint(), range(10 + i, 100 + i, 10)) # Each 3x3 box has different values problem.addConstraint(AllDifferentConstraint(), [11, 12, 13, 21, 22, 23, 31, 32, 33]) problem.addConstraint(AllDifferentConstraint(), [41, 42, 43, 51, 52, 53, 61, 62, 63]) problem.addConstraint(AllDifferentConstraint(), [71, 72, 73, 81, 82, 83, 91, 92, 93]) problem.addConstraint(AllDifferentConstraint(), [14, 15, 16, 24, 25, 26, 34, 35, 36]) problem.addConstraint(AllDifferentConstraint(), [44, 45, 46, 54, 55, 56, 64, 65, 66]) problem.addConstraint(AllDifferentConstraint(), [74, 75, 76, 84, 85, 86, 94, 95, 96]) problem.addConstraint(AllDifferentConstraint(), [17, 18, 19, 27, 28, 29, 37, 38, 39]) problem.addConstraint(AllDifferentConstraint(), [47, 48, 49, 57, 58, 59, 67, 68, 69]) problem.addConstraint(AllDifferentConstraint(), [77, 78, 79, 87, 88, 89, 97, 98, 99]) # Some value is given. initValue = [[0, 9, 0, 7, 0, 0, 8, 6, 0], [0, 3, 1, 0, 0, 5, 0, 2, 0], [8, 0, 6, 0, 0, 0, 0, 0, 0], [0, 0, 7, 0, 5, 0, 0, 0, 6], [0, 0, 0, 3, 0, 7, 0, 0, 0], [5, 0, 0, 0, 1, 0, 7, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 9], [0, 2, 0, 6, 0, 0, 0, 5, 0], [0, 5, 4, 0, 0, 8, 0, 7, 0]] for i in range(1, 10): for j in range(1, 10): if initValue[i - 1][j - 1] != 0: problem.addConstraint( lambda var, val=initValue[i - 1][j - 1]: var == val, (i * 10 + j, )) # Get the solutions. solutions = problem.getSolutions() return solutions
def solve(): problem = Problem() problem.addVariables("abcdxefgh", range(1, 10)) problem.addConstraint(lambda a, b, c, d, x: a < b < c < d and a + b + c + d + x == 27, "abcdx") problem.addConstraint(lambda e, f, g, h, x: e < f < g < h and e + f + g + h + x == 27, "efghx") problem.addConstraint(AllDifferentConstraint()) solutions = problem.getSolutions() return solutions
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
def solve(size): problem = Problem() cols = range(size) rows = range(size) problem.addVariables(cols, rows) for col1 in cols: for col2 in cols: if col1 < col2: problem.addConstraint(lambda row1, row2: row1 != row2, (col1, col2)) solutions = problem.getSolutions() return solutions
def solve(): problem = Problem() problem.addVariables("abcdxefgh", range(1, 10)) problem.addConstraint( lambda a, b, c, d, x: a < b < c < d and a + b + c + d + x == 27, "abcdx") problem.addConstraint( lambda e, f, g, h, x: e < f < g < h and e + f + g + h + x == 27, "efghx") problem.addConstraint(AllDifferentConstraint()) solutions = problem.getSolutions() return solutions
def solve(): problem = Problem() problem.addVariables("twofur", range(10)) problem.addConstraint(lambda o, r: (2 * o) % 10 == r, "or") problem.addConstraint(lambda w, o, u, r: ((10 * 2 * w) + (2 * o)) % 100 == u * 10 + r, "wour") problem.addConstraint(lambda t, w, o, f, u, r: 2 * (t * 100 + w * 10 + o) == f * 1000 + o * 100 + u * 10 + r, "twofur") problem.addConstraint(NotInSetConstraint([0]), "ft") problem.addConstraint(AllDifferentConstraint()) solutions = problem.getSolutions() return solutions
def solve(): problem = Problem() size = 8 cols = range(size) rows = range(size) problem.addVariables(cols, rows) for col1 in cols: for col2 in cols: if col1 < col2: problem.addConstraint(lambda row1, row2, col1=col1, col2=col2: abs(row1 - row2) != abs(col1 - col2) and row1 != row2, (col1, col2)) solutions = problem.getSolutions() return solutions, size
def solve(): problem = Problem() problem.addVariables("seidoz", range(10)) problem.addConstraint(lambda s, e: (2 * s) % 10 == e, "se") problem.addConstraint(lambda i, s, z, e: ((10 * 2 * i) + (2 * s)) % 100 == z * 10 + e, "isze") problem.addConstraint(lambda s, e, i, d, o, z: 2 * (s * 1000 + e * 100 + i * 10 + s) == d * 1000 + o * 100 + z * 10 + e, "seidoz") problem.addConstraint(lambda s: s != 0, "s") problem.addConstraint(lambda d: d != 0, "d") problem.addConstraint(AllDifferentConstraint()) solutions = problem.getSolutions() return solutions
def solve(): problem = Problem() # Define the variables: 9 rows of 9 variables rangin in 1...9 for i in range(1, 10): problem.addVariables(range(i * 10 + 1, i * 10 + 10), range(1, 10)) # Each row has different values for i in range(1, 10): problem.addConstraint(AllDifferentConstraint(), range(i * 10 + 1, i * 10 + 10)) # Each colum has different values for i in range(1, 10): problem.addConstraint(AllDifferentConstraint(), range(10 + i, 100 + i, 10)) # Each 3x3 box has different values problem.addConstraint(AllDifferentConstraint(), [11, 12, 13, 21, 22, 23, 31, 32, 33]) problem.addConstraint(AllDifferentConstraint(), [41, 42, 43, 51, 52, 53, 61, 62, 63]) problem.addConstraint(AllDifferentConstraint(), [71, 72, 73, 81, 82, 83, 91, 92, 93]) problem.addConstraint(AllDifferentConstraint(), [14, 15, 16, 24, 25, 26, 34, 35, 36]) problem.addConstraint(AllDifferentConstraint(), [44, 45, 46, 54, 55, 56, 64, 65, 66]) problem.addConstraint(AllDifferentConstraint(), [74, 75, 76, 84, 85, 86, 94, 95, 96]) problem.addConstraint(AllDifferentConstraint(), [17, 18, 19, 27, 28, 29, 37, 38, 39]) problem.addConstraint(AllDifferentConstraint(), [47, 48, 49, 57, 58, 59, 67, 68, 69]) problem.addConstraint(AllDifferentConstraint(), [77, 78, 79, 87, 88, 89, 97, 98, 99]) # Some value is given. initValue = [ [0, 9, 0, 7, 0, 0, 8, 6, 0], [0, 3, 1, 0, 0, 5, 0, 2, 0], [8, 0, 6, 0, 0, 0, 0, 0, 0], [0, 0, 7, 0, 5, 0, 0, 0, 6], [0, 0, 0, 3, 0, 7, 0, 0, 0], [5, 0, 0, 0, 1, 0, 7, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 9], [0, 2, 0, 6, 0, 0, 0, 5, 0], [0, 5, 4, 0, 0, 8, 0, 7, 0], ] for i in range(1, 10): for j in range(1, 10): if initValue[i - 1][j - 1] != 0: problem.addConstraint(lambda var, val=initValue[i - 1][j - 1]: var == val, (i * 10 + j,)) # Get the solutions. solutions = problem.getSolutions() return solutions
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 solve(): problem = Problem() problem.addVariables("seidoz", range(10)) problem.addConstraint(lambda s, e: (2 * s) % 10 == e, "se") problem.addConstraint( lambda i, s, z, e: ((10 * 2 * i) + (2 * s)) % 100 == z * 10 + e, "isze") problem.addConstraint( lambda s, e, i, d, o, z: 2 * (s * 1000 + e * 100 + i * 10 + s) == d * 1000 + o * 100 + z * 10 + e, "seidoz") problem.addConstraint(lambda s: s != 0, "s") problem.addConstraint(lambda d: d != 0, "d") problem.addConstraint(AllDifferentConstraint()) solutions = problem.getSolutions() return solutions
def derive_depths(marker_list, 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 marker_list: return [] problem = Problem() # Marker type per marker problem.addVariables(["type" + str(i) for i in range(len(marker_list))], markers.types) # Index within the marker list problem.addVariables(["idx" + str(i) for i in range(len(marker_list))], range(51)) # Depth in the tree, with an arbitrary limit of 10 problem.addVariables(["depth" + str(i) for i in range(len(marker_list))], range(10)) all_vars = [] for i in range(len(marker_list)): all_vars.extend(['type' + str(i), 'idx' + str(i), 'depth' + str(i)]) # Always start at depth 0 problem.addConstraint(rules.must_be(0), ("depth0",)) for idx, marker in enumerate(marker_list): idx_str = str(idx) problem.addConstraint(rules.type_match(marker), ("type" + idx_str, "idx" + idx_str)) prior_params = ['type' + idx_str, 'idx' + idx_str, 'depth' + idx_str] for i in range(idx): prior_params += ['type' + str(i), 'idx' + str(i), 'depth' + str(i)] problem.addConstraint(rules.same_type, prior_params) problem.addConstraint(rules.diff_type, prior_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_depth_same_type, all_vars) problem.addConstraint(rules.stars_occupy_space, all_vars) for constraint in additional_constraints: constraint(problem.addConstraint, all_vars) return [Solution(solution) for solution in problem.getSolutions()]
def derive_depths(marker_list, 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 marker_list: return [] problem = Problem() # Marker type per marker problem.addVariables(["type" + str(i) for i in range(len(marker_list))], markers.types) # Index within the marker list problem.addVariables(["idx" + str(i) for i in range(len(marker_list))], range(51)) # Depth in the tree, with an arbitrary limit of 10 problem.addVariables(["depth" + str(i) for i in range(len(marker_list))], range(10)) all_vars = [] for i in range(len(marker_list)): all_vars.extend(['type' + str(i), 'idx' + str(i), 'depth' + str(i)]) # Always start at depth 0 problem.addConstraint(rules.must_be(0), ("depth0", )) for idx, marker in enumerate(marker_list): idx_str = str(idx) problem.addConstraint(rules.type_match(marker), ("type" + idx_str, "idx" + idx_str)) prior_params = ['type' + idx_str, 'idx' + idx_str, 'depth' + idx_str] for i in range(idx): prior_params += ['type' + str(i), 'idx' + str(i), 'depth' + str(i)] problem.addConstraint(rules.same_type, prior_params) problem.addConstraint(rules.diff_type, prior_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_depth_same_type, all_vars) problem.addConstraint(rules.stars_occupy_space, all_vars) for constraint in additional_constraints: constraint(problem.addConstraint, all_vars) return [Solution(solution) for solution in problem.getSolutions()]
def solve(): problem = Problem() problem.addVariables("sendmory", range(10)) problem.addConstraint(lambda d, e, y: (d + e) % 10 == y, "dey") problem.addConstraint(lambda n, d, r, e, y: (n * 10 + d + r * 10 + e) % 100 == e * 10 + y, "ndrey") problem.addConstraint( lambda e, n, d, o, r, y: (e * 100 + n * 10 + d + o * 100 + r * 10 + e) % 1000 == n * 100 + e * 10 + y, "endory" ) 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, "sendmory", ) problem.addConstraint(NotInSetConstraint([0]), "sm") problem.addConstraint(AllDifferentConstraint()) solutions = problem.getSolutions() return solutions
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 solve(ac=False, pc=False): problem = Problem() # Countries countries = ['A', 'B', 'C'] # Number of colors num_colors = 2 # Neighbours on the map neighbours = ['AB', 'BC', 'CA'] neighbours = [''.join(sorted(n)) for n in neighbours] problem.addVariables(countries, range(num_colors)) for country1 in countries: for country2 in countries: if country1 != country2: constraint = lambda color1, color2, country1=country1, country2=country2: not any( ''.join(sorted(country1 + country2)) in s for s in neighbours) or color1 != color2 problem.addConstraint(constraint, [country1, country2]) solutions = problem.getSolutions(ac, pc) print('Solutions:\n', solutions) return solutions
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 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 solveStrimko(routes, values): res = [["." for i in range(size)] for j in range(size)] for r in res: for c in r: print(c, end=" ") print() S = len(res) problem = Problem() for route in routes: problem.addConstraint(AllDifferentConstraint(), route) print(route) for x in values: res[int(x[0])][int(x[1])] = str(x[2]) cellnames = [(i, j) for j, row in enumerate(res) for i, val in enumerate(row)] lookup = {(i, j): res[i][j] for i, j in cellnames} problem.addVariables(cellnames, [str(j) for j in range(1, size + 1)]) for j in range(size): # Columns in grid problem.addConstraint(AllDifferentConstraint(), [(i, j) for i in range(size)]) # Rows in grid problem.addConstraint(AllDifferentConstraint(), [(j, i) for i in range(size)]) for cell, value in lookup.items(): if value != ".": problem.addConstraint(InSetConstraint([str(value)]), [cell]) print("check") print("\n".join(" ".join(lookup[(i, j)] for j in range(size)) for i in range(size))) for solution in problem.getSolutions(): for i in range(0, size): for j in range(0, size): fields[i][j].value = solution[(i, j)]
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()
# add custom constraints to capture partitioning feasibility # in this constraint, we check the assigned levels do not conflict with explicit annotations p.addConstraint(noAnnotationConflict, funcs + gvars) # in this constraint, we check the assigned levels do not conflict with annotations on local variables p.addConstraint(noAnnotatedLocalVarConflict, funcs) # propagate taints based on assignment and make sure they do not lead to conflict # p.addConstraint(noTaintConflict, funcs + gvars) # if conflicts are resolvable through cross-domain RPC # p.addConstraint(conflictResolvableByRPC, funcs + gvars) # Solve and print solutions result = p.getSolutions() for r in result: print({a: levels[b] for a, b in r.items()}) ############################################################################################################# ''' Create a logic-variable for each variable and function in the program o Maintain a map between the logic variable, the corresponding program entity, and its metadata (e.g., source file reference) Define predicates o function(?fn) o global-var(?gv) o local-var(?lv) o level-of(?label, ?level) o allow-sharing(?label, ?fromlevel, ?tolevel) o label-taint(?x, ?label) // ?x is function, global-var or local-var
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
# N queens problem. # Place N queens on an NxN chessboard so that no two queens are attacking each other. # Finds all solutions. There are multiple solutions for N >= 4. from constraint import Problem # N = 8 solves in 0.1s # N = 10 solves in 0.9s # N = 12 solves in 21s # N = 14 solves in 11m N = 12 problem = Problem() values = list(range(N)) problem.addVariables(values, values) def ok(d): return lambda x, y: x - y not in (-d, 0, d) for row1 in values: for row2 in values: if row1 < row2: problem.addConstraint(ok(row2 - row1), (row1, row2)) for solution in problem.getSolutions(): print(solution)
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
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
# 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() for solution in solutions: print(solution)
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() print('Valid Pairs: {}'.format(get_number_of_pairs(solution_set))) print_solutions([solution for solution in solution_set])
for i in range(len(optativas)): for j in range(i + 1, len(optativas)): problema.addConstraint(lambda var_1, var_2: var_1 // 7 != var_2 // 7, (optativas[i], optativas[j])) horas = [ "7:00", "8:00", "9:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00" ] horario_uno = [" " for i in range(63)] horario_dos = [" " for i in range(63)] horario_tres = [" " for i in range(63)] soluciones = problema.getSolutions() for materia, posicion in soluciones[0].items(): horario_uno[posicion] = materia for materia, posicion in soluciones[20].items(): horario_dos[posicion] = materia for materia, posicion in soluciones[40].items(): horario_tres[posicion] = materia print(soluciones[0]) imprimir_horario(horas, horario_uno) print() imprimir_horario(horas, horario_dos)
# 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] print(" S = %s, E = %s, N = %s, D = %s, M = %s, O = %s, R = %s, Y = %s" % \ tuple((solution[x] for x in variables)))
from constraint import Problem, AllDifferentConstraint from itertools import product from functools import partial columns = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] rows = [1, 2, 3, 4, 5, 6, 7, 8] mapping = dict((x, y) for x, y in zip(columns, rows)) def bishop_func(a, b, x, y): return abs(mapping[x] - mapping[y]) != abs(a - b) or a == b problem = Problem() problem.addVariables(columns, rows) problem.addConstraint(AllDifferentConstraint()) for column1, column2 in product(columns, columns): problem.addConstraint(partial(bishop_func, x = column1, y = column2), (column1, column2)) for solution in problem.getSolutions(): print sorted(solution.iteritems())
for al in allergens: if not a_to_i[al]: a_to_i[al] = set(ingredients) else: a_to_i[al] &= set(ingredients) i.append(ingredients) p = Problem() p.addConstraint(AllDifferentConstraint()) for al, ingredients in a_to_i.items(): p.addVariable(al, list(ingredients)) not_seen = set(i_to_a.keys()) for soln in p.getSolutions(): for ing in soln.values(): if ing in not_seen: not_seen.remove(ing) count = 0 for ingredients in i: for ing in ingredients: if ing in not_seen: count += 1 # 1679 print("Ans 1)", count) allergens = [ t[1] for t in sorted(p.getSolutions()[0].items(), key=lambda t: t[0]) ]
def calculate_all_solutions(problem: constraint.Problem) -> List[Solution]: """ Solves for all of the solutions to the given problem in one go. """ return 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. """ assert plant != None assert orderList != None # enable/disable printing self.printing = True self.plant = plant self.orderList = orderList self.problem = Problem() # deadline margin self.endMargin = 1 # machine entrance time margin 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 the previous machine recipe is 0, there's no procedence for this one! if order.recipe[prevMachine.name] != 0: # if the quantity of the previous machine is less than this one and # the previous machine cannot unhook and the previous machine doesn't # have capacity then the probability of blockage is low (this isn't 100% # correct but helps performance). if prevMachine.quantity <= \ self.plant.machines[machineIndex].quantity \ and prevMachine.canUnhook == False and prevMachine.precedence == False: # add a constraint that the entering time at the current machine should # be equal to entering time of previous machine + processing time in # previous machine + crane move time self.problem.addConstraint(lambda x, y, yt: x == y + yt + \ self.plant.craneMoveTime, [enterVar, enterVar2, spendVar2]) else: # add a constraint that the entering time at the current machine should # be larger than or equal to entering time of previous machine + # processing time in previous machine + crane move time, this is in # case of any delays 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. The finish time of an order should be equal to its entering time at the last machine + its processing time at the last machine in 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 the machine isn't the order's currentMachine (for adaptive # scheduling) if order.recipe[machineName] != 0: # machine entering time should be between the maximum time the order can # finish in the plant - the minimum processing time starting at this # machine - the machine entering time margin... until the machine margin # + the minimum between ending margin and machine margin machineStart = (order.deadline + self.endMargin) - \ order.recipe.calcMinProcTime(self.plant, machineName) - \ self.machineMargin machineEnd = machineStart + self.machineMargin + \ min(self.endMargin, self.machineMargin) # min(machineStart, 0) so that an order cannot start before time unit 0 variableRange = range(max(machineStart, 0), machineEnd) else: # the order shouldn't be in that machine since it already passed it # or that it doesn't need to be processed by it variableRange = range(0, 1) self.problem.addVariable(var, variableRange) # if the machine isn't the first one in the plant then add the precedence # constraint if machineIndex != 0: self.addPrecedenceConstraint(var, order, machineIndex) def machineQuantityConstraintFunc(self, *args): """ This function is used while computing the quantity constraints. The idea is that there cannot be n orders in a machine at the same time if n is larger than the quantity of the machine. This function simply calculates the number of overlaps between all orders' processing time units and checks if the number of overlaps is larger than or equal to the quantity of the machine, if so, the constraint is violated, else, it's not. """ 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): last1 = et + spendTimes[i] - 1 numberOfCommons = 0 for j, et2 in enumerate(enterTimes): if i > j: last2 = et2 + spendTimes[j] - 1 if et < et2: if last1 >= et2 and last1 <= last2: numberOfCommons += 1 if numberOfCommons >= quantity: return False if et2 <= et: if last2 >= et and last2 <= last1: numberOfCommons += 1 if numberOfCommons >= quantity: return False else: break return True def addMachineQuantityConstraint(self, machine): """ Creates a python-constraint constraint for handling the quantities of the machines. Creates the lists of entering and processing times for all the orders and passes them to the quantity constraint function. """ 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): """ Computes the constraint of capacity (precedence). This is by forcing that for all orders if order1's entering time at the machine is > order2's entering time, then order1's exit time has and only has to be > order2's exit time. Here, exit time denotes the entering time at the next machine. """ 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): """ Adds the capacity (precedence) constraint variable domains. """ 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=3): assert len(self.orderList.orders) > 0 assert len(self.plant.machines) > 0 pprint("SCHED Started...", BLUE, self.printing) self.endMargin = 1 while self.endMargin <= endMarginLimit: self.machineMargin = 1 machineMarginLimit = self.endMargin 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: pprint("SCHED Finished.", BLUE) 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