Added a table reducer to remove variables from tables which are not used at all.

So the QMC algorithm has to deal with a reduced number of variables.
This commit is contained in:
hneemann 2017-03-12 12:38:57 +01:00
parent 0b44a0129f
commit d7cb8e1497
10 changed files with 369 additions and 22 deletions

View File

@ -23,7 +23,7 @@ import java.util.List;
* @author hneemann
*/
public class ModelAnalyser {
private static final int MAX_INPUTS_ALLOWED = 12;
private static final int MAX_INPUTS_ALLOWED = 24;
private final Model model;
private final ArrayList<Signal> inputs;
@ -109,7 +109,8 @@ public class ModelAnalyser {
* @throws NodeException NodeException
*/
public TruthTable analyse() throws NodeException {
System.out.print("start to analyse the model");
long time = System.currentTimeMillis();
BitSetter bitsetter = new BitSetter(inputs.size()) {
@Override
public void setBit(int row, int bit, boolean value) {
@ -128,7 +129,6 @@ public class ModelAnalyser {
tt.addResult(s.getName(), e);
}
model.init();
for (int row = 0; row < rows; row++) {
bitsetter.fill(row);
@ -137,6 +137,8 @@ public class ModelAnalyser {
data.get(i).set(row, outputs.get(i).getValue().getBool());
}
}
time = System.currentTimeMillis() - time;
System.out.println(": " + time / 1000.0 + " sec");
return tt;
}

View File

@ -1,15 +1,13 @@
package de.neemann.digital.analyse.expression;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.*;
/**
* @author hneemann
*/
public class ContextFiller extends ContextMap implements Iterable<Variable> {
private final ArrayList<Variable> vars;
private final List<Variable> vars;
private final int rowCount;
private final BitSetter bitSetter;
@ -29,7 +27,16 @@ public class ContextFiller extends ContextMap implements Iterable<Variable> {
*
* @param variables the variables to use
*/
public ContextFiller(ArrayList<Variable> variables) {
public ContextFiller(Variable... variables) {
this(Arrays.asList(variables));
}
/**
* Creates a new instance
*
* @param variables the variables to use
*/
public ContextFiller(List<Variable> variables) {
vars = variables;
rowCount = 1 << vars.size();
bitSetter = new BitSetter(vars.size()) {
@ -83,7 +90,7 @@ public class ContextFiller extends ContextMap implements Iterable<Variable> {
/**
* @return the variables to use
*/
public ArrayList<Variable> getVariables() {
public List<Variable> getVariables() {
return vars;
}
}

View File

@ -0,0 +1,110 @@
package de.neemann.digital.analyse.quinemc;
/**
* Checks if a bool table is independent from a certain variable
* Created by hneemann on 12.03.17.
*/
public final class IndependentChecker {
private final BoolTable boolTable;
private final int vars;
private final int checkSize;
/**
* Creates a new BoolTable
*
* @param boolTable the table to investigate
*/
public IndependentChecker(BoolTable boolTable) {
this.boolTable = boolTable;
int v = 0;
int s = boolTable.size();
while (s > 1) {
s = s / 2;
v++;
}
vars = v;
checkSize = boolTable.size() / 2;
}
/**
* @return the number of variables
*/
public int getVars() {
return vars;
}
/**
* checks if the given bool table is dependent on the given variable
*
* @param varNum the variable to check
* @return true if table is independent
*/
public boolean isIndependentFrom(int varNum) {
int bitMask = 1 << (vars - varNum - 1);
int lowMask = bitMask - 1;
int highMask = ~lowMask;
for (int n = 0; n < checkSize; n++) {
int i1 = (n & lowMask) | ((n & highMask) << 1);
int i2 = i1 | bitMask;
ThreeStateValue v1 = boolTable.get(i1);
ThreeStateValue v2 = boolTable.get(i2);
if (v1.equals(v2))
continue;
if (v1.equals(ThreeStateValue.dontCare) || v2.equals(ThreeStateValue.dontCare))
continue;
return false;
}
return true;
}
/**
* Returns a table with the given variable removed
*
* @param varNum the variable to remove
* @return the reduced BoolTable
*/
public BoolTable removeVar(int varNum) {
if (varNum >= vars || varNum < 0)
throw new RuntimeException("variable does not exist");
return new BoolTableRemoveVar(boolTable, vars, varNum);
}
private static final class BoolTableRemoveVar implements BoolTable {
private final BoolTable boolTable;
private final int bitMask;
private final int lowMask;
private final int highMask;
private final int size;
private BoolTableRemoveVar(BoolTable boolTable, int vars, int varNum) {
this.boolTable = boolTable;
bitMask = 1 << (vars - varNum - 1);
lowMask = bitMask - 1;
highMask = ~lowMask;
size = 1 << (vars - 1);
}
@Override
public int size() {
return size;
}
@Override
public ThreeStateValue get(int n) {
int i = (n & lowMask) | ((n & highMask) << 1);
ThreeStateValue v = boolTable.get(i);
if (v.equals(ThreeStateValue.dontCare))
return boolTable.get(i | bitMask);
else
return v;
}
}
}

View File

@ -0,0 +1,67 @@
package de.neemann.digital.analyse.quinemc;
import de.neemann.digital.analyse.expression.Variable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* If the result does not depend on a certain variable, this variable is removed.
* <p>
* Created by hneemann on 12.03.17.
*/
public class TableReducer {
private List<Variable> vars;
private BoolTable table;
/**
* Creates a new instance
*
* @param vars the variable
* @param table the bool table
*/
public TableReducer(List<Variable> vars, BoolTable table) {
this.vars = new ArrayList<>();
this.vars.addAll(vars);
this.table = table;
}
/**
* Returns true if there are reduce variables
*
* @return true is reduction was possible
*/
public boolean canReduce() {
boolean isReduced = false;
Iterator<Variable> it = vars.iterator();
int var = 0;
while (it.hasNext()) {
it.next();
IndependentChecker ic = new IndependentChecker(table);
if (ic.isIndependentFrom(var)) {
it.remove();
table = ic.removeVar(var);
isReduced = true;
} else {
var++;
}
}
return isReduced;
}
/**
* @return the remaining variables
*/
public List<Variable> getVars() {
return vars;
}
/**
* @return the reduced table
*/
public BoolTable getTable() {
return table;
}
}

View File

@ -1,14 +1,18 @@
package de.neemann.digital.gui.components.table;
import de.neemann.digital.analyse.AnalyseException;
import de.neemann.digital.analyse.TruthTable;
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.BoolTable;
import de.neemann.digital.analyse.quinemc.QuineMcCluskey;
import de.neemann.digital.analyse.quinemc.TableReducer;
import de.neemann.digital.analyse.quinemc.TableRow;
import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelector;
import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelectorDefault;
import de.neemann.digital.lang.Lang;
import java.util.ArrayList;
import java.util.Collections;
@ -24,6 +28,8 @@ import java.util.concurrent.TimeUnit;
*/
public class ExpressionCreator {
private static final int MAX_INPUTS_ALLOWED = 12;
private final TruthTable theTable;
/**
@ -40,24 +46,22 @@ public class ExpressionCreator {
*
* @throws ExpressionException ExpressionException
* @throws FormatterException FormatterException
* @throws AnalyseException AnalyseException
*/
public void create(ExpressionListener listener) throws ExpressionException, FormatterException {
public void create(ExpressionListener listener) throws ExpressionException, FormatterException, AnalyseException {
final List<Variable> vars = Collections.unmodifiableList(theTable.getVars());
long time = System.currentTimeMillis();
if (theTable.getResultCount() > 100) {
ExecutorService ex = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() / 2);
ExecutorService ex = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
ThreadSaveExpressionListener threadListener = new ThreadSaveExpressionListener(listener);
for (int table = 0; table < theTable.getResultCount(); table++) {
final QuineMcCluskey qmc = new QuineMcCluskey(vars)
.fillTableWith(theTable.getResult(table));
final String resultName = theTable.getResultName(table);
final int t = table;
ex.submit(() -> {
try {
System.out.println("start " + t);
calcColumn(threadListener, qmc, resultName, vars);
simplify(listener, vars, theTable.getResultName(t), theTable.getResult(t));
System.out.println("end " + t);
} catch (ExpressionException | FormatterException e) {
} catch (ExpressionException | FormatterException | AnalyseException e) {
e.printStackTrace();
}
});
@ -70,17 +74,30 @@ public class ExpressionCreator {
}
threadListener.close();
} else {
for (int table = 0; table < theTable.getResultCount(); table++) {
QuineMcCluskey qmc = new QuineMcCluskey(vars)
.fillTableWith(theTable.getResult(table));
calcColumn(listener, qmc, theTable.getResultName(table), vars);
}
for (int table = 0; table < theTable.getResultCount(); table++)
simplify(listener, vars, theTable.getResultName(table), theTable.getResult(table));
listener.close();
}
time = System.currentTimeMillis() - time;
System.out.println("time: " + time / 1000.0 + " sec");
}
private void simplify(ExpressionListener listener, List<Variable> vars, String resultName, BoolTable boolTable) throws AnalyseException, ExpressionException, FormatterException {
TableReducer tr = new TableReducer(vars, boolTable);
List<Variable> localVars = vars;
if (tr.canReduce()) {
System.out.println(resultName + " reduced from " + vars.size() + " to " + tr.getVars().size() + " variables");
boolTable = tr.getTable();
localVars = tr.getVars();
}
if (localVars.size() > MAX_INPUTS_ALLOWED)
throw new AnalyseException(Lang.get("err_toManyInputsIn_N0_max_N1_is_N2", resultName, MAX_INPUTS_ALLOWED, localVars.size()));
QuineMcCluskey qmc = new QuineMcCluskey(localVars)
.fillTableWith(boolTable);
calcColumn(listener, qmc, resultName, localVars);
}
private static void calcColumn(ExpressionListener listener, QuineMcCluskey qmc, String name, List<Variable> vars) throws ExpressionException, FormatterException {
PrimeSelector ps = new PrimeSelectorDefault();
Expression e = qmc.simplify(ps).getExpression();

View File

@ -1,5 +1,6 @@
package de.neemann.digital.gui.components.table;
import de.neemann.digital.analyse.AnalyseException;
import de.neemann.digital.analyse.TruthTable;
import de.neemann.digital.analyse.TruthTableTableModel;
import de.neemann.digital.analyse.expression.Expression;
@ -579,7 +580,7 @@ public class TableDialog extends JDialog {
lastGeneratedExpressions = new ExpressionListenerStore(expressionListener);
new ExpressionCreator(model.getTable()).create(lastGeneratedExpressions);
} catch (ExpressionException | FormatterException e1) {
} catch (ExpressionException | FormatterException | AnalyseException e1) {
new ErrorMessage(Lang.get("msg_errorDuringCalculation")).addCause(e1).show();
}
}

View File

@ -423,6 +423,8 @@ Zur Analyse können Sie die Schaltung im Gatterschrittmodus ausführen.</string>
<string name="err_splitterBitsMismatch">Die Bitzahl am Spiltter passt nicht</string>
<string name="err_tableBecomesToSmall">Es sind mindestens zwei Eingänge erforderlich!</string>
<string name="err_toManyInputs_max_N0_is_N1">Zu viele Eingänge. Es sind nur {0} Eingänge erlaubt, es wurden aber {1} gefunden.</string>
<string name="err_toManyInputsIn_N0_max_N1_is_N2">Zu viele Variablen bei der Vereinfachung von {0}.
Es sind nur {1} Variablen erlaubt, es wurden aber {2} gefunden.</string>
<string name="err_varNotAllowedInCUPL_N">In CUPL ist die Variable {0} nicht erlaubt!</string>
<string name="err_varNotDefined_N">Variable {0} ist nicht definiert.</string>
<string name="err_parserUnexpectedToken_N">Unerwartetes Zeichen {0}</string>

View File

@ -410,6 +410,8 @@ To analyse you can run the circuit in single gate step mode.</string>
<string name="err_splitterBitsMismatch">Bit count of splitter is not matching</string>
<string name="err_tableBecomesToSmall">Two inputs are required!</string>
<string name="err_toManyInputs_max_N0_is_N1">To many inputs, allowed are {0} but {1} are found.</string>
<string name="err_toManyInputsIn_N0_max_N1_is_N2">To many variables used in {0},
allowed are {1} variables but {2} are found.</string>
<string name="err_varNotAllowedInCUPL_N">Variable {0} is not allowed in CUPL source!</string>
<string name="err_varNotDefined_N">Variable {0} not defined</string>
<string name="err_parserUnexpectedToken_N">Unexpected Token {0}</string>

View File

@ -0,0 +1,76 @@
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 junit.framework.TestCase;
import static de.neemann.digital.analyse.expression.Operation.and;
/**
* Created by hneemann on 12.03.17.
*/
public class IndependentCheckerTest extends TestCase {
private Variable a = new Variable("A");
private Variable b = new Variable("B");
private Variable c = new Variable("C");
private Variable d = new Variable("D");
public void testSimple() {
Expression ex = and(a, b, c);
ContextFiller cf = new ContextFiller(a, b, c, d);
BoolTableExpression bte = new BoolTableExpression(ex, cf);
IndependentChecker ic = new IndependentChecker(bte);
assertEquals(4, ic.getVars());
assertFalse(ic.isIndependentFrom(0));
assertFalse(ic.isIndependentFrom(1));
assertFalse(ic.isIndependentFrom(2));
assertTrue(ic.isIndependentFrom(3));
}
public void testSimple2() {
Expression ex = and(a, c, d);
ContextFiller cf = new ContextFiller(a, b, c, d);
BoolTableExpression bte = new BoolTableExpression(ex, cf);
IndependentChecker ic = new IndependentChecker(bte);
assertEquals(4, ic.getVars());
assertFalse(ic.isIndependentFrom(0));
assertTrue(ic.isIndependentFrom(1));
assertFalse(ic.isIndependentFrom(2));
assertFalse(ic.isIndependentFrom(3));
}
public void testRemoveVar() {
Expression ex = and(a, b, c);
ContextFiller cf = new ContextFiller(a, b, c, d);
BoolTableExpression bte = new BoolTableExpression(ex, cf);
IndependentChecker ic = new IndependentChecker(bte);
BoolTable btr = ic.removeVar(3);
assertEquals(8, btr.size());
for (int i = 0; i < 7; i++)
assertEquals(ThreeStateValue.zero, btr.get(i));
assertEquals(ThreeStateValue.one, btr.get(7));
}
public void testRemoveVar2() {
Expression ex = and(a, c, d);
ContextFiller cf = new ContextFiller(a, b, c, d);
BoolTableExpression bte = new BoolTableExpression(ex, cf);
IndependentChecker ic = new IndependentChecker(bte);
BoolTable btr = ic.removeVar(1);
assertEquals(8, btr.size());
for (int i = 0; i < 7; i++)
assertEquals(ThreeStateValue.zero, btr.get(i));
assertEquals(ThreeStateValue.one, btr.get(7));
}
}

View File

@ -0,0 +1,63 @@
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 junit.framework.TestCase;
import java.util.ArrayList;
import java.util.List;
import static de.neemann.digital.analyse.expression.Operation.and;
/**
* Created by hneemann on 12.03.17.
*/
public class TableReducerTest extends TestCase {
private Variable a = new Variable("A");
private Variable b = new Variable("B");
private Variable c = new Variable("C");
private Variable d = new Variable("D");
public void testReduce() {
List<Variable> vars=new ArrayList<>();
vars.add(a);
vars.add(b);
vars.add(c);
vars.add(d);
Expression ex = and(a, b, c);
ContextFiller cf = new ContextFiller(vars);
BoolTableExpression bte = new BoolTableExpression(ex, cf);
TableReducer tr = new TableReducer(vars, bte);
assertTrue(tr.canReduce());
vars = tr.getVars();
assertEquals(3, vars.size());
assertEquals(a, vars.get(0));
assertEquals(b, vars.get(1));
assertEquals(c, vars.get(2));
}
public void testReduce2() {
List<Variable> vars=new ArrayList<>();
vars.add(a);
vars.add(b);
vars.add(c);
vars.add(d);
Expression ex = and(a, c, d);
ContextFiller cf = new ContextFiller(vars);
BoolTableExpression bte = new BoolTableExpression(ex, cf);
TableReducer tr = new TableReducer(vars, bte);
assertTrue(tr.canReduce());
vars = tr.getVars();
assertEquals(3, vars.size());
assertEquals(a, vars.get(0));
assertEquals(c, vars.get(1));
assertEquals(d, vars.get(2));
}
}