1 /* 2 * Copyright (c) 2016 Helmut Neemann 3 * Use of this source code is governed by the GPL v3 license 4 * that can be found in the LICENSE file. 5 */ 6 package de.neemann.digital.analyse.quinemc; 7 8 9 import de.neemann.digital.analyse.expression.*; 10 import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelector; 11 import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelectorDefault; 12 import de.neemann.digital.lang.Lang; 13 import org.slf4j.Logger; 14 import org.slf4j.LoggerFactory; 15 16 import java.util.*; 17 18 import static de.neemann.digital.analyse.expression.Operation.or; 19 20 /** 21 * The algorithm of Quine and McCluskey 22 */ 23 public class QuineMcCluskey { 24 private static final Logger LOGGER = LoggerFactory.getLogger(QuineMcCluskey.class); 25 26 private final List<Variable> variables; 27 private final ArrayList<TableRow> primes; 28 private TableRows rows; 29 30 /** 31 * Creates a new instance 32 * 33 * @param variables the variables to use 34 */ QuineMcCluskey(List<Variable> variables)35 public QuineMcCluskey(List<Variable> variables) { 36 this.variables = variables; 37 this.rows = new TableRows(); 38 this.primes = new ArrayList<>(); 39 } 40 QuineMcCluskey(List<Variable> variables, TableRows rows, ArrayList<TableRow> primes)41 QuineMcCluskey(List<Variable> variables, TableRows rows, ArrayList<TableRow> primes) { 42 this.variables = variables; 43 this.rows = rows; 44 this.primes = primes; 45 } 46 47 /** 48 * Creates a new instance. 49 * The Bool table is build using the given expression 50 * 51 * @param expression the expression used to build the table 52 * @throws ExpressionException ExpressionException 53 */ QuineMcCluskey(Expression expression)54 public QuineMcCluskey(Expression expression) throws ExpressionException { 55 ContextFiller context = new ContextFiller(expression); 56 variables = context.getVariables(); 57 rows = new TableRows(); 58 fillTableWith(new BoolTableExpression(expression, context)); 59 primes = new ArrayList<>(); 60 } 61 62 /** 63 * Fills the instance with the given values 64 * 65 * @param values the values 66 * @return this for chained calls 67 * @throws ExpressionException ExpressionException 68 */ fillTableWith(BoolTable values)69 public QuineMcCluskey fillTableWith(BoolTable values) throws ExpressionException { 70 int n = 1 << variables.size(); 71 if (n != values.size()) 72 throw new ExpressionException(Lang.get("err_exact_N0_valuesNecessaryNot_N1", n, values.size())); 73 for (int i = 0; i < n; i++) { 74 ThreeStateValue value = values.get(i); 75 if (!value.equals(ThreeStateValue.zero)) { 76 add(i, value.equals(ThreeStateValue.dontCare)); 77 } 78 } 79 return this; 80 } 81 82 add(int i, boolean dontCare)83 private void add(int i, boolean dontCare) { 84 rows.add(new TableRow(variables.size(), i, rows.size() + 1, dontCare)); 85 } 86 87 /** 88 * Simplifies the given expression. 89 * If no simplification was found, the original expression is returned unchanged. 90 * 91 * @param expression the expression to simplify 92 * @return the simplified expression 93 * @throws ExpressionException ExpressionException 94 */ simplify(Expression expression)95 public static Expression simplify(Expression expression) throws ExpressionException { 96 int initialCplx = expression.traverse(new ComplexityInclNotVisitor()).getComplexity(); 97 98 Expression newExp = new QuineMcCluskey(expression) 99 .simplify() 100 .getExpression(); 101 102 int newCplx = newExp.traverse(new ComplexityInclNotVisitor()).getComplexity(); 103 104 if (newCplx < initialCplx) 105 return newExp; 106 else 107 return expression; 108 } 109 110 /** 111 * Simplifies the table the the default {@link PrimeSelector} 112 * 113 * @return the simplified QMC instance 114 */ simplify()115 public QuineMcCluskey simplify() { 116 return simplify(new PrimeSelectorDefault()); 117 } 118 119 /** 120 * Simplifies the table the the given {@link PrimeSelector} 121 * 122 * @param ps the prime selector 123 * @return the simplified QMC instance 124 */ simplify(PrimeSelector ps)125 public QuineMcCluskey simplify(PrimeSelector ps) { 126 while (!isFinished()) { 127 LOGGER.debug("QMC rows " + rows.size()); 128 simplifyStep(); 129 } 130 simplifyPrimes(ps); 131 132 return this; 133 } 134 135 136 /** 137 * a single simplification iteration 138 */ simplifyStep()139 public void simplifyStep() { 140 TableRows newRows = new TableRows(); 141 142 for (TableRows.InnerList list : rows.listIterable()) 143 for (int i = 0; i < list.size() - 1; i++) 144 for (int j = i + 1; j < list.size(); j++) { 145 TableRow r1 = list.get(i); 146 TableRow r2 = list.get(j); 147 148 int index = r1.checkCompatible(r2); 149 if (index >= 0) { 150 // can optimize; 151 TableRow newRow = new TableRow(r1); 152 newRow.setToOptimized(index); 153 154 if (!newRows.contains(newRow)) { 155 newRow.addSource(r1.getSource()); 156 newRow.addSource(r2.getSource()); 157 newRows.add(newRow); 158 } 159 r1.setUsed(); 160 r2.setUsed(); 161 } 162 } 163 164 for (TableRow row : rows) 165 if (!row.isUsed() && row.getSource().size() > 0) 166 primes.add(row); 167 168 rows = newRows; 169 } 170 171 /** 172 * @return true id simplification is complete 173 */ isFinished()174 public boolean isFinished() { 175 return rows.isEmpty(); 176 } 177 178 /** 179 * @return the actual table rows 180 */ getRows()181 public TableRows getRows() { 182 return rows; 183 } 184 185 /** 186 * Sets the table rows. 187 * 188 * @param rows the rows to use 189 */ setRows(TableRows rows)190 public void setRows(TableRows rows) { 191 this.rows = rows; 192 } 193 194 @Override toString()195 public String toString() { 196 StringBuilder sb = new StringBuilder(); 197 ArrayList<TableRow> newList = new ArrayList<>(); 198 for (TableRow r : rows) { 199 newList.add(r); 200 } 201 Collections.sort(newList); 202 for (TableRow r : newList) { 203 sb.append(r.toString()); 204 sb.append("\n"); 205 } 206 return sb.toString(); 207 } 208 209 /** 210 * @return the final primes 211 */ getPrimes()212 public ArrayList<TableRow> getPrimes() { 213 return primes; 214 } 215 216 /** 217 * @return the variables used 218 */ getVariables()219 public List<Variable> getVariables() { 220 return variables; 221 } 222 223 /** 224 * @return the simplified expression which represent this table 225 */ getExpression()226 public Expression getExpression() { 227 if (primes.isEmpty() && rows.isEmpty()) 228 return Constant.ZERO; 229 230 Expression e = addAnd(null, primes, variables); 231 return addAnd(e, rows, variables); 232 } 233 234 /** 235 * Creates the final expression 236 * 237 * @param e the expression to complete 238 * @param rows the rows to add 239 * @param variables the variables to use to build the expression 240 * @return the expression 241 */ addAnd(Expression e, Iterable<TableRow> rows, List<Variable> variables)242 public static Expression addAnd(Expression e, Iterable<TableRow> rows, List<Variable> variables) { 243 for (TableRow r : rows) { 244 Expression n = r.getExpression(variables); 245 if (e == null) 246 e = n; 247 else 248 e = or(e, n); 249 } 250 return e; 251 } 252 253 /** 254 * Simplify the primes 255 * 256 * @param primeSelector the prime selector to use 257 */ simplifyPrimes(PrimeSelector primeSelector)258 public void simplifyPrimes(PrimeSelector primeSelector) { 259 260 TreeSet<Integer> columns = new TreeSet<>(); 261 for (TableRow r : primes) 262 columns.addAll(r.getSource()); 263 264 LOGGER.debug("initial primes " + primes.size()); 265 266 // remove all primes which are easy to remove 267 while (true) { 268 // find rows to delete 269 HashSet<TableRow> rowsToDelete = new HashSet<>(); 270 for (TableRow r1 : primes) 271 for (TableRow r2 : primes) { 272 if ((r1 != r2) && !rowsToDelete.contains(r1) && r1.getSource().containsAll(r2.getSource())) 273 rowsToDelete.add(r2); 274 } 275 276 primes.removeAll(rowsToDelete); 277 278 // find the cols to delete 279 HashSet<Integer> colsToDelete = new HashSet<>(); 280 for (int c1 : columns) { 281 for (int c2 : columns) { 282 if ((c1 != c2) && !colsToDelete.contains(c1) && smaller(c1, c2, primes)) 283 colsToDelete.add(c2); 284 } 285 } 286 287 if (colsToDelete.isEmpty() && rowsToDelete.isEmpty()) 288 break; 289 290 for (TableRow p : primes) 291 p.getSource().removeAll(colsToDelete); 292 293 columns.removeAll(colsToDelete); 294 } 295 296 LOGGER.debug("residual primes " + primes.size()); 297 298 // try to reduce the number of primes needed 299 if (primeSelector != null && !columns.isEmpty()) { 300 ArrayList<TableRow> availPrimes = new ArrayList<>(primes.size()); 301 availPrimes.addAll(primes); 302 primes.clear(); 303 primeSelector.select(primes, availPrimes, columns); 304 LOGGER.debug("final primes " + primes.size()); 305 } 306 } 307 smaller(int c1, int c2, ArrayList<TableRow> primes)308 private boolean smaller(int c1, int c2, ArrayList<TableRow> primes) { 309 for (TableRow r : primes) { 310 Collection<Integer> s = r.getSource(); 311 if (s.contains(c1) && !s.contains(c2)) 312 return false; 313 } 314 return true; 315 } 316 } 317