
    \hS                         S r SSKJr  SSKJrJr  SSKJr  SSKJ	r	  SSK
Jr  SS jrS r " S	 S
5      r " S S5      rg)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                    [        U [        5      (       d  [        5       nUR                  U 5        Un S1U R                  ;   a  U(       a	  S S 5       $ gU(       a  [        R
                  " U 5      u  pEOSn/ n[        U R                  U-   U R                  [        5       U R                  US9nUR                  5       nU(       a  [        U5      $  [        U5      $ ! [         a     gf = f)ah  
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   $   #    U  H  ov   M     g 7fN ).0fs     T/var/www/auris/envauris/lib/python3.13/site-packages/sympy/logic/algorithms/dpll2.py	<genexpr>#dpll_satisfiable.<locals>.<genexpr>.   s     'w!Aws   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              #   n   #    Sn  [        U 5      v   SnM  ! [         a    U(       d  Sv    g  g f = f7f)NFT)r   r   )r'   satisfiables     r   r   r   G   sC     Kv,K   K s   5 2525c                       \ rS rSrSr   SS jrS rS rS r\	S 5       r
S	 rS
 rS rS r S rS rS rS rS rS rS rS rS rS rS rS rSrg)r   R   zr
Class for representing a SAT solver capable of
 finding a model to a boolean theory in conjunctive
 normal form.
Nc	                    X0l         XPl        SU l        / U l        / U l        Xpl        Uc  [        [        U5      5      U l        OX@l        U R                  U5        U R                  U5        SU:X  aU  U R                  5         U R                  U l        U R                  U l        U R                   U l        U R$                  U l        O[(        eSU:X  aH  U R*                  U l        U R.                  U l        U R                  R3                  U R4                  5        OSU:X  a  S U l        S U l        O[(        e[7        S5      /U l        X0R:                  l        SU l        SU l         [C        U RD                  5      U l#        Xl$        g )NFvsidssimplenonec                     g r   r   )xs    r   <lambda>$SATSolver.__init__.<locals>.<lambda>~   s        c                      g r   r   r   r5   r   r3   r4      s    Dr5   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$   )	selfrW   r   r7   r   r8   clause_learningr<   r   s	            r   __init__SATSolver.__init__Y   sV    )"# " " ?	 23DL"L""9-  )i"&"7"7D%)%=%=D""&"7"7D%)%=%=D" &%&&*&E&ED#$($A$AD!!!(()C)CD&&4D#$0D!%% Qxj*6' #$ $'$5!r5   c                     [        [        5      U l        [        [        5      U l        S/[        U5      S-   -  U l        g)z+Set up the variable data structures needed.F   N)r   r   	sentinelsintoccurrence_countrV   variable_set)rY   r   s     r   r>   SATSolver._initialize_variables   s3    $S) +C 0"Gs9~'9:r5   c                    U Vs/ s H  n[        U5      PM     snU l        [        U R                  5       H  u  p2S[        U5      :X  a   U R                  R                  US   5        M4  U R                  US      R                  U5        U R                  US      R                  U5        U H  nU R                  U==   S-  ss'   M     M     gs  snf )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=   rW   	enumeraterV   r:   rN   r_   addra   )rY   rW   clauseilits        r   r?   SATSolver._initialize_clauses   s     4;;7V7;"4<<0IA CK%%,,VAY7NN6!9%))!,NN6":&**1-%%c*a/*  1 <s   Cc              #     ^#    SnU R                  5         U R                  (       a  g U R                  U R                  -  S:X  a  U R                   H
  nU" 5         M     U(       a  SnU R
                  R                  nGOHU R                  5       nU =R                  S-  sl        SU:X  Ga  U R                  (       ah  U R                   H#  nU R                  R                  U5      mTc  M#    O   U R                  R                  5       mU R                  R                  5         OSmTb
  TS   (       a=  U R                   Vs0 s H"  nU R                  [        U5      S-
     US:  _M$     snv   OU R                  TS   5        [!        U4S jU R
                  R                   5       5      (       d@  U R#                  5         [!        U4S jU R
                  R                   5       5      (       d  M@  U R
                  R$                  (       a-  U R#                  5         U R
                  R$                  (       a  M-  ['        U R(                  5      S:X  a  gU R
                  R                  * nU R#                  5         U R(                  R+                  [-        USS95        SnGM}  U R(                  R+                  [-        U5      5        U R/                  U5        U R                  5         U R                  (       a  SU l        U R
                  R$                  (       aG  U R#                  5         S['        U R(                  5      :X  a  gU R
                  R$                  (       a  MG  U R1                  U R3                  5       5        U R
                  R                  * nU R#                  5         U R(                  R+                  [-        USS95        SnGM  s  snf 7f)a  
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^   c              3   4   >#    U  H  o* TS    ;   v   M     g7f)r^   Nr   )r   rj   ress     r   r   (SATSolver._find_model.<locals>.<genexpr>   s     %a@`dc!fn@`s   )flipped)	_simplifyr9   rT   r<   r;   rR   decisionrB   r$   r7   
assert_litcheckreset_boundsr   absrJ   any_undorp   rV   rQ   rN   rP   _assign_literalrK   rM   )rY   flip_varfuncrj   enc_varflip_litrn   s         @r   r   SATSolver._find_model   s    8  	 !!DMM1Q6 11DF 2  ))22 ))+""a'" 8 xx'+'8'8G"&(("5"5g">C" % (9 #hhnn.--/"{c!f7;7H7HJ7H  $||CHqL9$'!G ,7HJ J 77A? #&%a@S@S@`@`%a"a"a JJL #&%a@S@S@`@`%a"a"a --55

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

""54#@A] <Js/   C&O-AO)O,BO4AO<D&O$A3Oc                      U R                   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}

re   )rQ   rY   s    r   rR   SATSolver._current_level"  s    & {{2r5   c                 R    U R                   U    H  nX R                  ;   d  M    g   g)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)rW   r7   rY   clsrj   s      r   _clause_satSATSolver._clause_sat7  s+    $ <<$C''' % r5   c                 $    X R                   U   ;   $ )aI  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_   )rY   rj   r   s      r   _is_sentinelSATSolver._is_sentinelN  s    " nnS)))r5   c                    U R                   R                  U5        U R                  R                   R                  U5        SU R                  [	        U5      '   U R                  U5        [        U R                  U*    5      nU H  nU R                  U5      (       a  M  SnU R                  U    H  nXQ* :w  d  M  U R                  XS5      (       a  UnM%  U R                  [	        U5         (       a  MD  U R                  U*    R                  U5        U R                  U   R                  U5        Sn  O   U(       d  M  U R                  R                  U5        M     g)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)r7   rg   rR   rb   rv   rD   r=   r_   r   rW   r   remover:   rN   )rY   rj   sentinel_listr   other_sentinelnewlits         r   ry   SATSolver._assign_literala  s!   > 	c"((,,S1&*#c(#s#T^^SD12 C##C((!%"ll3/F~,,V99-3N!%!2!23v;!?!? NNC4077< NN6266s;-1N! 0 ">))00@ !r5   c                     U R                   R                   HG  nU R                  R                  U5        U R                  U5        SU R                  [        U5      '   MI     U R                  R                  5         g)a  
_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)rR   r7   r   rF   rb   rv   rQ   poprY   rj   s     r   rx   SATSolver._undo  s`    , &&33C$$S)$*/Dc#h' 4 	r5   c                 v    SnU(       a0  SnXR                  5       -  nXR                  5       -  nU(       a  M/  gg)a  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)rY   changeds     r   rq   SATSolver._simplify  s8    . G((G))++G gr5   c                    [        U R                  5      S:  nU R                  (       a^  U R                  R                  5       nU* U R                  ;   a  SU l        / U l        gU R                  U5        U R                  (       a  M^  U$ )z/Perform unit propagation on the current theory.r   TF)rV   r:   r   r7   r9   ry   )rY   resultnext_lits      r   r   SATSolver._unit_prop  sz    T**+a/##,,002HyD---&*#(*%$$X. ### r5   c                     g)z2Look for pure literals and assign them when found.Fr   r   s    r   r   SATSolver._pure_literal  s    r5   c                    / U l         0 U l        [        S[        U R                  5      5       H  n[        U R                  U   * 5      U R                  U'   [        U R                  U*    * 5      U R                  U* '   [        U R                   U R                  U   U45        [        U R                   U R                  U*    U* 45        M     g)z>Initialize the data structures needed for the VSIDS heuristic.r^   N)lit_heap
lit_scoresrangerV   rb   floatra   r   )rY   vars     r   r@   SATSolver._vsids_init  s    C 1 123C#($*?*?*D)D#EDOOC $)4+@+@#+F*F$GDOOSD!T]]T__S%93$?@T]]T__cT%:SD$AB	 4r5   c                 t    U R                   R                  5        H  nU R                   U==   S-  ss'   M     g)as  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_decaySATSolver._vsids_decay  s/    * ??'')COOC C'  *r5   c                 |   [        U R                  5      S:X  a  gU R                  [        U R                  S   S   5         (       a^  [	        U R                  5        [        U R                  5      S:X  a  gU R                  [        U R                  S   S   5         (       a  M^  [	        U R                  5      S   $ )ay  
    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^   )rV   r   rb   rv   r   r   s    r   rA   SATSolver._vsids_calculate  s    * t}}" DMM!$4Q$7 89DMM"4==!Q& DMM!$4Q$7 899
 t}}%a((r5   c                     g)z;Handle the assignment of a literal for the VSIDS heuristic.Nr   r   s     r   rC   SATSolver._vsids_lit_assigned3      r5   c                     [        U5      n[        U R                  U R                  U   U45        [        U R                  U R                  U*    U* 45        g)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)rv   r   r   r   )rY   rj   r   s      r   rE   SATSolver._vsids_lit_unset7  sI    & #h!5s ;<#!6 =>r5   c                 n    U =R                   S-  sl         U H  nU R                  U==   S-  ss'   M     g)a  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)rU   r   r   s      r   rG   SATSolver._vsids_clause_addedN  s3    . 	  A% COOC A%  r5   c                 J   [        U R                  5      nU R                  R                  U5        U H  nU R                  U==   S-  ss'   M     U R                  US      R                  U5        U R                  US      R                  U5        U R                  U5        g)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   re   N)rV   rW   rN   ra   r_   rg   rH   )rY   r   cls_numrj   s       r   rJ   $SATSolver._simple_add_learned_clausel  s    2 dll#C C!!#&!+&  	s1v""7+s2w##G,s#r5   c                 `    U R                   SS  Vs/ s H  oR                  * PM     sn$ s  snf )aR  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)rQ   rr   )rY   levels     r   rL   "SATSolver._simple_compute_conflict  s+      04{{12?e..!???s   +c                     g)zClean up learned clauses.Nr   r   s    r   rO   SATSolver._simple_clean_clauses  r   r5   )r<   r:   rK   rW   rM   rB   rH   rD   rF   r8   r9   rQ   r   r   r$   rT   rU   ra   rX   r_   r   r;   r7   rb   )Nr.   r0   i  N)__name__
__module____qualname____firstlineno____doc__r[   r>   r?   r   propertyrR   r   r   ry   rx   rq   r   r   r@   r   rA   rC   rE   rG   rJ   rL   rO   __static_attributes__r   r5   r   r   r   R   s     BFDG"3j;0.r n  (.*&5AnB
,:	C(0)@?.&<"$H@$r5   r   c                   "    \ rS rSrSrSS jrSrg)rP   i  zv
Represents a single level in the DPLL algorithm, and contains
enough information for a sound backtracking procedure.
c                 :    Xl         [        5       U l        X l        g r   )rr   r   r7   rp   )rY   rr   rp   s      r   r[   Level.__init__  s     Er5   )rr   rp   r7   Nr   )r   r   r   r   r   r[   r   r   r5   r   rP   rP     s    
r5   rP   N)FF)r   collectionsr   heapqr   r   sympy.core.sortingr   sympy.assumptions.cnfr   !sympy.logic.algorithms.lra_theoryr   r(   r   r   rP   r   r5   r   <module>r      s=   	 $ # & , 7*dR	 R	j	 	r5   