o
    *ήcq=                     @   s   d Z ddlmZ ddlmZmZmZmZ dd Zdd Z	d	d
 Z
dd Zdd Zdd ZG dd deZG dd dZG dd dZG dd deZG d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   )LogicAndOrNotc                 C   s   t | tr| jS | S )zdReturn the literal fact of an atom.

    Effectively, this merely strips the Not around a fact.
    
isinstancer   argatom r   7/tmp/pip-target-vg8gfxp4/lib/python/sympy/core/facts.py
_base_fact6   s   
r   c                 C   s   t | tr
| jdfS | dfS )NFTr   r   r   r   r   _as_pairA   s   

r   c                 C   sb   t | }t  jtt | }|D ]}|D ]}||f|v r-|D ]}||f|v r,|||f qqq|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jr   r   r   transitive_closureJ   s   r   c                 C   s   | dd | D  } t t}t| }|D ]\}}||krq|| | q| D ]\}}|| t|}||v rBtd|||f q(|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                 S   s    g | ]\}}t |t |fqS r   r   ).0r   r   r   r   r   
<listcomp>r   s     z-deduce_alpha_implications.<locals>.<listcomp>z*implications are inconsistent: %s -> %s %s)r   r   r   r   itemsdiscardr   
ValueError)r   resr   abimplnar   r   r   deduce_alpha_implications^   s    
r'   c                    sj  i }|   D ]}t| | g f||< q|D ]\}|jD ]}||v r#qt g f||< qqd}|rxd}|D ]A\}t|tsAtdt|j | D ]*\}\}}||hB }	|	vrt |	rt| |	}
|
durr||
d O }d}qJq4|s0t
|D ]6\}\}t|j | D ]&\}\}}||hB }	|	v rqt fdd|	D rq |	@ r|| qq||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   s(    | ]}t | v pt |kV  qd S Nr   )r   xibargsbimplr   r   	<genexpr>   s   & z,apply_beta_to_alpha_route.<locals>.<genexpr>)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bidxr   r*   r   apply_beta_to_alpha_route   sP   








rB   c                 C   sf   t t}|  D ](\\}}}t|tr|jd }|D ]\}}t|tr(|jd }|| | qq|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   r/   r   )rulesprereqr#   _r%   r   r   r   r   rules_2prereq   s   



rF   c                   @   s   e Zd ZdZdS )TautologyDetectedz:(internal) Prover uses it for reporting detected tautologyN)__name__
__module____qualname____doc__r   r   r   r   rG      s    rG   c                   @   sH   e Zd ZdZdd Zdd Zedd Zedd	 Zd
d Z	d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                 C   s   g | _ t | _d S r(   )proved_rulesr   _rules_seenselfr   r   r   __init__  s   zProver.__init__c                 C   sH   g }g }| j D ]\}}t|tr|||f q|||f q||fS )z-split proved rules into alpha and beta chains)rM   r	   r   r5   )rP   rules_alpha
rules_betar#   r$   r   r   r   split_alpha_beta!  s   
zProver.split_alpha_betac                 C      |   d S )Nr   rT   rO   r   r   r   rR   ,     zProver.rules_alphac                 C   rU   )Nr   rV   rO   r   r   r   rS   0  rW   zProver.rules_betac                 C   sj   |rt |tr	dS t |trdS ||f| jv rdS | j||f z	| || W dS  ty4   Y dS w )zprocess a -> b ruleN)r	   boolrN   r   _process_rulerG   )rP   r#   r$   r   r   r   process_rule4  s   
zProver.process_rulec                 C   sh  t |tr|jD ]}| || qd S t |trft |ts(||jv r(t||d| tdd |jD  t| tt	|jD ]$}|j| }|jd | |j|d d   }| t|t|t|  q?d S t |tr||jv rvt||d| j
||f d S t |tr||jv rt||d|jD ]}| || qd S | j
||f | j
t|t|f d S )Nza -> a|c|...c                 S   s   g | ]}t |qS r   r   )r   bargr   r   r   r   W      z(Prover._process_rule.<locals>.<listcomp>r   z
a & b -> az
a | b -> a)r	   r   r/   rZ   r   r   rG   r   rangelenrM   r5   )rP   r#   r$   r[   rA   brestaargr   r   r   rY   E  s4   




 
 
	



zProver._process_ruleN)rH   rI   rJ   rK   rQ   rT   propertyrR   rS   rZ   rY   r   r   r   r   rL      s    

rL   c                   @   s   e Zd ZdZd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                 C   sz  t |tr	| }t }|D ]6}|dd\}}}t|}t|}|dkr.||| q|dkr?||| ||| qtd| g | _	|j
D ]\}}| j	dd |jD t|f qKt|j}	t|	|j
}
dd |
 D | _tt}tt}|
 D ]\}\}}d	d |D |t|< ||t|< q|| _|| _tt}t|}| D ]\}}||  |O  < q|| _dS )
z)Compile rules into internal lookup tablesN   z->z==zunknown op %rc                 S      h | ]}t |qS r   r   )r   r#   r   r   r   	<setcomp>  r\   z%FactRules.__init__.<locals>.<setcomp>c                 S   rd   r   )r   )r   r   r   r   r   rf     r\   c                 S   rd   r   re   r   r   r   r   r   rf     r\   )r	   str
splitlinesrL   splitr   
fromstringrZ   r!   r7   rS   r5   r/   r   r'   rR   rB   r.   defined_factsr   r   r   r   beta_triggersrF   rD   )rP   rC   Pruler#   opr$   r:   r,   impl_aimpl_abr   rm   r   r%   betaidxsrD   
rel_prereqpitemsr   r   r   rQ     sB   




zFactRules.__init__N)rH   rI   rJ   rK   rQ   r   r   r   r   rb   v  s    rb   c                   @   s   e Zd Zdd ZdS )InconsistentAssumptionsc                 C   s   | j \}}}d|||f S )Nz	%s, %s=%s)r/   )rP   kbfactvaluer   r   r   __str__  s   zInconsistentAssumptions.__str__N)rH   rI   rJ   rz   r   r   r   r   rv     s    rv   c                   @   s0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )FactKBzT
    A simple propositional knowledge base relying on compiled inference rules.
    c                 C   s    dd dd t|  D  S )Nz{
%s}z,
c                 S   s   g | ]}d | qS )z	%s: %sr   rg   r   r   r   r     r\   z"FactKB.__str__.<locals>.<listcomp>)joinsortedr   rO   r   r   r   rz     s   zFactKB.__str__c                 C   s
   || _ d S r(   )rC   )rP   rC   r   r   r   rQ     s   
zFactKB.__init__c                 C   s<   || v r| | du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)rv   )rP   r   vr   r   r   _tell  s   zFactKB._tellc                    s    j j} j j} j j}t|tr| }|rgt }|D ])\}} ||r*|du r+q|||f D ]
\}}	 ||	 q1|	|||f  qg }|D ]}
||
 \}}t
 fdd|D rb|| qJ|s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   s"    | ]\}}  ||u V  qd S r(   )r2   )r   r   r~   rO   r   r   r-     s     z*FactKB.deduce_all_facts.<locals>.<genexpr>)rC   r   rm   r7   r	   dictr   r   r   updateallr5   )rP   factsr   rm   r7   beta_maytriggerr   r~   keyry   rA   r:   r,   r   rO   r   deduce_all_facts  s(   	

zFactKB.deduce_all_factsN)rH   rI   rJ   rK   rz   rQ   r   r   r   r   r   r   r{     s    r{   N)rK   collectionsr   logicr   r   r   r   r   r   r   r'   rB   rF   	ExceptionrG   rL   rb   r!   rv   r   r{   r   r   r   r   <module>   s    0	(O&v[