
    sgZL                         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    C/var/www/html/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 6 	6A1v**! 6A1v!22)--q!f56	66 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; N4QV:@A2tLN 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+     HB3r7e#7s2w%'77Hs   '*)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$$& 5+A./4q	5" %u** 	%BV|%F2J	%% !
 %& 	1LE5eS) 122

OE#)<<> 1<FB!%%..*?JJu% "(E!2J!-*Q-/,0)1		1  * !** 5  nueEJJ%||~ 	 OA|aSLE~ H%HHu}		$	   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      s    . 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 A#D)$Ud+l4!89.EE!!#aT"3RZ@A 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 	7Dzz$*HAr1  #A  #ATzq!$tq!$q!$ 2!566	7  LL 	FLE5OO""',zz2!(1+2HUODF	F
 +1==9 ,FALLA 6=\\^DjmD (,#C(#*==? 	2AhCG-Hahqk-Hhqk*)1M(1+&	2 "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  sg     2wC 	"C#AHHT#YD#q!	" |, 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  s;     !!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3 2G$WKq112## 	  )(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  s     4;;' 	DB4&II%%D 12 , 	++,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  sv    *40!*4??!; 	;A~W )00#q:	; QP23 		G!KD%)$s5'::.w7 /Q
Q3sF3K#89xs++ 2../ H		 !!D../ 	2EKD%,0,>,>u,EFq
FHF	H<q11	2 "! 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:Z!^:< < 	<: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"     :DAqtxx{a':s    #)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"9 +JC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   