
    Rie]O                         d Z ddlmZ ddlmZmZ ddlmZ ddlm	Z	 ddZ
d Z G d	 d
          Z G d d          ZdS )zImplementation of DPLL algorithm

Features:
  - Clause learning
  - Watch literal scheme
  - VSIDS heuristic

References:
  - https://en.wikipedia.org/wiki/DPLL_algorithm
    )defaultdict)heappushheappop)ordered)
EncodedCNFFc                    t          | t                    s%t                      }|                    |            |} dh| j        v r|rd dD             S dS t	          | j        | j        t                      | j                  }|                                }|rt          |          S 	 t          |          S # t          $ r Y dS w xY w)a  
    Check satisfiability of a propositional sentence.
    It returns a model rather than True when it succeeds.
    Returns a generator of all models if all_models is True.

    Examples
    ========

    >>> from sympy.abc import A, B
    >>> from sympy.logic.algorithms.dpll2 import dpll_satisfiable
    >>> dpll_satisfiable(A & ~B)
    {A: True, B: False}
    >>> dpll_satisfiable(A & ~A)
    False

    r   c              3      K   | ]}|V  d S N ).0fs     <lib/python3.11/site-packages/sympy/logic/algorithms/dpll2.py	<genexpr>z#dpll_satisfiable.<locals>.<genexpr>,   s"      ''!A''''''    FF)
isinstancer   add_propdata	SATSolver	variablessetsymbols_find_model_all_modelsnextStopIteration)expr
all_modelsexprssolvermodelss        r   dpll_satisfiabler"      s    " dJ'' t 	
sdi 	(''w''''uty$.#%%FFF!!F #6"""F||   uus   (B7 7
CCc              #   j   K   d}	 	 t          |           V  d}# t          $ r |sdV  Y d S Y d S w xY w)NFT)r   r   )r!   satisfiables     r   r   r   @   st      K	v,,K	     	KKKKKK	 	 	s    22c                       e Zd ZdZ	 	 ddZd Zd Zd	 Zed
             Z	d Z
d Zd Zd Z	 d Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd ZdS )r   z
    Class for representing a SAT solver capable of
     finding a model to a boolean theory in conjunctive
     normal form.
    Nvsidsnone  c                    || _         || _        d| _        g | _        g | _        || _        |"t          t          |                    | _        n|| _        | 	                    |           | 
                    |           d|k    rE|                                  | j        | _        | j        | _        | j        | _        | j        | _        nt(          d|k    r8| j        | _        | j        | _        | j                            | j                   nd|k    rd | _        d | _        nt(          t7          d          g| _        || j        _        d| _        d| _         tC          | j"                  | _#        d S )NFr&   simpler'   c                     d S r
   r   )xs    r   <lambda>z$SATSolver.__init__.<locals>.<lambda>v   s     r   c                      d S r
   r   r   r   r   r-   z$SATSolver.__init__.<locals>.<lambda>w   s    D r   r   )$var_settings	heuristicis_unsatisfied_unit_prop_queueupdate_functionsINTERVALlistr   r   _initialize_variables_initialize_clauses_vsids_init_vsids_calculateheur_calculate_vsids_lit_assignedheur_lit_assigned_vsids_lit_unsetheur_lit_unset_vsids_clause_addedheur_clause_addedNotImplementedError_simple_add_learned_clauseadd_learned_clausesimple_compute_conflictcompute_conflictappendsimple_clean_clausesLevellevels_current_levelvarsettingsnum_decisionsnum_learned_clauseslenclausesoriginal_num_clauses)selfrO   r   r/   r   r0   clause_learningr4   s           r   __init__zSATSolver.__init__R   s    )"# " " ?	 2 233DLL"DL""9---  )))i"&"7D%)%=D""&"7D%)%=D"" &%&&&*&ED#$($@D!!(()BCCCC&&&4nD#$0LD!!%% Qxxj*6' #$ $'$5$5!!!r   c                     t          t                    | _        t          t                    | _        dgt          |          dz   z  | _        dS )z+Set up the variable data structures needed.F   N)r   r   	sentinelsintoccurrence_countrN   variable_set)rQ   r   s     r   r6   zSATSolver._initialize_variables   sA    $S)) +C 0 0"Gs9~~'9:r   c                    d |D             | _         t          | j                   D ]\  }}dt          |          k    r!| j                            |d                    9| j        |d                                      |           | j        |d                                      |           |D ]}| j        |xx         dz  cc<   dS )a<  Set up the clause data structures needed.

        For each clause, the following changes are made:
        - Unit clauses are queued for propagation right away.
        - Non-unit clauses have their first and last literals set as sentinels.
        - The number of clauses a literal appears in is computed.
        c                 ,    g | ]}t          |          S r   )r5   )r   clauses     r   
<listcomp>z1SATSolver._initialize_clauses.<locals>.<listcomp>   s    ;;;V;;;r   rU   r   N)rO   	enumeraterN   r2   rF   rV   addrX   )rQ   rO   ir\   lits        r   r7   zSATSolver._initialize_clauses   s     <;7;;;"4<00 	0 	0IAv CKK%,,VAY777N6!9%))!,,,N6":&**1--- 0 0%c***a/****0	0 	0r   c              #      K   d}                                    j        rdS 	  j         j        z  dk    r j        D ]} |             |rd} j        j        }n                                 } xj        dz  c_        d|k    r fd j        D             V   j        j	        r  
                                  j        j	         t           j                  dk    rdS  j        j         } 
                                  j                            t          |d                     d} j                            t          |                                          |                                               j        rd _         j        j	        r: 
                                 dt           j                  k    rdS  j        j	        :                                                                  j        j         } 
                                  j                            t          |d                     d}$)an  
        Main DPLL loop. Returns a generator of models.

        Variables are chosen successively, and assigned to be either
        True or False. If a solution is not found with this setting,
        the opposite is chosen and the search continues. The solver
        halts when every variable has a setting.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())
        >>> list(l._find_model())
        [{1: True, 2: False, 3: False}, {1: True, 2: True, 3: True}]

        >>> from sympy.abc import A, B, C
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set(), [A, B, C])
        >>> list(l._find_model())
        [{A: True, B: False, C: False}, {A: True, B: True, C: True}]

        FNTr   rU   c                 T    i | ]$}j         t          |          d z
           |dk    %S )rU   r   )r   abs)r   rb   rQ   s     r   
<dictcomp>z)SATSolver._find_model.<locals>.<dictcomp>   sG     F F F,/  <C15 #aF F Fr   )flipped)	_simplifyr1   rL   r4   r3   rJ   decisionr:   r/   rg   _undorN   rI   rF   rH   _assign_literalrC   rE   )rQ   flip_varfuncrb   flip_lits   `    r   r   zSATSolver._find_model   s     8  	 	F;	 !DM1Q66 1  DDFFFF / )2 ))++""a'"" 88F F F F373DF F F F F F-5 %

 -5 %4;''1,, $ 3 <<HJJLLLK&&uXt'D'D'DEEE#H ""5::...   %%% NN "  &+# )1 JJLLL C,,,, )1  ''(=(=(?(?@@@ !/88

""54#@#@#@AAAw;	 r   c                     | j         d         S )a  The current decision level data structure

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{1}, {2}], {1, 2}, set())
        >>> next(l._find_model())
        {1: True, 2: True}
        >>> l._current_level.decision
        0
        >>> l._current_level.flipped
        False
        >>> l._current_level.var_settings
        {1, 2}

        r^   rI   rQ   s    r   rJ   zSATSolver._current_level  s    & {2r   c                 >    | j         |         D ]}|| j        v r dS dS )a  Check if a clause is satisfied by the current variable setting.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{1}, {-1}], {1}, set())
        >>> try:
        ...     next(l._find_model())
        ... except StopIteration:
        ...     pass
        >>> l._clause_sat(0)
        False
        >>> l._clause_sat(1)
        True

        TF)rO   r/   rQ   clsrb   s      r   _clause_satzSATSolver._clause_sat  s9    $ <$ 	 	Cd'''tt (ur   c                      || j         |         v S )a  Check if a literal is a sentinel of a given clause.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())
        >>> next(l._find_model())
        {1: True, 2: False, 3: False}
        >>> l._is_sentinel(2, 3)
        True
        >>> l._is_sentinel(-3, 1)
        False

        )rV   )rQ   rb   rt   s      r   _is_sentinelzSATSolver._is_sentinel1  s    " dnS)))r   c                    | j                             |           | j        j                             |           d| j        t	          |          <   |                     |           t          | j        |                    }|D ]}|                     |          sd}| j	        |         D ]}|| k    rx| 
                    ||          r|}"| j        t	          |                   sE| j        |                              |           | j        |                             |           d} n|r| j                            |           dS )a  Make a literal assignment.

        The literal assignment must be recorded as part of the current
        decision level. Additionally, if the literal is marked as a
        sentinel of any clause, then a new sentinel must be chosen. If
        this is not possible, then unit propagation is triggered and
        another literal is added to the queue to be set in the future.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())
        >>> next(l._find_model())
        {1: True, 2: False, 3: False}
        >>> l.var_settings
        {-3, -2, 1}

        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())
        >>> l._assign_literal(-1)
        >>> try:
        ...     next(l._find_model())
        ... except StopIteration:
        ...     pass
        >>> l.var_settings
        {-1}

        TN)r/   r`   rJ   rY   re   r<   r5   rV   ru   rO   rw   remover2   rF   )rQ   rb   sentinel_listrt   other_sentinelnewlits         r   rk   zSATSolver._assign_literalD  sg   > 	c"""(,,S111&*#c((#s###T^SD122  	A 	AC##C(( A!%"l3/ " "F#~~,,VS99 "-3NN!%!23v;;!? " NC4077<<< N6266s;;;-1N!E " A)00@@@	A 	Ar   c                     | j         j        D ]H}| j                            |           |                     |           d| j        t          |          <   I| j                                         dS )ag  
        _undo the changes of the most recent decision level.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())
        >>> next(l._find_model())
        {1: True, 2: False, 3: False}
        >>> level = l._current_level
        >>> level.decision, level.var_settings, level.flipped
        (-3, {-3, -2}, False)
        >>> l._undo()
        >>> level = l._current_level
        >>> level.decision, level.var_settings, level.flipped
        (0, {1}, False)

        FN)rJ   r/   ry   r>   rY   re   rI   poprQ   rb   s     r   rj   zSATSolver._undo{  st    , &3 	0 	0C$$S)))$$$*/Dc#hh'' 	r   c                 v    d}|r4d}||                                  z  }||                                 z  }|2dS dS )ad  Iterate over the various forms of propagation to simplify the theory.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())
        >>> l.variable_set
        [False, False, False, False]
        >>> l.sentinels
        {-3: {0, 2}, -2: {3, 4}, 2: {0, 3}, 3: {2, 4}}

        >>> l._simplify()

        >>> l.variable_set
        [False, True, False, False]
        >>> l.sentinels
        {-3: {0, 2}, -2: {3, 4}, -1: set(), 2: {0, 3},
        ...3: {2, 4}}

        TFN)
_unit_prop_pure_literal)rQ   changeds     r   rh   zSATSolver._simplify  s^    .  	,Gt(((Gt))+++G  	, 	, 	, 	, 	,r   c                     t          | j                  dk    }| j        rO| j                                        }| | j        v rd| _        g | _        dS |                     |           | j        O|S )z/Perform unit propagation on the current theory.r   TF)rN   r2   r~   r/   r1   rk   )rQ   resultnext_lits      r   r   zSATSolver._unit_prop  s    T*++a/# 	/,0022HyD---&*#(*%u$$X... # 	/ r   c                     dS )z2Look for pure literals and assign them when found.Fr   rq   s    r   r   zSATSolver._pure_literal  s    ur   c                    g | _         i | _        t          dt          | j                            D ]}t          | j        |                    | j        |<   t          | j        |                     | j        | <   t          | j         | j        |         |f           t          | j         | j        |          | f           dS )z>Initialize the data structures needed for the VSIDS heuristic.rU   N)lit_heap
lit_scoresrangerN   rY   floatrX   r   )rQ   vars     r   r8   zSATSolver._vsids_init  s    C 12233 	C 	CC#($*?*D)D#E#EDOC $)4+@#+F*F$G$GDOSD!T]T_S%93$?@@@T]T_cT%:SD$ABBBB		C 	Cr   c                 h    | j                                         D ]}| j         |xx         dz  cc<   dS )a  Decay the VSIDS scores for every literal.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())

        >>> l.lit_scores
        {-3: -2.0, -2: -2.0, -1: 0.0, 1: 0.0, 2: -2.0, 3: -2.0}

        >>> l._vsids_decay()

        >>> l.lit_scores
        {-3: -1.0, -2: -1.0, -1: 0.0, 1: 0.0, 2: -1.0, 3: -1.0}

        g       @N)r   keysr   s     r   _vsids_decayzSATSolver._vsids_decay  sL    * ?'')) 	( 	(COC   C'    	( 	(r   c                 r   t          | j                  dk    rdS | j        t          | j        d         d                            rYt	          | j                   t          | j                  dk    rdS | j        t          | j        d         d                            Yt	          | j                  d         S )a  
            VSIDS Heuristic Calculation

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())

        >>> l.lit_heap
        [(-2.0, -3), (-2.0, 2), (-2.0, -2), (0.0, 1), (-2.0, 3), (0.0, -1)]

        >>> l._vsids_calculate()
        -3

        >>> l.lit_heap
        [(-2.0, -2), (-2.0, 2), (0.0, -1), (0.0, 1), (-2.0, 3)]

        r   rU   )rN   r   rY   re   r   rq   s    r   r9   zSATSolver._vsids_calculate  s    * t}""1 DM!$4Q$7 8 89 	DM"""4=!!Q&&q DM!$4Q$7 8 89 	
 t}%%a((r   c                     dS )z;Handle the assignment of a literal for the VSIDS heuristic.Nr   r   s     r   r;   zSATSolver._vsids_lit_assigned      r   c                     t          |          }t          | j        | j        |         |f           t          | j        | j        |          | f           dS )a  Handle the unsetting of a literal for the VSIDS heuristic.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())
        >>> l.lit_heap
        [(-2.0, -3), (-2.0, 2), (-2.0, -2), (0.0, 1), (-2.0, 3), (0.0, -1)]

        >>> l._vsids_lit_unset(2)

        >>> l.lit_heap
        [(-2.0, -3), (-2.0, -2), (-2.0, -2), (-2.0, 2), (-2.0, 3), (0.0, -1),
        ...(-2.0, 2), (0.0, 1)]

        N)re   r   r   r   )rQ   rb   r   s      r   r=   zSATSolver._vsids_lit_unset  sU    & #hh!5s ;<<<#!6 =>>>>>r   c                 Z    | xj         dz  c_         |D ]}| j        |xx         dz  cc<   dS )aD  Handle the addition of a new clause for the VSIDS heuristic.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())

        >>> l.num_learned_clauses
        0
        >>> l.lit_scores
        {-3: -2.0, -2: -2.0, -1: 0.0, 1: 0.0, 2: -2.0, 3: -2.0}

        >>> l._vsids_clause_added({2, -3})

        >>> l.num_learned_clauses
        1
        >>> l.lit_scores
        {-3: -1.0, -2: -2.0, -1: 0.0, 1: 0.0, 2: -1.0, 3: -2.0}

        rU   N)rM   r   rs   s      r   r?   zSATSolver._vsids_clause_added1  sR    . 	  A%   	& 	&COC   A%    	& 	&r   c                 X   t          | j                  }| j                            |           |D ]}| j        |xx         dz  cc<   | j        |d                                      |           | j        |d                                      |           |                     |           dS )a  Add a new clause to the theory.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())

        >>> l.num_learned_clauses
        0
        >>> l.clauses
        [[2, -3], [1], [3, -3], [2, -2], [3, -2]]
        >>> l.sentinels
        {-3: {0, 2}, -2: {3, 4}, 2: {0, 3}, 3: {2, 4}}

        >>> l._simple_add_learned_clause([3])

        >>> l.clauses
        [[2, -3], [1], [3, -3], [2, -2], [3, -2], [3]]
        >>> l.sentinels
        {-3: {0, 2}, -2: {3, 4}, 2: {0, 3}, 3: {2, 4, 5}}

        rU   r   r^   N)rN   rO   rF   rX   rV   r`   r@   )rQ   rt   cls_numrb   s       r   rB   z$SATSolver._simple_add_learned_clauseO  s    2 dl##C    	, 	,C!#&&&!+&&&&s1v""7+++s2w##G,,,s#####r   c                 4    d | j         dd         D             S )a   Build a clause representing the fact that at least one decision made
        so far is wrong.

        Examples
        ========

        >>> from sympy.logic.algorithms.dpll2 import SATSolver
        >>> l = SATSolver([{2, -3}, {1}, {3, -3}, {2, -2},
        ... {3, -2}], {1, 2, 3}, set())
        >>> next(l._find_model())
        {1: True, 2: False, 3: False}
        >>> l._simple_compute_conflict()
        [3]

        c                     g | ]
}|j          S r   )ri   )r   levels     r   r]   z6SATSolver._simple_compute_conflict.<locals>.<listcomp>  s    ???e%.!???r   rU   Nrp   rq   s    r   _simple_compute_conflictz"SATSolver._simple_compute_conflicts  s#      @?t{122????r   c                     dS )zClean up learned clauses.Nr   rq   s    r   _simple_clean_clauseszSATSolver._simple_clean_clauses  r   r   )Nr&   r'   r(   )__name__
__module____qualname____doc__rS   r6   r7   r   propertyrJ   ru   rw   rk   rj   rh   r   r   r8   r   r9   r;   r=   r?   rB   r   r   r   r   r   r   r   K   s         BFDG06 06 06 06d; ; ;0 0 0._  _  _ H   X(  .* * *&5A 5A 5An  B
, , ,:    	C 	C 	C( ( (0) ) )@  ? ? ?.& & &<"$ "$ "$H@ @ @$    r   r   c                       e Zd ZdZddZdS )rH   z
    Represents a single level in the DPLL algorithm, and contains
    enough information for a sound backtracking procedure.
    Fc                 H    || _         t                      | _        || _        d S r
   )ri   r   r/   rg   )rQ   ri   rg   s      r   rS   zLevel.__init__  s      EEr   Nr   )r   r   r   r   rS   r   r   r   rH   rH     s2         
     r   rH   Nr   )r   collectionsr   heapqr   r   sympy.core.sortingr   sympy.assumptions.cnfr   r"   r   r   rH   r   r   r   <module>r      s   	 	 $ # # # # # # # # # # # # # & & & & & & , , , , , ,% % % %Z  | | | | | | | |~	 	 	 	 	 	 	 	 	 	r   