ó
~9­\c           @  s  d  Z  d d l m Z m Z d d l m Z d d l m Z m Z m	 Z	 m
 Z
 d d l m Z m Z d „  Z d „  Z d	 „  Z d
 „  Z d „  Z d „  Z d e f d „  ƒ  YZ d e f d „  ƒ  YZ d e f d „  ƒ  YZ d e f d „  ƒ  YZ d e f d „  ƒ  YZ d S(   sD  This is rule-based deduction system for SymPy

The whole thing is split into two parts

 - rules compilation and preparation of tables
 - runtime inference

For rule-based inference engines, the classical work is RETE algorithm [1],
[2] Although we are not implementing it in full (or even significantly)
it's still still worth a read to understand the underlying ideas.

In short, every rule in a system of rules is one of two forms:

 - atom                     -> ...      (alpha rule)
 - And(atom1, atom2, ...)   -> ...      (beta rule)


The major complexity is in efficient beta-rules processing and usually for an
expert system a lot of effort goes into code that operates on beta-rules.


Here we take minimalistic approach to get something usable first.

 - (preparation)    of alpha- and beta- networks, everything except
 - (runtime)        FactRules.deduce_all_facts

             _____________________________________
            ( Kirr: I've never thought that doing )
            ( logic stuff is that difficult...    )
             -------------------------------------
                    o   ^__^
                     o  (oo)\_______
                        (__)\       )\/\
                            ||----w |
                            ||     ||


Some references on the topic
----------------------------

[1] https://en.wikipedia.org/wiki/Rete_algorithm
[2] http://reports-archive.adm.cs.cmu.edu/anon/1995/CMU-CS-95-113.pdf

https://en.wikipedia.org/wiki/Propositional_formula
https://en.wikipedia.org/wiki/Inference_rule
https://en.wikipedia.org/wiki/List_of_rules_of_inference
iÿÿÿÿ(   t   print_functiont   division(   t   defaultdicti   (   t   Logict   Andt   Ort   Not(   t   string_typest   rangec         C  s   t  |  t ƒ r |  j S|  Sd S(   sd   Return the literal fact of an atom.

    Effectively, this merely strips the Not around a fact.
    N(   t
   isinstanceR   t   arg(   t   atom(    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt
   _base_fact:   s    c         C  s*   t  |  t ƒ r |  j t f S|  t f Sd  S(   N(   R	   R   R
   t   Falset   True(   R   (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt   _as_pairE   s    c         C  s›   t  |  ƒ } t  ƒ  j t t  | ƒ Œ  } xm | D]e } x\ | D]T } | | f | k r; x9 | D]. } | | f | k rZ | j | | f ƒ qZ qZ Wq; q; Wq. W| S(   s½   
    Computes the transitive closure of a list of implications

    Uses Warshall's algorithm, as described at
    http://www.cs.hope.edu/~cusack/Notes/Notes/DiscreteMath/Warshall.pdf.
    (   t   sett   uniont   mapt   add(   t   implicationst   full_implicationst   literalst   kt   it   j(    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt   transitive_closureN   s    %c   	      C  sé   |  g  |  D]$ \ } } t  | ƒ t  | ƒ f ^ q
 }  t t ƒ } t |  ƒ } x7 | D]/ \ } } | | k rr qT n  | | j | ƒ qT Wx[ | j ƒ  D]M \ } } | j | ƒ t  | ƒ } | | k r” t d | | | f ƒ ‚ q” q” W| S(   s:  deduce all implications

       Description by example
       ----------------------

       given set of logic rules:

         a -> b
         b -> c

       we deduce all possible rules:

         a -> b, c
         b -> c


       implications: [] of (a,b)
       return:       {} of a -> set([b, c, ...])
    s*   implications are inconsistent: %s -> %s %s(   R   R   R   R   R   t   itemst   discardt
   ValueError(	   R   R   R   t   resR   t   at   bt   implt   na(    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt   deduce_alpha_implicationsb   s    5c           s(  i  } x. |  j  ƒ  D]  } t |  | ƒ g  f | | <q WxM | D]E \ } ‰ x6 | j D]+ } | | k rl qT n  t ƒ  g  f | | <qT Wq> Wt } xá | rpt } xÎ | D]Æ \ } ‰ t | t ƒ sÍ t d ƒ ‚ n  t | j ƒ ‰  xŠ | j ƒ  D]| \ } \ } } | | h B}	 ˆ |	 k ré ˆ  j	 |	 ƒ ré | j
 ˆ ƒ | j ˆ ƒ }
 |
 d k	 r\| |
 d O} n  t } qé qé Wq£ Wq Wx° t | ƒ D]¢ \ } \ } ‰ t | j ƒ ‰  x~ | j ƒ  D]p \ } \ } } | | h B}	 ˆ |	 k rÝq¬n  t ‡  ‡ f d †  |	 Dƒ ƒ rq¬n  ˆ  |	 @r¬| j | ƒ q¬q¬Wq~W| S(   s¶  apply additional beta-rules (And conditions) to already-built
    alpha implication tables

       TODO: write about

       - static extension of alpha-chains
       - attaching refs to beta-nodes to alpha chains


       e.g.

       alpha_implications:

       a  ->  [b, !c, d]
       b  ->  [d]
       ...


       beta_rules:

       &(b,d) -> e


       then we'll extend a's rule to the following

       a  ->  [b, !c, d, e]
    s   Cond is not Andi    c         3  s3   |  ]) } t  | ƒ ˆ  k p* t  | ƒ ˆ k Vq d  S(   N(   R   (   t   .0t   xi(   t   bargst   bimpl(    s/   lib/python2.7/site-packages/sympy/core/facts.pys	   <genexpr>Ð   s    N(   t   keysR   t   argsR   R   R	   R   t	   TypeErrorR   t   issubsetR   t   gett   Nonet	   enumeratet   anyt   append(   t   alpha_implicationst
   beta_rulest   x_implt   xt   bcondt   bkt   seen_static_extensiont   ximplst   bbt   x_allt
   bimpl_implt   bidx(    (   R&   R'   s/   lib/python2.7/site-packages/sympy/core/facts.pyt   apply_beta_to_alpha_routeŠ   sD    	
c         C  s™   t  t ƒ } x† |  j ƒ  D]x \ \ } } } t | t ƒ rJ | j d } n  xD | D]< \ } } t | t ƒ r| | j d } n  | | j | ƒ qQ Wq W| S(   sM  build prerequisites table from rules

       Description by example
       ----------------------

       given set of logic rules:

         a -> b, c
         b -> c

       we build prerequisites (from what points something can be deduced):

         b <- a
         c <- a, b

       rules:   {} of a -> [b, c, ...]
       return:  {} of c <- [a, b, ...]

       Note however, that this prerequisites may be *not* enough to prove a
       fact. An example is 'a -> b' rule, where prereq(a) is b, and prereq(b)
       is a. That's because a=T -> b=T, and b=F -> a=F, but a=F -> b=?
    i    (   R   R   R   R	   R   R)   R   (   t   rulest   prereqR   t   _R!   R   (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt   rules_2prereqÙ   s    t   TautologyDetectedc           B  s   e  Z d  Z RS(   s:   (internal) Prover uses it for reporting detected tautology(   t   __name__t
   __module__t   __doc__(    (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRB   ÿ   s   t   Proverc           B  sP   e  Z d  Z d „  Z d „  Z e d „  ƒ Z e d „  ƒ Z d „  Z d „  Z	 RS(   sS  ai - prover of logic rules

       given a set of initial rules, Prover tries to prove all possible rules
       which follow from given premises.

       As a result proved_rules are always either in one of two forms: alpha or
       beta:

       Alpha rules
       -----------

       This are rules of the form::

         a -> b & c & d & ...


       Beta rules
       ----------

       This are rules of the form::

         &(a,b,...) -> c & d & ...


       i.e. beta rules are join conditions that say that something follows when
       *several* facts are true at the same time.
    c         C  s   g  |  _  t ƒ  |  _ d  S(   N(   t   proved_rulesR   t   _rules_seen(   t   self(    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt   __init__!  s    	c         C  sh   g  } g  } xO |  j  D]D \ } } t | t ƒ rG | j | | f ƒ q | j | | f ƒ q W| | f S(   s-   split proved rules into alpha and beta chains(   RG   R	   R   R0   (   RI   t   rules_alphat
   rules_betaR   R    (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt   split_alpha_beta%  s    c         C  s   |  j  ƒ  d S(   Ni    (   RM   (   RI   (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRK   0  s    c         C  s   |  j  ƒ  d S(   Ni   (   RM   (   RI   (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRL   4  s    c         C  sˆ   | s t  | t ƒ r d St  | t ƒ r- d S| | f |  j k rF d S|  j j | | f ƒ y |  j | | ƒ Wn t k
 rƒ n Xd S(   s   process a -> b ruleN(   R	   t   boolRH   R   t   _process_ruleRB   (   RI   R   R    (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt   process_rule8  s    c         C  s  t  | t ƒ r6 xð| j D] } |  j | | ƒ q WnÌt  | t ƒ r$t  | t ƒ s{ | | j k r{ t | | d ƒ ‚ q{ n  |  j t g  | j D] } t | ƒ ^ qŽ Œ  t | ƒ ƒ xLt t	 | j ƒ ƒ D]T } | j | } | j |  | j | d } |  j t | t | ƒ ƒ t | Œ  ƒ qÉ WnÞ t  | t ƒ rp| | j k rWt | | d ƒ ‚ n  |  j
 j | | f ƒ n’ t  | t ƒ rÊ| | j k r£t | | d ƒ ‚ n  x\ | j D] } |  j | | ƒ q­Wn8 |  j
 j | | f ƒ |  j
 j t | ƒ t | ƒ f ƒ d  S(   Ns   a -> a|c|...i   s
   a & b -> as
   a | b -> a(   R	   R   R)   RP   R   R   RB   R   R   t   lenRG   R0   (   RI   R   R    t   bargR<   t   brestt   aarg(    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRO   I  s.    8,(
   RC   RD   RE   RJ   RM   t   propertyRK   RL   RP   RO   (    (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRF     s   			t	   FactRulesc           B  s   e  Z d  Z d „  Z RS(   s•  Rules that describe how to deduce facts in logic space

       When defined, these rules allow implications to quickly be determined
       for a set of facts. For this precomputed deduction tables are used.
       see `deduce_all_facts`   (forward-chaining)

       Also it is possible to gather prerequisites for a fact, which is tried
       to be proven.    (backward-chaining)


       Definition Syntax
       -----------------

       a -> b       -- a=T -> b=T  (and automatically b=F -> a=F)
       a -> !b      -- a=T -> b=F
       a == b       -- a -> b & b -> a
       a -> b & c   -- a=T -> b=T & c=T
       # TODO b | c


       Internals
       ---------

       .full_implications[k, v]: all the implications of fact k=v
       .beta_triggers[k, v]: beta rules that might be triggered when k=v
       .prereq  -- {} k <- [] of k's prerequisites

       .defined_facts -- set of defined fact names
    c         C  s0  t  | t ƒ r | j ƒ  } n  t ƒ  } x¥ | D] } | j d d ƒ \ } } } t j | ƒ } t j | ƒ } | d k rŒ | j | | ƒ q. | d k r» | j | | ƒ | j | | ƒ q. t	 d | ƒ ‚ q. Wg  |  _
 xF | j D]; \ } } |  j
 j t d „  | j Dƒ ƒ t | ƒ f ƒ qâ Wt | j ƒ }	 t |	 | j ƒ }
 t d „  |
 j ƒ  Dƒ ƒ |  _ t t ƒ } t t ƒ } xP |
 j ƒ  D]B \ } \ } } t d „  | Dƒ ƒ | t | ƒ <| | t | ƒ <q†W| |  _ | |  _ t t ƒ } t | ƒ } x* | j ƒ  D] \ } } | | c | O<qW| |  _ d S(	   s)   Compile rules into internal lookup tablesi   s   ->s   ==s   unknown op %rc         s  s   |  ] } t  | ƒ Vq d  S(   N(   R   (   R$   R   (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pys	   <genexpr>µ  s    c         s  s   |  ] } t  | ƒ Vq d  S(   N(   R   (   R$   R   (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pys	   <genexpr>Á  s    c         s  s   |  ] } t  | ƒ Vq d  S(   N(   R   (   R$   R   (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pys	   <genexpr>Ç  s    N(   R	   R   t
   splitlinesRF   t   splitR-   R   t
   fromstringRP   R   R2   RL   R0   R   R)   R   R#   RK   R=   R(   t   defined_factsR   R   R   t   beta_triggersRA   R?   (   RI   R>   t   Pt   ruleR   t   opR    R5   R'   t   impl_at   impl_abR   R[   R   R!   t   betaidxsR?   t
   rel_prereqt   pitems(    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRJ   ™  s@    			* 		(   RC   RD   RE   RJ   (    (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRV   z  s   t   InconsistentAssumptionsc           B  s   e  Z d  „  Z RS(   c         C  s#   |  j  \ } } } d | | | f S(   Ns	   %s, %s=%s(   R)   (   RI   t   kbt   factt   value(    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt   __str__Ö  s    (   RC   RD   Rh   (    (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRd   Õ  s   t   FactKBc           B  s2   e  Z d  Z d „  Z d „  Z d „  Z d „  Z RS(   sT   
    A simple propositional knowledge base relying on compiled inference rules.
    c         C  s4   d d j  g  t |  j ƒ  ƒ D] } d | ^ q ƒ S(   Ns   {
%s}s   ,
s   	%s: %s(   t   joint   sortedR   (   RI   R   (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRh   ß  s    	c         C  s   | |  _  d  S(   N(   R>   (   RI   R>   (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRJ   ã  s    c         C  sW   | |  k rE |  | d k	 rE |  | | k r0 t St |  | | ƒ ‚ n | |  | <t Sd S(   sx   Add fact k=v to the knowledge base.

        Returns True if the KB has actually been updated, False otherwise.
        N(   R-   R   Rd   R   (   RI   R   t   v(    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt   _tellæ  s    
c           s3  ˆ  j  j } ˆ  j  j } ˆ  j  j } t | t ƒ rB | j ƒ  } n  xê | r.t ƒ  } x | D]y \ } } ˆ  j | | ƒ s[ | d k rŒ q[ n  x. | | | f D] \ } }	 ˆ  j | |	 ƒ q W| j
 | | | f ƒ q[ Wg  } xJ | D]B }
 | |
 \ } } t ‡  f d †  | Dƒ ƒ rå | j | ƒ qå qå WqE Wd S(   s©   
        Update the KB with all the implications of a list of facts.

        Facts can be specified as a dictionary or as a list of (key, value)
        pairs.
        c         3  s*   |  ]  \ } } ˆ  j  | ƒ | k Vq d  S(   N(   R,   (   R$   R   Rl   (   RI   (    s/   lib/python2.7/site-packages/sympy/core/facts.pys	   <genexpr>  s    N(   R>   R   R[   R2   R	   t   dictR   R   Rm   R-   t   updatet   allR0   (   RI   t   factsR   R[   R2   t   beta_maytriggerR   Rl   t   keyRg   R<   R5   R'   (    (   RI   s/   lib/python2.7/site-packages/sympy/core/facts.pyt   deduce_all_facts÷  s$    			(   RC   RD   RE   Rh   RJ   Rm   Rt   (    (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyRi   Û  s
   			N(   RE   t
   __future__R    R   t   collectionsR   t   logicR   R   R   R   t   sympy.core.compatibilityR   R   R   R   R   R#   R=   RA   t	   ExceptionRB   t   objectRF   RV   R   Rd   Rn   Ri   (    (    (    s/   lib/python2.7/site-packages/sympy/core/facts.pyt   <module>1   s   "					(	O	&v[