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