
    gZL                         d Z ddlmZ ddlmZ ddlmZmZmZm	Z	 d Z
d Zd Zd	 Zd
 Zd Z G d de      Z G d d      Z G d d      Z G d de      Z G d de      Zy)a>  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 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
    )defaultdict)Iterator   )LogicAndOrNotc                 >    t        | t              r| j                  S | S )zdReturn the literal fact of an atom.

    Effectively, this merely strips the Not around a fact.
    
isinstancer	   argatoms    E/var/www/openai/venv/lib/python3.12/site-packages/sympy/core/facts.py
_base_factr   7   s    
 $xx    c                 F    t        | t              r| j                  dfS | dfS )NFTr   r   s    r   _as_pairr   B   s%    $%  d|r   c                     t        |       } t               j                  t        t         |       }|D ]1  }|D ]*  }||f|v s
|D ]  }||f|v s
|j                  ||f        , 3 |S )z
    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.
    )setunionmapadd)implicationsfull_implicationsliteralskijs         r   transitive_closurer    K   s{     L)su{{C%678HA1v**!A1v!22)--q!f5 "   r   c           	      ~   | | D cg c]  \  }}t        |      t        |      f c}}z   } t        t              }t        |       }|D ]  \  }}||k(  r||   j	                  |       ! |j                         D ]9  \  }}|j                  |       t        |      }||v s't        d|d|d|       |S c c}}w )a:  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, ...])
    zimplications are inconsistent: z ->  )r	   r   r   r    r   itemsdiscard
ValueError)	r   r   r   resr   abimplnas	            r   deduce_alpha_implicationsr+   _   s    (  ,"O,ACFCF#3,"OOL
c
C*<8!16A

1	 " 99;4QV:@A2tLN N	  J# #Ps    B9c                 
   i }| j                         D ]  }t        | |         g f||<    |D ]*  \  }|j                  D ]  }||v rt               g f||<    , d}|rd}|D ]  \  }t        |t              st        d      t        |j                        |j                         D ]S  \  }\  }}||hz  }	|	vsj                  |	      s&|j                         |j                        }
|
||
d   z  }d}U  |rt        |      D ]p  \  }\  }t        |j                        |j                         D ]@  \  }\  }}||hz  }	|	v rt        fd|	D              r*|	z  s0|j                  |       B r |S )a  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]
    TFzCond is not Andr   c              3   X   K   | ]!  }t        |      v xs t        |      k(   # y wN)r	   ).0xibargsbimpls     r   	<genexpr>z,apply_beta_to_alpha_route.<locals>.<genexpr>   s,     H%B3r7e#7s2w%'77%s   '*)keysr   argsr   r   	TypeErrorr#   issubsetr   get	enumerateanyappend)alpha_implications
beta_rulesx_implxbcondbkseen_static_extensionximplsbbx_all
bimpl_implbidxr1   r2   s               @@r   apply_beta_to_alpha_routerH      s   8 F$$&+A./4q	 '"u**BV|%F2J  # !
 %&LE5eS) 122

OE#)<<><FB!%%..*?JJu% "(E!2J!-*Q-/,0) $2	 '  * !** 5nueEJJ%||~OA|aSLE~ H%HHu}		$  . !6 Mr   c                    t        t              }| j                         D ]d  \  \  }}}t        |t              r|j
                  d   }|D ]8  \  }}t        |t              r|j
                  d   }||   j                  |       : f |S )aM  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=?
    r   )r   r   r#   r   r	   r5   r   )rulesprereqr'   _r)   r   s         r   rules_2prereqrM      sy    . FAaq	AFQ!S!FF1I1IMM!  & Mr   c                       e Zd ZdZy)TautologyDetectedz:(internal) Prover uses it for reporting detected tautologyN)__name__
__module____qualname____doc__ r   r   rO   rO      s    Dr   rO   c                   H    e Zd ZdZd Zd Zed        Zed        Zd Z	d Z
y)	ProveraS  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                 0    g | _         t               | _        y r.   )proved_rulesr   _rules_seenselfs    r   __init__zProver.__init__  s    5r   c                     g }g }| j                   D ]<  \  }}t        |t              r|j                  ||f       *|j                  ||f       > ||fS )z-split proved rules into alpha and beta chains)rX   r   r   r;   )r[   rules_alpha
rules_betar'   r(   s        r   split_alpha_betazProver.split_alpha_beta"  s[    
%%DAq!S!!!1a&)""Aq6*	 &
 J&&r   c                 (    | j                         d   S )Nr   r`   rZ   s    r   r^   zProver.rules_alpha-      $$&q))r   c                 (    | j                         d   S )Nr   rb   rZ   s    r   r_   zProver.rules_beta1  rc   r   c                     |rt        |t              ryt        |t              ry||f| j                  v ry| j                  j                  ||f       	 | j	                  ||       y# t
        $ r Y yw xY w)zprocess a -> b ruleN)r   boolrY   r   _process_rulerO   )r[   r'   r(   s      r   process_rulezProver.process_rule5  sr    jD)aq6T%%%  !Q(	q!$  		s   A' '	A32A3c           
      \   t        |t              r5t        |j                  t              }|D ]  }| j                  ||        y t        |t              rt        |j                  t              }t        |t              s||v rt        ||d      | j                  t        |j                  D cg c]  }t        |       c} t        |             t        t        |            D ]@  }||   }|d | ||dz   d  z   }| j                  t        |t        |            t        |        B y t        |t              rJt        |j                  t              }||v rt        ||d      | j                  j                  ||f       y t        |t              rFt        |j                  t              }||v rt        ||d      |D ]  }| j                  ||        y | j                  j                  ||f       | j                  j                  t        |      t        |      f       y c c}w )N)keyza -> a|c|...r   z
a & b -> az
a | b -> a)r   r   sortedr5   strrh   r   r   rO   r	   rangelenrX   r;   )	r[   r'   r(   sorted_bargsbargrG   brestsorted_aargsaargs	            r   rg   zProver._process_ruleF  s    a!!&&c2L$!!!T* % 2!!&&c2La'$+Aq.AAc!&&#A&$CI&#ABCFKc,/0#D)$Ud+l4!89.EE!!#aT"3RZ@ 1 3!!&&c2LL '1l;;$$aV, 2!!&&c2LL '1l;;$!!$* %
 $$aV,$$c!fc!f%569 $Bs   0H)N)rP   rQ   rR   rS   r\   r`   propertyr^   r_   rh   rg   rT   r   r   rV   rV     sC    8!	' * * * *"17r   rV   c                   b    e Zd ZdZd ZdefdZedefd       Z	d Z
d Zd	 Zd
 Zdee   fdZy)	FactRulesa  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           	         t        |t              r|j                         }t               }|D ]  }|j	                  dd      \  }}}t        j                  |      }t        j                  |      }|dk(  r|j                  ||       [|dk(  r%|j                  ||       |j                  ||       t        d|z         g | _	        |j                  D ]L  \  }}| j                  j                  |j                  D ch c]  }t        |       c}t        |      f       N t        |j                        }	t!        |	|j                        }
|
j#                         D ch c]  }t%        |       c}| _        t)        t*              }t)        t*              }|
j-                         D ];  \  }\  }}|D ch c]  }t        |       c}|t        |      <   ||t        |      <   = || _        || _        t)        t*              }t3        |      }|j-                         D ]  \  }}||xx   |z  cc<    || _        yc c}w c c}w c c}w )z)Compile rules into internal lookup tablesN   z->z==zunknown op %r)r   rl   
splitlinesrV   splitr   
fromstringrh   r%   r=   r_   r;   r5   r   r+   r^   rH   r4   r   defined_factsr   r   r#   r   beta_triggersrM   rK   )r[   rJ   Pruler'   opr(   r@   r2   impl_aimpl_abr   r   r}   r)   betaidxsr   rK   
rel_prereqpitemss                       r   r\   zFactRules.__init__  s
    eS!$$&E HDzz$*HAr1  #A  #ATzq!$tq!$q!$ 2!566   LLLE5OO""',zz2z!(1+z2HUODF )
 +1==9 ,FALLA 6=\\^D^jm^D (,#C(#*==?AhCG-H4ahqk4-Hhqk*)1M(1+& $3 "3* S!"#45
#))+IAv1II ,; 3 E .Is   =H;
 I 5Ireturnc                 @    dj                  | j                               S )zD Generate a string with plain python representation of the instance 
)joinprint_rulesrZ   s    r   
_to_pythonzFactRules._to_python  s    yy))+,,r   datac                      | d      }dD ]2  }t        t              }|j                  ||          t        |||       4 |d   |_        t        |d         |_        |S )z; Generate an instance from the plain python representation  )r   r}   rK   r=   r|   )r   r   updatesetattrr=   r|   )clsr   r[   rj   ds        r   _from_pythonzFactRules._from_python  sc     2wCC#AHHT#YD#q! D |, o!67r   c              #   `   K   d t        | j                        D ]
  }d|d  d y w)Nzdefined_facts = [    ,z] # defined_facts)rk   r|   )r[   facts     r   _defined_facts_lineszFactRules._defined_facts_lines  s6     !!4--.D"" /!!s   ,.c              #      K   d t        | j                        D ]P  }dD ]I  }d| d| d d|d|d | j                  ||f   }t        |      D ]
  }d	|d
  d d K R d y w)Nzfull_implications = dict( [)TFz    # Implications of  = :z    ((, z	), set( (        r   z       ) ),z     ),z ] ) # full_implications)rk   r|   r   )r[   r   valuer   implieds        r   _full_implications_linesz"FactRules._full_implications_lines  s     ++4--.D&.tfCwa@@thb	;;#55tUmD%l3G$WKq11  4## ' / )(s   A2A4c              #      K   d d t        | j                        D ]>  }d|  d|d t        | j                  |         D ]
  }d|d  d d @ d	 y w)
Nz
prereq = {r   z.    # facts that could determine the value of r   z: {r   r   z    },z
} # prereq)rk   rK   )r[   r   pfacts      r   _prereq_lineszFactRules._prereq_lines  sy     4;;'DB4&II%%D 12 	++ 3NH ( s   A$A&c           
   #   N  K   t        t              }t        | j                        D ]  \  }\  }}||   j	                  ||f         d d d d}i }t        |      D ]d  }|\  }}d| d|  ||   D ]G  \  }}|||<   |dz  }dj                  t        t        t        |                  }	d	|	 d
 d|d I d f d d t        | j                        D ]1  }
|
\  }}| j                  |
   D cg c]  }||   	 }}d|
d|d 3 d y c c}w w)Nz@# Note: the order of the beta rules is used in the beta_triggerszbeta_rules = [r   r   z    # Rules implying r   r   r   z    ({z},r   z),z] # beta_ruleszbeta_triggers = {r   z: r   z} # beta_triggers)
r   listr9   r=   r;   rk   r   r   rl   r}   )r[   reverse_implicationsnprer   mindicesr   r   setstrquerytriggerss               r   _beta_rules_lineszFactRules._beta_rules_lines  se    *40!*4??!;A~W )00#q: "< QP23G!KD%)$s5'::.w7Q
Q3sF3K#89xs++ 2.. 8 H 4 !!D../EKD%,0,>,>u,EF,Eq
,EHF	H<q11 0 "! Gs   C>D% D D%c              #   ,  K   | j                         E d{    d d | j                         E d{    d d | j                         E d{    d d | j                         E d{    d d d d y7 u7 W7 97 w)zA Returns a generator with lines to represent the facts and rules Nr   z`generated_assumptions = {'defined_facts': defined_facts, 'full_implications': full_implications,zZ               'prereq': prereq, 'beta_rules': beta_rules, 'beta_triggers': beta_triggers})r   r   r   r   rZ   s    r   r   zFactRules.print_rules#  s     ,,...00222%%'''))+++ppjj 	/ 	3 	( 	,sC   BBBBBBB6B7BBBBN)rP   rQ   rR   rS   r\   rl   r   classmethoddictr   r   r   r   r   r   r   rT   r   r   rv   rv   |  sZ    <9v-C - 
 
 
")
":kXc] kr   rv   c                       e Zd Zd Zy)InconsistentAssumptionsc                 6    | j                   \  }}}|d|d|S )Nr   =)r5   )r[   kbr   r   s       r   __str__zInconsistentAssumptions.__str__6  s    ))D% $..r   N)rP   rQ   rR   r   rT   r   r   r   r   5  s    /r   r   c                   (    e Zd ZdZd Zd Zd Zd Zy)FactKBzT
    A simple propositional knowledge base relying on compiled inference rules.
    c                     ddj                  t        | j                               D cg c]  }d|z  	 c}      z  S c c}w )Nz{
%s}z,
z	%s: %s)r   rk   r#   )r[   r   s     r   r   zFactKB.__str__?  s@    %**%+DJJL%9:%9Z!^%9:< < 	<:s   =
c                     || _         y r.   )rJ   )r[   rJ   s     r   r\   zFactKB.__init__C  s	    
r   c                 L    || v r| |   | |   |k(  ryt        | ||      || |<   y)zxAdd fact k=v to the knowledge base.

        Returns True if the KB has actually been updated, False otherwise.
        FT)r   )r[   r   vs      r   _tellzFactKB._tellF  s=    
 9a,Aw!|-dAq99DGr   c                      j                   j                  } j                   j                  } j                   j                  }t	        |t
              r|j                         }|rt               }|D ]Q  \  }} j                  ||      r||||f   D ]  \  }}	 j                  ||	        |j                  |||f          S g }|D ]0  }
||
   \  }}t         fd|D              s |j                  |       2 |ryy)z
        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.
        Nc              3   J   K   | ]  \  }}j                  |      |u   y wr.   )r8   )r/   r   r   r[   s      r   r3   z*FactKB.deduce_all_facts.<locals>.<genexpr>y  s#     :EDAqtxx{a'Es    #)rJ   r   r}   r=   r   r   r#   r   r   r   allr;   )r[   factsr   r}   r=   beta_maytriggerr   r   rj   r   rG   r@   r2   s   `            r   deduce_all_factszFactKB.deduce_all_factsW  s     !JJ88

00ZZ**
eT"KKME!eO 1zz!Q'19 #4AqD"9JCJJsE* #:  &&}QT':;  E')$/u:E::LL' (! r   N)rP   rQ   rR   rS   r   r\   r   r   rT   r   r   r   r   ;  s    <"#(r   r   N)rS   collectionsr   typingr   logicr   r   r   r	   r   r   r    r+   rH   rM   	ExceptionrO   rV   rv   r%   r   r   r   rT   r   r   <module>r      s{   .` $  & &(%PL^L		 	
v7 v7vvk vkr/j /?(T ?(r   