def FC(unAssignedVars, csp, allSolutions, trace): '''Forward checking search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 2 goes in this function body. #you must not change the function parameters. #Implementing handling of the trace parameter is optional #but it can be useful for debugging #trace = True if unAssignedVars.empty(): if trace: print "{} Solution Found".format(csp.name()) soln = [] for var in csp.variables(): soln.append((var, var.getValue())) #if allSolutions: return [soln] #else: #exit bt_search.nodesExplored += 1 solns = [] nxtvar = unAssignedVars.extract() if trace: print "==>Trying {}".format(nxtvar.name()) for val in nxtvar.curDomain(): #curDomain for FC!! if trace: print "==> {} = {}".format(nxtvar.name(), val) nxtvar.setValue(val) noDWO = True for constraint in csp.constraintsOf(nxtvar): #print "fc constr,", constraint if constraint.numUnassigned() == 1: if FCCheck(constraint, nxtvar, val) == "DWO": noDWO = False if trace: print "<==falsified constraint\n" break if noDWO: new_solns = FC(unAssignedVars, csp, allSolutions, trace) if new_solns: solns.extend(new_solns) Variable.restoreValues(nxtvar, val) if len(solns)> 0 and not allSolutions: break Variable.restoreValues(nxtvar, val) nxtvar.unAssign() #same as set Vlue none #nxtvar.setValue(None) unAssignedVars.insert(nxtvar) return solns
def my_csp_problem_1(): # Defined in notebook constraints = [] variables = [] variables.append(Variable("1", ['R', 'G', 'B', 'Y'])) variables.append(Variable("2", ['R', 'G', 'B', 'Y'])) variables.append(Variable("3", ['R', 'G', 'B', 'Y'])) variables.append(Variable("4", ['R', 'G', 'B', 'Y'])) variables.append(Variable("5", ['R', 'G', 'B', 'Y'])) # these are all variable pairing of adjacent slots adjacent_pairs = [ ("1", "2"), ("1", "5"), ("2", "1"), ("2", "3"), ("2", "5"), ("3", "2"), ("3", "4"), ("3", "5"), ("4", "3"), ("4", "5"), ("5", "1"), ("5", "2"), ("5", "3"), ("5", "4") ] # No same neighbor colors def base_rule(val_a, val_b, name_a, name_b): if val_a == val_b: return False return True for pair in adjacent_pairs: constraints.append( BinaryConstraint(pair[0], pair[1], base_rule, "No same color neighbors")) return CSP(constraints, variables)
def FC(unAssignedVars, csp, allSolutions, trace): '''Forward checking search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 2 goes in this function body. #you must not change the function parameters. #Implementing handling of the trace parameter is optional #but it can be useful for debugging if unAssignedVars.empty(): if trace: print "{} Solution Found".format(csp.name()) soln = [] for v in csp.variables(): soln.append((v, v.getValue())) return [soln] #each call returns a list of solutions found bt_search.nodesExplored += 1 solns = [] #so far we have no solutions recursive calls nxtvar = unAssignedVars.extract() if trace: print "==>Trying {}".format(nxtvar.name()) for val in nxtvar.curDomain(): if trace: print "==> {} = {}".format(nxtvar.name(), val) nxtvar.setValue(val) constraintsOK = True lastVar = None pruned_var = [] for cnstr in csp.constraintsOf(nxtvar): if cnstr.numUnassigned() == 1: lastVar = cnstr.unAssignedVars()[0] pruned_var.append(lastVar) if FCCheck(cnstr, nxtvar, val) == "DWO": constraintsOK = False if trace: print "<==falsified constraint\n" break if constraintsOK: new_solns = FC(unAssignedVars, csp, allSolutions, trace) if new_solns: solns.extend(new_solns) if len(solns) > 0 and not allSolutions: Variable.restoreValues(nxtvar, val) break #don't bother with other values of nxtvar #as we found a soln. # FCCheck failed or search rest solns, need to restore Variable.restoreValues(nxtvar, val) nxtvar.unAssign() unAssignedVars.insert(nxtvar) return solns
def GAC(unAssignedVars, csp, allSolutions, trace): '''GAC search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 3 goes in this function body #You must not change the function parameters. #implementing support for "trace" is optional, but it might #help you in debugging if unAssignedVars.empty(): if trace: print("{} Solution Found".format(csp.name())) soln = [] for v in csp.variables(): soln.append((v, v.getValue())) return [soln] #each call returns a list of solutions found solns = [] #so far we have no solutions recursive calls nxtvar = unAssignedVars.extract() if trace: print("==>Trying {}".format(nxtvar.name())) for val in nxtvar.curDomain(): if trace: print("==> {} = {}".format(nxtvar.name(), val)) nxtvar.setValue(val) # Prune all values of V ≠ d from CurDom[V] for p in nxtvar.curDomain(): if val != p: nxtvar.pruneValue(p, nxtvar, val) # for each constraint C whose scope contains V Put C on GACQueue GACQueue = [] for c in csp.constraints(): if nxtvar in c.scope(): GACQueue.append(c) if GacEnforce(GACQueue, csp, nxtvar, val) != "DWO": new_solns = GAC(unAssignedVars, csp, allSolutions, trace) if new_solns: solns.extend(new_solns) if len(solns) > 0 and not allSolutions: Variable.restoreValues(nxtvar, val) break Variable.restoreValues(nxtvar, val) nxtvar.unAssign() unAssignedVars.insert(nxtvar) return solns
def GAC(unAssignedVars, csp, allSolutions, trace): '''GAC search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 3 goes in this function body #You must not change the function parameters. #implementing support for "trace" is optional, but it might #help you in debugging if unAssignedVars.empty(): if trace: print("{} Solution Found".format(csp.name())) soln = [] for v in csp.variables(): soln.append((v, v.getValue())) return [soln] #each call returns a list of solutions found bt_search.nodesExplored += 1 solns = [] #so far we have no solutions recursive calls "Choos a value from the unassigned ones" nxtvar = unAssignedVars.extract() if trace: print("==>Trying {}".format(nxtvar.name())) "Assign every value to the next variable from its domain -- Note that curDomain is used instead of domain because cur is pruned" for val in nxtvar.curDomain(): if trace: print("==> {} = {}".format(nxtvar.name(), val)) nxtvar.setValue(val) "For every constraint that the next variable has to satisfy -- " if GacEnforce(csp.constraintsOf(nxtvar), csp, nxtvar, val) == "OK": new_solns = GAC(unAssignedVars, csp, allSolutions, trace) if new_solns: solns.extend(new_solns) if len(solns) > 0 and not allSolutions: Variable.restoreValues(nxtvar, val) break #don't bother with other values of nxtvar #as we found a soln. "Restore all the values that have been pruned away" Variable.restoreValues(nxtvar, val) nxtvar.unAssign() unAssignedVars.insert(nxtvar) return solns
def GAC(unAssignedVars, csp, allSolutions, trace): '''GAC search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 3 goes in this function body #You must not change the function parameters. #implementing support for "trace" is optional, but it might #help you in debugging # util.raiseNotDefined() # print unAssignedVars if unAssignedVars.empty(): if trace: print "{} Solution Found".format(csp.name()) soln = [] for var in csp.variables(): soln.append((var, var.getValue())) return [soln] bt_search.nodesExplored += 1 solns = [] nextVar = unAssignedVars.extract() if trace: print "{} Solution Found".format(csp.name()) for value in nextVar.curDomain(): nextVar.setValue(value) noDWO = True constraints = csp.constraintsOf(nextVar) if GacEnforce(constraints, csp, nextVar, value) == "DWO": if trace: print "<==falsified constraint\n" noDWO = False if noDWO: newSolns = GAC(unAssignedVars, csp, allSolutions, trace) if newSolns: solns.extend(newSolns) if len(solns) > 0 and not allSolutions: # restore pruned values even if FC is terminating after one solution found; if keep finding other # solutions, the second restoreValues gonna be executed Variable.restoreValues(nextVar, value) break Variable.restoreValues(nextVar, value) nextVar.unAssign() unAssignedVars.insert(nextVar) return solns
def FC(unAssignedVars, csp, allSolutions, trace): '''Forward checking search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 2 goes in this function body. #you must not change the function parameters. #Implementing handling of the trace parameter is optional #but it can be useful for debugging #Forward checking, pass unassigned variables if unAssignedVars.empty(): #no more unassigned variables # print "{} Solution Found".format(csp.name()) soln = [] for var in csp.variables(): soln.append((var, var.getValue())) if not allSolutions: Variable.restoreValues(var, var.getValue()) return [soln] #terminate after one solution found. var = unAssignedVars.extract() #select next variable to assign # print "==>Trying {}".format(var.name()) bt_search.nodesExplored += 1 solns = [] for val in var.curDomain(): # print "==> {} = {}".format(var.name(), val) var.setValue(val) noDWO = True for constraint in csp.constraintsOf(var): if constraint.numUnassigned() == 1: if FCCheck(constraint, var, val) == "DWO": #prune future variables noDWO = False #pass var/val as reason # print "<==falsified constraint\n" break if noDWO: solns.extend(FC(unAssignedVars, csp, allSolutions, trace)) if len(solns) > 0 and not allSolutions: break #don't bother with other values of var #as we found a soln. Variable.restoreValues(var, val) #restore values pruned by this assignment var.setValue(None) #undo assignemnt to var unAssignedVars.insert(var) #restore var to unAssignedVars return solns
def GAC(unAssignedVars, csp, allSolutions, trace): '''GAC search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 3 goes in this function body #You must not change the function parameters. #implementing support for "trace" is optional, but it might #help you in debugging if unAssignedVars.empty(): if trace: print "{} Solution Found".format(csp.name()) soln = [] for v in csp.variables(): soln.append((v, v.getValue())) return [soln] #each call returns a list of solutions found bt_search.nodesExplored += 1 solns = [] #so far we have no solutions recursive calls nxtvar = unAssignedVars.extract() if trace: print "==>Trying {}".format(nxtvar.name()) for val in nxtvar.curDomain(): if trace: print "==> {} = {}".format(nxtvar.name(), val) nxtvar.setValue(val) constraintsOK = True constraints = csp.constraintsOf(nxtvar) subResult = GacEnforce(constraints, csp, nxtvar, val) if subResult == "DWO": constraintsOK = False if trace: print "<==falsified constraint\n" if constraintsOK: new_solns = GAC(unAssignedVars, csp, allSolutions, trace) if new_solns: solns.extend(new_solns) if len(solns) > 0 and not allSolutions: Variable.restoreValues(nxtvar, val) break #don't bother with other values of nxtvar #as we found a soln. Variable.restoreValues(nxtvar, val) nxtvar.unAssign() unAssignedVars.insert(nxtvar) return solns
def FC(unAssignedVars, csp, allSolutions, trace): '''Forward checking search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 2 goes in this function body. #you must not change the function parameters. #Implementing handling of the trace parameter is optional #but it can be useful for debugging if unAssignedVars.empty(): soln = [] for var in csp.variables(): soln.append((var, var.getValue())) return [soln] bt_search.nodesExplored += 1 solns = [] #so far we have no solutions recursive calls var = unAssignedVars.extract() for val in var.curDomain(): var.setValue(val) noDwo = True for cnstr in csp.constraintsOf(var): if cnstr.numUnassigned() == 1: if FCCheck(cnstr, var, val) == "DWO": noDwo = False break if noDwo: new_solns = FC(unAssignedVars, csp, allSolutions, trace) Variable.restoreValues(var, val) if new_solns: solns.extend(new_solns) if len(solns) > 0 and not allSolutions: break #don't bother with other values of nxtvar #as we found a soln. Variable.restoreValues(var, val) var.unAssign() unAssignedVars.insert(var) return solns
def GAC(unAssignedVars, csp, allSolutions, trace): '''GAC search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 3 goes in this function body #You must not change the function parameters. #implementing support for "trace" is optional, but it might #help you in debugging # util.raiseNotDefined() if unAssignedVars.empty(): if trace: print("{} Solution Found".format(csp.name())) solution = [] for var in csp.variables(): if trace: print(var.name(), ",", var.getValue()) solution.append((var, var.getValue())) return [solution] all_solutions = [] var = unAssignedVars.extract() bt_search.nodesExplored += 1 if trace: print(" Trying {}".format(var.name())) for val in var.curDomain(): if trace: print(" {} = {}".format(var.name(), val)) var.setValue(val) noDWO = True if GacEnforce(csp.constraintsOf(var), csp, var, val) == "DWO": noDWO = False if noDWO: currentSolution = GAC(unAssignedVars, csp, allSolutions, trace) all_solutions.extend(currentSolution) if (not allSolutions) and len(all_solutions) >= 1: Variable.restoreValues(var, val) break Variable.restoreValues(var, val) var.unAssign() unAssignedVars.insert(var) return all_solutions
def planeCSP(planes_problem): planes = planes_problem.planes flights = planes_problem.flights flights_can_follow = planes_problem.can_follow maintenance_flights = planes_problem.maintenance_flights min_maintenance_frequency = planes_problem.min_maintenance_frequency # first define the variables var_array = [] for i, plane in enumerate(planes): can_fly = planes_problem.can_fly(plane) var_array.append([]) for j in range(len(can_fly)): # -1 indicates no flight assigned if j == 0: dom = [ x for x in planes_problem.can_start(plane) if x in can_fly ] + [-1] var = Variable("V{},{}".format(plane, j), dom) else: var = Variable("V{},{}".format(plane, j), can_fly + [-1]) var_array[i].append(var) # Set up the constraints constraint_list = [] # can_follow constraint for flight in flights: flights_can_follow.append((flight, -1)) flights_can_follow.append((-1, -1)) for i in range(len(var_array)): for j in range(len(var_array[i]) - 1): scope = [var_array[i][j], var_array[i][j + 1]] constraint_list.append( TableConstraint("Follow {}{}".format(i, j), scope, flights_can_follow)) # maintenance for i in range(len(var_array)): for j in range(len(var_array[i]) - min_maintenance_frequency + 1): scope = [] for k in range(j, j + min_maintenance_frequency): scope.append(var_array[i][k]) constraint_list.append( NValuesConstraint("Maintenance {}{}".format(i, j), scope, maintenance_flights + [-1], 1, min_maintenance_frequency)) # Exactly one flight vars = [var for row in var_array for var in row] for flight in flights: constraint_list.append( NValuesConstraint("Exactly One {}".format(flight), vars, [flight], 1, 1)) return CSP("Plane Schedule", vars, constraint_list), var_array
def GAC(unAssignedVars, csp, allSolutions, trace): '''GAC search. #TODO: q4 unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 3 goes in this function body #You must not change the function parameters. #implementing support for "trace" is optional, but it might #help you in debugging # TODO: q4 in CSP pseudo code if unAssignedVars.empty(): soln = [] for var in csp.variables(): soln.append((var, var.getValue())) #if allSolutions: return [soln] var = unAssignedVars.extract() bt_search.nodesExplored += 1 solns = [] for val in var.curDomain(): var.setValue(val) noDWO = True if GacEnforce(csp.constraintsOf(var), csp, var, val) == "DWO": noDWO = False if noDWO: new_solns = GAC(unAssignedVars, csp, allSolutions, trace) if new_solns: solns.extend(new_solns) Variable.restoreValues(var, val) if len(solns)> 0 and not allSolutions: break Variable.restoreValues(var, val) var.setValue(None) unAssignedVars.insert(var) return solns
def GAC(unAssignedVars, csp, allSolutions, trace): '''GAC search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 3 goes in this function body #You must not change the function parameters. #implementing support for "trace" is optional, but it might #help you in debugging if unAssignedVars.empty(): soln = [] for v in csp.variables(): soln.append((v, v.getValue())) return [soln] # each call returns a list of solutions found bt_search.nodesExplored += 1 solns = [] # so far we have no solutions recursive calls nxtvar = unAssignedVars.extract() for val in nxtvar.curDomain(): nxtvar.setValue(val) status = "none" DWO = "DWO" nDWO = "nDWO" if GacEnforce(csp.constraintsOf(nxtvar), csp, nxtvar, val) == DWO: status = DWO else: status = nDWO if status == nDWO: new_solns = GAC(unAssignedVars, csp, allSolutions, trace) if new_solns: solns.extend(new_solns) if len(solns) > 0 and not allSolutions: Variable.restoreValues(nxtvar, val) break Variable.restoreValues(nxtvar, val) nxtvar.unAssign() unAssignedVars.insert(nxtvar) return solns
def PlaneCSP(planes_problem): constraint_list = [] result = [] can_follow = planes_problem.can_follow variables = [] min_main_freq = planes_problem.min_maintenance_frequency maintenance_flights = planes_problem.maintenance_flights planes_list = [] for x in range(len(planes_problem.planes)): planes_list.append([]) for ind, plane in enumerate(planes_problem.planes): can_fly = planes_problem.can_fly(plane) planes_list[ind] = [] for y in range(len(can_fly)): if y!=0: variable = Variable("{}{}".format(plane,str(y)),can_fly+["NULL"]) else: can_start = planes_problem.can_start(plane) variable = Variable("{}{}".format(plane,str(y)),can_start+["NULL"]) planes_list[ind].append(variable) variables.append(variable) #----constraints----- #terminate at a maintenance location for x in range(len(planes_list)): k = len(planes_list[x]) - min_main_freq + 1 if k > 0: for y in range(k): scope = [] for z in range(y,y+min_main_freq): scope.append(planes_list[x][z]) constraint = NValuesConstraint("Maintenance {}{}".format(x,z), scope, maintenance_flights + ["NULL"], 1, min_main_freq) constraint_list.append(constraint) #constraint to schedule each flight only once for flight in planes_problem.flights: constraint = NValuesConstraint("Flight {}".format(flight), variables, [flight], 1, 1) constraint_list.append(constraint) #can_follow constraint for flight in planes_problem.flights: can_follow.append((flight,"NULL")) can_follow.append(("NULL","NULL")) for x in range(len(planes_list)): for y in range(len(planes_list[x]) - 1): scope = [planes_list[x][y], planes_list[x][y+1]] constraint = TableConstraint("Can_follow {}{}".format(x,y), scope, can_follow) constraint_list.append(constraint) result.append(CSP("PlaneProblem",variables,constraint_list)) result.append(planes_list) return result
def FC(unAssignedVars, csp, allSolutions, trace): '''Forward checking search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' if unAssignedVars.empty(): if trace: print "{} Solution Found".format(csp.name()) soln = [] for v in csp.variables(): soln.append((v, v.getValue())) return [soln] # each call returns a list of solutions found bt_search.nodesExplored += 1 solns = [] # so far we have no solutions recursive calls nxtvar = unAssignedVars.extract() if trace: print "==>Trying {}".format(nxtvar.name()) for val in nxtvar.curDomain(): if trace: print "==> {} = {}".format(nxtvar.name(), val) nxtvar.setValue(val) noDWO = True for cnstr in csp.constraintsOf(nxtvar): if cnstr.numUnassigned() == 1: if FCCheck(cnstr, nxtvar, val) == "DWO": noDWO = False if trace: print "<==Domain Wipe Out\n" break if noDWO: new_solns = FC(unAssignedVars, csp, allSolutions, trace) if new_solns: solns.extend(new_solns) if len(solns) > 0 and not allSolutions: Variable.restoreValues(nxtvar, val) break # don't bother with other values of nxtvar # as we found a soln. Variable.restoreValues(nxtvar, val) nxtvar.unAssign() unAssignedVars.insert(nxtvar) return solns
def schedules(schedule_problem): '''Return an n-queens CSP, optionally use tableContraints''' #your implementation for Question 4 changes this function #implement handling of model == 'alldiff' t_dom = defaultdict(list) course_classes_dict = defaultdict(list) for class_info in schedule_problem.classes: info = class_info.split('-') time_slot = int(info[2]) # Domain for each possible timeslot t_dom[time_slot].append(class_info) # A dictionary of course-class mappings course_classes_dict[info[0]].append(class_info) vars = [] for t in range(1,schedule_problem.num_time_slots + 1): t_dom[t].append(NOCLASS) vars.append(Variable('T_{}'.format(t), t_dom[t])) cnstrs = [] for course in schedule_problem.courses: lectures = [c for c in course_classes_dict[course] if c.split('-')[3] == LEC] tuts = [c for c in course_classes_dict[course] if c.split('-')[3] == TUT] one_lec_cnstr = NValuesConstraint('One_lecture', vars, lectures, 1, 1) one_tut_cnstr = NValuesConstraint('One_tutorial', vars, tuts, 1, 1) cnstrs.append(one_lec_cnstr) cnstrs.append(one_tut_cnstr) for ti in range(schedule_problem.num_time_slots): for tj in range(ti + 1, schedule_problem.num_time_slots): scope = [vars[ti], vars[tj]] sat_assignments = lecture_tut_sat_assignments(scope) tut_after_lec_cnstr = TableConstraint("C(T{},T{})".format(ti+1,tj+1), scope, sat_assignments) cnstrs.append(tut_after_lec_cnstr) for ti in range(schedule_problem.num_time_slots - 1): scope = [vars[ti], vars[ti + 1]] sat_assignments = building_sat_assignments(scope, schedule_problem.connected_buildings) close_buildings_cnstr = TableConstraint("C(B{})".format(ti+1), scope, sat_assignments) cnstrs.append(close_buildings_cnstr) min_rest = schedule_problem.min_rest_frequency for ti in range(schedule_problem.num_time_slots - (min_rest -1)): scope = vars[ti:ti + min_rest] rest_cnstr = NValuesConstraint("Min_rest{}".format(min_rest), scope, [NOCLASS], 1, min_rest) cnstrs.append(rest_cnstr) csp = CSP("{}-Schedule".format(schedule_problem.num_time_slots), vars, cnstrs) return csp
def nQueens(n, tableCnstr): '''Return an n-queens CSP, optionally use tableContraints''' i = 0 dom = [] for i in range(n): dom.append(i + 1) vars = [] for i in dom: vars.append(Variable('Q{}'.format(i), dom)) cons = [] for qi in range(len(dom)): for qj in range(qi + 1, len(dom)): if tableCnstr: con = QueensTableConstraint( "C(Q{},Q{})".format(qi + 1, qj + 1), vars[qi], vars[qj], qi + 1, qj + 1) else: con = QueensConstraint("C(Q{},Q{})".format(qi + 1, qj + 1), vars[qi], vars[qj], qi + 1, qj + 1) cons.append(con) csp = CSP("{}-Queens".format(n), vars, cons) return csp
def nQueens(n, model): '''Return an n-queens CSP, optionally use tableContraints''' #your implementation for Question 4 changes this function #implement handling of model == 'alldiff' if not model in ['table', 'alldiff', 'row']: print("Error wrong sudoku model specified {}. Must be one of {}").format( model, ['table', 'alldiff', 'row']) i = 0 dom = [] for i in range(n): dom.append(i+1) vars = [] for i in dom: vars.append(Variable('Q{}'.format(i), dom)) cons = [] if model == 'alldiff': cons.append(AllDiffConstraint("nQueens_alldiff", vars)) for i in range(n): for j in range(i+1, n): cons.append(NeqConstraint("nQueens_neq", [vars[i], vars[j]], i+1, j+1)) else: constructor = QueensTableConstraint if model == 'table' else QueensConstraint for qi in range(len(dom)): for qj in range(qi+1, len(dom)): con = constructor("C(Q{},Q{})".format(qi+1,qj+1), vars[qi], vars[qj], qi+1, qj+1) cons.append(con) csp = CSP("{}-Queens".format(n), vars, cons) return csp
def GAC(unAssignedVars, csp, allSolutions, trace): '''GAC search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 3 goes in this function body #You must not change the function parameters. #implementing support for "trace" is optional, but it might #help you in debugging if unAssignedVars.empty(): soln = [] for var in csp.variables(): soln.append((var, var.getValue())) if not allSolutions: Variable.restoreValues(var, var.getValue()) return [soln] var = unAssignedVars.extract() #select next variable to assign bt_search.nodesExplored += 1 solns = [] for val in var.curDomain(): #current domain! var.setValue(val) noDWO = True if GacEnforce(csp.constraintsOf(var), csp, var, val) == "DWO": #only var's domain changed-constraints with var have to be checked noDWO = False if noDWO: solns.extend(GAC(unAssignedVars, csp, allSolutions, trace)) if len(solns) > 0 and not allSolutions: break #don't bother with other values of var #as we found a soln. Variable.restoreValues( var, val) #restore values pruned by var = val assignment var.setValue(None) #set var to be unassigned and return to list unAssignedVars.insert(var) return solns
def FC(unAssignedVars, csp, allSolutions, trace): '''Forward checking search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 2 goes in this function body. #you must not change the function parameters. #Implementing handling of the trace parameter is optional #but it can be useful for debugging solutions = [] if unAssignedVars.empty(): solution = [(var,var.getValue()) for var in csp.variables()] solutions.append(solution) return solutions var = unAssignedVars.extract() for val in var.curDomain(): var.setValue(val) noDWO = True for constraint in csp.constraintsOf(var): if constraint.numUnassigned()==1: if FCCheck(constraint,var,val)=="DWO": noDWO = False break if noDWO: new = FC(unAssignedVars,csp,allSolutions,trace) if new: solutions.extend(new) if not(len(solutions)==0 or allSolutions): Variable.restoreValues(var,val) break Variable.restoreValues(var,val) var.unAssign() unAssignedVars.insert(var) return solutions util.raiseNotDefined()
def GAC(unAssignedVars, csp, allSolutions, trace): '''GAC search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 3 goes in this function body #You must not change the function parameters. #implementing support for "trace" is optional, but it might #help you in debugging solutions = [] if unAssignedVars.empty(): if trace: for var in csp.variables(): print var.name(), " = ", var.getValue() solution = [(var,var.getValue()) for var in csp.variables()] solutions.append(solution) return solutions var = unAssignedVars.extract() for val in var.curDomain(): var.setValue(val) noDWO = True if GacEnforce(csp.constraintsOf(var),csp,var,val)=="DWO": noDWO = False if noDWO: new = GAC(unAssignedVars,csp,allSolutions,trace) if new: solutions.extend(new) if not(len(solutions)==0 or allSolutions): Variable.restoreValues(var,val) break Variable.restoreValues(var,val) var.unAssign() unAssignedVars.insert(var) return solutions
def time_traveling_csp_problem(): constraints = [] variables = [] # order of the variables here is the order given in the problem variables.append(Variable("T", ["1"])) variables.append(Variable("L", ["1", "2", "3", "4"])) variables.append(Variable("B", ["1", "2", "3", "4"])) variables.append(Variable("C", ["1", "2", "3", "4"])) variables.append(Variable("S", ["1", "2", "3", "4"])) variables.append(Variable("P", ["1", "2", "3", "4"])) variables.append(Variable("N", ["1", "2", "3", "4"])) # these are all variable pairing of adjacent seats edges = [("C", "P"), ("C", "L"), ("C", "N"), ("C", "B"), ("B", "C"), ("B", "N"), ("N", "B"), ("N", "C"), ("N", "P"), ("N", "L"), ("N", "T"), ("T", "N"), ("T", "L"), ("L", "T"), ("L", "N"), ("L", "P"), ("P", "L"), ("P", "N"), ("P", "C"), ("P", "S"), ("S", "P")] # not allowed constraints: def conflict(val_a, val_b, var_a, var_b): if val_a == val_b: return False return True for pair in edges: constraints.append( BinaryConstraint(pair[0], pair[1], conflict, "Time conflict")) return CSP(constraints, variables)
def bt_search(algo, csp, variableHeuristic, allSolutions, trace): '''Main interface routine for calling different forms of backtracking search algorithm is one of ['BT', 'FC', 'GAC'] csp is a CSP object specifying the csp problem to solve variableHeuristic is one of ['random', 'fixed', 'mrv'] allSolutions True or False. True means we want to find all solutions. trace True of False. True means turn on tracing of the algorithm bt_search returns a list of solutions. Each solution is itself a list of pairs (var, value). Where var is a Variable object, and value is a value from its domain. ''' varHeuristics = ['random', 'fixed', 'mrv'] algorithms = ['BT', 'FC', 'GAC'] #statistics bt_search.nodesExplored = 0 if variableHeuristic not in varHeuristics: print( "Error. Unknown variable heursitics {}. Must be one of {}.".format( variableHeuristic, varHeuristics)) if algo not in algorithms: print("Error. Unknown algorithm heursitics {}. Must be one of {}.". format(algo, algorithms)) uv = UnassignedVars(variableHeuristic, csp) Variable.clearUndoDict() for v in csp.variables(): v.reset() if algo == 'BT': solutions = BT(uv, csp, allSolutions, trace) elif algo == 'FC': for cnstr in csp.constraints(): if cnstr.arity() == 1: FCCheck(cnstr, None, None) #FC with unary constraints at the root solutions = FC(uv, csp, allSolutions, trace) elif algo == 'GAC': GacEnforce(csp.constraints(), csp, None, None) #GAC at the root solutions = GAC(uv, csp, allSolutions, trace) return solutions, bt_search.nodesExplored
def FC(unAssignedVars, csp, allSolutions, trace): '''Forward checking search. unAssignedVars is the current set of unassigned variables. csp is the csp problem, allSolutions is True if you want all solutionsl trace if you want some tracing of variable assignments tried and constraints failed. RETURNS LIST OF ALL SOLUTIONS FOUND. Finding allSolutions is handled just as it was in BT. Except that when we are not looking for all solutions and we stop early because one of the recursive calls found a solution we must make sure that we restore all pruned values before returning. ''' #your implementation for Question 2 goes in this function body. #you must not change the function parameters. #Implementing handling of the trace parameter is optional #but it can be useful for debugging if unAssignedVars.empty(): return [[(v, v.getValue()) for v in csp.variables()]] sol_a = [] v = unAssignedVars.extract() bt_search.nodesExplored += 1 for val in v.curDomain(): v.setValue(val) DWO = False for c in csp.constraintsOf(v): if c.numUnassigned() == 1: if FCCheck(c, v, val) == 'DWO': DWO = True break if not DWO: sol_a += FC(unAssignedVars, csp, allSolutions, trace) if not allSolutions and len(sol_a): Variable.restoreValues(v, val) break Variable.restoreValues(v, val) v.unAssign() unAssignedVars.insert(v) return sol_a
def my_csp_problem_2(): # Defined in notebook constraints = [] variables = [] colors = ['R', 'O', 'Y', 'G', 'B'] variables.append(Variable("1", colors.copy())) variables.append(Variable("2", colors.copy())) variables.append(Variable("3", colors.copy())) variables.append(Variable("4", colors.copy())) variables.append(Variable("5", colors.copy())) variables.append(Variable("6", colors.copy())) variables.append(Variable("7", colors.copy())) variables.append(Variable("8", colors.copy())) variables.append(Variable("9", colors.copy())) variables.append(Variable("10", colors.copy())) # these are all variable pairing of adjacent slots adjacent_pairs = [("1", "2"), ("1", "10"), ("10", "9"), ("10", "1")] for i in range(1, 9): adjacent_pairs.append( (variables[i].get_name(), variables[i - 1].get_name())) adjacent_pairs.append( (variables[i].get_name(), variables[i + 1].get_name())) # No same neighbor colors def base_rule(val_a, val_b, name_a, name_b): if val_a == val_b: return False return True for pair in adjacent_pairs: constraints.append( BinaryConstraint(pair[0], pair[1], base_rule, "No same color neighbors")) return CSP(constraints, variables)
def schedules_csp(sp): ''' sp.courses = courses sp.classes = classes sp.buildings = buildings sp.num_time_slots = num_time_slots sp._connected_buildings = dict() sp.min_rest_frequency = min_rest_frequency ''' "Define the domain and variables" classDom = [] # class in form of 'CSC108-BA-1-LEC-01' courses_sessions = [] for class_info in sp.classes: classDom.append(class_info) courses_session = class_info.split('-')[0] + class_info.split('-')[3] if courses_session not in courses_sessions: courses_sessions.append(courses_session) "Add a number of none classes to ones that have classes" num_noclass = sp.num_time_slots - len(courses_sessions) for i in range(num_noclass): classDom.append(NOCLASS + '-' + str(i)) timeVars = [] for i in range(sp.num_time_slots): timeVars.append(Variable('Time-{}'.format(i + 1), classDom)) "Construct the constraints" cons = [] con = AllDiffConstraint("Alldiff", timeVars) cons.append(con) for slot in range(sp.num_time_slots - sp.min_rest_frequency): con = NoclassConstraint( "C(Time{} to Time{})".format(slot + 1, slot + sp.min_rest_frequency + 1), timeVars[slot + 1:slot + sp.min_rest_frequency + 1], 1, sp.min_rest_frequency) cons.append(con) timeslot_pairs = combinations(list(range(sp.num_time_slots)), 2) for (slot1, slot2) in timeslot_pairs: con = BinaryClassConstraint("C(Time{},Time{})".format(slot1, slot2), [timeVars[slot1], timeVars[slot2]], sp) cons.append(con) csp = CSP("{}-ClassScheduling".format(sp.num_time_slots), timeVars, cons) return csp
def planeCSP(planes_problem): """My code start""" p, f, mp, mf = planes_problem.planes[:], planes_problem.flights[:], \ planes_problem.maintenance_flights[:], planes_problem.min_maintenance_frequency var_array = [[ Variable("{},{}".format(p[i], j), [0] + planes_problem._can_fly[p[i]]) for j in range(len(planes_problem._can_fly[p[i]])) ] for i in range(len(p))] valid_connect = [[0, 0]] + [ list(pair) for pair in planes_problem.can_follow[:] ] + [[fl, 0] for fl in f] vars = [var for row in var_array for var in row] constraint_list = [NValuesConstraint("[{}] maintenance".format(var_array[i]),var_array[i][j:j + mf], [0] + mp, 1, mf) for i in range(len(var_array)) for j in range(len(var_array[i]) - mf + 1)] + \ [TableConstraint("[{}] follow".format(var_array[i]),[var_array[i][j], var_array[i][j + 1]], valid_connect) for i in range(len(var_array)) for j in range(len(var_array[i]) - 1)] +\ [TableConstraint("[{}] initial".format(var_array[i][0]),[var_array[i][0]],[[0]] + [[start] for start in planes_problem._flights_at_start[p[i]]]) for i in range(len(var_array))] \ + [allOnce("all once", vars, f)] return CSP("planeSchedule", vars, constraint_list)
def ta_scheduling_csp_problem(): constraints = [] variables = [] # order of the variables here is the order given in the problem # the domains are those pre-reduced in part A. # constraints variables.append(Variable("C", ["Mark", "Rob", "Sam"])) # optimal search variables.append(Variable("O", ["Mark", "Mike", "Sam"])) # games variables.append(Variable("G", ["Mark"])) # rules variables.append(Variable("R", ["Mark", "Mike", "Sam"])) # id-trees variables.append(Variable("I", ["Mark", "Rob", "Sam"])) # neural nets variables.append(Variable("N", ["Mike", "Sam"])) # svms variables.append(Variable("S", ["Rob"])) # boosting variables.append(Variable("B", ["Mike"])) # these are all variable pairing of adjacent seats edges = [("C", "R"), ("B", "I"), ("B", "S"), ("S", "O"), ("S", "G"), ("S", "R"), ("S", "N"), ("N", "G")] # not allowed constraints: def conflict(val_a, val_b, var_a, var_b): if val_a == val_b: return False return True for pair in edges: constraints.append( BinaryConstraint(pair[0], pair[1], conflict, "TA(subject_a) != TA(subject_b) constraint")) return CSP(constraints, variables)
def question_5(): print_title(4) fails = [False] * 2 test1 = " v1 = Variable('V1', [1, 2])\n\ v2 = Variable('V2', [1, 2])\n\ v3 = Variable('V3', [1, 2, 3, 4, 5])\n\ v4 = Variable('V4', [1, 2, 3, 4, 5])\n\ v5 = Variable('V5', [1, 2, 3, 4, 5])\n\ v6 = Variable('V6', [1, 2, 3, 4, 5, 6, 7, 8, 9])\n\ v7 = Variable('V7', [1, 2, 3, 4, 5, 6, 7, 8, 9])\n\ v8 = Variable('V8', [1, 2, 3, 4, 5, 6, 7, 8, 9])\n\ v9 = Variable('V9', [1, 2, 3, 4, 5, 6, 7, 8, 9])\n\ vars = [v1, v2, v3, v4, v5, v6, v7, v8, v9]\n\ nv9 = NValuesConstraint('9', vars, [9], 4, 5)\n\ testcsp = CSP('test', vars, [nv9])\n\ GacEnforce([nv9], testcsp, None, None)" v1 = Variable('V1', [1, 2]) v2 = Variable('V2', [1, 2]) v3 = Variable('V3', [1, 2, 3, 4, 5]) v4 = Variable('V4', [1, 2, 3, 4, 5]) v5 = Variable('V5', [1, 2, 3, 4, 5]) v6 = Variable('V6', [1, 2, 3, 4, 5, 6, 7, 8, 9]) v7 = Variable('V7', [1, 2, 3, 4, 5, 6, 7, 8, 9]) v8 = Variable('V8', [1, 2, 3, 4, 5, 6, 7, 8, 9]) v9 = Variable('V9', [1, 2, 3, 4, 5, 6, 7, 8, 9]) vars = [v1, v2, v3, v4, v5, v6, v7, v8, v9] nv9 = NValuesConstraint('9', vars, [9], 4, 5) testcsp = CSP('test', vars, [nv9]) GacEnforce([nv9], testcsp, None, None) soln_doms = [ set([1, 2]), set([1, 2]), set([1, 2, 3, 4, 5]), set([1, 2, 3, 4, 5]), set([1, 2, 3, 4, 5]), set([9]), set([9]), set([9]), set([9]) ] for i, v in enumerate(vars): if set(v.curDomain()) != soln_doms[i]: fails[0] = True print "Error: {}.curDomain() == {}".format(v.name(), v.curDomain()) print "Correct curDomin should be == {}".format(list(soln_doms[i])) if fails[0]: print "\nFail Q5 test 1\nErrors were generated on the following code:" print test1 else: print "Pass Q5 test 1" print_sep() test2 = " v1 = Variable('V1', [1, 2])\n\ v2 = Variable('V2', [1, 2])\n\ v3 = Variable('V3', [1, 2, 3, 4, 5])\n\ v4 = Variable('V4', [1, 2, 3, 4, 5])\n\ v5 = Variable('V5', [1, 2, 3, 4, 5])\n\ v6 = Variable('V6', [1, 2, 3, 4, 5, 6, 7, 8, 9])\n\ v7 = Variable('V7', [1, 2, 3, 4, 5, 6, 7, 8, 9])\n\ v8 = Variable('V8', [1, 2, 3, 4, 5, 6, 7, 8, 9])\n\ v9 = Variable('V9', [1, 2, 3, 4, 5, 6, 7, 8, 9])\n\ vars = [v1, v2, v3, v4, v5, v6, v7, v8, v9]\n\ nv9 = NValuesConstraint('9', vars, [9], 4, 5)\n\ nv1 = NValuesConstraint('1', vars, [1], 5, 5)\n\ testcsp = CSP('test', vars, [nv1, nv9])\n\ GacEnforce([nv1, nv9], testcsp, None, None)" v1 = Variable('V1', [1, 2]) v2 = Variable('V2', [1, 2]) v3 = Variable('V3', [1, 2, 3, 4, 5]) v4 = Variable('V4', [1, 2, 3, 4, 5]) v5 = Variable('V5', [1, 2, 3, 4, 5]) v6 = Variable('V6', [1, 2, 3, 4, 5, 6, 7, 8, 9]) v7 = Variable('V7', [1, 2, 3, 4, 5, 6, 7, 8, 9]) v8 = Variable('V8', [1, 2, 3, 4, 5, 6, 7, 8, 9]) v9 = Variable('V9', [1, 2, 3, 4, 5, 6, 7, 8, 9]) vars = [v1, v2, v3, v4, v5, v6, v7, v8, v9] nv9 = NValuesConstraint('9', vars, [9], 4, 5) nv1 = NValuesConstraint('1', vars, [1], 5, 5) testcsp = CSP('test', vars, [nv1, nv9]) GacEnforce([nv1, nv9], testcsp, None, None) soln_doms = [ set([1]), set([1]), set([1]), set([1]), set([1]), set([9]), set([9]), set([9]), set([9]) ] #vars[0].pruneValue(1, None, None) for i, v in enumerate(vars): if set(v.curDomain()) != soln_doms[i]: fails[1] = True print "Error: {}.curDomain() == {}".format(v.name(), v.curDomain()) print "Correct curDomin should be == {}".format(list(soln_doms[i])) if fails[1]: print "\nFail Q5 test 2\nErrors were generated on the following code:" print test3 else: print "Pass Q5 test 2" print_sep() if not any(fails): grades[4] = outof[4]
def question_1(): print_title(0) tested[0] = True ntests = 3 fails = [False] * ntests #test1 constraint.check() q2 = Variable("Q2", [1, 2, 3, 4, 5]) q5 = Variable("Q5", [1, 2, 3, 4, 5]) c = QueensTableConstraint("Q2/Q5", q2, q5, 2, 5) q2.setValue(2) for val in q5.domain(): q5.setValue(val) if c.check(): if val in [2, 5]: print "Queens table constraint check routine failed" print "Q2={}, Q5={} not detected as falsifying constraint".format( q2.getValue(), q5.getValue()) fails[0] = True else: if val in [1, 3, 4]: print "Queens table constraint check routine failed" print "Q2={}, Q5={} not detected as satisfying constraint".format( q2.getValue(), q5.getValue()) fails[0] = True if fails[0]: print "Fail Q1 test 1" else: print "Pass Q1 test 1" print_sep() #test2 constraint.hasSupport() q2.reset() q3 = Variable("Q3", [1, 2, 3, 4, 5]) q2.pruneValue(1, None, None) q2.pruneValue(4, None, None) q2.pruneValue(5, None, None) c = QueensTableConstraint("Q2/Q5", q2, q3, 2, 3) for val in q3.domain(): if c.hasSupport(q3, val): if val not in [1, 4, 5]: print "Queens table constraint hasSupport routine failed" print "Q2 current domain = {}, Q3 = {} detected to have support (doesn't)".format( q2.curDomain(), val) fails[1] = True else: if val not in [2, 3]: print "Queens table constraint hasSupport routine failed" print "Q2 current domain = {}, Q3 = {} detected to not have support (does)".format( q2.curDomain(), val) fails[1] = True if fails[1]: print "Fail Q1 test 2" else: print "Pass Q1 test 2" print_sep() #test3 within backtracking search csp = nQueens(8, True) solutions, num_nodes = bt_search('BT', csp, 'fixed', True, False) if num_nodes != 1965: print "Queens table constraint not working correctly. BT should explore 1965 nodes." print "With your implementation it explores {}".format(num_nodes) fails[2] = True if len(solutions) != 92: print "Queens table constraint not working correctly. BT should return 92 solutions" print "With your implementation it returns {}".format(len(solutions)) fails[2] = True if fails[2]: print "Fail Q1 test 3" else: print "Pass Q1 test 3" if any(fails): grades[0] = 0 else: grades[0] = outof[0]