
    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dS )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    L/var/www/html/ai-engine/env/lib/python3.11/site-packages/sympy/core/facts.py
_base_factr   7   s"    
 $ x    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 ]/}|D ]*}||f|v r"|D ]}||f|v r|                    ||f            +0|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))suu{C%6778H 6 6 	6 	6A1v***! 6 6A1v!222)--q!f555		6 r   c           	      r   | d | D             z   } t          t                    }t          |           }|D ]'\  }}||k    r||                             |           (|                                D ]E\  }}|                    |           t          |          }||v rt          d|d|d|          F|S )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, ...])
    c                 P    g | ]#\  }}t          |          t          |          f$S  r	   ).0r   r   s      r   
<listcomp>z-deduce_alpha_implications.<locals>.<listcomp>s   s-    "O"O"OACFFCFF#3"O"O"Or   zimplications are inconsistent: z ->  )r   r   r    r   itemsdiscardr	   
ValueError)r   resr   abimplnas          r   deduce_alpha_implicationsr0   _   s    (  "O"O,"O"O"OOL
c

C*<88!  166A

1 99;; N N4QVV::*@A222ttLN N N  Jr   c                 T   i }|                                  D ]}t          | |                   g f||<   |D ]'\  }|j        D ]}||v rt                      g f||<   (d}|rd}|D ]\  }t          |t                    st          d          t          |j                  |                                D ]`\  }\  }}||hz  }	|	vrN                    |	          r9|                               |	                              }
|
||
d         z  }d}a|t          |          D ]{\  }\  }t          |j                  |                                D ]J\  }\  }}||hz  }	|	v rt          fd|	D                       r0|	z  r|                    |           K||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 AndNr   c              3   `   K   | ](}t          |          v pt          |          k    V  )d S Nr$   )r%   xibargsbimpls     r   	<genexpr>z,apply_beta_to_alpha_route.<locals>.<genexpr>   s>      HHB3r77e#7s2ww%'7HHHHHHr   )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bidxr5   r6   s               @@r   apply_beta_to_alpha_routerL      s2   8 F$$&& 5 5+A.//4q		" % %u* 	% 	%BV||%%F2JJ	% !
 1 %& 	1 	1LE5eS)) 3 1222
OOE#)<<>> 1 1<FB!%%%..*?*?%JJu%%% "(E!2!2J!-*Q-/,0)1   1* !** 5 5    nueEJ%||~~ 	  	 OA|aSLE~~ HHHHH%HHHHH u}  		$	  Mr   c                 6   t          t                    }|                                 D ]o\  \  }}}t          |t                    r|j        d         }|D ]B\  }}t          |t                    r|j        d         }||                             |           Cp|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	   r9   r   )rulesprereqr,   _r.   r   s         r   rules_2prereqrQ      s    . F  Aa 	q	A 	 	FQ!S!! F1I1IMM!	 Mr   c                       e Zd ZdZdS )TautologyDetectedz:(internal) Prover uses it for reporting detected tautologyN)__name__
__module____qualname____doc__r#   r   r   rS   rS      s        DDDr   rS   c                   V    e Zd ZdZd Zd Zed             Zed             Zd Z	d Z
dS )	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                 :    g | _         t                      | _        d S r3   )proved_rulesr   _rules_seenselfs    r   __init__zProver.__init__  s    55r   c                     g }g }| j         D ]I\  }}t          |t                    r|                    ||f           2|                    ||f           J||fS )z-split proved rules into alpha and beta chains)r[   r   r   r?   )r^   rules_alpha
rules_betar,   r-   s        r   split_alpha_betazProver.split_alpha_beta"  su    
% 	+ 	+DAq!S!! +!!1a&))))""Aq6****J&&r   c                 6    |                                  d         S )Nr   rc   r]   s    r   ra   zProver.rules_alpha-      $$&&q))r   c                 6    |                                  d         S )Nr   re   r]   s    r   rb   zProver.rules_beta1  rf   r   c                    |rt          |t                    rdS t          |t                    rdS ||f| j        v rdS | j                            ||f           	 |                     ||           dS # t
          $ r Y dS w xY w)zprocess a -> b ruleN)r   boolr\   r   _process_rulerS   )r^   r,   r-   s      r   process_rulezProver.process_rule5  s     	jD)) 	Fa 	Fq6T%%%F  !Q(((	q!$$$$$  	 	 	DD	s   A3 3
B Bc           	         t          |t                    r8t          |j        t                    }|D ]}|                     ||           d S t          |t                    rt          |j        t                    }t          |t                    s||v rt          ||d          |                     t          d |j        D              t          |                     t          t          |                    D ]Z}||         }|d |         ||dz   d          z   }|                     t          |t          |                    t          |            [d S t          |t                    rNt          |j        t                    }||v rt          ||d          | j                            ||f           d S t          |t                    rMt          |j        t                    }||v rt          ||d          |D ]}|                     ||           d S | j                            ||f           | j                            t          |          t          |          f           d S )N)keyza -> a|c|...c                 ,    g | ]}t          |          S r#   r$   )r%   bargs     r   r&   z(Prover._process_rule.<locals>.<listcomp>[  s    #A#A#A$CII#A#A#Ar   r   z
a & b -> az
a | b -> a)r   r   sortedr9   strrk   r   r   rS   r	   rangelenr[   r?   )	r^   r,   r-   sorted_bargsro   rK   brestsorted_aargsaargs	            r   rj   zProver._process_ruleF  s    a +	7!!&c222L$ + +!!!T****+ + 2 #	7!!&c222La'' B$$+Aq.AAAc#A#A!&#A#A#ABCFFKKKc,//00 A A#D)$UdU+l4!899.EE!!#aT"3"3RZ@@@@A A 3 	7!!&c222LL  '1l;;;$$aV,,,,, 2 
	7!!&c222LL  '1l;;;$ + +!!$****+ +
 $$aV,,,$$c!ffc!ff%566666r   N)rT   rU   rV   rW   r_   rc   propertyra   rb   rk   rj   r#   r   r   rY   rY     s         8! ! !	' 	' 	' * * X* * * X*  "17 17 17 17 17r   rY   c                   p    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dS )	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|                                }t                      }|D ]}|                    dd          \  }}}t          j        |          }t          j        |          }|dk    r|                    ||           a|dk    r-|                    ||           |                    ||           t          d|z            g | _	        |j
        D ]=\  }}| j	                            d |j        D             t          |          f           >t          |j                  }	t!          |	|j
                  }
d |
                                D             | _        t'          t(                    }t'          t(                    }|
                                D ]6\  }\  }}d |D             |t          |          <   ||t          |          <   7|| _        || _        t'          t(                    }t1          |          }|                                D ]\  }}||xx         |z  cc<   || _        dS )	z)Compile rules into internal lookup tablesN   z->z==zunknown op %rc                 ,    h | ]}t          |          S r#   r   )r%   r,   s     r   	<setcomp>z%FactRules.__init__.<locals>.<setcomp>  s    222!(1++222r   c                 ,    h | ]}t          |          S r#   )r   )r%   r   s     r   r   z%FactRules.__init__.<locals>.<setcomp>  s    DDDjmmDDDr   c                 ,    h | ]}t          |          S r#   r~   r%   r   s     r   r   z%FactRules.__init__.<locals>.<setcomp>  s    -H-H-Hahqkk-H-H-Hr   )r   rq   
splitlinesrY   splitr   
fromstringrk   r*   rA   rb   r?   r9   r   r0   ra   rL   r8   defined_factsr   r   r(   r   beta_triggersrQ   rO   )r^   rN   Pruler,   opr-   rD   r6   impl_aimpl_abr   r   r   r.   betaidxsrO   
rel_prereqpitemss                      r   r_   zFactRules.__init__  sf    eS!! 	'$$&&E HH 	7 	7Dzz$**HAr1 ##A ##ATzzq!$$$$tq!$$$q!$$$$ 2!5666 L 	F 	FLE5O""22uz222HUOODF F F F +1=99 ,FALAA EDW\\^^DDD (,,#C((#*==?? 	2 	2Ah-H-H4-H-H-Hhqkk*)1M(1++&&!2* S!!"#455
#))++ 	  	 IAv1IIIIIIIr   returnc                 P    d                     |                                           S )zD Generate a string with plain python representation of the instance 
)joinprint_rulesr]   s    r   
_to_pythonzFactRules._to_python  s     yy))++,,,r   datac                      | d          }dD ]B}t          t                    }|                    ||                    t          |||           C|d         |_        t          |d                   |_        |S )z; Generate an instance from the plain python representation  )r   r   rO   rA   r   )r   r   updatesetattrrA   r   )clsr   r^   rm   ds        r   _from_pythonzFactRules._from_python  s~     s2wwC 	" 	"C#AHHT#YD#q!!!!|, o!677r   c              #   X   K   dV  t          | j                  D ]
}d|dV  dV  d S )Nzdefined_facts = [    ,z] # defined_facts)rp   r   )r^   facts     r   _defined_facts_lineszFactRules._defined_facts_lines  sX      !!!!4-.. 	# 	#D""""""""!!!!!!r   c              #      K   dV  t          | j                  D ]N}dD ]I}d| d| dV  d|d|dV  | j        ||f         }t          |          D ]
}d	|d
V  dV  dV  JOdV  d S )Nzfull_implications = dict( [)TFz    # Implications of  = :z    ((, z	), set( (        r   z       ) ),z     ),z ] ) # full_implications)rp   r   r   )r^   r   valuer   implieds        r   _full_implications_linesz"FactRules._full_implications_lines  s      ++++4-.. 	  	 D&    @t@@@@@@@@;t;;;;;;;;#5tUmD%l33 2 2G1W1111111####  )(((((r   c              #      K   dV  dV  t          | j                  D ]>}d| V  d|dV  t          | j        |                   D ]
}d|dV  dV  dV  ?d	V  d S )
Nz
prereq = {r   z.    # facts that could determine the value of r   z: {r   r   z    },z
} # prereq)rp   rO   )r^   r   pfacts      r   _prereq_lineszFactRules._prereq_lines  s      4;'' 	 	DI4IIIII%%%%%%%D 122 , ,++++++++NNNHHHHr   c           
   #   d  K   t          t                    }t          | j                  D ]%\  }\  }}||                             ||f           &dV  dV  dV  d}i t          |          D ]r}|\  }}d| d| V  ||         D ]T\  }}||<   |dz  }d                    t          t          t          |                              }d	| d
V  d|dV  UdV  sdV  dV  t          | j	                  D ]+}	|	\  }}fd| j	        |	         D             }
d|	d|
dV  ,dV  d S )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 = {c                      g | ]
}|         S r#   r#   )r%   nindicess     r   r&   z/FactRules._beta_rules_lines.<locals>.<listcomp>  s    FFFq
FFFr   r   z: r   z} # beta_triggers)
r   listr=   rA   r?   rp   r   r   rq   r   )r^   reverse_implicationsr   prer   mr   r   setstrquerytriggersr   s              @r   _beta_rules_lineszFactRules._beta_rules_lines  s     *400!*4?!;!; 	; 	;A~W )00#q::::PPPP233 		 		G!KD%:$::5:::::.w7 / /Q
Q3sF3KK#8#899+++++++........HHHH!!!!D.// 	2 	2EKD%FFFFD,>u,EFFFH111H1111111!!!!!!r   c              #   *  K   |                                  E d{V  dV  dV  |                                 E d{V  dV  dV  |                                 E d{V  dV  dV  |                                 E d{V  dV  dV  dV  dV  dS )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   r]   s    r   r   zFactRules.print_rules#  s      ,,.........00222222222%%'''''''''))+++++++++ppppjjjjjjr   N)rT   rU   rV   rW   r_   rq   r   classmethoddictr   r   r   r   r   r   r   r#   r   r   rz   rz   |  s         <9 9 9v-C - - - - 
 
 
 
 [
" " ") ) )
 
 
" " ":kXc] k k k k k kr   rz   c                       e Zd Zd ZdS )InconsistentAssumptionsc                 ,    | j         \  }}}|d|d|S )Nr   =)r9   )r^   kbr   r   s       r   __str__zInconsistentAssumptions.__str__6  s&    )D% bb$$$..r   N)rT   rU   rV   r   r#   r   r   r   r   5  s#        / / / / /r   r   c                   *    e Zd ZdZd Zd Zd Zd ZdS )FactKBzT
    A simple propositional knowledge base relying on compiled inference rules.
    c                     dd                     d t          |                                           D                       z  S )Nz{
%s}z,
c                     g | ]}d |z  S )z	%s: %sr#   r   s     r   r&   z"FactKB.__str__.<locals>.<listcomp>A  s    :::Z!^:::r   )r   rp   r(   r]   s    r   r   zFactKB.__str__?  s@    %**::VDJJLL%9%9:::< < < 	<r   c                     || _         d S r3   )rN   )r^   rN   s     r   r_   zFactKB.__init__C  s    


r   c                 f    || v r'| |         | |         |k    rdS t          | ||          || |<   dS )zxAdd fact k=v to the knowledge base.

        Returns True if the KB has actually been updated, False otherwise.
        NFT)r   )r^   r   vs      r   _tellzFactKB._tellF  sH    
 99a,Aw!||u-dAq999DG4r   c                      j         j        } j         j        } j         j        }t	          |t
                    r|                                }|rt                      }|D ]a\  }}                     ||          r||||f         D ]\  }}	                     ||	           |	                    |||f                    bg }|D ]=}
||
         \  }}t           fd|D                       r|                    |           >|dS dS )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   L   K   | ]\  }}                     |          |u V  d S r3   )r<   )r%   r   r   r^   s      r   r7   z*FactKB.deduce_all_facts.<locals>.<genexpr>y  s6      ::DAqtxx{{a'::::::r   )rN   r   r   rA   r   r   r(   r   r   r   allr?   )r^   factsr   r   rA   beta_maytriggerr   r   rm   r   rK   rD   r6   s   `            r   deduce_all_factszFactKB.deduce_all_factsW  sU    !J8
0Z*
eT"" 	"KKMME 	(!eeO  < <1zz!Q'' 19 #4AqD"9 + +JCJJsE****&&}QT':;;;; E' ( ()$/u::::E::::: (LL''''  	( 	( 	( 	( 	(r   N)rT   rU   rV   rW   r   r_   r   r   r#   r   r   r   r   ;  sZ         < < <    "#( #( #( #( #(r   r   N)rW   collectionsr   typingr   logicr   r   r   r	   r   r   r    r0   rL   rQ   	ExceptionrS   rY   rz   r*   r   r   r   r#   r   r   <module>r      s  . .` $ # # # # #       & & & & & & & & & & & &      (% % %PL L L^  L	 	 	 	 		 	 	 	
v7 v7 v7 v7 v7 v7 v7 v7vvk vk vk vk vk vk vk vkr/ / / / /j / / /?( ?( ?( ?( ?(T ?( ?( ?( ?( ?(r   