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

View File

@ -1,15 +1,13 @@
package de.neemann.digital.analyse.expression; package de.neemann.digital.analyse.expression;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.Iterator;
/** /**
* @author hneemann * @author hneemann
*/ */
public class ContextFiller extends ContextMap implements Iterable<Variable> { public class ContextFiller extends ContextMap implements Iterable<Variable> {
private final ArrayList<Variable> vars; private final List<Variable> vars;
private final int rowCount; private final int rowCount;
private final BitSetter bitSetter; private final BitSetter bitSetter;
@ -29,7 +27,16 @@ public class ContextFiller extends ContextMap implements Iterable<Variable> {
* *
* @param variables the variables to use * @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; vars = variables;
rowCount = 1 << vars.size(); rowCount = 1 << vars.size();
bitSetter = new BitSetter(vars.size()) { bitSetter = new BitSetter(vars.size()) {
@ -83,7 +90,7 @@ public class ContextFiller extends ContextMap implements Iterable<Variable> {
/** /**
* @return the variables to use * @return the variables to use
*/ */
public ArrayList<Variable> getVariables() { public List<Variable> getVariables() {
return vars; 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; package de.neemann.digital.gui.components.table;
import de.neemann.digital.analyse.AnalyseException;
import de.neemann.digital.analyse.TruthTable; import de.neemann.digital.analyse.TruthTable;
import de.neemann.digital.analyse.expression.Expression; import de.neemann.digital.analyse.expression.Expression;
import de.neemann.digital.analyse.expression.ExpressionException; import de.neemann.digital.analyse.expression.ExpressionException;
import de.neemann.digital.analyse.expression.Variable; import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.expression.format.FormatterException; 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.QuineMcCluskey;
import de.neemann.digital.analyse.quinemc.TableReducer;
import de.neemann.digital.analyse.quinemc.TableRow; import de.neemann.digital.analyse.quinemc.TableRow;
import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelector; import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelector;
import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelectorDefault; import de.neemann.digital.analyse.quinemc.primeselector.PrimeSelectorDefault;
import de.neemann.digital.lang.Lang;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -24,6 +28,8 @@ import java.util.concurrent.TimeUnit;
*/ */
public class ExpressionCreator { public class ExpressionCreator {
private static final int MAX_INPUTS_ALLOWED = 12;
private final TruthTable theTable; private final TruthTable theTable;
/** /**
@ -40,24 +46,22 @@ public class ExpressionCreator {
* *
* @throws ExpressionException ExpressionException * @throws ExpressionException ExpressionException
* @throws FormatterException FormatterException * @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()); final List<Variable> vars = Collections.unmodifiableList(theTable.getVars());
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
if (theTable.getResultCount() > 100) { if (theTable.getResultCount() > 100) {
ExecutorService ex = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() / 2); ExecutorService ex = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
ThreadSaveExpressionListener threadListener = new ThreadSaveExpressionListener(listener); ThreadSaveExpressionListener threadListener = new ThreadSaveExpressionListener(listener);
for (int table = 0; table < theTable.getResultCount(); table++) { 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; final int t = table;
ex.submit(() -> { ex.submit(() -> {
try { try {
System.out.println("start " + t); System.out.println("start " + t);
calcColumn(threadListener, qmc, resultName, vars); simplify(listener, vars, theTable.getResultName(t), theTable.getResult(t));
System.out.println("end " + t); System.out.println("end " + t);
} catch (ExpressionException | FormatterException e) { } catch (ExpressionException | FormatterException | AnalyseException e) {
e.printStackTrace(); e.printStackTrace();
} }
}); });
@ -70,17 +74,30 @@ public class ExpressionCreator {
} }
threadListener.close(); threadListener.close();
} else { } else {
for (int table = 0; table < theTable.getResultCount(); table++) { for (int table = 0; table < theTable.getResultCount(); table++)
QuineMcCluskey qmc = new QuineMcCluskey(vars) simplify(listener, vars, theTable.getResultName(table), theTable.getResult(table));
.fillTableWith(theTable.getResult(table));
calcColumn(listener, qmc, theTable.getResultName(table), vars);
}
listener.close(); listener.close();
} }
time = System.currentTimeMillis() - time; time = System.currentTimeMillis() - time;
System.out.println("time: " + time / 1000.0 + " sec"); 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 { private static void calcColumn(ExpressionListener listener, QuineMcCluskey qmc, String name, List<Variable> vars) throws ExpressionException, FormatterException {
PrimeSelector ps = new PrimeSelectorDefault(); PrimeSelector ps = new PrimeSelectorDefault();
Expression e = qmc.simplify(ps).getExpression(); Expression e = qmc.simplify(ps).getExpression();

View File

@ -1,5 +1,6 @@
package de.neemann.digital.gui.components.table; package de.neemann.digital.gui.components.table;
import de.neemann.digital.analyse.AnalyseException;
import de.neemann.digital.analyse.TruthTable; import de.neemann.digital.analyse.TruthTable;
import de.neemann.digital.analyse.TruthTableTableModel; import de.neemann.digital.analyse.TruthTableTableModel;
import de.neemann.digital.analyse.expression.Expression; import de.neemann.digital.analyse.expression.Expression;
@ -579,7 +580,7 @@ public class TableDialog extends JDialog {
lastGeneratedExpressions = new ExpressionListenerStore(expressionListener); lastGeneratedExpressions = new ExpressionListenerStore(expressionListener);
new ExpressionCreator(model.getTable()).create(lastGeneratedExpressions); new ExpressionCreator(model.getTable()).create(lastGeneratedExpressions);
} catch (ExpressionException | FormatterException e1) { } catch (ExpressionException | FormatterException | AnalyseException e1) {
new ErrorMessage(Lang.get("msg_errorDuringCalculation")).addCause(e1).show(); 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_splitterBitsMismatch">Die Bitzahl am Spiltter passt nicht</string>
<string name="err_tableBecomesToSmall">Es sind mindestens zwei Eingänge erforderlich!</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_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_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_varNotDefined_N">Variable {0} ist nicht definiert.</string>
<string name="err_parserUnexpectedToken_N">Unerwartetes Zeichen {0}</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_splitterBitsMismatch">Bit count of splitter is not matching</string>
<string name="err_tableBecomesToSmall">Two inputs are required!</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_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_varNotAllowedInCUPL_N">Variable {0} is not allowed in CUPL source!</string>
<string name="err_varNotDefined_N">Variable {0} not defined</string> <string name="err_varNotDefined_N">Variable {0} not defined</string>
<string name="err_parserUnexpectedToken_N">Unexpected Token {0}</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));
}
}