
    \hZL                         S r SSKJr  SSKJr  SSKJrJrJrJ	r	  S r
S rS rS	 rS
 rS r " S S\5      r " S S5      r " S S5      r " S S\5      r " S S\5      rg)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                 H    [        U [        5      (       a  U R                  $ U $ )z\Return the literal fact of an atom.

Effectively, this merely strips the Not around a fact.

isinstancer	   argatoms    H/var/www/auris/envauris/lib/python3.13/site-packages/sympy/core/facts.py
_base_factr   7   s    
 $xx    c                 P    [        U [        5      (       a  U R                  S4$ U S4$ )NFTr   r   s    r   _as_pairr   B   s(    $%  d|r   c                     [        U 5      n[        5       R                  " [        [         U5      6 nU H9  nU H0  nXC4U;   d  M  U H  nX54U;   d  M  UR                  XE45        M      M2     M;     U$ )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   ss     L)u{{C%678HAv**!Av!22)--qf5 "   r   c           	         X  VVs/ s H  u  p[        U5      [        U5      4PM     snn-   n [        [        5      n[        U 5      nU H  u  pVXV:X  a  M  X5   R	                  U5        M!     UR                  5        H=  u  pWUR                  U5        [        U5      nX;   d  M(  [        SU< SU< SU< 35      e   U$ s  snnf )zdeduce 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,CFCF#3,"OOL
c
C*<8!6

1	 " 99;QV:@A2tLN N	  J# #Ps   "C c                 n  ^^ 0 nU R                  5        H  n[        X   5      / 4X#'   M     U H.  u  nmUR                   H  nXR;   a  M
  [        5       / 4X%'   M     M0     SnU(       a  SnU H  u  nm[        U[        5      (       d  [        S5      e[        UR                  5      mUR                  5        H[  u  nu  pxXs1-  n	TU	;  d  M  TR                  U	5      (       d  M-  UR                  T5        UR                  T5      n
U
b  XzS   -  nSnM]     M     U(       a  M  [        U5       H  u  nu  nm[        UR                  5      mUR                  5        HO  u  nu  pxXs1-  n	TU	;   a  M  [        UU4S jU	 5       5      (       a  M2  TU	-  (       d  M>  UR                  U5        MQ     M     U$ )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   j   >#    U  H(  n[        U5      T;   =(       d    [        U5      T:H  v   M*     g 7fN)r	   ).0xibargsbimpls     r   	<genexpr>,apply_beta_to_alpha_route.<locals>.<genexpr>   s+     H%B3r7e#7s2w%'77%s   03)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_routerI      s   8 F$$&+./4	 '"u**B|%FJ  # !
 %&LE5eS)) 122

OE#)<<><F%%..*?*?JJu% "(E!2J!-Q-/,0) $2	 '  
* !** 5nueEJJ%||~OA|SLE~ H%HHHu}}		$  . !6 Mr   c                 (   [        [        5      nU R                  5        Hn  u  u  p#n[        U[        5      (       a  UR
                  S   nU H<  u  pS[        U[        5      (       a  UR
                  S   nX   R                  U5        M>     Mp     U$ )a  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	   r6   r   )rulesprereqr'   _r)   r   s         r   rules_2prereqrN      sy    . Faq	AFQ!S!!FF1IIMM!  & Mr   c                       \ rS rSrSrSrg)TautologyDetected   z:(internal) Prover uses it for reporting detected tautology N)__name__
__module____qualname____firstlineno____doc____static_attributes__rR   r   r   rP   rP      s    Dr   rP   c                   P    \ rS rSrSrS rS r\S 5       r\S 5       r	S r
S rS	rg
)Proveri  a  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    / U l         [        5       U l        g r.   )proved_rulesr   _rules_seenselfs    r   __init__Prover.__init__  s    5r   c                     / n/ nU R                    H@  u  p4[        U[        5      (       a  UR                  X445        M.  UR                  X445        MB     X4$ )z-split proved rules into alpha and beta chains)r\   r   r   r<   )r_   rules_alpha
rules_betar'   r(   s        r   split_alpha_betaProver.split_alpha_beta"  sV    
%%DA!S!!!!1&)""A6*	 &
 &&r   c                 (    U R                  5       S   $ )Nr   re   r^   s    r   rc   Prover.rules_alpha-      $$&q))r   c                 (    U R                  5       S   $ )Nr   rh   r^   s    r   rd   Prover.rules_beta1  rj   r   c                 
   U(       a  [        U[        5      (       a  g[        U[        5      (       a  gX4U R                  ;   a  gU R                  R                  X45         U R	                  X5        g! [
         a     gf = f)zprocess a -> b ruleN)r   boolr]   r   _process_rulerP   )r_   r'   r(   s      r   process_ruleProver.process_rule5  sr    jD))a6T%%%  !(	q$  		s   #A5 5
BBc           
      |   [        U[        5      (       a3  [        UR                  [        S9nU H  nU R                  X5        M     g [        U[        5      (       a  [        UR                  [        S9n[        U[        5      (       d  X;   a  [        XS5      eU R                  [        UR                   Vs/ s H  n[        U5      PM     sn6 [        U5      5        [        [        U5      5       H?  nX5   nUS U X5S-   S  -   nU R                  [        U[        U5      5      [        U6 5        MA     g [        U[        5      (       aF  [        UR                  [        S9nX';   a  [        XS5      eU R                  R                  X45        g [        U[        5      (       aD  [        UR                  [        S9nX';   a  [        XS5      eU H  nU R                  X5        M     g U R                  R                  X45        U R                  R                  [        U5      [        U5      45        g s  snf )N)keyza -> a|c|...r   z
a & b -> az
a | b -> a)r   r   sortedr6   strrp   r   r   rP   r	   rangelenr\   r<   )	r_   r'   r(   sorted_bargsbargrH   brestsorted_aargsaargs	            r   ro   Prover._process_ruleF  s    a!!&&c2L$!!!* % 2!!&&c2La''$+A.AAc!&&#A&$CI&#ABCFKc,/0#)$Ud+l!89.EE!!#aT"3RZ@ 1 3!!&&c2L 'l;;$$aV, 2!!&&c2L 'l;;$!!$* %
 $$aV,$$c!fc!f%569 $Bs   :H9)r]   r\   N)rS   rT   rU   rV   rW   r`   re   propertyrc   rd   rp   ro   rX   rR   r   r   rZ   rZ     sC    8!	' * * * *"17r   rZ   c                   p    \ rS rSrSrS rS\4S jr\S\	4S j5       r
S rS	 rS
 rS rS\\   4S jrSrg)	FactRulesi|  a  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           	         [        U[        5      (       a  UR                  5       n[        5       nU H  nUR	                  SS5      u  pEn[
        R                  " U5      n[
        R                  " U5      nUS:X  a  UR                  XF5        M]  US:X  a$  UR                  XF5        UR                  Xd5        M  [        SU-  5      e   / U l	        UR                   HO  u  pxU R                  R                  UR                   Vs1 s H  n[        U5      iM     sn[        U5      45        MQ     [        UR                  5      n	[!        XR                  5      n
U
R#                  5        Vs1 s H  n[%        U5      iM     snU l        [)        [*        5      n[)        [*        5      nU
R-                  5        H=  u  nu  pU Vs1 s H  n[        U5      iM     snU[        U5      '   X[        U5      '   M?     Xl        Xl        [)        [*        5      n[3        U5      nUR-                  5        H  u  nnUU==   U-  ss'   M     UU l        gs  snf s  snf s  snf )z)Compile rules into internal lookup tablesN   z->z==zunknown op %r)r   ru   
splitlinesrZ   splitr   
fromstringrp   r%   r>   rd   r<   r6   r   r+   rc   rI   r5   r   defined_factsr   r   r#   r   beta_triggersrN   rL   )r_   rK   Pruler'   opr(   rA   r2   impl_aimpl_abr   r   r   r)   betaidxsr   rL   
rel_prereqpitemss                       r   r`   FactRules.__init__  s    eS!!$$&E HDzz$*HA1  #A  #ATzq$tq$q$ 2!566   LLLEOO""',zz2z!(1+z2HUODF )
 +1==9 ,FLLA 6=\\^D^jm^D (,#C(#*==?ACG-H4ahqk4-Hhqk*)1(1+& $3 "3* S!"#45
#))+IAv1II ,; 3 E .Is   I
+IIreturnc                 @    SR                  U R                  5       5      $ )zCGenerate a string with plain python representation of the instance 
)joinprint_rulesr^   s    r   
_to_pythonFactRules._to_python  s    yy))+,,r   datac                     U " S5      nS H1  n[        [        5      nUR                  X   5        [        X#U5        M3     US   Ul        [        US   5      Ul        U$ )z:Generate an instance from the plain python representation  )r   r   rL   r>   r   )r   r   updatesetattrr>   r   )clsr   r_   rs   ds        r   _from_pythonFactRules._from_python  s_     2wCC#AHHTYDq! D |, o!67r   c              #   f   #    Sv   [        U R                  5       H  nSU< S3v   M     Sv   g 7f)Nzdefined_facts = [    ,z] # defined_facts)rt   r   )r_   facts     r   _defined_facts_linesFactRules._defined_facts_lines  s5     !!4--.D"" /!!s   /1c              #      #    Sv   [        U R                  5       HW  nS HN  nSU SU S3v   SU< SU< S3v   U R                  X4   n[        U5       H  nS	U< S
3v   M     Sv   Sv   MP     MY     Sv   g 7f)Nzfull_implications = dict( [)TFz    # Implications of  = :z    ((, z	), set( (        r   z       ) ),z     ),z ] ) # full_implications)rt   r   r   )r_   r   valuer   implieds        r   _full_implications_lines"FactRules._full_implications_lines  s     ++4--.D&.tfCwa@@thb	;;#55tmD%l3G$WKq11  4## ' / )(s   A:A<c              #      #    Sv   Sv   [        U R                  5       HC  nSU 3v   SU< S3v   [        U R                  U   5       H  nSU< S3v   M     Sv   Sv   ME     S	v   g 7f)
Nz
prereq = {r   z.    # facts that could determine the value of r   z: {r   r   z    },z
} # prereq)rt   rL   )r_   r   pfacts      r   _prereq_linesFactRules._prereq_lines  sx     4;;'DB4&II%%D 12 	++ 3NH ( s   A*A,c           
   #   X  #    [        [        5      n[        U R                  5       H  u  nu  p4X   R	                  X245        M     Sv   Sv   Sv   Sn0 n[        U5       Hd  nUu  pxSU SU 3v   X    HG  u  p2XVU'   US-  nSR                  [        [        [        U5      5      5      n	S	U	 S
3v   SU< S3v   MI     Sv   Mf     Sv   Sv   [        U R                  5       H5  n
U
u  pxU R                  U
    Vs/ s H  o&U   PM	     nnSU
< SU< S3v   M7     Sv   g s  snf 7f)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   listr:   r>   r<   rt   r   r   ru   r   )r_   reverse_implicationsnprer   mindicesr   r   setstrquerytriggerss               r   _beta_rules_linesFactRules._beta_rules_lines  sR    *40!*4??!;A~ )00#: "< QP23G!KD)$s5'::.7
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              #   ,  #    U R                  5        Sh  vN   Sv   Sv   U R                  5        Sh  vN   Sv   Sv   U R                  5        Sh  vN   Sv   Sv   U R                  5        Sh  vN   Sv   Sv   Sv   Sv   g Nu NW N9 N7f)z@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   r^   s    r   r   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)r>   r   r   r   rL   N)rS   rT   rU   rV   rW   r`   ru   r   classmethoddictr   r   r   r   r   r   r   rX   rR   r   r   r   r   |  sZ    <9v-C - 
 
 
")
":kXc] kr   r   c                       \ rS rSrS rSrg)InconsistentAssumptionsi5  c                 :    U R                   u  pnU< SU< SU< 3$ )Nr   =)r6   )r_   kbr   r   s       r   __str__InconsistentAssumptions.__str__6  s    ))% $..r   rR   N)rS   rT   rU   rV   r   rX   rR   r   r   r   r   5  s    /r   r   c                   0    \ rS rSrSrS rS rS rS rSr	g)	FactKBi;  zL
A simple propositional knowledge base relying on compiled inference rules.
c                     SSR                  [        U R                  5       5       Vs/ s H  nSU-  PM
     sn5      -  $ s  snf )Nz{
%s}z,
z	%s: %s)r   rt   r#   )r_   r   s     r   r   FactKB.__str__?  s@    %**%+DJJL%9:%9Z!^%9:< < 	<:s   A 
c                     Xl         g r.   rK   )r_   rK   s     r   r`   FactKB.__init__C  s    
r   c                 J    X;   a  X   b  X   U:X  a  g[        XU5      eX U'   g)zhAdd fact k=v to the knowledge base.

Returns True if the KB has actually been updated, False otherwise.
FT)r   )r_   r   vs      r   _tellFactKB._tellF  s3    
 9,w!|-dq99Gr   c                 <  ^  T R                   R                  nT R                   R                  nT R                   R                  n[	        U[
        5      (       a  UR                  5       nU(       a  [        5       nU HU  u  pgT R                  Xg5      (       a  Uc  M   X&U4    H  u  pT R                  X5        M     UR                  X6U4   5        MW     / nU H6  n
XJ   u  p[        U 4S jU 5       5      (       d  M%  UR                  U5        M8     U(       a  M  gg)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   N   >#    U  H  u  pTR                  U5      UL v   M     g 7fr.   )r9   )r/   r   r   r_   s      r   r3   *FactKB.deduce_all_facts.<locals>.<genexpr>y  s      :EDAtxx{a'Es   "%)rK   r   r   r>   r   r   r#   r   r   r   allr<   )r_   factsr   r   r>   beta_maytriggerr   r   rs   r   rH   rA   r2   s   `            r   deduce_all_factsFactKB.deduce_all_factsW  s     !JJ88

00ZZ**
eT""KKME!eO zz!''19 #4qD"9JCJJs* #:  &&}T':;  E')/:E:::LL' (! er   r   N)
rS   rT   rU   rV   rW   r   r`   r   r   rX   rR   r   r   r   r   ;  s    <"#(r   r   N)rW   collectionsr   typingr   logicr   r   r   r	   r   r   r    r+   rI   rN   	ExceptionrP   rZ   r   r%   r   r   r   rR   r   r   <module>r      s{   .` $  & &(%PL^L		 	
v7 v7vvk vkr/j /?(T ?(r   