Exemple #1
0
def member(E: Term, A_List: Union[SuperSequence, Var]):
    """
  Is E in A_List?
  """
    # If A_List is empty, it can't have a member. So fail.
    if A_List.is_empty():
        return

    # The following is an implicit 'or'. Either unify E with A_List.head() or call member(E, A_List.tail()).

    # The first case is easy.
    # for _ in unify(E, A_List.head( )):
    #   yield
    yield from unify(E, A_List.head())

    # The second case--member(E, A_List.tail())--is trickier.
    # Since A_List may be an open-ended LinkedList, A_List.tail() may be a Var.
    # In that case, we must first instantiate A_List.tail() to LinkedList( (Var, Var) ).
    A_List_Tail = A_List.tail()
    # Create A_List_New_Tail to be unified with A_List_Tail.
    # A_List_New_Tail will be A_List_Tail in most cases.
    # But if isinstance(A_List_Tail, Var), make A_List_New_Tail = LinkedList( (Var( ), Var( )) ).
    # In either case, unify A_List_Tail with A_List_New_Tail and call member(E, A_List_New_Tail).
    # An issue is that we can't import LinkedList since that would create an import cycle.
    # Instead use type(A_List), which will be LinkedList if A_List_Tail is a Var.
    A_List_New_Tail = type(A_List)(
        (Var(), Var())) if isinstance(A_List_Tail, Var) else A_List_Tail
    # If A_List_New_Tail is A_List_Tail, this unify does nothing.
    for _ in unify(A_List_New_Tail, A_List_Tail):
        yield from member(E, A_List_New_Tail)
Exemple #2
0
def has_station(L: PyValue, S: PyValue):
  # print(f'-> has_station({L}, {S})?')
  for line in lines:
    # If L is instantiated, find out whether it is line before looking at all the stations.
    for _ in unify(L, line):
      for station in lines[line]:
        for _ in unify(S, station):
          # print(f'<- has_station({L}, {S})')
          yield
Exemple #3
0
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
Exemple #4
0
    def fill_column(PVs: List[PyValue], index: int, digits_in: List[int]):
        """
    PVs are the digits in the current column to be added together, one from each term.
    digits-in are the digits that have not yet been assigned to a Var.
    Find digits in digits_in that make the column add up properly.
    Return (through yield) the digits that are not yet used after the new assignments.
    We do this recursively on PVs--even though we are currently assuming only two terms.
    """
        if not PVs:
            # We have instantiated the digits to be added.
            # Instantiate Sum_Dig (if possible) and Carries[index - 1] to the total.
            # Completing the column is a bit more work than it might seem.
            (carry_in, digit_1,
             digit_2) = (D.get_py_value()
                         for D in [Carries[index], Term1[index], Term2[index]])
            total = sum([carry_in, digit_1, digit_2])
            (carry_out, sum_dig) = divmod(total, 10)
            yield from complete_column(carry_out, Carries[index - 1], sum_dig,
                                       Sum[index], digits_in, Leading_Digits)

        else:
            # Get head and tail of PVs.
            [PV, *PVs] = PVs
            # If PV already has a value, nothing to do. Go on to the remaining PVs.
            if PV.is_instantiated():
                yield from fill_column(PVs, index, digits_in)
            else:
                # Give PV one of the available digits. Through "backup" all digits will be tried.
                for i in range(len(digits_in)):
                    if digits_in[i] != 0 or all(PV is not LV
                                                for LV in Leading_Digits):
                        for _ in unify(PV, digits_in[i]):
                            yield from fill_column(
                                PVs, index, digits_in[:i] + digits_in[i + 1:])
Exemple #5
0
def transversal_yield_lv(sets: List[PyList], so_far: PyList, Answer: Var):
  print(f'sets/[{", ".join([str(S) for S in sets])}]; so_far_reversed/{reversed(so_far)}')
  if not sets:
    yield from unify(reversed(so_far), Answer)
  else:
    [S, *Ss] = sets
    X = Var( )
    for _ in member(X, S):
      for _ in fails(member)(X, so_far):
        yield from transversal_yield_lv(Ss, PyList([X]) + so_far, Answer)
Exemple #6
0
def transversal_yield_lv(Sets: List[PyList], Partial_Transversal: PyList,
                         Complete_Transversal: Var):
    print(
        f'Sets/[{", ".join([str(S) for S in Sets])}]; Partial_Transversal/{Partial_Transversal}'
    )
    if not Sets:
        yield from unify(Partial_Transversal, Complete_Transversal)
    else:
        (S, Ss) = (Sets[0], Sets[1:])
        Element = Var()
        for _ in member(Element, S):
            for _ in fails(member)(Element, Partial_Transversal):
                yield from transversal_yield_lv(
                    Ss, Partial_Transversal + PyList([Element]),
                    Complete_Transversal)
Exemple #7
0
 def __getitem__(self, key: Union[int, slice]):
     (prefix, tail) = self.prefix_and_tail()
     stop = key if isinstance(key, int) else \
            key.stop if isinstance(key.stop, int) else \
            len(self) if key.stop is None else \
            None
     if len(prefix) >= stop:
         slice_elements = prefix[key]
         if isinstance(key, int):
             return slice_elements
         else:
             return LinkedList(slice_elements)
     if not isinstance(tail, Var):
         return None
     template = LinkedList(n_Vars(stop))
     for _ in unify(self, template):
         (prefix, _) = template.prefix_and_tail()
         return LinkedList(prefix[key])
Exemple #8
0
def append(Xs: Union[PySequence, Var], Ys: Union[PySequence, Var],
           Zs: Union[PySequence, Var]):
    """
    append([], Ys, Zs).
    append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).

    See discussion in linked_list version.

    This version assumes we are working with Python lists or tuples, i.e., no uninstantiated tails.
  """

    if isinstance(Zs, Var) and (isinstance(Xs, Var) or isinstance(Ys, Var)):
        # Can't have Xs or Ys Var if Zs is Var.
        return

    if isinstance(Zs, Var):
        ListType = type(Xs)
        # Make Zs a list of Var's of length len(Xs) + len(Ys)
        # After this unification, Zs will still be a Var, but
        # by the time we reach this point again, Zs will refer
        # to its trail end, which is not a Var.
        for _ in unify(Zs, ListType(n_Vars(len(Xs) + len(Ys)))):
            yield from append(Xs, Ys, Zs)
        return

    # We now know that: Zs is not a Var -- although it may be a sequence of Vars.
    # Divide up its length among Xs and Ys
    ListType = type(Zs)
    len_Zs = len(Zs)
    for i in range(len_Zs + 1):
        # If Xs or Ys are already instantiated to some fixed length Sequence, unify will fail when given the wrong length.
        for _ in unify_pairs([(Xs, ListType(n_Vars(i))),
                              (Ys, ListType(n_Vars(len_Zs - i)))]):
            # Although the lengths of Xs and Ys vary with i,
            # Xs, Ys, and Zs are all of fixed lengths in which len(Xs) + len(Ys) = len(Zs).
            # Concatenate Xs and Ys and then unify the concatenation with Zs.
            XYs = [
                *Xs.unification_chain_end().args,
                *Ys.unification_chain_end().args
            ]
            yield from unify_sequences(XYs, Zs.args)
Exemple #9
0
def place_remaining_queens(placement: List[PyValue]):
    """
  Find a safe spot for the next queen and eithe quit if it's the last position or call this recursively.
  """
    # next_col is the next column to be instantiated
    next_col = len([c for c in placement if c.is_instantiated()])
    for d in range(len(placement)):
        for _ in unify(placement[next_col], d):
            # Note that there is no 'else' for if is_safe(placement). Whether or not d is a safe position,
            # we go on to the next value after processing it.
            if is_safe(placement):
                # Have we filled the board? If so, next_col will be, e.g., 7, and len(placement) will be 8.
                last_col = len(placement) - 1
                if next_col == last_col:
                    # Found a solution; yield.
                    yield

                # More queens to place.
                else:
                    # Find columns for the remaining queens.
                    yield from place_remaining_queens(placement)
Exemple #10
0
def is_a_subsequence_of(As: List, Zs: SuperSequence):
  """
  As may be spread out in Zs but must be in the same order as in Zs.
  """
  if not As:
    # If no more As to match, we're done. Succeed.
    yield

  elif not Zs:
    # If no more Zs to match the remaining As, fail.
    return

  else:
    for _ in forany([
                     # Match As[0] and Zs[0]; go on to is_a_subsequence_of(As[1:], Zs[1:])
                     lambda: forall([lambda: unify(As[0], Zs[0]),
                                     lambda: is_a_subsequence_of(As[1:], Zs[1:])]),
                     # Whether or not we matched As[0] and Zs[0] above, try is_a_subsequence_of(As, Zs[1:])
                     lambda: is_a_subsequence_of(As, Zs[1:])
                     ]):
      yield
Exemple #11
0
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)))
Exemple #12
0
def zebra_problem(Houses):
    for _ in forall([
            # 1. The English live in the red house.
            lambda: member(House(nationality='English', color='red'), Houses),
            # lambda: print_SF(f'After 1: {Houses}', 'Succeed'),

            # 2. The Spanish have a dog.
            lambda: member(House(nationality='Spanish', pet='dog'), Houses),
            # lambda: print_SF(f'After 2: {Houses}', 'Succeed'),

            # 3. They drink coffee in the green house.
            lambda: member(House(drink='coffee', color='green'), Houses),
            # lambda: print_SF(f'After 3: {Houses}', 'Succeed'),

            # 4. The Ukrainians drink tea.
            lambda: member(House(nationality='Ukrainians', drink='tea'), Houses
                           ),
            # lambda: print_SF(f'After 4: {Houses}', 'Succeed'),

            # 5. The green house is immediately to the right of the white house.
            lambda: is_contiguous_in(
                [House(color='white'),
                 House(color='green')], Houses),
            # lambda: print_SF(f'After 5: {Houses}', 'Succeed'),

            # 6. The Old Gold smokers have snails.
            lambda: member(House(smoke='Old Gold', pet='snails'), Houses),
            # lambda: print_SF(f'After 6: {Houses}', 'Succeed'),

            # 7. They smoke Kool in the yellow house.
            lambda: member(House(smoke='Kool', color='yellow'), Houses),
            # lambda: print_SF(f'After 7: {Houses}', 'Succeed'),

            # 8. They drink milk in the middle house.
            # Note the use of a slice. Houses[2] picks the middle house.
            lambda: unify(House(drink='milk'), Houses[2]),
            # lambda: print_SF(f'After 8: {Houses}', 'Succeed'),

            # 9. The Norwegians live in the first house on the left.
            lambda: unify(House(nationality='Norwegians'), Houses.head()),
            # lambda: print_SF(f'After 9: {Houses}', 'Succeed'),

            # 10. The Chesterfield smokers live next to the fox.
            lambda: next_to(House(smoke='Chesterfield'), House(pet='fox'),
                            Houses),
            # lambda: print_SF(f'After 10: {Houses}', 'Succeed'),

            # 11. They smoke Kool in the house next to the horse.
            lambda: next_to(House(smoke='Kool'), House(pet='horse'), Houses),
            # lambda: print_SF(f'After 11: {Houses}', 'Succeed'),

            # 12. The Lucky smokers drink juice.
            lambda: member(House(drink='juice', smoke='Lucky'), Houses),
            # lambda: print_SF(f'After 12: {Houses}', 'Succeed'),

            # 13. The Japanese smoke Parliament.
            lambda: member(House(nationality='Japanese', smoke='Parliament'),
                           Houses),
            # lambda: print_SF(f'After 13: {Houses}', 'Succeed'),

            # 14. The Norwegians live next to the blue house.
            lambda: next_to(House(nationality='Norwegians'), House(color='blue'
                                                                   ), Houses),
            # lambda: print_SF(f'After 14: {Houses}', 'Succeed'),

            # Fill in unmentioned properties.
            lambda: members([House(pet='zebra'),
                             House(drink='water')], Houses),
    ]):
        yield
 def is_even_1(i: int) -> Generator[None, None, None]:
   for _ in unify(PyValue(True), PyValue(i % 2 == 0)):
       yield
Exemple #14
0
 def clue_8(self, Houses: SuperSequence):
     """ 8. They drink milk in the middle house.
     Note the use of a slice. Houses[2] picks the middle house. """
     yield from unify(House(drink='milk'), Houses[2])
Exemple #15
0
    ]):
        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]}')

    print(f'\nemptyLinkedList: {emptyLinkedList}')

    Xs = LinkedList([*range(10)])
    print(f'\nSlices: Xs: {Xs}')
    print(f'\tXs[2:-1:2]: {Xs[2:-1:2]}')
    print(f'\tXs[-1:2:-2]: {Xs[-1:2:-2]}')
    print(f'\tXs[5]: {Xs[5]}')
Exemple #16
0
 def clue_9(self, Houses: SuperSequence):
     """ 9. The Norwegians live in the first house on the left.
     Instead of Houses.head(), could have written Houses[0]. """
     yield from unify(House(nationality='Norwegians'), Houses.head())
Exemple #17
0
    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()
    Ys = Var()
    Zs = PyTuple(tuple(range(5)))

    print(f'\nappend({Xs}, {Ys}, {Zs})')
    for _ in append(Xs, Ys, Zs):
        print(f'\tXs: {Xs}, Ys: {Ys}, Zs: {Zs}')

    X = tuple(n_Vars(15))
    Y = X[4:8]
    print(f'\nX: {PyTuple(X)}, Y: {PyTuple(Y)}')
    for _ in unify_sequences(Y, tuple(map(PyValue, ['A', 'B', 'C', 'D']))):
        print(
            f"unify_sequences(Y, tuple(map(PyValue, ['A', 'B', 'C', 'D']))) => X: {PyTuple(X)}, Y: {PyTuple(Y)}"
        )

    B = tuple(n_Vars(8))
    print(f'\nB: {PyTuple(B)}')

    for _ in unify(B[3], PyValue('XYZ')):
        print(f"unify(B[3], PyValue('XYZ')) => B: {PyTuple(B)}")