def clues_3_4(Stdnts): """ Combine clues 3 and 4 into a single clue. The Stdnt who studies CS has a $5,000 larger scholarship than Lynn. Marie has a $10,000 bigger scholarship than Lynn. """ yield from Solver_FD.is_contiguous_in( [Const_Stdnt(name='Lynn'), Const_Stdnt(major='CS'), Const_Stdnt(name='Marie')], Stdnts)
def complete_col(self, col_index): if self.col_is_ok(col_index): if self.summands_sum_upper_bound(col_index) == self.summands_sum_lower_bound(col_index): (carry_out_var, sum_dig_var) = (self.carry_out_var(col_index), self.cols[col_index][-1]) (carry_out, sum_dig) = divmod(self.summands_sum_upper_bound(col_index), 10) (carry_out_cvar, sum_dig_cvar) = (Const_FD({carry_out}), Const_FD({sum_dig})) yield from Solver_FD.unify_pairs_FD([(carry_out_var, carry_out_cvar), (sum_dig_var, sum_dig_cvar)]) else: yield
def set_up(board_size, trace=False): """ Set up the solver and All_Different for the transversals problem. """ Solver_FD.set_up() # Solver_FD.propagate = True # Solver_FD.smallest_first = True # Create a Queen_FD for each column. Each has an initial range of {c+1 for c in range(board_size)}. vars = {Queen_FD(board_size=board_size) for _ in range(board_size)} All_Different(vars) # Keep a record of the vars in Queen_FD so that we can propagate diagonals. Queen_FD.vars = vars # Don't need constraints since every time a var is instantiated it is # propagated, which ensures that the constraints are always satisfied. solver_fd = Queens_Solver_FD(vars, constraints=set()) solver_fd.trace = trace return solver_fd
def set_up(sets, propagate, smallest_first): """ Set up the solver and All_Different for the transversals problem. """ Solver_FD.set_up() # Create a Var_FD for each set. Its initial range is the entire set. vars = {Var_FD(s.domain) for s in sets} All_Different(vars) trace = propagate and smallest_first solver_fd = Solver_FD(vars, propagate=propagate, smallest_first=smallest_first, trace=trace) if solver_fd.trace: print(f'{"~" * 90}\n') print('Following is the trace of the final search.') print(Solver_FD.to_str(sets)) print(f'propagate: {propagate}; smallest_first: {smallest_first};\n') return solver_fd
The search is a standard depth-first search with two heuristics: propagate and smallest_first. The search is done four time with different settings of propagate and smallest-first. When propagate is true, once an element is selected as the representative of one set, it is removed from consideration as a posible representative of other sets. When smallest-first is true, the search selectes an element for an unrepresented set by chosing the smallest unrepresented set to find a representative for. When both propagate and smallest_first are true, a trace of the search is shown. """) print('The sets for which to find a traversal are:\n', Solver_FD.to_str(sets), '\n') print('The (alphabetized) traversals are:') for propagate in [False, True]: for smallest_first in [False, True]: solver_fd = set_up(sets, propagate=propagate, smallest_first=smallest_first) sol_str_set = set() if solver_fd.trace: print( '*: Var was directly instantiated--and propagated if propagation is on.\n' '-: Var was indirectly instantiated but not propagated.\n') assert solver_fd.propagate == propagate and solver_fd.smallest_first == smallest_first for _ in solver_fd.solve():
def clue_d(Stdnts): """ A derived clue. From the other clues can exclude some values at the start and end.""" yield from Solver_FD.is_a_subsequence_of( [Const_Stdnt(name=Stdnt.names-{'Ada', 'Marie', 'Emmy'}, major=Stdnt.majors-{'CS'}), Const_Stdnt(), Const_Stdnt(), Const_Stdnt(name=Stdnt.names-{'Lynn'}, major=Stdnt.majors-{'Bio', 'CS', 'Phys'})], Stdnts)
def clue_5(Stdnts): """ Ada has a larger scholarship than the Stdnt who studies Bio. """ yield from Solver_FD.is_a_subsequence_of( [Const_Stdnt(major='Bio'), Const_Stdnt(name='Ada')], Stdnts)
def clue_1(Stdnts): """ The student who studies Phys gets a smaller scholarship than Emmy. """ yield from Solver_FD.is_a_subsequence_of( [Const_Stdnt(major='Phys'), Const_Stdnt(name='Emmy')], Stdnts)
def clue_d(Stdnts): """ A derived clue. From the other clues can exclude some values at the start and end.""" yield from Solver_FD.is_a_subsequence_of( [Const_Stdnt(name=Stdnt.names-{'Ada', 'Marie', 'Emmy'}, major=Stdnt.majors-{'CS'}), Const_Stdnt(), Const_Stdnt(), Const_Stdnt(name=Stdnt.names-{'Lynn'}, major=Stdnt.majors-{'Bio', 'CS', 'Phys'})], Stdnts) if __name__ == '__main__': students = [Stdnt(name=Stdnt.names, major=Stdnt.majors) for _ in range(4)] name_vars = {std.name for std in students} major_vars = {std.major for std in students} Solver_FD.set_up() All_Different(name_vars) All_Different(major_vars) # Do the derived clue first since it sets 3 things and has no alternatives. # Do clue_3_4 next since it sets 3 things and after the derived clue has no alternatives. # Then clue_2 since it now has no alternatives. # Clue_5 finishes the job, again with no alternatives. # Can drop clue_1 since it is satisfied after clue 2. clues = [clue_d, clues_3_4, clue_2, clue_5] #, clue_1] print('\nStudents:', ', '.join(sorted(Stdnt.names))) print('Majors:', ', '.join(sorted(Stdnt.majors))) print(""" The original clues 1. The student who studies Phys gets a smaller scholarship than Emmy. 2. Emmy studies either Bio or Math.