|
| 1 | +/*******************************************************************\ |
| 2 | +
|
| 3 | +Module: AIG Simplifier |
| 4 | +
|
| 5 | +Author: Daniel Kroening, [email protected] |
| 6 | +
|
| 7 | +\*******************************************************************/ |
| 8 | + |
| 9 | +#include "aig_simplifier.h" |
| 10 | + |
| 11 | +/// Apply a substitution map to a literal |
| 12 | +/// \param l: The literal to substitute |
| 13 | +/// \param substitution: Map from variable numbers to replacement literals |
| 14 | +/// \return The substituted literal |
| 15 | +static literalt |
| 16 | +substitute(literalt l, const std::vector<literalt> &substitution) |
| 17 | +{ |
| 18 | + if(l.is_constant()) |
| 19 | + return l; |
| 20 | + |
| 21 | + auto var_no = l.var_no(); |
| 22 | + PRECONDITION(var_no < substitution.size()); |
| 23 | + |
| 24 | + // Get the replacement literal and apply the sign |
| 25 | + return substitution[var_no] ^ l.sign(); |
| 26 | +} |
| 27 | + |
| 28 | +/// Simplify an AND node after substitution |
| 29 | +/// \param a: First input literal (after substitution) |
| 30 | +/// \param b: Second input literal (after substitution) |
| 31 | +/// \param dest: The destination AIG to add the node to |
| 32 | +/// \return The literal representing the simplified AND |
| 33 | +static literalt simplify_and(literalt a, literalt b, aigt &dest) |
| 34 | +{ |
| 35 | + // Constant propagation |
| 36 | + if(a.is_false() || b.is_false()) |
| 37 | + return const_literal(false); |
| 38 | + |
| 39 | + if(a.is_true()) |
| 40 | + return b; |
| 41 | + |
| 42 | + if(b.is_true()) |
| 43 | + return a; |
| 44 | + |
| 45 | + // a AND a = a |
| 46 | + if(a == b) |
| 47 | + return a; |
| 48 | + |
| 49 | + // a AND !a = false |
| 50 | + if(a == !b) |
| 51 | + return const_literal(false); |
| 52 | + |
| 53 | + // Normalize: smaller variable number first |
| 54 | + if(b.var_no() < a.var_no()) |
| 55 | + std::swap(a, b); |
| 56 | + |
| 57 | + // Create the AND node |
| 58 | + return dest.new_and_node(a, b); |
| 59 | +} |
| 60 | + |
| 61 | +aigt aig_simplify(const aigt &src, const equivalencest &equivalences) |
| 62 | +{ |
| 63 | + if(src.empty()) |
| 64 | + return aigt(); |
| 65 | + |
| 66 | + const auto num_nodes = src.number_of_nodes(); |
| 67 | + |
| 68 | + // Build a substitution map: for each variable, what literal should it |
| 69 | + // be replaced with? Initially, each variable maps to itself. |
| 70 | + std::vector<literalt> substitution; |
| 71 | + substitution.reserve(num_nodes); |
| 72 | + |
| 73 | + for(std::size_t i = 0; i < num_nodes; i++) |
| 74 | + substitution.emplace_back(i, false); |
| 75 | + |
| 76 | + // Process equivalences. For each equivalence (l1, l2), we want to |
| 77 | + // replace references to the larger variable with the smaller one. |
| 78 | + for(const auto &eq : equivalences) |
| 79 | + { |
| 80 | + literalt l1 = eq.first; |
| 81 | + literalt l2 = eq.second; |
| 82 | + |
| 83 | + // Skip invalid equivalences |
| 84 | + if(l1.is_constant() && l2.is_constant()) |
| 85 | + continue; |
| 86 | + |
| 87 | + // If one is constant, the other should map to that constant |
| 88 | + if(l1.is_constant()) |
| 89 | + { |
| 90 | + if(l2.var_no() < num_nodes) |
| 91 | + substitution[l2.var_no()] = l1 ^ l2.sign(); |
| 92 | + continue; |
| 93 | + } |
| 94 | + |
| 95 | + if(l2.is_constant()) |
| 96 | + { |
| 97 | + if(l1.var_no() < num_nodes) |
| 98 | + substitution[l1.var_no()] = l2 ^ l1.sign(); |
| 99 | + continue; |
| 100 | + } |
| 101 | + |
| 102 | + // Both are non-constant |
| 103 | + // l1 ≡ l2 means: v1 ^ s1 ≡ v2 ^ s2 |
| 104 | + // We replace the larger variable with the smaller |
| 105 | + auto v1 = l1.var_no(); |
| 106 | + auto v2 = l2.var_no(); |
| 107 | + |
| 108 | + if(v1 >= num_nodes || v2 >= num_nodes) |
| 109 | + continue; |
| 110 | + |
| 111 | + // Compute the sign relationship: if l1 ≡ l2, then |
| 112 | + // v1 should map to v2 ^ (s1 ^ s2), or vice versa |
| 113 | + bool signs_differ = l1.sign() != l2.sign(); |
| 114 | + |
| 115 | + if(v1 < v2) |
| 116 | + { |
| 117 | + // Replace v2 with v1 |
| 118 | + substitution[v2] = literalt(v1, signs_differ); |
| 119 | + } |
| 120 | + else if(v2 < v1) |
| 121 | + { |
| 122 | + // Replace v1 with v2 |
| 123 | + substitution[v1] = literalt(v2, signs_differ); |
| 124 | + } |
| 125 | + // If v1 == v2, the equivalence is trivial (l1 ≡ l1 or l1 ≡ !l1) |
| 126 | + // The latter case (l1 ≡ !l1) would be a contradiction, ignore it |
| 127 | + } |
| 128 | + |
| 129 | + // Compute transitive closure of substitutions |
| 130 | + // Repeat until no changes |
| 131 | + bool changed; |
| 132 | + do |
| 133 | + { |
| 134 | + changed = false; |
| 135 | + for(std::size_t i = 0; i < num_nodes; i++) |
| 136 | + { |
| 137 | + literalt subst = substitution[i]; |
| 138 | + if(subst.is_constant() || subst.var_no() == i) |
| 139 | + continue; |
| 140 | + |
| 141 | + // Follow the substitution chain |
| 142 | + literalt next = substitution[subst.var_no()] ^ subst.sign(); |
| 143 | + if(next != subst) |
| 144 | + { |
| 145 | + substitution[i] = next; |
| 146 | + changed = true; |
| 147 | + } |
| 148 | + } |
| 149 | + } while(changed); |
| 150 | + |
| 151 | + // Now rebuild the AIG with substitutions applied |
| 152 | + aigt dest; |
| 153 | + |
| 154 | + // Map from old node indices to new literals |
| 155 | + std::vector<literalt> old_to_new(num_nodes); |
| 156 | + |
| 157 | + for(std::size_t i = 0; i < num_nodes; i++) |
| 158 | + { |
| 159 | + const auto &node = src.nodes[i]; |
| 160 | + |
| 161 | + // First, check what this node is substituted with |
| 162 | + literalt subst = substitution[i]; |
| 163 | + |
| 164 | + if(subst.is_constant()) |
| 165 | + { |
| 166 | + // This node is equivalent to a constant |
| 167 | + old_to_new[i] = subst; |
| 168 | + continue; |
| 169 | + } |
| 170 | + |
| 171 | + if(subst.var_no() != i) |
| 172 | + { |
| 173 | + // This node is equivalent to another (smaller) node |
| 174 | + // Use what that node maps to, applying the sign |
| 175 | + old_to_new[i] = old_to_new[subst.var_no()] ^ subst.sign(); |
| 176 | + continue; |
| 177 | + } |
| 178 | + |
| 179 | + // This node is a representative (maps to itself), we need to create it |
| 180 | + if(node.is_var()) |
| 181 | + { |
| 182 | + // Variable node |
| 183 | + old_to_new[i] = dest.new_var_node(node.var_no()); |
| 184 | + } |
| 185 | + else |
| 186 | + { |
| 187 | + // AND node - substitute inputs and simplify |
| 188 | + literalt new_a = substitute(node.a, old_to_new); |
| 189 | + literalt new_b = substitute(node.b, old_to_new); |
| 190 | + old_to_new[i] = simplify_and(new_a, new_b, dest); |
| 191 | + } |
| 192 | + } |
| 193 | + |
| 194 | + return dest; |
| 195 | +} |
0 commit comments