U
    L?hS  ã                   @   sv   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d	„Zd
d„ ZG dd„ dƒZG dd„ dƒZdS )z«Implementation of DPLL algorithm

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

References:
  - https://en.wikipedia.org/wiki/DPLL_algorithm
é    )Údefaultdict)ÚheappushÚheappop)Úordered)Ú
EncodedCNF)Ú	LRASolverFc                 C   s´   t | tƒstƒ }| | ¡ |} dh| jkr@|r<dd„ dD ƒS dS |rTt | ¡\}}nd}g }t| j| | jtƒ | j	|d}| 
¡ }|rŽt|ƒS z
t|ƒW S  tk
r®   Y dS X dS )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                 s   s   | ]
}|V  qd S ©N© )Ú.0Úfr	   r	   úN/var/www/html/venv/lib/python3.8/site-packages/sympy/logic/algorithms/dpll2.pyÚ	<genexpr>.   s     z#dpll_satisfiable.<locals>.<genexpr>)FFN)Ú
lra_theory)Ú
isinstancer   Zadd_propÚdatar   Zfrom_encoded_cnfÚ	SATSolverÚ	variablesÚsetÚsymbolsÚ_find_modelÚ_all_modelsÚnextÚStopIteration)ÚexprZ
all_modelsZuse_lra_theoryZexprsÚlraZimmediate_conflictsZsolverÚmodelsr	   r	   r   Údpll_satisfiable   s(    


r   c                 c   s<   d}zt | ƒV  d}qW n tk
r6   |s2dV  Y nX d S )NFT)r   r   )r   Zsatisfiabler	   r	   r   r   G   s    

r   c                   @   s¾   e Zd ZdZd0dd„Zdd	„ Zd
d„ Zdd„ Zedd„ ƒZ	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zd d!„ Zd"d#„ Zd$d%„ Zd&d'„ Zd(d)„ Zd*d+„ Zd,d-„ Zd.d/„ ZdS )1r   z‚
    Class for representing a SAT solver capable of
     finding a model to a boolean theory in conjunctive
     normal form.
    NÚvsidsÚnoneéô  c	           	      C   s  || _ || _d| _g | _g | _|| _|d kr<tt|ƒƒ| _n|| _|  	|¡ |  
|¡ d|krˆ|  ¡  | j| _| j| _| j| _| j| _nt‚d|kr´| j| _| j| _| j | j¡ n"d|krÒdd„ | _dd„ | _nt‚tdƒg| _|| j_d| _d| _ t!| j"ƒ| _#|| _$d S )	NFr   Úsimpler   c                 S   s   d S r   r	   )Úxr	   r	   r   Ú<lambda>~   ó    z$SATSolver.__init__.<locals>.<lambda>c                   S   s   d S r   r	   r	   r	   r	   r   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_levelZvarsettingsÚnum_decisionsÚnum_learned_clausesÚlenÚclausesZoriginal_num_clausesr   )	ÚselfrC   r   r$   r   r%   Zclause_learningr)   r   r	   r	   r   Ú__init__Y   s@    



zSATSolver.__init__c                 C   s,   t tƒ| _t tƒ| _dgt|ƒd  | _dS )z+Set up the variable data structures needed.Fé   N)r   r   Ú	sentinelsÚintÚoccurrence_countrB   Úvariable_set)rD   r   r	   r	   r   r+   Ž   s    

zSATSolver._initialize_variablesc                 C   sŠ   dd„ |D ƒ| _ t| j ƒD ]j\}}dt|ƒkr@| j |d ¡ q| j|d   |¡ | j|d   |¡ |D ]}| j|  d7  < qlq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                 S   s   g | ]}t |ƒ‘qS r	   )r*   )r
   Úclauser	   r	   r   Ú
<listcomp>œ   s     z1SATSolver._initialize_clauses.<locals>.<listcomp>rF   r   éÿÿÿÿN)rC   Ú	enumeraterB   r'   r;   rG   ÚaddrI   )rD   rC   ÚirK   Úlitr	   r	   r   r,   ”   s    zSATSolver._initialize_clausesc                 #   sÌ  d}ˆ   ¡  ˆ jrdS ˆ jˆ j dkr8ˆ jD ]
}|ƒ  q,|rJd}ˆ jj}nüˆ  ¡ }ˆ  jd7  _d|kr6ˆ jrªˆ j	D ]}ˆ j 
|¡}|dk	rv q”qvˆ j ¡ }ˆ j ¡  nd}|dks¾|d rÖ‡ fdd„ˆ j	D ƒV  nˆ  |d ¡ ˆ jjröˆ  ¡  qätˆ jƒdkr
dS ˆ jj }ˆ  ¡  ˆ j t|dd¡ d}qˆ j t|ƒ¡ ˆ  |¡ ˆ   ¡  ˆ jrdˆ _ˆ jjrŽˆ  ¡  dtˆ jƒkrddS qdˆ  ˆ  ¡ ¡ ˆ jj }ˆ  ¡  ˆ j t|dd¡ d}qdS )	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}]

        FNr   rF   c                    s$   i | ]}ˆ j t|ƒd   |dk“qS )rF   r   )r   Úabs)r
   rQ   ©rD   r	   r   Ú
<dictcomp>í   s   ÿz)SATSolver._find_model.<locals>.<dictcomp>T)Úflipped)Ú	_simplifyr&   r@   r)   r(   r?   Údecisionr/   r   r$   Z
assert_litÚcheckZreset_boundsr7   rU   Ú_undorB   r>   r;   r=   Ú_assign_literalr8   r:   )rD   Zflip_varÚfuncrQ   Zenc_varÚresZflip_litr	   rS   r   r   «   sb    





ÿ





zSATSolver._find_modelc                 C   s
   | 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}

        rM   ©r>   rS   r	   r	   r   r?     s    zSATSolver._current_levelc                 C   s$   | j | D ]}|| jkr
 dS q
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)rC   r$   ©rD   ÚclsrQ   r	   r	   r   Ú_clause_sat3  s    
zSATSolver._clause_satc                 C   s   || j | k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

        )rG   )rD   rQ   r_   r	   r	   r   Ú_is_sentinelJ  s    zSATSolver._is_sentinelc                 C   sÒ   | j  |¡ | jj  |¡ d| jt|ƒ< |  |¡ t| j|  ƒ}|D ]†}|  |¡sFd}| j	| D ]X}|| krb|  
||¡r‚|}qb| jt|ƒ sb| j|   |¡ | j|  |¡ d} q¼qb|rF| j |¡ qFd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$   rO   r?   rJ   rR   r1   r*   rG   r`   rC   ra   Úremover'   r;   )rD   rQ   Zsentinel_listr_   Zother_sentinelZnewlitr	   r	   r   rZ   ]  s&    


zSATSolver._assign_literalc                 C   s@   | j jD ](}| j |¡ |  |¡ d| jt|ƒ< q| 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)r?   r$   rb   r3   rJ   rR   r>   Úpop©rD   rQ   r	   r	   r   rY   ”  s
    
zSATSolver._undoc                 C   s*   d}|r&d}||   ¡ O }||  ¡ O }q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)rD   Úchangedr	   r	   r   rV   º  s
    zSATSolver._simplifyc                 C   sJ   t | jƒdk}| jrF| j ¡ }| | jkr:d| _g | _dS |  |¡ q|S )z/Perform unit propagation on the current theory.r   TF)rB   r'   rc   r$   r&   rZ   )rD   ÚresultZnext_litr	   r	   r   re   ×  s    
zSATSolver._unit_propc                 C   s   dS )z2Look for pure literals and assign them when found.Fr	   rS   r	   r	   r   rf   å  s    zSATSolver._pure_literalc                 C   s†   g | _ i | _tdt| jƒƒD ]d}t| j|  ƒ| j|< t| j|   ƒ| j| < t| j | j| |fƒ t| j | j|  | fƒ qdS )z>Initialize the data structures needed for the VSIDS heuristic.rF   N)Úlit_heapÚ
lit_scoresÚrangerB   rJ   ÚfloatrI   r   )rD   Úvarr	   r	   r   r-   ì  s    zSATSolver._vsids_initc                 C   s&   | j  ¡ D ]}| j |  d  < q
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)rj   Úkeysrd   r	   r	   r   Ú_vsids_decay÷  s    zSATSolver._vsids_decayc                 C   sV   t | jƒdkrdS | jt| jd d ƒ rHt| jƒ t | jƒdkrdS qt| 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   rF   )rB   ri   rJ   rR   r   rS   r	   r	   r   r.     s    
zSATSolver._vsids_calculatec                 C   s   dS )z;Handle the assignment of a literal for the VSIDS heuristic.Nr	   rd   r	   r	   r   r0   /  s    zSATSolver._vsids_lit_assignedc                 C   s<   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)rR   r   ri   rj   )rD   rQ   rm   r	   r	   r   r2   3  s    zSATSolver._vsids_lit_unsetc                 C   s.   |  j d7  _ |D ]}| j|  d7  < q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}

        rF   N)rA   rj   r^   r	   r	   r   r4   J  s    zSATSolver._vsids_clause_addedc                 C   sh   t | jƒ}| j |¡ |D ]}| j|  d7  < q| 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}}

        rF   r   rM   N)rB   rC   r;   rI   rG   rO   r5   )rD   r_   Zcls_numrQ   r	   r	   r   r7   h  s    
z$SATSolver._simple_add_learned_clausec                 C   s   d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                 S   s   g | ]}|j  ‘qS r	   )rW   )r
   Úlevelr	   r	   r   rL   œ  s     z6SATSolver._simple_compute_conflict.<locals>.<listcomp>rF   Nr]   rS   r	   r	   r   r9   Œ  s    z"SATSolver._simple_compute_conflictc                 C   s   dS )zClean up learned clauses.Nr	   rS   r	   r	   r   r<   ž  s    zSATSolver._simple_clean_clauses)Nr   r   r   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__rE   r+   r,   r   Úpropertyr?   r`   ra   rZ   rY   rV   re   rf   r-   ro   r.   r0   r2   r4   r7   r9   r<   r	   r	   r	   r   r   R   s8          þ
5s
7& $r   c                   @   s   e Zd ZdZddd„ZdS )r=   z‚
    Represents a single level in the DPLL algorithm, and contains
    enough information for a sound backtracking procedure.
    Fc                 C   s   || _ tƒ | _|| _d S r   )rW   r   r$   rU   )rD   rW   rU   r	   r	   r   rE   ©  s    zLevel.__init__N)F)rq   rr   rs   rt   rE   r	   r	   r	   r   r=   £  s   r=   N)FF)rt   Úcollectionsr   Úheapqr   r   Zsympy.core.sortingr   Zsympy.assumptions.cnfr   Z!sympy.logic.algorithms.lra_theoryr   r   r   r   r=   r	   r	   r	   r   Ú<module>   s   
2    U