📦 axelbdt / cp2mdd

📄 search.cpython-313.pyc · 147 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147�

ǚ(i�9���SrSSKrSSKJrJr SSKJrJrJrJ	r	 \"SS55r
\"SS55rS	\
S
\\	\\\4S\\\	\\\44Sjr
S
\S\\	\	\\\4\\44SjrS\\
S\S\S\\4SjrS\
SS4SjrS\\	\	\\\4\\4S\
4SjrS\
SS4SjrSS	\
S
\\	\\\4S\S\\	\\	\\\4\44SjjrS\\	\\\4SS4SjrSr\S:Xa\"5 gg)zFParse CSP solver search traces into tree structure for BDD conversion.�N)�	dataclass�field)�Any�List�Optional�Tuplec���\rSrSr%Sr\\\\\4\	S'Sr
\\\	S'\"\S9r
\S\	S'Sr\S\	S'S	r\\	S
'S\4SjrS\\\\\44S
jrS\4SjrSS\S\S\4SjjrSrg)�Node�z4Node in search tree representing a decision or root.�decisionN�label)�default_factory�children�parentr�depth�returnc��URSL$)z Check if node is a labeled leaf.N)r
��selfs �/home/axel/dev/cp2bdd/search.py�is_leaf�Node.is_leafs���z�z��%�%�c���/nUnURbCURbURUR5 URnURbMC[[	U55$)z-Collect all decisions from root to this node.)rr�append�list�reversed)r�path�nodes   r�path_from_root�Node.path_from_rootsV�������k�k�%��}�}�(����D�M�M�*��;�;�D��k�k�%��H�T�N�#�#rc�
�SUR0nURbURup#nX#US.US'OSUS'UR(a/URVs/sHoUR5PM snUS'U$/US'U$s snf)z2Convert node to dictionary for JSON serialization.r
N)�variable�operator�valuerr)r
rr�to_dict)r�result�var�op�val�childs      rr&�Node.to_dict"s����4�:�:�&���=�=�$��=�=�L�C�S�.1�C�!P�F�:��!%�F�:���=�=�?C�}�}�!M�}�e�-�-�/�}�!M�F�:���
�"$�F�:���
��	"Ns�B�prefix�is_rootc�x�URcSnO<URupEnUR(aSURS3OSnUUUU3n[UR5HTup�U[	UR5S-
:Hn
U
(aSOSnX(aSOS	-nUS
U-U-U	RUSS9--
nMV U$)
zPretty print tree structure.z[ROOT]z [�]��u
└── u
├── z    u│   �
F)r.)rr
�	enumerater�len�__str__)
rr-r.�sr(r)r*�	label_str�ir+�is_last�	connector�child_prefixs
             rr6�Node.__str__3s����=�=� ��A��=�=�L�C�S�.2�j�j�"�T�Z�Z�L��*�b�I��%��t�C�5���,�A�!�$�-�-�0�H�A��3�t�}�}�-��1�1�G�(/��\�I�!�w�V�H�E�L�
�����*�U�]�]�<�QV�]�-W�W�W�A�	1��r�)r1T)�__name__�
__module__�__qualname__�__firstlineno__�__doc__rr�strr�__annotations__r
rrrrrr�int�boolrr �dictr&r6�__static_attributes__r>rrr
r
s���>��
�c�3��m���� �E�8�C�=��"�4�8�H�d�6�l�8�#�F�H�V��#��E�3�N�&��&�$��U�3��S�=�%9� :�$����"�c������rr
c�\�\rSrSr%Sr\\S'\S\SS4Sj5r	S\
4SjrS\4SjrS	r
g
)�
SearchTree�Ez$Search tree built from solver trace.�root�log_textrc�>�[U5n[U5nU"U5$)z�Parse log text into search tree.

Args:
    log_text: Solver trace with decision lines and leaf labels

Returns:
    SearchTree with all paths from trace

Raises:
    ValueError: If trace is malformed
)�parse_log_to_decisions�build_tree_from_decisions)�clsrN�	decisionsrMs    r�from_log�SearchTree.from_logKs"��+�8�4�	�(��3���4�y�rc�6�URR5$)z2Convert tree to dictionary for JSON serialization.)rMr&rs rr&�SearchTree.to_dict\s���y�y� � �"�"rc�,�[UR5$)zPretty print entire tree.)rDrMrs rr6�SearchTree.__str__`s���4�9�9�~�rr>N)r?r@rArBrCr
rE�classmethodrDrTrHr&r6rIr>rrrKrKEsB��.�
�J��������� #��#���rrKr�path_so_farrc��URb URup#nUS:XaXX44/-nOUnOUnURS:XaU/$URS:Xa/$/nURHnUR[	Xu55 M U$)z�Recursively extract minimal UNSAT paths.

Args:
    node: Current node in traversal
    path_so_far: Accumulated positive decisions from root

Returns:
    List of minimal UNSAT paths
�=�UNSAT�SAT)rr
r�extend� _extract_minimal_unsat_recursive)rr[r(r)r*r�unsat_pathsr+s        rraraes����}�}� ��}�}����
��9���.�!1�1�D��D����z�z�W���v�
��z�z�U���	��K��������;�E�H�I���rrNc��Sn/nUR5RS5nUGH
nUR5nU(dMUS:XaU(aUSupVUcUS4US' U$US;a:U(d[SUS	35eUSupVUb[S
USUS35eXT4US'M~[R"X5nU(dM�URS
5R5nURS5n	URS5R5n
[
U
5nURX�U4S45 GM U$![a U
nN*f=f)z�Parse log text into flat list of decisions with optional labels.

Args:
    log_text: Raw solver trace

Returns:
    List of ((variable, operator, value), label) tuples
    where label is 'SAT', 'UNSAT', or None
z0### choiceId=\d+; branching on (.+?)(=|!=)(.+?);r3zSolution found�����Nr_)r_r^zLeaf label 'z' without preceding decisionzDecision already has label 'z', cannot add '�'r2��)�strip�split�
ValueError�re�match�grouprFr)rN�patternrS�lines�line�
prev_decision�
prev_labelrlr(r)�val_strr*s            rrPrP�sv��B�G��I��N�N��"�"�4�(�E����z�z�|�����#�#��,5�b�M�)�
��%�%2�E�$:�I�b�M��>��9�#�#�� �<��v�5Q�!R�S�S�(1�"�
�%�M��%� �2�:�,�o�d�V�ST�U���+�1�I�b�M������'�����k�k�!�n�"�"�$��
�[�[��^���+�+�a�.�&�&�(��	��g�,�C�	���3�C�.�$�/�0�S�V����	��C�	�s�D:�:E	�E	�stackr(r*c���[[[U555H=nXnURcMURupVnXQ:XdM,US:XdM4Xr:XdM;Us $ g)z�Find index of most recent ancestor with decision (var, '=', val).

Args:
    stack: Current path from root to current node
    var: Variable name to search for
    val: Value to match

Returns:
    Index in stack, or None if not found
Nr])r�ranger5r)rtr(r*r9r�node_var�node_op�node_vals        r�find_positive_ancestorrz�sW���e�C��J�'�
(���x���=�=� ��&*�m�m�#��8��?�w�#�~�(�/��H�
)�rrMc�"^�U4SjmT"U5 g)z�Propagate SAT/UNSAT labels through the tree.

Rules:
- If a node has a SAT child, mark it SAT
- If all children are UNSAT, mark it UNSAT
- Traverse bottom-up (post-order)
c�8>�URHnT"U5 M
 URbgUR(dSUlgURVs/sHoRPM nnSU;aSUlg[SU55(aSUlggs snf)Nr^r_c3�4# �UHocMUS:Hv� M g7f)Nr^r>)�.0r
s  r�	<genexpr>�6propagate_labels.<locals>.postorder.<locals>.<genexpr>�s���Q�|�e�!��'�!�|�s��)rr
�all)rr+�child_labels�	postorders   �rr��#propagate_labels.<locals>.postorder�s�����]�]�E��e��#��:�:�!���}�}� �D�J��15���?�������?��L� ��D�J�
�Q�|�Q�
Q�
Q� �D�J�R��
@s�BNr>)rMr�s @r�propagate_labelsr��s���!�2�d�OrrSc���[SSSS9nUnU/nUGHHuupEpgUS:Xao[XEU4SUURS-S9nURRU5 URU5 UnU(aXrlUR5 USnM|M~US:Xa�[
X4U5n	U	c[S	USUS
USU35eX9n
U
RcSU
lUSU	nUSn[XEU4SUURS-S9nURRU5 URU5 UnU(aXrlUR5 USnGM:GM=[SU35e [U5 [U5 U$)
aBuild search tree from flat decision list.

Args:
    decisions: List of ((var, op, val), label) from parse_log_to_decisions

Returns:
    Root node of constructed tree

Raises:
    ValueError: If trace is malformed (e.g., negation without positive ancestor)
Nr)rr
rr]r2�rr
rrrd�!=z	Negation z$ without matching positive ancestor r^zUnknown operator: )
r
rrrr
�poprzrj�add_unexplored_siblingsr�)rSrM�currentrtr(r)r*r
r+�ancestor_idx�target_nodes           rrQrQs�����T��3�D��G�
�F�E�!*����#�
��9���3�����m�m�a�'�	�E�
���#�#�E�*��L�L����G�� %�
��	�	����)��	��4�Z�2�%�c�B�L��#� ���u�B�s�e�+O�PS�u�TU�VY�UZ�[��� �-�K�� � �(�$+��!��-�<�(�E��B�i�G���3�����m�m�a�'�	�E�
���#�#�E�*��L�L����G�� %�
��	�	����)����1�"��6�7�7�y"+�~�D�!��T���Krc�"^�U4SjmT"U5 g)z�Add UNKNOWN sibling nodes for every positive decision.

For each node with decision (var, '=', val), add a sibling
with decision (var, '!=', val) and label 'UNKNOWN'.
c�V>�[UR5HnT"U5 M
 URb�URb�URup#nUS:Xa�SnURRH=nUR(dMURupxn	Xr:XdM,US:XdM4X�:XdM;Sn O U(dr[	USU4SURUR
S9nURRR
U5n
URRRU
S-U5 ggggg)Nr]Fr�T�UNKNOWNr�r2)rrrrr
r�index�insert)rr+r(r)r*�has_negation�sibling�sib_var�sib_op�sib_val�idx�process_nodes           �rr��-add_unexplored_siblings.<locals>.process_nodees
����$�-�-�(�E����)��=�=�$����)@��=�=�L�C�S��S�y�$��#�{�{�3�3�G��'�'�'�3:�3C�3C�0���"�>�f��n���+/�L�!� 4�$�"�"%�t�S�!1�'�#�{�{�"�j�j�	�G��+�+�.�.�4�4�T�:�C��K�K�(�(�/�/��a���A�$��*A�$rNr>)rMr�s @rr�r�^s���B�:��r�
positive_onlyc�N�Uc/nURb/URup4nU(aUS:XaXXE4/-nO
UnO
XXE4/-nOUnURbX`R4/$UR(dUS4/$/nURHnUR[	X�U55 M  U$)a&Recursively extract all paths from root to labeled leaves.

Args:
    node: Current node in traversal
    path_so_far: Accumulated path from root
    positive_only: If True, only include positive (=) decisions in paths

Returns:
    List of (path, label) tuples where path is list of decisions
r]r^)rr
rr`�
extract_paths)	rr[r�r(r)r*r�pathsr+s	         rr�r��s��������}�}� ��}�}������S�y�"�B�n�%5�5��"����.�!1�1�D����z�z���z�z�"�#�#��=�=��w�� � �
�E�����
���]�5�
�>�?���Lrrc��0nUH8up#nUS:XdMX!;a!XnXT:wa[SUSUSUSUS3	5eXAU'M: g)z�Validate that path satisfies one-hot constraint.

Args:
    path: List of (variable, operator, value) decisions

Raises:
    ValueError: If path violates one-hot (multiple positive assignments to same variable)
r]z"Path violates one-hot constraint: z and z
 both presentN)rj)r�positive_assignmentsr(r)r*�existings      r�
validate_pathr��sk��������
��9��*�/�4���?�$�<��%�q��
�%��u�A�c�U�-�I���),��%�rc���Sn[S5 [S5 [RU5n[S5 [U5 g![an[SU35 SnAgSnAff=f)z)Test search tree parser on example trace.a�
### choiceId=0; branching on x[4][5]=8; nb of ties=0
### choiceId=1; branching on x[3][3]=14; nb of ties=0
### choiceId=1; branching on x[3][3]!=14
### choiceId=2; branching on x[3][3]=22; nb of ties=0
### choiceId=2; branching on x[3][3]!=22
### choiceId=3; branching on x[3][3]=15; nb of ties=0
### choiceId=3; branching on x[3][3]!=15
### choiceId=0; branching on x[4][5]!=8
### choiceId=4; branching on x[4][5]=3; nb of ties=0
### choiceId=4; branching on x[4][5]!=3
### choiceId=5; branching on x[4][5]=7; nb of ties=0
### choiceId=5; branching on x[4][5]!=7
### choiceId=6; branching on x[4][5]=11; nb of ties=0
### choiceId=7; branching on x[3][4]=19; nb of ties=0
Solution found
zParsing search trace...z<============================================================z
Tree structure:zError parsing trace: N)�printrKrTrj)rN�tree�es   r�mainr��sc���H�$
�
#�$�	�(�O�+��"�"�8�,��
�!�"�
�d����+�
�%�a�S�)�*�*��+�s�+A�
A(�A#�#A(�__main__)NF)rCrk�dataclassesrr�typingrrrrr
rKrDrarPrFrzr�rQr�rGr�r�r�r?r>rr�<module>r�s���L�	�(�-�-��9�9��9�x�����>$�
�$�!�%��S�#�
�"6�7�$�	�$�u�S�#�s�]�#�
$�%�$�N<��<�	�%��c�3��m�$�h�s�m�3�
4�5�<�~�$�t�*��3��S��X�c�]��*"�4�"�D�"�JV��E�%��S�#�
�.���
�=�>�?�V�	�V�r$�$�$�4�$�R/3��.�
�.��e�C��c�M�*�+�.��.�
�%��U�3��S�=�)�*�C�/�
0�1�	.�b,��U�3��S�=�1�2�,�t�,�.+�B�z���F�r