Пример #1
0
 def test_input_or_despite_factors(self):
     """Test part of the process of checking contradiction."""
     rural = Entity(name="Rural's telephone directory")
     compilation = Predicate(
         content="${rural_s_telephone_directory} was a compilation of facts"
     )
     idea = Predicate(content="${rural_s_telephone_directory} was an idea")
     copyrightable = Fact(
         predicate=Predicate(
             content="${rural_s_telephone_directory} was copyrightable"
         ),
         terms=rural,
     )
     left = Procedure(
         outputs=(copyrightable),
         inputs=(Fact(predicate=compilation, terms=rural)),
     )
     right = Procedure(
         outputs=(copyrightable),
         inputs=(Fact(predicate=idea, terms=rural)),
     )
     context = ContextRegister()
     explanation = Explanation.from_context(context)
     gen = left._has_input_or_despite_factors_implied_by_all_inputs_of(
         right, explanation
     )
     with pytest.raises(StopIteration):
         next(gen)
Пример #2
0
    def triggers_next_procedure(
        self,
        other: Procedure,
        context: Optional[Union[ContextRegister, Explanation]] = None,
    ) -> Iterator[Explanation]:
        r"""
        Test if :class:`.Factor`\s from firing ``self`` would trigger ``other``.

        .. Note::
            To be confident that ``self`` actually can trigger ``other``,
            we would have to assume that self or other has ``universal: True``
            since otherwise there could be a mismatch between what is provided
            and what is needed to trigger ``other``.

        :param other:
            another :class:`Procedure` to test to see whether it can
            be triggered by triggering ``self``

        :returns:
            whether the set of :class:`Factor`\s that exist after ``self``
            is fired could trigger ``other``
        """
        self_despite_or_input = FactorGroup((*self.despite, *self.inputs))
        self_output_or_input = FactorGroup((*self.outputs, *self.inputs))

        if not isinstance(context, Explanation):
            context = Explanation.from_context(context)

        for explanation_1 in self_output_or_input.explanations_implication(
                other.inputs_group, context=context):
            yield from self_despite_or_input.explanations_implication(
                other.despite_group, context=explanation_1)
Пример #3
0
    def explain_contradiction_some_to_all(
        self,
        other: Procedure,
        context: Optional[Union[ContextRegister, Explanation]] = None,
    ) -> Iterator[Explanation]:
        """Explain why ``other`` can't apply in all cases if ``self`` applies in some."""
        if not isinstance(context, Explanation):
            context = Explanation.from_context(context)

        # For self to contradict other, either every input of other
        # must imply some input or despite factor of self...
        implied_contexts = self._has_input_or_despite_factors_implied_by_all_inputs_of(
            other, context)

        # or every input of other must be implied by
        # some input or despite factor of self.
        implying_contexts = self._has_input_or_despite_factors_implying_all_inputs_of(
            other, context)

        # For self to contradict other, some output of other
        # must be contradicted by some output of self.
        seen_contexts = []
        for m in chain(implying_contexts, implied_contexts):
            if m.context not in seen_contexts:
                seen_contexts.append(m.context)
                yield from self.outputs_group._explanations_contradiction(
                    other.outputs_group, m)
Пример #4
0
 def explanations_implication(
     self,
     other: Comparable,
     context: Optional[Union[ContextRegister, Explanation]] = None,
 ) -> Iterator[ContextRegister]:
     """Yield contexts that would cause self and other to have same meaning."""
     if not self.comparable_with(other):
         raise TypeError(f"Type Holding cannot be compared with type {type(other)}.")
     if not isinstance(context, Explanation):
         context = Explanation.from_context(context)
     if isinstance(other, Procedure):
         other = Rule(procedure=other)
     if isinstance(other, Rule):
         other = Holding(rule=other)
     if not isinstance(other, self.__class__):
         if context:
             context = context.reversed_context()
         if other.implied_by(self, context=context):
             yield context
     elif self.exclusive is other.exclusive is False:
         yield from self._explanations_implies_if_not_exclusive(
             other, context=context
         )
     else:
         yield from self.nonexclusive_holdings.explanations_implication(
             other.nonexclusive_holdings, context=context
         )
Пример #5
0
 def test_contradicts_if_present_both_present(self, make_statement):
     """
     Test a helper function that checks whether there would
     be a contradiction if neither Factor was "absent".
     """
     assert make_statement["crime"]._contradicts_if_present(
         make_statement["absent_no_crime"],
         explanation=Explanation.from_context())
Пример #6
0
 def test_wrong_type_in_input_list(self, make_statement):
     explanation = Explanation(reasons=[])
     with pytest.raises(TypeError):
         ContextRegister.from_lists(
             to_replace=[Entity(name="Al"), explanation],
             replacements=[Entity(name="Bo"),
                           Entity(name="Cid")],
         )
Пример #7
0
    def verbose_comparison(
        self,
        operation: Callable,
        still_need_matches: Sequence[Factor],
        explanation: Explanation,
    ) -> Iterator[Explanation]:
        r"""
        Find one way for two unordered sets of :class:`.Factor`\s to satisfy a comparison.

        All of the elements of `other` need to fit the comparison. The elements of
        `self` don't all need to be used.

        Only returns one answer, to prevent expensive fruitless searching.

        :param context:
            a mapping of :class:`.Factor`\s that have already been matched
            to each other in the recursive search for a complete group of
            matches. Usually starts empty when the method is first called.

        :param still_need_matches:
            :class:`.Factor`\s that need to satisfy the comparison
            :attr:`comparison` with some :class:`.Factor` of :attr:`available`
            for the relation to hold, and have not yet been matched.

        :param explanation:
            an :class:`.Explanation` showing which :class:`.Factor`\s listed in the
            FactorGroups were matched to each other, and also including a
            :class:`.ContextRegister`\.

        :yields:
            context registers showing how each :class:`.Factor` in
            ``need_matches`` can have the relation ``comparison``
            with some :class:`.Factor` in ``available_for_matching``,
            with matching context.
        """
        still_need_matches = list(still_need_matches)

        if not still_need_matches:
            yield explanation
        else:
            other_holding = still_need_matches.pop()
            for self_holding in self:
                if operation(self_holding, other_holding):
                    new_explanation = explanation.with_match(
                        FactorMatch(
                            left=self_holding,
                            operation=explanation.operation,
                            right=other_holding,
                        )
                    )
                    next_step = iter(
                        self.verbose_comparison(
                            still_need_matches=still_need_matches,
                            operation=operation,
                            explanation=new_explanation,
                        )
                    )
                    yield next(next_step)
Пример #8
0
 def explanations_implication(
     self,
     other: Comparable,
     context: Optional[Union[ContextRegister, Explanation]] = None,
 ) -> Iterator[Explanation]:
     """Find contexts that would cause ``self`` to imply ``other``."""
     context = Explanation.from_context(context=context,
                                        current=self,
                                        incoming=other)
     yield from self._explanations_implication(other, context)
Пример #9
0
 def explanations_implied_by(
     self,
     other: Comparable,
     context: Optional[Union[ContextRegister, Explanation]] = None,
 ) -> Iterator[Explanation]:
     """Generate explanations for how other may imply self."""
     context = context = Explanation.from_context(context=context,
                                                  current=self,
                                                  incoming=other)
     yield from self._explanations_implied_by(other=other,
                                              explanation=context)
Пример #10
0
    def explanations_contradiction(
        self,
        other,
        context: Optional[Union[ContextRegister, Explanation]] = None
    ) -> Iterator[ContextRegister]:
        """Find context matches that would result in a contradiction with other."""
        if not isinstance(context, Explanation):
            context = Explanation.from_context(context)

        yield from self._explanations_contradiction(other=other,
                                                    context=context)
Пример #11
0
 def explain_implication_all_to_some(
     self,
     other: Factor,
     context: Optional[Union[ContextRegister, Explanation]] = None,
 ) -> Iterator[Explanation]:
     """Yield contexts establishing that if self is always valid, other is sometimes valid."""
     if not isinstance(context, Explanation):
         context = Explanation.from_context(context)
     if isinstance(other, self.__class__):
         yield from self._explain_implication_of_procedure_all_to_some(
             other=other, context=context)
Пример #12
0
 def _explanations_implied_by(
     self,
     other: Comparable,
     explanation: Explanation,
 ) -> Iterator[Explanation]:
     """Generate explanations for how other may imply self."""
     reversed = explanation.reversed_context()
     if isinstance(other, Factor):
         other = FactorGroup(other)
     if isinstance(other, FactorGroup):
         yield from other._explanations_implication(self,
                                                    explanation=reversed)
Пример #13
0
 def implied_by_holding(
     self, other: Holding, context: Optional[ContextRegister] = None
 ) -> Iterator[Explanation]:
     """Check if a Holding implies all the Holdings of self."""
     if all(
         other.implies(self_holding, context=context)
         for self_holding in self.holdings
     ):
         yield Explanation(
             reasons=[(other, self_holding) for self_holding in self.holdings],
             operation=operator.ge,
         )
Пример #14
0
    def explanations_same_meaning(
        self,
        other: Comparable,
        context: Optional[Union[ContextRegister, Explanation]] = None,
    ) -> Iterator[Explanation]:
        """Yield contexts that could cause self to have the same meaning as other."""

        if not isinstance(context, Explanation):
            context = Explanation.from_context(context)

        if isinstance(other, self.__class__):
            yield from self._explanations_same_meaning_as_procedure(
                other, context)
Пример #15
0
 def implied_by(
     self,
     other: Union[OpinionReading, Holding, Rule],
     context: Optional[Union[ContextRegister, Explanation]] = None,
 ) -> bool:
     """Determine if other implies all the Holdings of self."""
     if not isinstance(context, Explanation):
         context = Explanation.from_context(context)
     if isinstance(other, Holding):
         return self._implied_by_holding(other, context=context)
     elif isinstance(other, Rule):
         return self._implied_by_rule(other, context=context)
     return other.implies(self, context=context.reversed_context())
Пример #16
0
 def union(
     self,
     other: Comparable,
     context: Optional[Union[ContextRegister, Explanation]] = None,
 ) -> Optional[Comparable]:
     """Get a procedure with all the inputs and outputs of self and other."""
     if not isinstance(context, Explanation):
         context = Explanation.from_context(context)
     explanations = self.explanations_union(other, context)
     try:
         explanation = next(explanations)
     except StopIteration:
         return None
     return self._union_from_explanation(other, explanation)
Пример #17
0
    def _explanations_contradiction(
            self, other, context: Explanation) -> Iterator[Explanation]:
        self_to_other = self.procedure.explain_contradiction_some_to_all(
            other.procedure, context)
        other_to_self = (
            register.reversed_context()
            for register in other.procedure.explain_contradiction_some_to_all(
                self.procedure, context.reversed_context()))

        if other.universal:
            yield from self_to_other

        if self.universal:
            yield from other_to_self
Пример #18
0
 def explanations_union(
     self,
     other: Procedure,
     context: Optional[Union[ContextRegister, Explanation]] = None,
 ) -> Iterator[ContextRegister]:
     """Yield explanations of contexts that allow combining these Procedures."""
     if not isinstance(context, Explanation):
         context = Explanation.from_context(context)
     for partial in self._explanations_union_partial(
             other, context.context):
         for guess in self.possible_contexts(other, partial):
             answer = self._union_from_explanation(other, guess)
             if answer:
                 yield guess
Пример #19
0
 def _contexts_has_all_factors_of(
     self,
     other: FactorGroup,
     context: Optional[ContextRegister] = None,
 ) -> Iterator[Explanation]:
     """Find contexts that would cause all of ``other``'s Factors to be in ``self``."""
     explanation = Explanation(
         reasons=[],
         context=context or ContextRegister(),
         operation=means,
     )
     yield from self._verbose_comparison(
         still_need_matches=list(other.sequence),
         explanation=explanation,
     )
Пример #20
0
    def _contradicts_if_present(
            self, other: Comparable,
            explanation: Explanation) -> Iterator[Explanation]:
        """
        Test if ``self`` contradicts :class:`Fact` ``other`` if neither is ``absent``.

        :returns:
            whether ``self`` and ``other`` can't both be true at
            the same time under the given assumption.
        """
        if isinstance(other, self.__class__) and self.predicate.contradicts(
                other.predicate):
            for context in self._context_registers(other, operator.ge,
                                                   explanation.context):
                yield explanation.with_context(context)
Пример #21
0
    def _explanations_contradiction(
        self,
        other: Comparable,
        explanation: Explanation,
    ) -> Iterator[Explanation]:
        """Find contexts that would cause ``self`` to contradict ``other``."""

        explanation.operation = contradicts

        if isinstance(other, FactorGroup):
            for other_factor in other:
                yield from self._explain_contradicts_factor(
                    other_factor, explanation=explanation)
        else:
            yield from self._explain_contradicts_factor(
                other, explanation=explanation)
Пример #22
0
 def explanations_same_meaning(
     self,
     other: Comparable,
     context: Optional[Union[ContextRegister, Explanation]] = None,
 ) -> Iterator[Explanation]:
     """Yield explanations for how ``self`` can have the same meaning as ``other``."""
     context = context = Explanation.from_context(context=context,
                                                  current=self,
                                                  incoming=other)
     context.operation = means
     to_match = self.from_comparable(other)
     if to_match is not None:
         for new_context in self._contexts_shares_all_factors_with(
                 to_match, context.context):
             yield from self._verbose_comparison(
                 still_need_matches=list(to_match.sequence),
                 explanation=context.with_context(new_context),
             )
Пример #23
0
    def _verbose_comparison(
        self,
        still_need_matches: List[Factor],
        explanation: Explanation,
    ) -> Iterator[Explanation]:
        r"""
        Find ways for two unordered sets of :class:`.Factor`\s to satisfy a comparison.

        All of the elements of `other` need to fit the comparison. The elements of
        `self` don't all need to be used.

        :param context:
            a mapping of :class:`.Factor`\s that have already been matched
            to each other in the recursive search for a complete group of
            matches. Usually starts empty when the method is first called.

        :param still_need_matches:
            :class:`.Factor`\s that need to satisfy the comparison
            :attr:`comparison` with some :class:`.Factor` of :attr:`available`
            for the relation to hold, and have not yet been matched.

        :param explanation:
            an :class:`.Explanation` showing which :class:`.Factor`\s listed in the
            FactorGroups were matched to each other, and also including a
            :class:`.ContextRegister`\.

        :yields:
            context registers showing how each :class:`.Factor` in
            ``need_matches`` can have the relation ``comparison``
            with some :class:`.Factor` in ``available_for_matching``,
            with matching context.
        """
        if not still_need_matches:
            yield explanation
        else:
            other_factor = still_need_matches.pop()
            for self_factor in self:
                for new_explanation in explanation.operate(
                        self_factor, other_factor):
                    yield from iter(
                        self._verbose_comparison(
                            still_need_matches=deepcopy(still_need_matches),
                            explanation=new_explanation,
                        ))
Пример #24
0
    def _contexts_shares_all_factors_with(
        self,
        other: FactorGroup,
        context: Optional[ContextRegister] = None
    ) -> Iterator[ContextRegister]:
        """Find context that would cause all of ``self``'s Factors to be in ``other``."""
        context = context or ContextRegister()
        context_for_other = context.reversed()

        blank = Explanation(
            reasons=[],
            context=context_for_other,
            operation=means,
        )
        yield from (explanation.context.reversed()
                    for explanation in other._verbose_comparison(
                        still_need_matches=list(self),
                        explanation=blank,
                    ))
Пример #25
0
    def _explanations_implication(
        self,
        other: Comparable,
        explanation: Explanation,
    ) -> Iterator[Explanation]:
        """Find contexts that would cause ``self`` to imply ``other``."""

        explanation.operation = operator.ge

        if isinstance(other, FactorGroup):
            yield from self._verbose_comparison(
                still_need_matches=list(other.sequence),
                explanation=explanation,
            )
        elif isinstance(other, Factor):
            yield from self._verbose_comparison(
                still_need_matches=[other],
                explanation=explanation,
            )
Пример #26
0
    def explanations_contradiction(
        self, other: Factor, context: ContextRegister = None
    ) -> Iterator[Explanation]:
        r"""
        Find context matches that would result in a contradiction with other.

        Works by testing whether ``self`` would imply ``other`` if
        ``other`` had an opposite value for ``rule_valid``.

        This method takes three main paths depending on
        whether the holdings ``self`` and ``other`` assert that
        rules are decided or undecided.

        A ``decided`` :class:`Rule` can never contradict
        a previous statement that any :class:`Rule` was undecided.

        If rule A implies rule B, then a holding that B is undecided
        contradicts a prior :class:`Rule` deciding that
        rule A is valid or invalid.

        :param other:
            The :class:`.Factor` to be compared to self. Unlike with
            :meth:`~Holding.contradicts`\, this method cannot be called
            with an :class:`.Opinion` for `other`.

        :returns:
            a generator yielding :class:`.ContextRegister`\s that cause a
            contradiction.
        """
        if not self.comparable_with(other):
            raise TypeError(f"Type Holding cannot be compared with type {type(other)}.")
        if not isinstance(context, Explanation):
            context = Explanation.from_context(context)
        if isinstance(other, Procedure):
            other = Rule(procedure=other)
        if isinstance(other, Rule):
            other = Holding(rule=other)
        if isinstance(other, self.__class__):
            yield from self._explanations_contradiction_of_holding(other, context)
        else:
            yield from other.explanations_contradiction(self)
Пример #27
0
 def explanations_implication(
     self,
     other: Comparable,
     context: Optional[Union[ContextRegister, Explanation]] = None,
 ) -> Iterator[Explanation]:
     """Generate contexts in which all Holdings in other are implied by self."""
     if isinstance(other, Rule):
         other = Holding(rule=other)
     explanation = Explanation.from_context(
         context=context, current=self, incoming=other
     )
     if isinstance(other, Holding):
         yield from self._explanations_implication_of_holding(
             other=other, context=context
         )
     elif isinstance(other, self.__class__):
         yield from self.verbose_comparison(
             operation=operator.ge,
             still_need_matches=list(other),
             explanation=explanation,
         )
Пример #28
0
    def explanations_contradiction(
        self,
        other: Comparable,
        context: Optional[Union[ContextMemo, Explanation]] = None,
    ) -> Iterator[Explanation]:
        """
        Find contexts that would cause ``self`` to contradict ``other``.

        In this example, by adding a context parameter to this method's comparison
        of two FactorGroups for contradiction, we can narrow down how nettlesome
        discovers analogies between the Entity objects. The result is that
        nettlesome finds only two Explanations for how a contradiction can exist.

            >>> from nettlesome import Statement, Entity
            >>> nafta = FactorGroup([
            ... Statement(predicate="$country1 signed a treaty with $country2",
            ...     terms=[Entity(name="Mexico"), Entity(name="USA")]),
            ... Statement(predicate="$country2 signed a treaty with $country3",
            ...     terms=[Entity(name="USA"), Entity(name="Canada")]),
            ... Statement(predicate="$country3 signed a treaty with $country1",
            ...    terms=[Entity(name="USA"), Entity(name="Canada")])])
            >>> brexit = FactorGroup([
            ... Statement(predicate="$country1 signed a treaty with $country2",
            ...     terms=[Entity(name="UK"), Entity(name="European Union")]),
            ... Statement(predicate="$country2 signed a treaty with $country3",
            ...     terms=[Entity(name="European Union"), Entity(name="Germany")]),
            ... Statement(predicate="$country3 signed a treaty with $country1",
            ...     terms=[Entity(name="Germany"), Entity(name="UK")], truth=False)])
            >>> explanations_usa_like_uk = nafta.explanations_contradiction(
            ...     brexit,
            ...     context=([Entity(name="USA")], [Entity(name="UK")]))
            >>> len(list(explanations_usa_like_uk))
            2
        """

        context = Explanation.from_context(context=context,
                                           current=self,
                                           incoming=other)
        yield from self._explanations_contradiction(other=other,
                                                    explanation=context)
Пример #29
0
 def test_contradicts_if_present_one_absent(self, make_statement):
     assert make_statement["crime"]._contradicts_if_present(
         make_statement["no_crime"], explanation=Explanation.from_context())
Пример #30
0
 def _implied_by_holding(self, other: Holding,
                         context: Explanation) -> bool:
     return all(
         other.implies(self_holding, context=context.reversed_context())
         for self_holding in self.holdings)