
    MZd]O                     r    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y)zImplementation of DPLL algorithm

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

References:
  - https://en.wikipedia.org/wiki/DPLL_algorithm
    )defaultdict)heappushheappop)ordered)
EncodedCNFc                 j   t        | t              st               }|j                  |        |} dh| j                  v r|r	d dD        S yt	        | j                  | j
                  t               | j                        }|j                         }|rt        |      S 	 t        |      S # t        $ r Y y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   | ]  }|  y wN ).0fs     >/usr/lib/python3/dist-packages/sympy/logic/algorithms/dpll2.py	<genexpr>z#dpll_satisfiable.<locals>.<genexpr>,   s     '!A's   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 	
sdii'w''tyy$..#%FF!F6""F| s   
B& &	B21B2c              #   `   K   d}	 	 t        |        d}# t        $ r |sd Y y Y y w xY ww)NFT)r   r   )r    satisfiables     r   r   r   @   sD     Kv,K   K s   . +.+.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y)r   z
    Class for representing a SAT solver capable of
     finding a model to a boolean theory in conjunctive
     normal form.
    Nc                    || _         || _        d| _        g | _        g | _        || _        |t        t        |            | _        n|| _        | j                  |       | j                  |       d|k(  rU| j                          | j                  | _        | j                  | _        | j                   | _        | j$                  | _        nt(        d|k(  rH| j*                  | _        | j.                  | _        | j                  j3                  | j4                         nd|k(  rd | _        d | _        nt(        t7        d      g| _        || j:                  _        d| _        d| _         tC        | jD                        | _#        y )NFvsidssimplenonec                      y r
   r   )xs    r   <lambda>z$SATSolver.__init__.<locals>.<lambda>v           c                       y r
   r   r   r-   r   r+   z$SATSolver.__init__.<locals>.<lambda>w   r,   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3DL"DL""9-  )i"&"7"7D%)%=%=D""&"7"7D%)%=%=D" &%&&*&E&ED#$($@$@D!!!(()B)BC&&4D#$0D!%% Qxj*6' #$ $'$5!r-   c                     t        t              | _        t        t              | _        dgt        |      dz   z  | _        y)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   s3    $S) +C 0"Gs9~'9:r-   c                    |D cg c]  }t        |       c}| _        t        | j                        D ]  \  }}dt        |      k(  r| j                  j                  |d          3| j                  |d      j                  |       | j                  |d      j                  |       |D ]  }| j                  |xx   dz  cc<     yc c}w )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.
        rU   r   N)	r5   rO   	enumeraterN   r2   rF   rV   addrX   )rQ   rO   clauseilits        r   r7   zSATSolver._initialize_clauses   s     4;;V;"4<<0 	0IAv CK%%,,VAY7NN6!9%))!,NN6":&**1- 0%%c*a/*0	0 <s   C
c              #   D  K   d}| j                          | j                  ry	 | j                  | j                  z  dk(  r| j                  D ]	  } |         |rd}| j
                  j                  }n.| j                         }| xj                  dz  c_        d|k(  r| j                  D ci c]!  }| j                  t        |      dz
     |dkD  # c} | j
                  j                  r'| j                          | j
                  j                  r't        | j                        dk(  ry| j
                  j                   }| j                          | j                  j                  t!        |d             d}[| j                  j                  t!        |             | j#                  |       | j                          | j                  rd| _        | j
                  j                  r@| j                          dt        | j                        k(  ry| j
                  j                  r@| j%                  | j'                                | j
                  j                   }| j                          | j                  j                  t!        |d             d}xc c}w w)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   )flipped)	_simplifyr1   rL   r4   r3   rJ   decisionr:   r/   r   absrb   _undorN   rI   rF   rH   _assign_literalrC   rE   )rQ   flip_varfuncr`   flip_lits        r   r   zSATSolver._find_model   sF    8  	 !!DMM1Q6 11 DF  ))22 ))+""a'" 8373D3DF,/  <<C15 #a( F F--55

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

""54#@Aw $Fs!   B)J +&JAJ DJ +A5J 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                 L    | j                   |   D ]  }|| j                  v s y y)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   clsr`   s      r   _clause_satzSATSolver._clause_sat  s2    $ <<$ 	Cd'''	 r-   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   r`   ro   s      r   _is_sentinelzSATSolver._is_sentinel1  s    " dnnS)))r-   c                    | j                   j                  |       | j                  j                   j                  |       d| j                  t	        |      <   | j                  |       t        | j                  |          }|D ]  }| j                  |      rd}| j                  |   D ]w  }|| k7  s
| j                  ||      r|}| j                  t	        |         r8| j                  |    j                  |       | j                  |   j                  |       d} n |s| j                  j                  |        y)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   rp   rO   rr   remover2   rF   )rQ   r`   sentinel_listro   other_sentinelnewlits         r   rg   zSATSolver._assign_literalD  s&   > 	c"((,,S1&*#c(#s#T^^SD12  	AC##C(!%"ll3/ "F#~,,VS9-3N!%!2!23v;!? NNC4077< NN6266s;-1N!" "))00@	Ar-   c                     | j                   j                  D ]F  }| j                  j                  |       | j                  |       d| j                  t        |      <   H | j                  j                          y)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/   rt   r>   rY   re   rI   poprQ   r`   s     r   rf   zSATSolver._undo{  se    , &&33 	0C$$S)$*/Dc#h'	0 	r-   c                 d    d}|r,d}|| j                         z  }|| j                         z  }|r+yy)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   rc   zSATSolver._simplify  s:    . Gt((Gt))++G r-   c                     t        | j                        dkD  }| j                  rV| j                  j                         }| | j                  v rd| _        g | _        y| j                  |       | j                  rV|S )z/Perform unit propagation on the current theory.r   TF)rN   r2   ry   r/   r1   rg   )rQ   resultnext_lits      r   r|   zSATSolver._unit_prop  sw    T**+a/##,,002HyD---&*#(*%$$X. ## r-   c                      y)z2Look for pure literals and assign them when found.Fr   rl   s    r   r}   zSATSolver._pure_literal  s    r-   c                    g | _         i | _        t        dt        | j                              D ]  }t        | j                  |          | j                  |<   t        | j                  |           | j                  | <   t        | j                   | j                  |   |f       t        | j                   | j                  |    | f        y)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 123 	CC#($*?*?*D)D#EDOOC $)4+@+@#+F*F$GDOOSD!T]]T__S%93$?@T]]T__cT%:SD$AB		Cr-   c                 p    | j                   j                         D ]  }| j                   |xx   dz  cc<    y)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   keysrz   s     r   _vsids_decayzSATSolver._vsids_decay  s4    * ??'') 	(COOC C' 	(r-   c                 b   t        | j                        dk(  ry| j                  t        | j                  d   d            rWt	        | j                         t        | j                        dk(  ry| j                  t        | j                  d   d            rWt	        | 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   rl   s    r   r9   zSATSolver._vsids_calculate  s    * t}}" DMM!$4Q$7 89DMM"4==!Q& DMM!$4Q$7 89
 t}}%a((r-   c                      y)z;Handle the assignment of a literal for the VSIDS heuristic.Nr   rz   s     r   r;   zSATSolver._vsids_lit_assigned      r-   c                     t        |      }t        | j                  | j                  |   |f       t        | j                  | j                  |    | f       y)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   r`   r   s      r   r=   zSATSolver._vsids_lit_unset  sI    & #h!5s ;<#!6 =>r-   c                 j    | xj                   dz  c_         |D ]  }| j                  |xx   dz  cc<    y)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   rn   s      r   r?   zSATSolver._vsids_clause_added1  s8    . 	  A%  	&COOC A% 	&r-   c                 F   t        | j                        }| j                  j                  |       |D ]  }| j                  |xx   dz  cc<    | j                  |d      j                  |       | j                  |d      j                  |       | j                  |       y)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   ro   cls_numr`   s       r   rB   z$SATSolver._simple_add_learned_clauseO  s    2 dll#C  	,C!!#&!+&	, 	s1v""7+s2w##G,s#r-   c                 \    | j                   dd D cg c]  }|j                    c}S c c}w )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]

        rU   N)rI   rd   )rQ   levels     r   _simple_compute_conflictz"SATSolver._simple_compute_conflicts  s)      04{{12?e%..!???s   )c                      y)zClean up learned clauses.Nr   rl   s    r   _simple_clean_clauseszSATSolver._simple_clean_clauses  r   r-   )Nr&   r(   i  )__name__
__module____qualname____doc__rS   r6   r7   r   propertyrJ   rp   rr   rg   rf   rc   r|   r}   r8   r   r9   r;   r=   r?   rB   r   r   r   r-   r   r   r   K   s     BFDG06d;0._ H  (.*&5AnB
,:	C(0)@?.&<"$H@$r-   r   c                       e Zd ZdZddZy)rH   z
    Represents a single level in the DPLL algorithm, and contains
    enough information for a sound backtracking procedure.
    c                 >    || _         t               | _        || _        y r
   )rd   r   r/   rb   )rQ   rd   rb   s      r   rS   zLevel.__init__  s     Er-   Nr   )r   r   r   r   rS   r   r-   r   rH   rH     s    
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-   