def complete_column(carry_out: int, Carry_Out_Dig: PyValue, sum_dig: int, Sum_Dig: PyValue, digits_in: List[int], Leading_Digits): """ If Sum_Dig (the variable representing the digit in the sum for this column) is not yet instantiated, instantiate it to sum_dig (if that digit is available). If Sum_Dig is already instantiated, ensure it is consistent with the sum_dig. Instantiate Carry_Out_Dig to carry_out. """ # Is Sum_Dig uninstantiated? If so, instantiate it to sum_digit if possible. # Then instantiate Carry_Out_Dig, and return (yield) digits_in with sum_digit removed. if not Sum_Dig.is_instantiated(): if sum_dig not in digits_in: # sum_dig is not available in digits_in. Fail, i.e., return instead of yield. return # sum_dig is available in digits_in. Give it to Sum_Dig as long as this does not give # 0 to one of the leading digits. if sum_dig != 0 or all(Sum_Dig is not LD for LD in Leading_Digits): for _ in unify_pairs([(Carry_Out_Dig, carry_out), (Sum_Dig, sum_dig)]): # Remove sum_digit from digits_in i = digits_in.index(sum_dig) yield digits_in[:i] + digits_in[i + 1:] # If Sum_Dig is instantiated, is it equal to sum_digit? # If so, instantiate Carry_Out_Dig and return the current digits_in. elif sum_dig == Sum_Dig.get_py_value(): for _ in unify(Carry_Out_Dig, carry_out): yield digits_in
def solution_to_string(PV_Digits: List[PyValue], _Z=PyValue('a'), Blank=PyValue('b')) -> str: """ Convert a list of PyValue digits to a single number represented as a string. Replace _Z with Blank. """ PyDigits = PyValue.get_py_values([(Blank if PV is _Z else PV) for PV in PV_Digits]) return ''.join(map(str, PyDigits))
def find_transversal_with_sum_n(sets: List[PySet[int]], n: int): Trace.trace = False (A, B, C) = (PyValue(), PyValue(), PyValue()) for _ in transversal_yield_lv(sets, (A, B, C)): if A + B + C == PyValue(n): return (A.get_py_value(), B.get_py_value(), C.get_py_value()) else: print(f'{A} + {B} + {C} != {n}') print(f'No more transversals') # This is the default even without the following statement return None
def best_route(Start: PyValue, End: PyValue): """ A Route will be: [Station, (Line, Dist), Station, (Line, Dist), ..., Station]. Ignoring Dist, this will be the sequence of stations and lines to take from Start to End. The Dist components are the number of intermediate stations on the associated line. The best route uses the fewest lines, and of routes using the same number of lines, the fewest total Dist values. """ # Look for routes that use the fewest lines. for i in range(1, len(lines)+1): # i is the number of lines used. # The middle sequence is the intermediate lines and stations. # One line involves one intermediate element, the line used. # Two lines involves 3 intermediate elements, the two lines used and the transfer station. # Etc. # The "+1" above allows i to take on the value len(lines), in which case we are using all the lines. intermediate = (PyValue() for _ in range(2*i-1)) legs = [Start, *intermediate, End] # If route(*legs) succeeds, route will instantiate legs to # [Station, (Line, int), Station, (Line, int), ... , Station] # Once legs is instantiated, must extract the py_values so that the collection # of routes remains instantiated after the list comprehension terminates. route_options = [ [elt.get_py_value() for elt in legs] for _ in route(*legs) ] # Once we find an i so that there is at least one route from Start to End using i lines, # find the best of them and quit--by using return at the bottom. # No point in looking for routes that use more lines. if route_options: routes_with_totals: Iterator[Tuple[List[str], int]] = map(sum_distances, route_options) best_option: Tuple[List[str], int] = min(routes_with_totals, key=lambda routeDist: routeDist[1]) yield best_option # return prevents backtracking, i.e., looking for longer routes. # Has an effect similar to a cut (!) at the end of a clause in Prolog. # break would work as well since it jumps out of the for-loop and returns. return
def set_up_puzzle(t1: str, t2: str, sum: str, _Z: PyValue) -> \ Optional[Tuple[List[PyValue], List[PyValue], List[PyValue], List[PyValue], List[PyValue]]]: """ Convert the initial string representation to (uninstantiated) PyValues. t1 and t2 are the numbers to be added. sum is the sum. _Z is PyValue(0). It will be replaced by leading blanks. """ Var_Letters = sorted(list(set(t1 + t2 + sum))) if len(Var_Letters) > 10: print(f'Too many variables: {Var_Letters}') return Vars_Dict = {V: PyValue() for V in Var_Letters} length = len(sum) + 1 T1 = [_Z for _ in range((length - len(t1)))] + letters_to_vars(t1, Vars_Dict) T2 = [_Z for _ in range((length - len(t2)))] + letters_to_vars(t2, Vars_Dict) Sum = [_Z for _ in range((length - len(sum)))] + letters_to_vars(sum, Vars_Dict) # Leading_Digits are the variables that should not be assigned 0. Leading_Digits = letters_to_vars({t1[0], t2[0], sum[0]}, Vars_Dict) Carries = [PyValue( ) for _ in range(length - 1)] + [PyValue(0)] return (Carries, T1, T2, Sum, Leading_Digits)
def connected(S1: PyValue, Line_Dist: PyValue, S2: PyValue): # S1: Union[PyValue, Var], Line_Dist: Var, S2: Union[PyValue, Var """ Are stations S1 and S2 connected on the same train line? If so, which line is it, and how many stations are between them? Line_Dist will be unified with (line, count_of_stations) """ # print(f'-> connected({S1}, {Line_Dist}, {S2})?') Line = PyValue() # Can use either forall or nested for _ in has_station's # for _ in forall([lambda: has_station(Line, S1), # lambda: has_station(Line, S2)]): for _ in has_station(Line, S1): for _ in has_station(Line, S2): # Ensure that S1 != S2 if S1 != S2: line = Line.get_py_value() stations = lines[line] pos1 = stations.index(S1.get_py_value()) pos2 = stations.index(S2.get_py_value()) yield from unify(Line_Dist, (line, abs(pos1 - pos2)))
def solve_crypto(t1: str, t2: str, sum: str): _Z = PyValue(0) (Carries, T1, T2, Sum, Leading_Digits) = set_up_puzzle(t1, t2, sum, _Z) want_more = None Blank = PyValue(' ') for _ in solve(Carries, T1, T2, Sum, Leading_Digits): # We have a solution. # Replace the leading _Z zeros with blanks and convert each number to a string. # We can discard T1[0], T2[0], and Sum[0] because we know they will be 0. (t1_out, t2_out, tot_out) = (solution_to_string(T, _Z, Blank) for T in [T1[1:], T2[1:], Sum[1:]]) print() print(f' {t1} -> {t1_out}') print(f'+ {t2} -> {t2_out}') print(f'{"-" * (len(sum)+1)} {"-" * len(sum)}') print(f' {sum} -> {tot_out}') ans = input('\nLook for more solutions? (y/n) > ').lower( ) want_more = ans[0] if len(ans) > 0 else 'n' if want_more != 'y': break if want_more == 'y': print('No more solutions.')
def place_n_queens(board_width: int): """ Generate and display all solutions to the n-queens problem. A Placement is a list of n PyValue integers. The rth integer indicates where the queen is to be placed in the rth row. I.e., Placement[r] is the position of the queen in row r. """ start = timer() # Create the entire list of PyValue variables. placement = [PyValue() for _ in range(board_width)] solutionNbr = 0 # place_remaining_queens will instantiate the PyValue variables one by one. for _ in place_remaining_queens(placement): solutionNbr += 1 solution_display = layout([c.get_py_value() for c in placement], board_width) print(f'\n{solutionNbr}.\n{solution_display}') end = timer() print(f'time: {round(end-start, 3)}') inp = input('\nMore? (y, or n)? > ').lower() if inp != 'y': break start = timer()
transfer_stns = f', including {nbr_lines -1} transfer station{add_plural_s(nbr_lines -1)}' if nbr_lines > 1 else '' print(f'This route uses {nbr_lines} line{add_plural_s(nbr_lines)} and passes {stations_passed - 1} ', end='') print(f'intermediate station{add_plural_s(stations_passed - 1)}{transfer_stns}.') for (s1, s2) in [("Takatsuki", "Yamashina"), # Direct ("Takatsuki", "Kyoto"), # Direct ("Yamashina", "Sakamoto"), # Direct ("Yamashina", "Ishiyama"), # Direct ("Otsukyo", "Sakamoto"), # Direct ("Otsukyo", "Hamaotsu"), # One-change ("Otsukyo", "Ano"), # One-change ("Takatsuki", "Otsukyo"), # One-change ("Yamashina", "Ano"), # One-change ("Takatsuki", "Ano"), # Two-changes ("Hamaotsu", "Otsu"), # Two-changes ("Zeze", "Takatsuki"), # Two-changes ("Zeze", "Kusatsu"), # Two-changes ]: (S1, S2) = (PyValue(s1), PyValue(s2)) # Use Route in Prolog style to pass back the route. # In this case it's simply a basket in which the best route is conveyed. # Route = Var( ) for Route in best_route(S1, S2): print(f'\nA route from {S1} to {S2} that uses fewest lines, ', end='') print(f'and of those the one that passes the fewest intermediate stations:') print_route( *Route )
def discard(self, Other: PyValue) -> PySet: Other_EoT = Other.unification_chain_end() new_args = [arg for arg in self.args if arg != Other_EoT] new_set = PySet(new_args) return new_set
XYs = [ *Xs.unification_chain_end().args, *Ys.unification_chain_end().args ] yield from unify_sequences(XYs, Zs.args) # for _ in unify_sequences(XYs, Zs.args): # yield if __name__ == '__main__': print(PyTuple((1, 2, 3))) print(PyList([1, 2, 3])) Xs = PyList(['Python']) # list(map(PyValue, range(3))) Ys = PyList([PyValue('Q')]) # [PyValue(i+3) for i in range(3)] Zs = Var() print(f'\nappend({Xs}, {Ys}, {Zs})') for _ in append(Xs, Ys, Zs): print(f'\tXs: {Xs}, Ys: {Ys}, Zs: {Zs}') Xs = Var() Ys = Var() Zs = PyList(list(range(5))) print(f'\nappend({Xs}, {Ys}, {Zs})') for _ in append(Xs, Ys, Zs): print(f'\tXs: {Xs}, Ys: {Ys}, Zs: {Zs}') Xs = Var()
def is_even_2(n: int, Res: Var) -> Generator[None, None, None]: for i in range(n): for _ in unify_pairs([ (PyValue(i % 2 == 0), PyValue(True)), (PyValue(i), Res) ]): yield
def clue_2(self, Students: SuperSequence): """ 2. Amy studies either Philosophy or English. """ # Create Major as a local logic variable. Major = PyValue() for _ in member(Student(name='Amy', major=Major), Students): yield from member(Major, self.ListType(['Philosophy', 'English']))
used_values = PyList( [tnvsl[i] for i in range(len(tnvsl)) if i not in var_indxs]) T_Var = tnvsl[nxt_indx] for _ in member(T_Var, sets[nxt_indx]): for _ in fails(member)(T_Var, used_values): new_sets = [set.discard(T_Var) for set in sets] yield from tnvsl_dfs_gen_lv(new_sets, tnvsl) if __name__ == '__main__': print('\n\n latest') Trace.trace = True print(f'\n{"=" * 15}') (A, B, C) = (Var(), Var(), Var()) Py_Sets = [PySet(set) for set in sets] N = PyValue(6) for _ in tnvsl_dfs_gen_lv(Py_Sets, (A, B, C)): sum_string = ' + '.join(str(i) for i in (A, B, C)) equals = '==' if A + B + C == N else '!=' print(f'{sum_string} {equals} 6') if A + B + C == N: break print(f'{"=" * 15}') # propagate = True # smallest_first = True # def find_transversal_with_sum_n(py_sets: List[PySet], n: PyValue): # (A, B, C) = (Var(), Var(), Var()) # for _ in tnvsl_dfs_gen_lv(py_sets, (A, B, C)): # if A + B + C == n: # return (A, B, C) # else:
# Clause 1. lambda: unify_pairs([(Xs, emptyLinkedList), (Ys, Zs)]), # Clause 2. lambda: forall([ lambda: unify_pairs([(Xs, LinkedList((XZ_Head, Xs_Tail))), (Zs, LinkedList((XZ_Head, Zs_Tail)))]), lambda: append(Xs_Tail, Ys, Zs_Tail) ]) ]): yield if __name__ == '__main__': print(emptyLinkedList) E = PyValue(3) for _ in member(E, emptyLinkedList): print(f'Error: should not get here.') A = LinkedList((Var(), Var())) A112 = A[4:11:2] A37 = A[3:7] print(f'\nA: {A}\nA[3:7]: {A37}\nA[4:11:2]: {A112}') for _ in unify(A37, LinkedList('ABCD')): print(f'\nA: {A}\nA[3:7]: {A37}\nA[4:11:2]: {A112}') print() print(f'A[:4]: {A[:4]}') A_tail = A.tail() print(f'A.tail()[:3]: {A_tail[:3]}') print(f'A.tail().tail()[:2]: {A.tail().tail()[:2]}')
def is_even_1(i: int) -> Generator[None, None, None]: for _ in unify(PyValue(True), PyValue(i % 2 == 0)): yield
# T_next is the PyValue to be instantiated this time around. T_next = transversal[next_index] used_values = PyList([transversal[i] for i in range(len(transversal)) if i not in remaining_indices]) for _ in member(T_next, sets[next_index]): for _ in fails(member)(T_next, used_values): new_sets = sets if not propagate else [set.discard(T_next) for set in sets] yield from transversal_yield_lv(new_sets, transversal) if __name__ == '__main__': for smallest_first in [False, True]: for propagate in [False, True]: print(f'\n{"-" * 75}' f'\ntransversal_yield_lv({sets_lv_string}, (PyValue(None), PyValue(None), PyValue(None)))' f'\n propagate: {propagate}; smallest_first: {smallest_first}\n') transversal = (PyValue(), PyValue(), PyValue()) for _ in transversal_yield_lv(sets_lv, transversal): print(f'Yielded logic-variable traversal: {Trace.to_str(transversal)}\n') """ --------------------------------------------------------------------------- transversal_yield_lv([{1, 2, 3}, {1, 2, 4}, {1}], (PyValue(None), PyValue(None), PyValue(None))) propagate: False; smallest_first: False sets: [{1, 2, 3}, {1, 2, 4}, {1}], transversal: (_, _, _) sets: [{1, 2, 3}, {1, 2, 4}, {1}], transversal: (1, _, _) sets: [{1, 2, 3}, {1, 2, 4}, {1}], transversal: (1, 2, _) sets: [{1, 2, 3}, {1, 2, 4}, {1}], transversal: (1, 4, _) sets: [{1, 2, 3}, {1, 2, 4}, {1}], transversal: (2, _, _) sets: [{1, 2, 3}, {1, 2, 4}, {1}], transversal: (2, 1, _) sets: [{1, 2, 3}, {1, 2, 4}, {1}], transversal: (2, 4, _)
def is_even_2_decorated(n: int, Res: Var) -> Bool_Yield_Wrapper: for i in range(n): for _ in unify_pairs( [(PyValue(i % 2 == 0), PyValue(True)), (PyValue(i), Res) ]): yield
# Corresponds to append([], Ys, Ys). yield from unify_pairs([(Xs, emptyLinkedList), (Ys, Zs)]) # Corresponds to append([X | Xs], Ys, [X | Zs]): - append(Xs, Ys, Zs). (XZ_Head, Xs_Tail, Zs_Tail) = n_Vars(3) for _ in unify_pairs([(Xs, LinkedList(XZ_Head, Xs_Tail)), (Zs, LinkedList(XZ_Head, Zs_Tail))]): yield from append(Xs_Tail, Ys, Zs_Tail) if __name__ == '__main__': print(LinkedList([]) == LinkedList(())) print(emptyLinkedList) E = PyValue(3) for _ in member(E, emptyLinkedList): print(f'Error: should not get here.') A = LinkedList(Var(), Var()) A112 = A[4:11:2] A37 = A[3:7] print(f'\nA: {A}\nA[3:7]: {A37}\nA[4:11:2]: {A112}') for _ in unify(A37, LinkedList('ABCD')): print(f'\nA: {A}\nA[3:7]: {A37}\nA[4:11:2]: {A112}') print() print(f'A[:4]: {A[:4]}') A_tail = A.tail() print(f'A.tail()[:3]: {A_tail[:3]}') print(f'A.tail().tail()[:2]: {A.tail().tail()[:2]}')