o
    o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 )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)	LRASolverFc                 C   s   t | tst }||  |} dh| jv r |rdd dD S dS |r*t| \}}nd}g }t| j| | jt | j	|d}|
 }|rGt|S zt|W S  tyV   Y dS 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                 s   s    | ]}|V  qd S N ).0fr	   r	   p/var/www/html/construction_image-detection-poc/venv/lib/python3.10/site-packages/sympy/logic/algorithms/dpll2.py	<genexpr>.   s    z#dpll_satisfiable.<locals>.<genexpr>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modelsr	   r	   r   dpll_satisfiable   s*   


r$   c                 c   s>    d}z		 t | V  d}q ty   |sdV  Y d S Y d S w )NFT)r   r   )r#   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 u rtt|| _n|| _| 	| | 
| d|krD|   | j| _| j| _| j| _| j| _ntd|krZ| j| _| j| _| j| j nd|kridd | _dd | _nttdg| _|| j_d| _d| _ t!| j"| _#|| _$d S )	NFr&   simpler'   c                 S      d S r   r	   )xr	   r	   r   <lambda>~       z$SATSolver.__init__.<locals>.<lambda>c                   S   r*   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_levelvarsettingsnum_decisionsnum_learned_clauseslenclausesoriginal_num_clausesr    )	selfrN   r   r.   r   r/   clause_learningr3   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_countrM   variable_set)rP   r   r	   r	   r   r5      s   

zSATSolver._initialize_variablesc                 C   s   dd |D | _ t| j D ]5\}}dt|kr | j|d  q| j|d  | | j|d  | |D ]}| j|  d7  < q6q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	   )r4   )r
   clauser	   r	   r   
<listcomp>       z1SATSolver._initialize_clauses.<locals>.<listcomp>rS   r   N)rN   	enumeraterM   r1   rE   rT   addrV   )rP   rN   irX   litr	   r	   r   r6      s   zSATSolver._initialize_clausesc                 #   s   d}     jrdS 	  j j dkr jD ]}|  q|r'd} jj}n  }  jd7  _d|kr jrV j	D ]} j
|}|durJ nq< j } j  nd}|du s`|d rl fdd j	D V  n |d   jjr    jjswt jdkrdS  jj }    jt|dd d}q jt|  |      jrd _ jjrʈ   dt jkrdS  jjs     jj }    jt|dd d}q)	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   rS   c                    s$   i | ]} j t|d   |dkqS )rS   r   )r   abs)r
   r_   rP   r	   r   
<dictcomp>   s
    z)SATSolver._find_model.<locals>.<dictcomp>)flipped)	_simplifyr0   rK   r3   r2   rI   decisionr9   r    r.   
assert_litcheckreset_boundsrA   rc   _undorM   rH   rE   rG   _assign_literalrB   rD   )rP   flip_varfuncr_   enc_varresflip_litr	   ra   r   r      sn   








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}

        r[   rH   ra   r	   r	   r   rI     s   
zSATSolver._current_levelc                 C   s$   | j | D ]
}|| jv 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)rN   r.   rP   clsr_   r	   r	   r   _clause_sat3  s
   
zSATSolver._clause_satc                 C   s   || 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

        )rT   )rP   r_   rr   r	   r	   r   _is_sentinelJ  s   zSATSolver._is_sentinelc                 C   s   | j | | jj | d| jt|< | | t| j|  }|D ]C}| |sfd}| j	| D ],}|| kr]| 
||rA|}q1| jt| s]| j|  | | j| | d} nq1|rf| j| q#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]   rI   rW   r`   r;   r4   rT   rs   rN   rt   remover1   rE   )rP   r_   sentinel_listrr   other_sentinelnewlitr	   r	   r   rj   ]  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)rI   r.   ru   r=   rW   r`   rH   poprP   r_   r	   r	   r   ri     s
   
zSATSolver._undoc                 C   s0   d}|rd}||   O }||  O }|sd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)rP   changedr	   r	   r   rd     s   zSATSolver._simplifyc                 C   sN   t | jdk}| jr%| j }| | jv rd| _g | _dS | | | js
|S )z/Perform unit propagation on the current theory.r   TF)rM   r1   ry   r.   r0   rj   )rP   resultnext_litr	   r	   r   r{     s   

	zSATSolver._unit_propc                 C      dS )z2Look for pure literals and assign them when found.Fr	   ra   r	   r	   r   r|        zSATSolver._pure_literalc                 C   s   g | _ i | _tdt| jD ]2}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.rS   N)lit_heap
lit_scoresrangerM   rW   floatrV   r   )rP   varr	   r	   r   r7     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)r   keysrz   r	   r	   r   _vsids_decay  s   zSATSolver._vsids_decayc                 C   sl   t | jdkr	dS | jt| jd d  r/t| j t | jdkr#dS | jt| jd d  s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   rS   )rM   r   rW   r`   r   ra   r	   r	   r   r8     s   
zSATSolver._vsids_calculatec                 C   r   )z;Handle the assignment of a literal for the VSIDS heuristic.Nr	   rz   r	   r	   r   r:   /  r   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)r`   r   r   r   )rP   r_   r   r	   r	   r   r<   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}

        rS   N)rL   r   rq   r	   r	   r   r>   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}}

        rS   r   r[   N)rM   rN   rE   rV   rT   r]   r?   )rP   rr   cls_numr_   r	   r	   r   rA   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	   )re   )r
   levelr	   r	   r   rY     rZ   z6SATSolver._simple_compute_conflict.<locals>.<listcomp>rS   Nrp   ra   r	   r	   r   rC     s   z"SATSolver._simple_compute_conflictc                 C   r   )zClean up learned clauses.Nr	   ra   r	   r	   r   rF     r   zSATSolver._simple_clean_clauses)Nr&   r'   r(   N)__name__
__module____qualname____doc__rR   r5   r6   r   propertyrI   rs   rt   rj   ri   rd   r{   r|   r7   r   r8   r:   r<   r>   rA   rC   rF   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 )rG   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   )re   r   r.   rc   )rP   re   rc   r	   r	   r   rR     s   
zLevel.__init__Nr   )r   r   r   r   rR   r	   r	   r	   r   rG     s    rG   N)FF)r   collectionsr   heapqr   r   sympy.core.sortingr   sympy.assumptions.cnfr   !sympy.logic.algorithms.lra_theoryr   r$   r   r   rG   r	   r	   r	   r   <module>   s    
2    U