added the algorithm from Quine and McClusky

This commit is contained in:
hneemann 2016-05-04 23:01:43 +02:00
parent 5aeb8525a0
commit ba4df394cb
24 changed files with 1552 additions and 0 deletions

View File

@ -0,0 +1,18 @@
package de.neemann.digital.analyse.expression;
/**
* @author hneemann
*/
public class ComplexityInclNotVisitor implements ExpressionVisitor {
private int counter = 0;
@Override
public boolean visit(Expression expression) {
counter++;
return true;
}
public int getComplexity() {
return counter;
}
}

View File

@ -0,0 +1,19 @@
package de.neemann.digital.analyse.expression;
/**
* @author hneemann
*/
public class ComplexityVisitor implements ExpressionVisitor {
private int counter = 0;
@Override
public boolean visit(Expression expression) {
if (!(expression instanceof Not))
counter++;
return true;
}
public int getComplexity() {
return counter;
}
}

View File

@ -0,0 +1,24 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.ExpressionException;
/**
* A simple bool table
*
* @author hneemann
*/
public interface BoolTable {
/**
* @return the table row count
*/
int size();
/**
* returns the value at the given row
*
* @param i the index
* @return the value
* @throws ExpressionException ExpressionException
*/
ThreeStateValue get(int i) throws ExpressionException;
}

View File

@ -0,0 +1,33 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.ExpressionException;
/**
* A simple boolean array
*
* @author hneemann
*/
public class BoolTableBoolArray implements BoolTable {
private final boolean[] table;
/**
* Creates a new instance
*
* @param table the bool values
*/
public BoolTableBoolArray(boolean[] table) {
this.table = table;
}
@Override
public int size() {
return table.length;
}
@Override
public ThreeStateValue get(int i) throws ExpressionException {
return ThreeStateValue.value(table[i]);
}
}

View File

@ -0,0 +1,30 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.ContextFiller;
import de.neemann.digital.analyse.expression.Expression;
import de.neemann.digital.analyse.expression.ExpressionException;
/**
* @author hneemann
*/
public class BoolTableExpression implements BoolTable {
private final Expression expression;
private final ContextFiller context;
public BoolTableExpression(Expression expression, ContextFiller context) {
this.expression = expression;
this.context = context;
}
@Override
public int size() {
return 1 << context.getVarCount();
}
@Override
public ThreeStateValue get(int i) throws ExpressionException {
context.setContextTo(i);
return ThreeStateValue.value(expression.calculate(context));
}
}

View File

@ -0,0 +1,34 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.ExpressionException;
/**
* A int array.
* Zero and one behave as expected, any other value represents "don't care"
*
* @author hneemann
*/
public class BoolTableIntArray implements BoolTable {
private final int[] table;
/**
* Creates a new instace
*
* @param table the int values
*/
public BoolTableIntArray(int[] table) {
this.table = table;
}
@Override
public int size() {
return table.length;
}
@Override
public ThreeStateValue get(int i) throws ExpressionException {
return ThreeStateValue.value(table[i]);
}
}

View File

@ -0,0 +1,317 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.*;
import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelector;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.TreeSet;
import static de.neemann.digital.analyse.expression.Operation.or;
/**
* The algorithm from Quine and McClusky
*
* @author hneemann
*/
public class QuineMcClusky {
private final ArrayList<TableRow> rows;
private final ArrayList<Variable> variables;
private final ArrayList<TableRow> primes;
/**
* Creates a new instance
*
* @param variables the variables to use
*/
public QuineMcClusky(ArrayList<Variable> variables) {
this.variables = variables;
this.rows = new ArrayList<>();
this.primes = new ArrayList<>();
}
private QuineMcClusky(ArrayList<Variable> variables, ArrayList<TableRow> rows, ArrayList<TableRow> primes) {
this.variables = variables;
this.rows = rows;
this.primes = primes;
}
/**
* Creates a new instance.
* The Bool table is build using the given expression
*
* @param expression the expression used to build the table
* @throws ExpressionException ExpressionException
*/
public QuineMcClusky(Expression expression) throws ExpressionException {
ContextFiller context = new ContextFiller(expression);
variables = context.getVariables();
rows = new ArrayList<>();
fillTableWith(new BoolTableExpression(expression, context));
primes = new ArrayList<>();
}
/**
* Fills the instance with the given values
*
* @param values the values
* @return this for chained calls
* @throws ExpressionException ExpressionException
*/
public QuineMcClusky fillTableWith(BoolTable values) throws ExpressionException {
int n = 1 << variables.size();
if (n != values.size())
throw new ExpressionException("exact " + n + " values necessary, not " + values.size());
for (int i = 0; i < n; i++) {
ThreeStateValue value = values.get(i);
if (!value.equals(ThreeStateValue.zero)) {
add(i, value.equals(ThreeStateValue.dontCare));
}
}
Collections.sort(rows);
return this;
}
private void add(int i, boolean dontCare) {
rows.add(new TableRow(variables.size(), i, rows.size() + 1, dontCare));
}
/**
* Simplifies the given expression
*
* @param expression the expression to simplify
* @return the simplified expression
* @throws ExpressionException ExpressionException
*/
public static Expression simplify(Expression expression) throws ExpressionException {
int initialCplx = expression.traverse(new ComplexityInclNotVisitor()).getComplexity();
Expression newExp = new QuineMcClusky(expression)
.simplify()
.getExpression();
int newCplx = newExp.traverse(new ComplexityInclNotVisitor()).getComplexity();
if (newCplx < initialCplx)
return newExp;
else
return null;
}
/**
* Simplifies the table the the default {@link PrimeSelector}
*
* @return the simplified QMC instance
*/
public QuineMcClusky simplify() {
return simplify(PrimeSelector.DEFAULT);
}
/**
* Simplifies the table the the given {@link PrimeSelector}
*
* @param ps the prome selector
* @return the simplified QMC instance
*/
public QuineMcClusky simplify(PrimeSelector ps) {
QuineMcClusky t = this;
while (!t.isFinished())
t = t.simplifyStep().removeDuplicates();
return t.simplifyPrimes(ps);
}
QuineMcClusky simplifyStep() {
ArrayList<TableRow> newRows = new ArrayList<>();
for (int i = 0; i < rows.size() - 1; i++)
for (int j = i + 1; j < rows.size(); j++) {
TableRow r1 = rows.get(i);
TableRow r2 = rows.get(j);
int index = checkCompatible(r1, r2);
if (index >= 0) {
// can optimize;
TableRow newRow = new TableRow(r1);
newRow.setToOptimized(index);
newRow.addSource(r1.getSource());
newRow.addSource(r2.getSource());
r1.setUsed();
r2.setUsed();
newRows.add(newRow);
}
}
ArrayList<TableRow> np = new ArrayList<TableRow>();
np.addAll(primes);
for (TableRow row : rows)
if (!row.isUsed() && row.getSource().size() > 0)
np.add(row);
return new QuineMcClusky(variables, newRows, np);
}
QuineMcClusky removeDuplicates() {
ArrayList<TableRow> newRows = new ArrayList<TableRow>();
for (TableRow r : rows) {
int i = newRows.indexOf(r);
if (i < 0) {
newRows.add(r);
} else {
newRows.get(i).addSource(r.getSource());
}
}
rows.clear();
rows.addAll(newRows);
return this;
}
/**
* @return true id simplification is complete
*/
public boolean isFinished() {
return rows.isEmpty();
}
private int checkCompatible(TableRow r1, TableRow r2) {
for (int i = 0; i < r1.size(); i++) {
if (r1.get(i) == TableItem.optimized || r2.get(i) == TableItem.optimized) {
if (!r1.get(i).equals(r2.get(i)))
return -1;
}
}
int difIndex = -1;
for (int i = 0; i < r1.size(); i++) {
if (!r1.get(i).equals(r2.get(i))) {
if (difIndex >= 0)
return -1;
difIndex = i;
}
}
return difIndex;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (TableRow r : rows) {
sb.append(r.toString());
sb.append("\n");
}
return sb.toString();
}
/**
* @return the final primes
*/
public ArrayList<TableRow> getPrimes() {
return primes;
}
/**
* @return the simplified expression which represent this table
*/
public Expression getExpression() {
if (primes.isEmpty() && rows.isEmpty())
return Constant.ZERO;
Expression e = addAnd(null, primes, variables);
return addAnd(e, rows, variables);
}
/**
* Creates the final expression
*
* @param e the expression to complete
* @param rows the rows to add
* @param variables the variables to use to build the expression
* @return the expression
*/
public static Expression addAnd(Expression e, ArrayList<TableRow> rows, ArrayList<Variable> variables) {
for (TableRow r : rows) {
Expression n = r.getExpression(variables);
if (e == null)
e = n;
else
e = or(e, n);
}
return e;
}
/**
* Simplify the primes
*
* @param primeSelector the prome selector to use
* @return this for call chaning
*/
public QuineMcClusky simplifyPrimes(PrimeSelector primeSelector) {
ArrayList<TableRow> primesAvail = new ArrayList<TableRow>(primes);
primes.clear();
TreeSet<Integer> termIndices = new TreeSet<>();
for (TableRow r : primesAvail)
termIndices.addAll(r.getSource());
// Nach primtermen suchen, welche einen index exclusiv enthalten
// Diese müssen in jedem Falle enthalten sein!
for (int pr : termIndices) {
TableRow foundPrime = null;
for (TableRow tr : primesAvail) {
if (tr.getSource().contains(pr)) {
if (foundPrime == null) {
foundPrime = tr;
} else {
foundPrime = null;
break;
}
}
}
if (foundPrime != null) {
if (!primes.contains(foundPrime))
primes.add(foundPrime);
}
}
primesAvail.removeAll(primes);
// Die, Indices die wir schon haben können raus;
for (TableRow pr : primes) {
termIndices.removeAll(pr.getSource());
}
if (!termIndices.isEmpty()) {
//Die noch übrigen Terme durchsuchen ob sie schon komplett dabei sind;
Iterator<TableRow> it = primesAvail.iterator();
while (it.hasNext()) {
TableRow tr = it.next();
boolean needed = false;
for (int i : tr.getSource()) {
if (termIndices.contains(i)) {
needed = true;
break;
}
}
if (!needed) {
it.remove();
}
}
primeSelector.select(primes, primesAvail, termIndices);
}
return this;
}
}

View File

@ -0,0 +1,12 @@
package de.neemann.digital.analyse.quinemc;
/**
* A QMC tables entry
*
* @author hneemann
*/
public enum TableItem {
zero,
one,
optimized
}

View File

@ -0,0 +1,211 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.BitSetter;
import de.neemann.digital.analyse.expression.Constant;
import de.neemann.digital.analyse.expression.Expression;
import de.neemann.digital.analyse.expression.Variable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.TreeSet;
import static de.neemann.digital.analyse.expression.Not.not;
import static de.neemann.digital.analyse.expression.Operation.and;
/**
* Represents a row in a QMC table
*
* @author hneemann
*/
public class TableRow implements Comparable<TableRow> {
private final TableItem[] items;
private boolean used = false;
private final TreeSet<Integer> source;
/**
* Copies the given table row
*
* @param tr the row to copy
*/
public TableRow(TableRow tr) {
this(tr.size());
for (int i = 0; i < size(); i++)
items[i] = tr.get(i);
}
/**
* Creates a new tyble row
*
* @param cols number of columns
*/
public TableRow(int cols) {
items = new TableItem[cols];
source = new TreeSet<>();
}
/**
* Creates a new row
*
* @param cols the number of columns
* @param bitValue the value representing the bits in the row
* @param index the index of the original source row
* @param dontCare dont care
*/
public TableRow(int cols, int bitValue, int index, boolean dontCare) {
this(cols);
if (!dontCare)
source.add(index);
new BitSetter(cols) {
@Override
public void setBit(int row, int bit, boolean value) {
if (value)
items[bit] = TableItem.one;
else
items[bit] = TableItem.zero;
}
}.fill(bitValue);
}
/**
* The item at the given indes
*
* @param index the comumns index
* @return the value
*/
public TableItem get(int index) {
return items[index];
}
/**
* Sets the given idex to optimized
*
* @param index the columns index
*/
public void setToOptimized(int index) {
items[index] = TableItem.optimized;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int c = 0; c < items.length; c++)
switch (items[c]) {
case zero:
sb.append('0');
break;
case one:
sb.append('1');
break;
case optimized:
sb.append('-');
break;
}
for (Integer i : source)
sb.append(",").append(i);
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TableRow tableRow = (TableRow) o;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
return Arrays.equals(items, tableRow.items);
}
@Override
public int hashCode() {
return Arrays.hashCode(items);
}
/**
* Set the used flag
*/
public void setUsed() {
this.used = true;
}
/**
* @return the used flag
*/
public boolean isUsed() {
return used;
}
/**
* @return the number of one values in this row
*/
public int countOnes() {
int c = 0;
for (int i = 0; i < items.length; i++)
if (items[i] == TableItem.one)
c++;
return c;
}
@Override
public int compareTo(TableRow tableRow) {
return Integer.compare(countOnes(), tableRow.countOnes());
}
/**
* @return the number of columns
*/
public int size() {
return items.length;
}
/**
* @return the source line numbers
*/
public Collection<Integer> getSource() {
return source;
}
/**
* Adds some sources to this line
*
* @param s the sources to add
*/
public void addSource(Collection<Integer> s) {
source.addAll(s);
}
/**
* Returns an expression build with the given variables
*
* @param vars the variables to use
* @return the expression
*/
public Expression getExpression(ArrayList<Variable> vars) {
Expression e = null;
for (int i = 0; i < size(); i++) {
Expression term = null;
switch (items[i]) {
case one:
term = vars.get(i);
break;
case zero:
term = not(vars.get(i));
break;
}
if (term != null) {
if (e == null)
e = term;
else
e = and(e, term);
}
}
if (e == null)
return Constant.ONE;
else
return e;
}
}

View File

@ -0,0 +1,30 @@
package de.neemann.digital.analyse.quinemc;
/**
* @author hneemann
*/
public enum ThreeStateValue {
one,
zero,
dontCare;
public static ThreeStateValue value(boolean bool) {
if (bool) {
return one;
} else {
return zero;
}
}
public static ThreeStateValue value(int value) {
switch (value) {
case 0:
return ThreeStateValue.zero;
case 1:
return ThreeStateValue.one;
default:
return ThreeStateValue.dontCare;
}
}
}

View File

@ -0,0 +1,6 @@
/**
* Implementation of the algorithm from Quine and McClusky
*
* @author hneemann
*/
package de.neemann.digital.analyse.quinemc;

View File

@ -0,0 +1,54 @@
package de.neemann.digital.analyse.quinemc.primeselector;
import de.neemann.digital.analyse.quinemc.TableRow;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;
/**
* @author hneemann
*/
public class BruteForce implements PrimeSelector {
@Override
public void select(ArrayList<TableRow> primes, ArrayList<TableRow> primesAvail, TreeSet<Integer> termIndices) {
int comb = 1 << primesAvail.size();
ArrayList<Integer> list = new ArrayList<>(comb);
for (int i = 1; i < comb; i++) {
list.add(i);
}
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer i1, Integer i2) {
return Integer.bitCount(i1) - Integer.bitCount(i2);
}
});
ArrayList<Integer> l = new ArrayList<>();
for (int mask : list) {
l.addAll(termIndices);
int m = mask;
for (TableRow aPrimesAvail : primesAvail) {
if ((m & 1) > 0) {
l.removeAll(aPrimesAvail.getSource());
}
m >>= 1;
}
if (l.isEmpty()) {
m = mask;
for (TableRow aPrime : primesAvail) {
if ((m & 1) > 0) {
primes.add(aPrime);
}
m >>= 1;
}
return;
} else {
l.clear();
}
}
throw new RuntimeException("BruteForce Error!");
}
}

View File

@ -0,0 +1,73 @@
package de.neemann.digital.analyse.quinemc.primeselector;
import de.neemann.digital.analyse.quinemc.TableRow;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;
/**
* @author hneemann
*/
public class BruteForceGetAll implements PrimeSelector, PrimeSelectorGetAll {
private ArrayList<ArrayList<TableRow>> foundSolutions;
@Override
public void select(ArrayList<TableRow> primes, ArrayList<TableRow> primesAvail, TreeSet<Integer> termIndices) {
int comb = 1 << primesAvail.size();
ArrayList<Integer> list = new ArrayList<>(comb);
for (int i = 1; i < comb; i++) {
list.add(i);
}
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer i1, Integer i2) {
return Integer.bitCount(i1) - Integer.bitCount(i2);
}
});
int primesUsed = 0;
foundSolutions = new ArrayList<>();
ArrayList<Integer> indicesOpen = new ArrayList<>();
for (int mask : list) {
if (primesUsed != 0 && Integer.bitCount(mask) > primesUsed)
break;
indicesOpen.clear();
indicesOpen.addAll(termIndices);
int m = mask;
for (TableRow aPrimesAvail : primesAvail) {
if ((m & 1) > 0) {
indicesOpen.removeAll(aPrimesAvail.getSource());
}
m >>= 1;
}
if (indicesOpen.isEmpty()) {
primesUsed = Integer.bitCount(mask);
ArrayList<TableRow> singleSolution = new ArrayList<>(primes);
m = mask;
for (TableRow aPrime : primesAvail) {
if ((m & 1) > 0) {
singleSolution.add(aPrime);
}
m >>= 1;
}
foundSolutions.add(singleSolution);
}
}
primes.clear();
primes.addAll(foundSolutions.get(0));
}
@Override
public ArrayList<ArrayList<TableRow>> getAllSolutions() {
return foundSolutions;
}
}

View File

@ -0,0 +1,36 @@
package de.neemann.digital.analyse.quinemc.primeselector;
import de.neemann.digital.analyse.quinemc.TableRow;
import java.util.ArrayList;
import java.util.TreeSet;
/**
* Tries at first the primes containing the most indices
*
* @author hneemann
*/
public class LargestFirst implements PrimeSelector {
@Override
public void select(ArrayList<TableRow> primes, ArrayList<TableRow> primesAvail, TreeSet<Integer> termIndices) {
while (!termIndices.isEmpty()) {
TableRow bestRow = null;
int maxCount = 0;
for (TableRow tr : primesAvail) {
int count = 0;
for (int i : tr.getSource()) {
if (termIndices.contains(i))
count++;
}
if (count > maxCount) {
maxCount = count;
bestRow = tr;
}
}
primes.add(bestRow);
primesAvail.remove(bestRow);
termIndices.removeAll(bestRow.getSource());
}
}
}

View File

@ -0,0 +1,44 @@
package de.neemann.digital.analyse.quinemc.primeselector;
import de.neemann.digital.analyse.quinemc.TableRow;
import java.util.ArrayList;
import java.util.TreeSet;
/**
* Represents an algorithm which chooses the final primes
*
* @author hneemann
*/
public interface PrimeSelector {
/**
* The default prime selector
*/
PrimeSelector DEFAULT = new PrimeSelector() {
private final PrimeSelector bruteForce = new BruteForceGetAll();
private final PrimeSelector largestFirst = new LargestFirst();
@Override
public void select(ArrayList<TableRow> primes, ArrayList<TableRow> primesAvail, TreeSet<Integer> termIndices) {
int count = primesAvail.size();
if (count <= 12) {
bruteForce.select(primes, primesAvail, termIndices);
} else {
largestFirst.select(primes, primesAvail, termIndices);
}
}
};
/**
* Selects the primes to use
*
* @param primes the list to add the primes to
* @param primesAvail the available promes
* @param termIndices the indices
*/
void select(ArrayList<TableRow> primes, ArrayList<TableRow> primesAvail, TreeSet<Integer> termIndices);
}

View File

@ -0,0 +1,19 @@
package de.neemann.digital.analyse.quinemc.primeselector;
import de.neemann.digital.analyse.quinemc.TableRow;
import java.util.ArrayList;
/**
* Used to create all possible sollutions
*
* @author hneemann
*/
public interface PrimeSelectorGetAll {
/**
* @return all possible solutions
*/
ArrayList<ArrayList<TableRow>> getAllSolutions();
}

View File

@ -0,0 +1,6 @@
/**
* Classes used to select the necessary primes
*
* @author hneemann
*/
package de.neemann.digital.analyse.quinemc.primeselector;

View File

@ -0,0 +1,54 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.ExpressionException;
import de.neemann.digital.analyse.expression.format.FormatterException;
/**
* @author hneemann
*/
public abstract class FullVariantDontCareCreator {
private final int nmax;
private final int step;
public FullVariantDontCareCreator() {
this(3, 1);
}
public FullVariantDontCareCreator(int nmax) {
this(nmax, 1);
}
public FullVariantDontCareCreator(int nmax, int step) {
this.nmax = nmax;
this.step = step;
}
public void create() throws ExpressionException, FormatterException {
for (int n = 1; n <= nmax; n++) {
int tables = 1;
int c = 1 << n;
for (int i = 0; i < c; i++) tables *= 3;
int count = 0;
int[] tab = new int[1 << n];
for (int i = 0; i < tables; i += step) {
int value = i;
for (int j = 0; j < tab.length; j++) {
tab[j] = value % 3;
value /= 3;
}
handleTable(n, tab);
if (count++ > 10000) {
System.out.println(i + "/" + tables);
count = 0;
}
}
}
}
public abstract void handleTable(int n, int[] tab) throws ExpressionException, FormatterException;
}

View File

@ -0,0 +1,124 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.*;
import de.neemann.digital.analyse.expression.format.FormatToExpression;
import de.neemann.digital.analyse.expression.format.FormatToTable;
import de.neemann.digital.analyse.expression.format.FormatterException;
import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.Arrays;
import static de.neemann.digital.analyse.expression.Variable.vars;
import static de.neemann.digital.analyse.expression.format.FormatToExpression.FORMATTER_UNICODE;
/**
* @author hneemann
*/
public class QuineMcCluskyDontCareTest extends TestCase {
public void testDontCare() throws Exception, FormatterException {
ArrayList<Variable> v = vars("A", "B", "C");
Expression e = new QuineMcClusky(v)
.fillTableWith(new BoolTableIntArray(new int[]{1, 1, 0, 0, 1, 2, 2, 0}))
.simplify()
.getExpression();
System.out.println(new FormatToTable().format("y", e));
assertEquals("!B", FormatToExpression.FORMATTER_JAVA.format(e));
}
/**
* up to 3 variables we can calculate all tables possible!
*
* @throws ExpressionException
*/
public void testFull() throws ExpressionException, FormatterException {
new FullVariantDontCareCreator() {
@Override
public void handleTable(int n, int[] tab) throws ExpressionException {
performTestCalculation(n, tab);
}
}.create();
}
/**
* for more the 3 variables we only test some random tables
*
* @throws ExpressionException
*/
public void testRegression() throws ExpressionException {
for (int n = 4; n < 8; n++) {
for (int i = 0; i < 200; i++) {
performTestCalculationRandom(n);
}
}
}
static private void performTestCalculationRandom(int n) throws ExpressionException {
int[] tab = new int[1 << n];
for (int i = 0; i < tab.length; i++)
tab[i] = (int) Math.round(Math.random() * 3); // half of the values are don't care
performTestCalculation(n, tab);
}
static private void performTestCalculation(int n, int[] tab) throws ExpressionException {
ArrayList<Variable> v = vars(n);
Expression e = new QuineMcClusky(v)
.fillTableWith(new BoolTableIntArray(tab))
.simplify()
.getExpression();
assertNotNull(e);
ContextFiller context = new ContextFiller(v);
for (int i = 0; i < tab.length; i++) {
if (tab[i] <= 1)
assertEquals(tab[i] == 1, e.calculate(context.setContextTo(i)));
}
}
public void testComplexity() throws Exception, FormatterException {
new FullVariantDontCareCreator() {
@Override
public void handleTable(int n, int[] tab) throws ExpressionException, FormatterException {
Expression e = createExpression(n, tab);
int[] tabZero = Arrays.copyOf(tab, tab.length);
for (int i = 0; i < tabZero.length; i++)
if (tabZero[i] > 1) tabZero[i] = 0;
Expression eZero = createExpression(n, tabZero);
int[] tabOne = Arrays.copyOf(tab, tab.length);
for (int i = 0; i < tabOne.length; i++)
if (tabOne[i] > 1) tabOne[i] = 1;
Expression eOne = createExpression(n, tabOne);
int c = e.traverse(new ComplexityVisitor()).getComplexity();
int cOne = eOne.traverse(new ComplexityVisitor()).getComplexity();
int cZero = eZero.traverse(new ComplexityVisitor()).getComplexity();
boolean ok = (c <= cOne) && (c <= cZero);
if (!ok) {
System.out.println("\nX: " + FORMATTER_UNICODE.format(e) + ", " + c);
System.out.println("0: " + FORMATTER_UNICODE.format(eZero) + ", " + cZero);
System.out.println("1: " + FORMATTER_UNICODE.format(eOne) + ", " + cOne);
assertTrue(false);
}
}
}.create();
}
private Expression createExpression(int n, int[] tab) throws ExpressionException {
ArrayList<Variable> v = vars(n);
return new QuineMcClusky(v)
.fillTableWith(new BoolTableIntArray(tab))
.simplify()
.getExpression();
}
}

View File

@ -0,0 +1,87 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.ContextFiller;
import de.neemann.digital.analyse.expression.Expression;
import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.expression.format.FormatToExpression;
import de.neemann.digital.analyse.expression.format.FormatterException;
import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelector;
import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.Collections;
import static de.neemann.digital.analyse.expression.Operation.or;
/**
* @author hneemann
*/
public class QuineMcCluskyRegressionTest extends TestCase {
public void testRegression() throws Exception, FormatterException {
testRegression(8, 128);
testRegression(8, 16);
testRegression(4, 8);
testRegression(4, 4);
}
public void testRegression2() throws Exception, FormatterException {
for (int i = 0; i < 100; i++) {
testRegression(5, 16);
testRegression(5, 8);
testRegression(5, 4);
}
}
public void testRegression3() throws Exception, FormatterException {
Variable a = new Variable("A");
Variable b = new Variable("B");
Variable c = new Variable("C");
Variable d = new Variable("D");
ArrayList<Variable> vars = new ArrayList<>();
vars.add(a);
vars.add(b);
vars.add(c);
vars.add(d);
QuineMcClusky t = new QuineMcClusky(vars);
Expression ex = or(a, c);
t.fillTableWith(new BoolTableExpression(ex, new ContextFiller(vars)));
System.out.println("--");
while (!t.isFinished()) {
System.out.println(FormatToExpression.FORMATTER_JAVA.format(t.getExpression()));
t = t.simplifyStep().removeDuplicates();
}
t.simplifyPrimes(PrimeSelector.DEFAULT);
assertEquals("A || C", FormatToExpression.FORMATTER_JAVA.format(t.getExpression()));
System.out.println("--");
}
public static void testRegression(int n, int j) throws Exception, FormatterException {
int size = 1 << n;
boolean[] table = new boolean[size];
ArrayList<Integer> index = new ArrayList<>();
for (int i = 0; i < size; i++) index.add(i);
Collections.shuffle(index);
for (int i = 0; i < j; i++)
table[index.get(i)] = true;
ArrayList<Variable> var = Variable.vars(n);
Expression expression =
new QuineMcClusky(var)
.fillTableWith(new BoolTableBoolArray(table))
.simplify()
.getExpression();
ContextFiller cf = new ContextFiller(var);
for (int i = 0; i < table.length; i++)
assertEquals(table[i], expression.calculate(cf.setContextTo(i)));
}
}

View File

@ -0,0 +1,34 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.expression.format.FormatToExpression;
import de.neemann.digital.analyse.expression.format.FormatterException;
import junit.framework.TestCase;
import java.util.ArrayList;
/**
* @author hneemann
*/
public class QuineMcCluskyRowTest extends TestCase {
public void testSimple() throws FormatterException {
ArrayList<Variable> vars = Variable.vars("A", "B", "C", "D");
TableRow tr = new TableRow(4, 15, 0, false);
assertEquals("A && B && C && D", FormatToExpression.FORMATTER_JAVA.format(tr.getExpression(vars)));
tr = new TableRow(4, 5, 0, false);
assertEquals("!A && B && !C && D", FormatToExpression.FORMATTER_JAVA.format(tr.getExpression(vars)));
tr = new TableRow(4, 10, 0, false);
assertEquals("A && !B && C && !D", FormatToExpression.FORMATTER_JAVA.format(tr.getExpression(vars)));
tr = new TableRow(4, 10, 0, false);
tr.setToOptimized(2);
assertEquals("A && !B && !D", FormatToExpression.FORMATTER_JAVA.format(tr.getExpression(vars)));
tr = new TableRow(4, 10, 0, false);
tr.setToOptimized(0);
assertEquals("!B && C && !D", FormatToExpression.FORMATTER_JAVA.format(tr.getExpression(vars)));
}
}

View File

@ -0,0 +1,161 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.Constant;
import de.neemann.digital.analyse.expression.Expression;
import de.neemann.digital.analyse.expression.ExpressionException;
import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.expression.format.FormatToExpression;
import de.neemann.digital.analyse.expression.format.FormatterException;
import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelector;
import junit.framework.TestCase;
import java.util.ArrayList;
import static de.neemann.digital.analyse.expression.Not.not;
import static de.neemann.digital.analyse.expression.Operation.and;
import static de.neemann.digital.analyse.expression.Operation.or;
/**
* @author hneemann
*/
public class QuineMcCluskyTest extends TestCase {
public void testGenerator() throws ExpressionException {
Variable a = new Variable("A");// Vorlesung
Variable b = new Variable("B");
Variable c = new Variable("C");
Variable d = new Variable("D");
Expression e = or(and(a, and(c, d)), or(and(not(c), not(d)), and(not(b), c)));
QuineMcClusky t = new QuineMcClusky(e);
assertEquals("0000,1\n" +
"0010,2\n" +
"0100,4\n" +
"1000,5\n" +
"0011,3\n" +
"1010,6\n" +
"1100,8\n" +
"1011,7\n" +
"1111,9\n", t.toString());
}
public void testSimplify() throws ExpressionException, FormatterException {
Variable a = new Variable("A");// Vorlesung
Variable b = new Variable("B");
Variable c = new Variable("C");
Variable d = new Variable("D");
Expression e = or(and(a, and(c, d)), or(and(not(c), not(d)), and(not(b), c)));
QuineMcClusky t = new QuineMcClusky(e).simplifyStep();
assertFalse(t.isFinished());
assertEquals("00-0,1,2\n" +
"0-00,1,4\n" +
"-000,1,5\n" +
"001-,2,3\n" +
"-010,2,6\n" +
"-100,4,8\n" +
"10-0,5,6\n" +
"1-00,5,8\n" +
"-011,3,7\n" +
"101-,6,7\n" +
"1-11,7,9\n", t.toString());
t = t.simplifyStep();
assertFalse(t.isFinished());
assertEquals("-0-0,1,2,5,6\n" +
"--00,1,4,5,8\n" +
"-0-0,1,2,5,6\n" +
"--00,1,4,5,8\n" +
"-01-,2,3,6,7\n" +
"-01-,2,3,6,7\n", t.toString());
ArrayList<TableRow> primes = t.getPrimes();
assertEquals(1, primes.size());
assertEquals("1-11,7,9", primes.get(0).toString());
t = t.removeDuplicates();
assertFalse(t.isFinished());
assertEquals("-0-0,1,2,5,6\n" +
"--00,1,4,5,8\n" +
"-01-,2,3,6,7\n", t.toString());
t = t.simplifyStep();
assertTrue(t.isFinished());
assertEquals("", t.toString());
primes = t.getPrimes();
assertEquals(4, primes.size());
assertEquals("1-11,7,9", primes.get(0).toString());
assertEquals("-0-0,1,2,5,6", primes.get(1).toString());
assertEquals("--00,1,4,5,8", primes.get(2).toString());
assertEquals("-01-,2,3,6,7", primes.get(3).toString());
Expression exp = t.getExpression();
assertEquals("(A && C && D) || (!B && !D) || (!B && C) || (!C && !D)", FormatToExpression.FORMATTER_JAVA.format(exp));
t.simplifyPrimes(PrimeSelector.DEFAULT);
exp = t.getExpression();
assertEquals("(A && C && D) || (!B && C) || (!C && !D)", FormatToExpression.FORMATTER_JAVA.format(exp));
}
public void testSimplify2() throws ExpressionException, FormatterException {
QuineMcClusky t = new QuineMcClusky(Variable.vars("A", "B", "C", "D"));
t.fillTableWith(new BoolTableIntArray(new int[]{1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1}));
t = t.simplify();
assertEquals("(!A && !C) || (B && D) || (B && !C)", FormatToExpression.FORMATTER_JAVA.format(t.getExpression()));
}
public void testZero() throws Exception {
QuineMcClusky t = new QuineMcClusky(Variable.vars("A", "B", "C"));
t.fillTableWith(new BoolTableIntArray(new int[]{0, 0, 0, 0, 0, 0, 0, 0}));
t = t.simplify();
Expression e = t.getExpression();
assertNotNull(e);
assertTrue(e instanceof Constant);
assertFalse(((Constant) e).getValue());
}
public void testZero2() throws Exception {
QuineMcClusky t = new QuineMcClusky(Variable.vars("A", "B", "C"));
t.fillTableWith(new BoolTableIntArray(new int[]{0, 0, 0, 0, 0, 0, 2, 2}));
t = t.simplify();
Expression e = t.getExpression();
assertNotNull(e);
assertTrue(e instanceof Constant);
assertFalse(((Constant) e).getValue());
}
public void testOne() throws Exception {
QuineMcClusky t = new QuineMcClusky(Variable.vars("A", "B", "C"));
t.fillTableWith(new BoolTableIntArray(new int[]{1, 1, 1, 1, 1, 1, 1, 1}));
t = t.simplify();
Expression e = t.getExpression();
assertNotNull(e);
assertTrue(e instanceof Constant);
assertTrue(((Constant) e).getValue());
}
public void testOne2() throws Exception {
QuineMcClusky t = new QuineMcClusky(Variable.vars("A", "B", "C"));
t.fillTableWith(new BoolTableIntArray(new int[]{1, 1, 1, 1, 1, 1, 2, 2}));
t = t.simplify();
Expression e = t.getExpression();
assertNotNull(e);
assertTrue(e instanceof Constant);
assertTrue(((Constant) e).getValue());
}
}

View File

@ -0,0 +1,46 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.Expression;
import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.expression.format.FormatToExpression;
import de.neemann.digital.analyse.expression.format.FormatterException;
import junit.framework.TestCase;
import static de.neemann.digital.analyse.expression.Operation.and;
import static de.neemann.digital.analyse.expression.Operation.or;
import static de.neemann.digital.analyse.expression.Variable.v;
/**
* @author hneemann
*/
public class SimplifyTest extends TestCase {
public void testSimplify() throws Exception, FormatterException {
Variable a = v("a");
Variable b = v("b");
Expression e = or(and(a, b), a);
Expression s = QuineMcClusky.simplify(e);
assertEquals("a", FormatToExpression.FORMATTER_UNICODE.format(s));
}
public void testSimplify2() throws Exception, FormatterException {
Variable a = v("a");
Variable b = v("b");
Expression e = and(or(a, b), a);
Expression s = QuineMcClusky.simplify(e);
assertEquals("a", FormatToExpression.FORMATTER_UNICODE.format(s));
}
public void testSimplify3() throws Exception {
Variable a = v("a");
Variable b = v("b");
Variable c = v("c");
Expression e = and(or(a, b), c);
Expression s = QuineMcClusky.simplify(e);
assertNull(s);
}
}

View File

@ -0,0 +1,80 @@
package de.neemann.digital.analyse.quinemc.primeselector;
import de.neemann.digital.analyse.expression.ContextFiller;
import de.neemann.digital.analyse.expression.Expression;
import de.neemann.digital.analyse.expression.ExpressionException;
import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.expression.format.FormatterException;
import de.neemann.digital.analyse.quinemc.BoolTableIntArray;
import de.neemann.digital.analyse.quinemc.FullVariantDontCareCreator;
import de.neemann.digital.analyse.quinemc.QuineMcClusky;
import de.neemann.digital.analyse.quinemc.TableRow;
import junit.framework.TestCase;
import java.util.ArrayList;
import static de.neemann.digital.analyse.expression.Variable.vars;
/**
* @author hneemann
*/
public class BruteForceGetAllTest extends TestCase {
/**
* up to 3 variables we can calculate all tables possible!
*
* @throws ExpressionException
*/
public void testFullRegression() throws ExpressionException, FormatterException {
new FullVariantDontCareCreator() {
@Override
public void handleTable(int n, int[] tab) throws ExpressionException {
performTestCalculation(n, tab);
}
}.create();
new FullVariantDontCareCreator(4, 241) {
@Override
public void handleTable(int n, int[] tab) throws ExpressionException {
performTestCalculation(n, tab);
}
}.create();
}
/*
public void testFull() throws ExpressionException, FormatterException {
new FullVariantDontCareCreator(4) {
@Override
public void handleTable(int n, int[] tab) throws ExpressionException {
performTestCalculation(n, tab);
}
}.create();
} /**/
static private void performTestCalculation(int n, int[] tab) throws ExpressionException {
BruteForceGetAll ps = new BruteForceGetAll();
ArrayList<Variable> v = vars(n);
new QuineMcClusky(v)
.fillTableWith(new BoolTableIntArray(tab))
.simplify(ps);
ArrayList<ArrayList<TableRow>> solutions = ps.getAllSolutions();
if (solutions != null) {
for (ArrayList<TableRow> sol : solutions) {
Expression e = QuineMcClusky.addAnd(null, sol, v);
ContextFiller context = new ContextFiller(v);
for (int i = 0; i < tab.length; i++) {
if (tab[i] <= 1) {
assertEquals(tab[i] == 1, e.calculate(context.setContextTo(i)));
}
}
}
}
}
}