
    sgS                     ~    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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)
EncodedCNF)	LRASolverc                    t        | t              st               }|j                  |        |} dh| j                  v r|r	d dD        S y|rt	        j
                  |       \  }}nd}g }t        | j                  |z   | 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     O/var/www/html/venv/lib/python3.12/site-packages/sympy/logic/algorithms/dpll2.py	<genexpr>z#dpll_satisfiable.<locals>.<genexpr>.   s     '!A's   FFN)
lra_theory)
isinstancer   add_propdatar   from_encoded_cnf	SATSolver	variablessetsymbols_find_model_all_modelsnextStopIteration)expr
all_modelsuse_lra_theoryexprslraimmediate_conflictssolvermodelss           r   dpll_satisfiabler'      s    " dJ't 	
sdii'w''#,#=#=d#C   tyy#66t||hklF!F6""F| s   ?
C
 
	CC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   G   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>~           c                       y r   r   r   r3   r   r1   z$SATSolver.__init__.<locals>.<lambda>   r2   r3   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_clausesr#   )	selfrU   r   r5   r   r6   clause_learningr:   r   s	            r   __init__zSATSolver.__init__Y   sb    )"# " " ?	 23DL"DL""9-  )i"&"7"7D%)%=%=D""&"7"7D%)%=%=D" &%&&*&E&ED#$($A$AD!!!(()C)CD&&4D#$0D!%% Qxj*6' #$ $'$5!r3   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_countrT   variable_set)rW   r   s     r   r<   zSATSolver._initialize_variables   s3    $S) +C 0"Gs9~'9:r3   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.
        r[   r   N)	r;   rU   	enumeraterT   r8   rL   r\   addr^   )rW   rU   clauseilits        r   r=   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(  ro| j                  re| j                  D ]!  }| j                  j                  |      }|! n | j                  j                         }| j                  j                          nd}||d   r:| j                  D ci c]!  }| j                  t        |      dz
     |dkD  # c} n| j                  |d          | 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/                  | j1                                | j
                  j                   }| j#                          | j&                  j)                  t+        |d             d}c 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   r[   )flipped)	_simplifyr7   rR   r:   r9   rP   decisionr@   r#   r5   
assert_litcheckreset_boundsr   absrH   rh   _undorT   rO   rL   rN   _assign_literalrI   rK   )rW   flip_varfuncrf   enc_varresflip_lits          r   r   zSATSolver._find_model   s    8  	 !!DMM1Q6 11 DF  ))22 ))+""a'" 8 xx'+'8'8 &G"&(("5"5g">C" %& #hhnn.--/"{c!f7;7H7HJ03  $||CHqL9$'!G , J J 77A?--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U <Js(   CL0AL0&&L+AL0#DL0;A5L0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}

        ra   )rO   rW   s    r   rP   zSATSolver._current_level  s    & {{2r3   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)rU   r5   rW   clsrf   s      r   _clause_satzSATSolver._clause_sat3  s2    $ <<$ 	Cd'''	 r3   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

        )r\   )rW   rf   rz   s      r   _is_sentinelzSATSolver._is_sentinelJ  s    " dnnS)))r3   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)r5   rc   rP   r_   rn   rB   r;   r\   r{   rU   r}   remover8   rL   )rW   rf   sentinel_listrz   other_sentinelnewlits         r   rp   zSATSolver._assign_literal]  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r3   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)rP   r5   r   rD   r_   rn   rO   poprW   rf   s     r   ro   zSATSolver._undo  se    , &&33 	0C$$S)$*/Dc#h'	0 	r3   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)rW   changeds     r   ri   zSATSolver._simplify  s:    . Gt((Gt))++G r3   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)rT   r8   r   r5   r7   rp   )rW   resultnext_lits      r   r   zSATSolver._unit_prop  sw    T**+a/##,,002HyD---&*#(*%$$X. ## r3   c                      y)z2Look for pure literals and assign them when found.Fr   rw   s    r   r   zSATSolver._pure_literal  s    r3   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.r[   N)lit_heap
lit_scoresrangerT   r_   floatr^   r   )rW   vars     r   r>   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r3   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   keysr   s     r   _vsids_decayzSATSolver._vsids_decay  s4    * ??'') 	(COOC C' 	(r3   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   r[   )rT   r   r_   rn   r   rw   s    r   r?   zSATSolver._vsids_calculate  s    * t}}" DMM!$4Q$7 89DMM"4==!Q& DMM!$4Q$7 89
 t}}%a((r3   c                      y)z;Handle the assignment of a literal for the VSIDS heuristic.Nr   r   s     r   rA   zSATSolver._vsids_lit_assigned/      r3   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)rn   r   r   r   )rW   rf   r   s      r   rC   zSATSolver._vsids_lit_unset3  sI    & #h!5s ;<#!6 =>r3   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}

        r[   N)rS   r   ry   s      r   rE   zSATSolver._vsids_clause_addedJ  s8    . 	  A%  	&COOC A% 	&r3   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}}

        r[   r   ra   N)rT   rU   rL   r^   r\   rc   rF   )rW   rz   cls_numrf   s       r   rH   z$SATSolver._simple_add_learned_clauseh  s    2 dll#C  	,C!!#&!+&	, 	s1v""7+s2w##G,s#r3   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]

        r[   N)rO   rj   )rW   levels     r   rJ   z"SATSolver._simple_compute_conflict  s)      04{{12?e%..!???s   )c                      y)zClean up learned clauses.Nr   rw   s    r   rM   zSATSolver._simple_clean_clauses  r   r3   )Nr,   r.   i  N)__name__
__module____qualname____doc__rY   r<   r=   r   propertyrP   r{   r}   rp   ro   ri   r   r   r>   r   r?   rA   rC   rE   rH   rJ   rM   r   r3   r   r   r   R   s     BFDG"3j;0.n f  (.*&5AnB
,:	C(0)@?.&<"$H@$r3   r   c                       e Zd ZdZddZy)rN   z
    Represents a single level in the DPLL algorithm, and contains
    enough information for a sound backtracking procedure.
    c                 >    || _         t               | _        || _        y r   )rj   r   r5   rh   )rW   rj   rh   s      r   rY   zLevel.__init__  s     Er3   Nr   )r   r   r   r   rY   r   r3   r   rN   rN     s    
r3   rN   N)FF)r   collectionsr   heapqr   r   sympy.core.sortingr   sympy.assumptions.cnfr   !sympy.logic.algorithms.lra_theoryr   r'   r   r   rN   r   r3   r   <module>r      s=   	 $ # & , 7*dN	 N	b	 	r3   