From 6264792205b0a905ded6d01cf36ba77d4f2b0c67 Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Sat, 7 May 2022 12:22:04 -0600 Subject: [PATCH 01/11] Initial work on prime implicant generation --- src/QuineMcCluskey.js | 142 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 src/QuineMcCluskey.js diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js new file mode 100644 index 0000000..99f37f5 --- /dev/null +++ b/src/QuineMcCluskey.js @@ -0,0 +1,142 @@ +/* + * Copyright 2021 Magnus Madsen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {applySubst} from "./Substitution.js"; +import {Table} from "./Table" +import { boolFreeVars, TRUE, truthTable } from "./Bools.js"; + + +export let DASH = {type: '-'} + +function groupBy(items, pred) { + return items.reduce(function(groups, item) { + const val = pred(item) + groups[val] = groups[val] || [] + groups[val].push(item) + return groups + }, []) +} + +/** + * Given a row entry from a truth table, returns the number of elements + * in the row that are True. + */ +function trueCountInRow(row) { + return row.reduce((ts, e) => ts + (e === TRUE ? 1 : 0), 0) +} + +function dashCountInRow(row) { + return row.reduce((ts, e) => ts + (e === DASH ? 1 : 0), 0) +} + +/** + * Compares `row` against the rows in `others` to determine which differ by + * one elements. Returns an object containing those rows in `others` which + * differed from `row` by only one element, alongisde those same rows with + * the differing element replaced by a dash (the generated implicant). + */ +function compareRowAgainst(row, others, expectedDashes) { + let implicants = [] + let matched = [] + for (let other of others) { + // generate a new implicant with non-matching elements replaced by '-' + let test = row.map((elem, ind) => elem === other[ind] ? elem : DASH) + if (dashCountInRow(test) === expectedDashes) { + implicants.push(test) + matched.push(other) + } + } + return { implicants: implicants, matched: matched } +} + +function dedupRowSet(rows) { + function rowEq(r1, r2) { + return r1.length === r2.length && r1.map((elem, ind) => elem === r2[ind]) + } + var seen = []; + for (let item of rows) { + let add = true + for (let s of seen) { + if (rowEq(item, s)) { + add = false + break + } + } + if (add) { + seen.push(item) + } + } + return seen +} + +export function primeImplicants(term) { + let free = boolFreeVars(term) + let truth = truthTable(term, free) + + // minTerms are elements of the truth table where the result is T. + // Using pop() here also removes the truth column element since we + // don't want it when comparing rows. + let minTerms = truth.filter(row => row.pop() === TRUE) + + // Group the minTerms by the number of T elements each row contains. + // We will compare rows with no Ts against rows with one T, rows with + // one T against rows with two Ts, and so on... + let grouped = groupBy(minTerms, trueCountInRow) + + // keep reducing as long as there are terms with only one differing row element + let primes = [] + let remaining = grouped + let checked = [] + // keep track of how many iterations we've performed; equivalent to the number + // of dashes in the remaining minTerms + let iterSteps = 0 + + do { + let newRemaining = [] + checked.length = 0 + + // don't iterate over the last row, there's nothing beyond to compare it to + for (let i = 0; i < remaining.length - 1; i++) { + for (let row of remaining[i]) { + // compare this row against all rows in the following group + let { implicants, matched } = compareRowAgainst(row, remaining[i+1], iterSteps) + // save matched implicants so we know not to make them prime + if (matched.length > 0) { + checked.push(row) + } + checked = checked.concat(matched) + // save generated implicants to iterate over them next + newRemaining = newRemaining.concat(implicants) + } + } + + dedupRowSet(checked) + dedupRowSet(newRemaining) + + // one last iteration over each row to determine if it was matched with another + // if not, add it to the prime set + for (let i = 0; i < remaining.length; i++) { + for (let row of remaining[i]) { + // if not checked.contains(row) then primes.push(row) + } + } + + iterSteps += 1 + remaining = newRemaining + } while(checked.length > 0) + + dedupRowSet(primes) + return primes +} \ No newline at end of file From 0d22fa6c5485e3798b9c9893041280cf6ea832da Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Mon, 9 May 2022 22:55:20 -0600 Subject: [PATCH 02/11] Essential implicant filtering and some minor efficiency improvements --- src/QuineMcCluskey.js | 75 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js index 99f37f5..481fd69 100644 --- a/src/QuineMcCluskey.js +++ b/src/QuineMcCluskey.js @@ -29,6 +29,10 @@ function groupBy(items, pred) { }, []) } +function rowToInt(row) { + return row.reduce((res, val, ind) => res + (val === TRUE ? 1 << ind : 0), 0) +} + /** * Given a row entry from a truth table, returns the number of elements * in the row that are True. @@ -52,9 +56,9 @@ function compareRowAgainst(row, others, expectedDashes) { let matched = [] for (let other of others) { // generate a new implicant with non-matching elements replaced by '-' - let test = row.map((elem, ind) => elem === other[ind] ? elem : DASH) + let test = row.row.map((elem, ind) => elem === other.row[ind] ? elem : DASH) if (dashCountInRow(test) === expectedDashes) { - implicants.push(test) + implicants.push({ name: row.name + "," + other.name, row: test }) matched.push(other) } } @@ -62,14 +66,11 @@ function compareRowAgainst(row, others, expectedDashes) { } function dedupRowSet(rows) { - function rowEq(r1, r2) { - return r1.length === r2.length && r1.map((elem, ind) => elem === r2[ind]) - } var seen = []; for (let item of rows) { let add = true for (let s of seen) { - if (rowEq(item, s)) { + if (item.name === s.name) { add = false break } @@ -81,7 +82,7 @@ function dedupRowSet(rows) { return seen } -export function primeImplicants(term) { +export function quineMcCluskeyMinimize(term) { let free = boolFreeVars(term) let truth = truthTable(term, free) @@ -89,11 +90,18 @@ export function primeImplicants(term) { // Using pop() here also removes the truth column element since we // don't want it when comparing rows. let minTerms = truth.filter(row => row.pop() === TRUE) + let namedMinTerms = minTerms.map(row => { name: rowToInt(row).toString(), row: row }) + + let primes = primeImplicants(namedMinTerms) + let chart = implicantChart(primes, namedMinTerms) + let { essentials, covered, remaining, uncovered } = essentialPrimes(primes, namedMinTerms) +} +export function primeImplicants(namedMinTerms) { // Group the minTerms by the number of T elements each row contains. // We will compare rows with no Ts against rows with one T, rows with // one T against rows with two Ts, and so on... - let grouped = groupBy(minTerms, trueCountInRow) + let grouped = groupBy(namedMinTerms, named => trueCountInRow(named.row)) // keep reducing as long as there are terms with only one differing row element let primes = [] @@ -129,7 +137,10 @@ export function primeImplicants(term) { // if not, add it to the prime set for (let i = 0; i < remaining.length; i++) { for (let row of remaining[i]) { - // if not checked.contains(row) then primes.push(row) + // if the row wasn't checked, it's a prime implicant + if (checked.filter(c => c.name == row.name).length < 1) { + primes.push(row) + } } } @@ -139,4 +150,50 @@ export function primeImplicants(term) { dedupRowSet(primes) return primes +} + +export function implicantChart(primes, minTerms) { + let chart = [] + for (let p of primes) { + let row = [] + let names = p.name.split(",") + for (let m of minTerms) { + row.push(names.includes(m.name) ? true : false) + } + chart.push(p) + } + return chart +} + +export function essentialPrimes(primes, minTerms) { + let essentials = [] + let covered = [] + let remaining = [] + let uncovered = [] + let primeNames = primes.map(p => p.name.split(",")) + for (let m of minTerms) { + let checks = [] + primeNames.forEach((ns, i) => { + if (ns.includes(m.name)) { + checks.push[i] + } + }); + if (checks.length === 1) { + essentials.push(primes[checks[0]]) + covered.concat(primeNames[checks[0]]) + } + } + + // determine which prime implicants and minterms are remaining to be investigated + for (let p of primes) { + if (essentials.filter(e => p.name == e.name).length < 1) { + remaining.push(p) + } + } + for (let m of minTerms) { + if (!covered.includes(m.name)) { + uncovered.push(m) + } + } + return { essentials, covered, remaining, uncovered } } \ No newline at end of file From 44268b601594205ed61ec436ab8b775983d92195 Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Wed, 11 May 2022 08:45:37 -0600 Subject: [PATCH 03/11] Outline of algorithm complete, need to start debugging & testing --- src/QuineMcCluskey.js | 107 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 5 deletions(-) diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js index 481fd69..f068b73 100644 --- a/src/QuineMcCluskey.js +++ b/src/QuineMcCluskey.js @@ -15,7 +15,7 @@ */ import {applySubst} from "./Substitution.js"; import {Table} from "./Table" -import { boolFreeVars, TRUE, truthTable } from "./Bools.js"; +import { boolFreeVars, isFalse, isTrue, mkAnd, mkNot, mkVar, TRUE, truthTable } from "./Bools.js"; export let DASH = {type: '-'} @@ -38,11 +38,11 @@ function rowToInt(row) { * in the row that are True. */ function trueCountInRow(row) { - return row.reduce((ts, e) => ts + (e === TRUE ? 1 : 0), 0) + return row.reduce((ts, e) => ts + (isTrue(e) ? 1 : 0), 0) } function dashCountInRow(row) { - return row.reduce((ts, e) => ts + (e === DASH ? 1 : 0), 0) + return row.reduce((ts, e) => ts + (e.type === '-' ? 1 : 0), 0) } /** @@ -89,12 +89,35 @@ export function quineMcCluskeyMinimize(term) { // minTerms are elements of the truth table where the result is T. // Using pop() here also removes the truth column element since we // don't want it when comparing rows. - let minTerms = truth.filter(row => row.pop() === TRUE) + let minTerms = truth.filter(row => isTrue(row.pop())) let namedMinTerms = minTerms.map(row => { name: rowToInt(row).toString(), row: row }) + // get the set of prime implicants from the minTerms let primes = primeImplicants(namedMinTerms) let chart = implicantChart(primes, namedMinTerms) + // find which implicants are essential prime implicants; these must + // be included because they are the only implicants that cover certain minTerms let { essentials, covered, remaining, uncovered } = essentialPrimes(primes, namedMinTerms) + // for the remaining uncovered minTerms, there are ambiguities in which should be chosen + // Petrick's method is a way to systematically compute the result + if (uncovered.length > 0) { + let selectedImplicants = petricks(remaining, uncovered) + essentials = essentials.concat(selectedImplicants) + } + + // convert back to a bool term + let finalTerm = [] + for (let e of essentials) { + let vars = e.row.filter(rowElem => rowElem.type !== '-').map((rowElem, rowInd) => { + if (isTrue(rowElem)) { + return mkVar(free[rowInd]) + } + return mkNot(mkVar(free[rowInd])) + }) + let subTerm = vars.reduce((sub, v) => mkAnd(sub, v), TRUE) + finalTerm.push(subTerm) + } + return finalTerm.reduce((final, sub) => mkOr(final, sub), FALSE) } export function primeImplicants(namedMinTerms) { @@ -180,7 +203,7 @@ export function essentialPrimes(primes, minTerms) { }); if (checks.length === 1) { essentials.push(primes[checks[0]]) - covered.concat(primeNames[checks[0]]) + covered = covered.concat(primeNames[checks[0]]) } } @@ -196,4 +219,78 @@ export function essentialPrimes(primes, minTerms) { } } return { essentials, covered, remaining, uncovered } +} + +/** + * Given a set of prime implicants and minTerms where each minTerm is covered by + * at least two implicants, Petrick's method constructs a minimum sum-of-product + * term that covers all the given minTerms. The smallest term of this sum-of-product + * is returned as the minimal set of covering implicants. + */ +export function petricks(primes, minTerms) { + let product = productOfSums(primes, minTerms) + let sum = productOfSumsToSumOfProducts(product) + // bring a smallest length term to the front so we can select it + sum.sort((l, r) => l.length - r.length) + // the sums is a list of list of string, we need to bring it back to the implicant + let implicants = [] + for (let name in sum[0]) { + implicants.push(primes.find(p => p.name === name)) + } + return implicants +} + +export function productOfSums(primes, minTerms) { + let products = [] + for (let m of minTerms) { + let sums = [] + for (let p of primes) { + if (p.name.split(",").includes(m.name)) { + sums.push(p.name) + } + } + products.push(sums) + } + return products +} + +// (a+b+c)(d+e)(f+g) = (da+db+dc)(f+g) = (fda+fdb+fdc+gda+gdb+gdc) +export function productOfSumsToSumOfProducts(products) { + let sums = [products.pop()] + while (products.length > 0) { + let term = products.pop() + let newSums = [] + for (t of term) { + for (s of sums) { + if (s.includes(t)) { + newSums.push(s) + } else { + newSums.push([t,...s]) + } + } + } + sums = newSums + } + return reduceByAbsorption(sums) +} + +function reduceByAbsorption(sums) { + let reduced = [] + while (sums.length > 0) { + let product = sums.pop() + if (sums.some(s => productIsSubset(s, product)) || reduced.some(s => productIsSubset(s, product))) { + continue + } + reduced.push(product) + } + return reduced +} + +function productIsSubset(test, cmp) { + for (let prime of test) { + if (!cmp.includes(prime)) { + return false + } + } + return true } \ No newline at end of file From 917fd6df2af0a09e412b11f6977c38e1d10504b5 Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Fri, 13 May 2022 18:44:54 -0600 Subject: [PATCH 04/11] Basic UI to enable Quine-McCluskey, but problems resulted from initial test --- src/App.js | 11 +++++++++++ src/QuineMcCluskey.js | 8 ++++---- src/TermUnification.js | 6 +++++- src/components/FormInput.js | 10 ++++++++++ src/components/Substitution.js | 7 ++++--- 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/App.js b/src/App.js index 5d5d9a1..ffdb7e9 100644 --- a/src/App.js +++ b/src/App.js @@ -41,6 +41,7 @@ class App extends Component { logicSymbols: true, minimize: true, minimizeSubFormulas: true, + minimizeQuineMcCluskey: false, truthTable: false, parenthesize: false, unifyMethod: METHOD.SVE @@ -91,9 +92,11 @@ class App extends Component { if (this.state.minimize) { this.setState({minimize: false}) this.setState({minimizeSubFormulas: false}) + this.setState({minimizeQuineMcCluskey: false}) } else { this.setState({minimize: true}) this.setState({minimizeSubFormulas: true}) + this.setState({minimizeQuineMcCluskey: false}) } } @@ -101,6 +104,10 @@ class App extends Component { this.setState({minimizeSubFormulas: !this.state.minimizeSubFormulas}) } + toggleMinimizeQuineMcCluskey() { + this.setState({minimizeQuineMcCluskey: !this.state.minimizeQuineMcCluskey}) + } + toggleTruthTable() { this.setState({truthTable: !this.state.truthTable}) } @@ -131,6 +138,9 @@ class App extends Component { minimizeSubFormulas={this.state.minimizeSubFormulas} toggleMinimizeSubFormulas={this.toggleMinimizeSubFormulas.bind(this)} + minimizeQuineMcCluskey={this.state.minimizeQuineMcCluskey} + toggleMinimizeQuineMcCluskey={this.toggleMinimizeQuineMcCluskey.bind(this)} + truthTable={this.state.truthTable} toggleTruthTable={this.toggleTruthTable.bind(this)} @@ -162,6 +172,7 @@ class App extends Component { logicSymbols={this.state.logicSymbols} minimize={this.state.minimize} minimizeSubFormulas={this.state.minimizeSubFormulas} + minimizeQuineMcCluskey={this.state.minimizeQuineMcCluskey} parenthesize={this.state.parenthesize}/> {this.state.truthTable ? : []} diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js index f068b73..bf8b07c 100644 --- a/src/QuineMcCluskey.js +++ b/src/QuineMcCluskey.js @@ -15,7 +15,7 @@ */ import {applySubst} from "./Substitution.js"; import {Table} from "./Table" -import { boolFreeVars, isFalse, isTrue, mkAnd, mkNot, mkVar, TRUE, truthTable } from "./Bools.js"; +import { boolFreeVars, isFalse, isTrue, mkAnd, mkOr, mkNot, mkVar, TRUE, FALSE, truthTable } from "./Bools.js"; export let DASH = {type: '-'} @@ -90,7 +90,7 @@ export function quineMcCluskeyMinimize(term) { // Using pop() here also removes the truth column element since we // don't want it when comparing rows. let minTerms = truth.filter(row => isTrue(row.pop())) - let namedMinTerms = minTerms.map(row => { name: rowToInt(row).toString(), row: row }) + let namedMinTerms = minTerms.map(row => { return { name: rowToInt(row).toString(), row: row }; }) // get the set of prime implicants from the minTerms let primes = primeImplicants(namedMinTerms) @@ -260,8 +260,8 @@ export function productOfSumsToSumOfProducts(products) { while (products.length > 0) { let term = products.pop() let newSums = [] - for (t of term) { - for (s of sums) { + for (let t of term) { + for (let s of sums) { if (s.includes(t)) { newSums.push(s) } else { diff --git a/src/TermUnification.js b/src/TermUnification.js index 54b8f04..2810ac6 100644 --- a/src/TermUnification.js +++ b/src/TermUnification.js @@ -16,6 +16,7 @@ import {isBool, minBool, showBool} from "./Bools"; import {applySubst, mergeSubst} from "./Substitution"; import {isConstructor, mkConstructor, showTerm} from "./Terms"; +import { quineMcCluskeyMinimize } from "./QuineMcCluskey"; /** * Returns a substitution that unifies x and y (if it exists). @@ -108,7 +109,7 @@ function unifyTermLists(l1, l2, unifyBool) { * * Minimizes recursively if `recurse` is true. */ -export function minimizeTerm(term, recurse) { +export function minimizeTerm(term, recurse, quineMcCluskey) { if (term === undefined) { throw new Error(`Illegal argument 't': ${term}.`) } @@ -119,6 +120,9 @@ export function minimizeTerm(term, recurse) { if (isConstructor(term)) { return mkConstructor(term.name, term.ts.map(t => minimizeTerm(t, recurse))) } else if (isBool(term)) { + if (quineMcCluskey) { + return quineMcCluskeyMinimize(term) + } return minBool(term, recurse) } else { throw new Error(`Illegal argument 'term': ${term}.`) diff --git a/src/components/FormInput.js b/src/components/FormInput.js index 0fa7455..acd2f14 100644 --- a/src/components/FormInput.js +++ b/src/components/FormInput.js @@ -100,6 +100,7 @@ class FormInput extends Component { let logicSymbols = this.props.logicSymbols let minimize = this.props.minimize let minimizeSubFormulas = this.props.minimizeSubFormulas + let minimizeQuineMcCluskey = this.props.minimizeQuineMcCluskey let truthTable = this.props.truthTable let parenthesize = this.props.parenthesize let method = this.props.unifyMethod @@ -108,6 +109,7 @@ class FormInput extends Component { let toggleLogicSymbols = this.props.toggleLogicSymbols let toggleMinimize = this.props.toggleMinimize let toggleMinimizeSubFormulas = this.props.toggleMinimizeSubFormulas + let toggleMinimizeQuineMcCluskey = this.props.toggleMinimizeQuineMcCluskey let toggleTruthTable = this.props.toggleTruthTable let toggleParenthesize = this.props.toggleParenthesize @@ -161,6 +163,14 @@ class FormInput extends Component { inline /> + + ( {varSym} - {this.renderTerm(term, logicSymbols, minimize, minimizeSubFormulas, parenthesize)} + {this.renderTerm(term, logicSymbols, minimize, minimizeSubFormulas, minimizeQuineMcCluskey, parenthesize)} ))} @@ -61,9 +62,9 @@ class Substitution extends Component { ) } - renderTerm(term, logicSymbols, minimize, minimizeSubFormulas, parenthesize) { + renderTerm(term, logicSymbols, minimize, minimizeSubFormulas, minimizeQuineMcCluskey, parenthesize) { if (minimize) { - term = minimizeTerm(term, minimizeSubFormulas); + term = minimizeTerm(term, minimizeSubFormulas, minimizeQuineMcCluskey); } return From 5f13d120b115a56c86c5205ce678e8198405026f Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Fri, 13 May 2022 23:42:54 -0600 Subject: [PATCH 05/11] Fixing a few key bugs, still seems to be problems in Petrick's but we're getting correct results when we don't need it --- src/QuineMcCluskey.js | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js index bf8b07c..a0c7314 100644 --- a/src/QuineMcCluskey.js +++ b/src/QuineMcCluskey.js @@ -13,9 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {applySubst} from "./Substitution.js"; -import {Table} from "./Table" -import { boolFreeVars, isFalse, isTrue, mkAnd, mkOr, mkNot, mkVar, TRUE, FALSE, truthTable } from "./Bools.js"; +import { boolFreeVars, isFalse, isTrue, mkAnd, mkOr, mkNot, mkVar, TRUE, FALSE, truthTable, showBool } from "./Bools.js"; export let DASH = {type: '-'} @@ -26,7 +24,7 @@ function groupBy(items, pred) { groups[val] = groups[val] || [] groups[val].push(item) return groups - }, []) + }, []).filter(val => val !== undefined) } function rowToInt(row) { @@ -83,6 +81,7 @@ function dedupRowSet(rows) { } export function quineMcCluskeyMinimize(term) { + console.log('reducing: ', showBool(term)) let free = boolFreeVars(term) let truth = truthTable(term, free) @@ -98,23 +97,27 @@ export function quineMcCluskeyMinimize(term) { // find which implicants are essential prime implicants; these must // be included because they are the only implicants that cover certain minTerms let { essentials, covered, remaining, uncovered } = essentialPrimes(primes, namedMinTerms) + console.log('original essentials: ', essentials) + console.log('uncovered', uncovered) // for the remaining uncovered minTerms, there are ambiguities in which should be chosen // Petrick's method is a way to systematically compute the result if (uncovered.length > 0) { let selectedImplicants = petricks(remaining, uncovered) essentials = essentials.concat(selectedImplicants) } + console.log('final essentials: ', essentials) // convert back to a bool term let finalTerm = [] for (let e of essentials) { - let vars = e.row.filter(rowElem => rowElem.type !== '-').map((rowElem, rowInd) => { + let subTerm = e.row.reduce((sub, rowElem, rowInd) => { if (isTrue(rowElem)) { - return mkVar(free[rowInd]) + return mkAnd(sub, mkVar(free[rowInd])) + } else if (isFalse(rowElem)) { + return mkAnd(sub, mkNot(mkVar(free[rowInd]))) } - return mkNot(mkVar(free[rowInd])) - }) - let subTerm = vars.reduce((sub, v) => mkAnd(sub, v), TRUE) + return sub + }, TRUE) finalTerm.push(subTerm) } return finalTerm.reduce((final, sub) => mkOr(final, sub), FALSE) @@ -132,7 +135,7 @@ export function primeImplicants(namedMinTerms) { let checked = [] // keep track of how many iterations we've performed; equivalent to the number // of dashes in the remaining minTerms - let iterSteps = 0 + let iterSteps = 1 do { let newRemaining = [] @@ -153,25 +156,26 @@ export function primeImplicants(namedMinTerms) { } } - dedupRowSet(checked) - dedupRowSet(newRemaining) + checked = dedupRowSet(checked) + newRemaining = dedupRowSet(newRemaining) // one last iteration over each row to determine if it was matched with another // if not, add it to the prime set for (let i = 0; i < remaining.length; i++) { for (let row of remaining[i]) { // if the row wasn't checked, it's a prime implicant - if (checked.filter(c => c.name == row.name).length < 1) { + if (!checked.some(c => c.name === row.name)) { primes.push(row) } } } iterSteps += 1 - remaining = newRemaining + remaining = groupBy(newRemaining, named => trueCountInRow(named.row)) } while(checked.length > 0) dedupRowSet(primes) + console.log('primes:', primes) return primes } @@ -198,7 +202,7 @@ export function essentialPrimes(primes, minTerms) { let checks = [] primeNames.forEach((ns, i) => { if (ns.includes(m.name)) { - checks.push[i] + checks.push(i) } }); if (checks.length === 1) { @@ -207,9 +211,11 @@ export function essentialPrimes(primes, minTerms) { } } + essentials = dedupRowSet(essentials) + // determine which prime implicants and minterms are remaining to be investigated for (let p of primes) { - if (essentials.filter(e => p.name == e.name).length < 1) { + if (!essentials.some(e => p.name === e.name)) { remaining.push(p) } } From c63a471a37ced3424d42d265028aae253e9e007e Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Sun, 15 May 2022 10:48:34 -0600 Subject: [PATCH 06/11] Petricks now seems to work, but there's a bug which causes more computation than necessary in `primeImplicants` --- src/QuineMcCluskey.js | 47 ++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js index a0c7314..ab68732 100644 --- a/src/QuineMcCluskey.js +++ b/src/QuineMcCluskey.js @@ -56,7 +56,10 @@ function compareRowAgainst(row, others, expectedDashes) { // generate a new implicant with non-matching elements replaced by '-' let test = row.row.map((elem, ind) => elem === other.row[ind] ? elem : DASH) if (dashCountInRow(test) === expectedDashes) { - implicants.push({ name: row.name + "," + other.name, row: test }) + let nameLeft = row.name.split(",") + let nameRight = other.name.split(",") + let newName = [...new Set(nameLeft.concat(nameRight))].join(",") + implicants.push({ name: newName, row: test }) matched.push(other) } } @@ -68,7 +71,7 @@ function dedupRowSet(rows) { for (let item of rows) { let add = true for (let s of seen) { - if (item.name === s.name) { + if (new Set(item.name.split(",")) === new Set(s.name.split(","))) { add = false break } @@ -84,15 +87,18 @@ export function quineMcCluskeyMinimize(term) { console.log('reducing: ', showBool(term)) let free = boolFreeVars(term) let truth = truthTable(term, free) + console.log('truth: ', truth) // minTerms are elements of the truth table where the result is T. // Using pop() here also removes the truth column element since we // don't want it when comparing rows. let minTerms = truth.filter(row => isTrue(row.pop())) + console.log('minTerms: ', minTerms) let namedMinTerms = minTerms.map(row => { return { name: rowToInt(row).toString(), row: row }; }) // get the set of prime implicants from the minTerms let primes = primeImplicants(namedMinTerms) + console.log('primes: ', primes) let chart = implicantChart(primes, namedMinTerms) // find which implicants are essential prime implicants; these must // be included because they are the only implicants that cover certain minTerms @@ -175,7 +181,6 @@ export function primeImplicants(namedMinTerms) { } while(checked.length > 0) dedupRowSet(primes) - console.log('primes:', primes) return primes } @@ -185,26 +190,29 @@ export function implicantChart(primes, minTerms) { let row = [] let names = p.name.split(",") for (let m of minTerms) { - row.push(names.includes(m.name) ? true : false) + row.push(names.includes(m.name)) } chart.push(p) } return chart } +/** + * Given a set of prime implicants, and a set of minTerms that they collectively cover, + * returns the implicants which are 'essential'. An essential implicant is one which covers + * one of the minTerms and is the only implicant to cover that minTerm. + */ export function essentialPrimes(primes, minTerms) { let essentials = [] let covered = [] let remaining = [] let uncovered = [] - let primeNames = primes.map(p => p.name.split(",")) + let primeNames = primes.map(p => [...new Set(p.name.split(","))]) for (let m of minTerms) { - let checks = [] - primeNames.forEach((ns, i) => { - if (ns.includes(m.name)) { - checks.push(i) - } - }); + let checks = primeNames + .map((ns, i) => { return { names: ns, index: i }; }) + .filter(obj => obj.names.includes(m.name)) + .map(obj => obj.index) if (checks.length === 1) { essentials.push(primes[checks[0]]) covered = covered.concat(primeNames[checks[0]]) @@ -235,13 +243,18 @@ export function essentialPrimes(primes, minTerms) { */ export function petricks(primes, minTerms) { let product = productOfSums(primes, minTerms) + console.log('product: ', product) let sum = productOfSumsToSumOfProducts(product) + console.log('sum: ', sum) // bring a smallest length term to the front so we can select it sum.sort((l, r) => l.length - r.length) // the sums is a list of list of string, we need to bring it back to the implicant let implicants = [] - for (let name in sum[0]) { - implicants.push(primes.find(p => p.name === name)) + console.log('chosen min cover: ', sum[0]) + console.log('primes search: ', primes) + for (let name of sum[0]) { + console.log('searching "%s"', name, primes.filter(p => p.name === name)[0]) + implicants.push(primes.filter(p => p.name === name)[0]) } return implicants } @@ -255,6 +268,7 @@ export function productOfSums(primes, minTerms) { sums.push(p.name) } } + console.log('sums: ', sums) products.push(sums) } return products @@ -262,9 +276,10 @@ export function productOfSums(primes, minTerms) { // (a+b+c)(d+e)(f+g) = (da+db+dc)(f+g) = (fda+fdb+fdc+gda+gdb+gdc) export function productOfSumsToSumOfProducts(products) { - let sums = [products.pop()] - while (products.length > 0) { - let term = products.pop() + let prodCopy = [...products] + let sums = [prodCopy.pop()] + while (prodCopy.length > 0) { + let term = prodCopy.pop() let newSums = [] for (let t of term) { for (let s of sums) { From b9f4942abc3c0536384dc8619cfa0379a39081b2 Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Sun, 15 May 2022 15:09:49 -0600 Subject: [PATCH 07/11] Improving performance somewhat, but still very slow with 6 variables --- src/QuineMcCluskey.js | 56 +++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js index ab68732..e747979 100644 --- a/src/QuineMcCluskey.js +++ b/src/QuineMcCluskey.js @@ -66,12 +66,12 @@ function compareRowAgainst(row, others, expectedDashes) { return { implicants: implicants, matched: matched } } -function dedupRowSet(rows) { +function dedupBy(pred, seq) { var seen = []; - for (let item of rows) { + for (let item of seq) { let add = true for (let s of seen) { - if (new Set(item.name.split(",")) === new Set(s.name.split(","))) { + if (pred(item, s)) { add = false break } @@ -83,6 +83,14 @@ function dedupRowSet(rows) { return seen } +function dedupImplicantsByName(rows) { + return dedupBy((item, cmp) => item.name === cmp.name, rows) +} + +function dedupImplicantsByRow(rows) { + return dedupBy((item, cmp) => item.row.every((rElem, ind) => cmp.row[ind] === rElem), rows) +} + export function quineMcCluskeyMinimize(term) { console.log('reducing: ', showBool(term)) let free = boolFreeVars(term) @@ -93,13 +101,12 @@ export function quineMcCluskeyMinimize(term) { // Using pop() here also removes the truth column element since we // don't want it when comparing rows. let minTerms = truth.filter(row => isTrue(row.pop())) - console.log('minTerms: ', minTerms) let namedMinTerms = minTerms.map(row => { return { name: rowToInt(row).toString(), row: row }; }) + console.log('minTerms: ', namedMinTerms) // get the set of prime implicants from the minTerms let primes = primeImplicants(namedMinTerms) console.log('primes: ', primes) - let chart = implicantChart(primes, namedMinTerms) // find which implicants are essential prime implicants; these must // be included because they are the only implicants that cover certain minTerms let { essentials, covered, remaining, uncovered } = essentialPrimes(primes, namedMinTerms) @@ -162,8 +169,8 @@ export function primeImplicants(namedMinTerms) { } } - checked = dedupRowSet(checked) - newRemaining = dedupRowSet(newRemaining) + checked = dedupImplicantsByName(checked) + newRemaining = dedupImplicantsByName(newRemaining) // one last iteration over each row to determine if it was matched with another // if not, add it to the prime set @@ -180,21 +187,10 @@ export function primeImplicants(namedMinTerms) { remaining = groupBy(newRemaining, named => trueCountInRow(named.row)) } while(checked.length > 0) - dedupRowSet(primes) - return primes -} - -export function implicantChart(primes, minTerms) { - let chart = [] - for (let p of primes) { - let row = [] - let names = p.name.split(",") - for (let m of minTerms) { - row.push(names.includes(m.name)) - } - chart.push(p) - } - return chart + // bigger names in front, so that de-duplicating chooses the one that covers + // the most minTerms + primes.sort((l, r) => r.name.length - l.name.length) + return dedupImplicantsByRow(primes) } /** @@ -219,7 +215,7 @@ export function essentialPrimes(primes, minTerms) { } } - essentials = dedupRowSet(essentials) + essentials = dedupImplicantsByName(essentials) // determine which prime implicants and minterms are remaining to be investigated for (let p of primes) { @@ -243,22 +239,25 @@ export function essentialPrimes(primes, minTerms) { */ export function petricks(primes, minTerms) { let product = productOfSums(primes, minTerms) - console.log('product: ', product) let sum = productOfSumsToSumOfProducts(product) - console.log('sum: ', sum) // bring a smallest length term to the front so we can select it sum.sort((l, r) => l.length - r.length) // the sums is a list of list of string, we need to bring it back to the implicant let implicants = [] - console.log('chosen min cover: ', sum[0]) - console.log('primes search: ', primes) for (let name of sum[0]) { - console.log('searching "%s"', name, primes.filter(p => p.name === name)[0]) implicants.push(primes.filter(p => p.name === name)[0]) } return implicants } +/** + * Given a set of prime implicants and a set of minTerms, constructs + * a Boolean equation of the form ((b11+b12+b13+...)(b21+b22+b23+...)...(bn1+bn2+bn3+...)) + * such that each sum b[i] contains the primes that cover minTerm[i]. Each b[i][j] is the name + * of the prime implicant, not the named row object representing the prime implicant itself, + * to make comparison easy during expansion. The names will be translated back into named + * implicant objects in the final step of Petricks. + */ export function productOfSums(primes, minTerms) { let products = [] for (let m of minTerms) { @@ -268,7 +267,6 @@ export function productOfSums(primes, minTerms) { sums.push(p.name) } } - console.log('sums: ', sums) products.push(sums) } return products From 44ea07cb5bcef9c44d6d496863a14b3e729f5dd9 Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Tue, 17 May 2022 11:36:02 -0600 Subject: [PATCH 08/11] Some documentation and cleanup --- src/QuineMcCluskey.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js index e747979..236cd4a 100644 --- a/src/QuineMcCluskey.js +++ b/src/QuineMcCluskey.js @@ -272,7 +272,11 @@ export function productOfSums(primes, minTerms) { return products } -// (a+b+c)(d+e)(f+g) = (da+db+dc)(f+g) = (fda+fdb+fdc+gda+gdb+gdc) +/** + * Converts a product of sums (represented in list of lists of terms form) into an equivalent sum + * of products, applying some reduction laws to minify the result sum. Returns the new sum of + * products as a list of lists of terms. + */ export function productOfSumsToSumOfProducts(products) { let prodCopy = [...products] let sums = [prodCopy.pop()] @@ -293,6 +297,10 @@ export function productOfSumsToSumOfProducts(products) { return reduceByAbsorption(sums) } +/** + * Given a sum of products (represented as a list of lists of terms) removes redundant products + * by applying the absorption laws (X+XY) = X. The result is still a sum of products. + */ function reduceByAbsorption(sums) { let reduced = [] while (sums.length > 0) { @@ -305,11 +313,9 @@ function reduceByAbsorption(sums) { return reduced } +/** + * Returns whether a product of Boolean terms is a subset of another Boolean product. + */ function productIsSubset(test, cmp) { - for (let prime of test) { - if (!cmp.includes(prime)) { - return false - } - } - return true + return test.every(prime => cmp.includes(prime)) } \ No newline at end of file From 1b2fce62db642ea6eb4c83972860ad0ab97275da Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Thu, 19 May 2022 21:48:38 -0600 Subject: [PATCH 09/11] Fix bug causing some equations to simplify to True erroneously, massive performance improvements --- src/QuineMcCluskey.js | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js index 236cd4a..bd18d11 100644 --- a/src/QuineMcCluskey.js +++ b/src/QuineMcCluskey.js @@ -43,23 +43,41 @@ function dashCountInRow(row) { return row.reduce((ts, e) => ts + (e.type === '-' ? 1 : 0), 0) } +function tryGenerateComparedRow(left, right) { + let difference = false + let row = [] + for (let i = 0; i < left.length; i++) { + if (left[i] != right[i]) { + // have we already seen a difference? if so, these rows are too different + if (difference) { + return { compared: false } + } + difference = true + row.push(DASH) + } else { + row.push(left[i]) + } + } + return difference ? { compared: true, row: row } : { compared: false } +} + /** * Compares `row` against the rows in `others` to determine which differ by * one elements. Returns an object containing those rows in `others` which * differed from `row` by only one element, alongisde those same rows with * the differing element replaced by a dash (the generated implicant). */ -function compareRowAgainst(row, others, expectedDashes) { +function compareRowAgainst(row, others) { let implicants = [] let matched = [] for (let other of others) { // generate a new implicant with non-matching elements replaced by '-' - let test = row.row.map((elem, ind) => elem === other.row[ind] ? elem : DASH) - if (dashCountInRow(test) === expectedDashes) { + let cmp = tryGenerateComparedRow(row.row, other.row) + if (cmp.compared) { let nameLeft = row.name.split(",") let nameRight = other.name.split(",") let newName = [...new Set(nameLeft.concat(nameRight))].join(",") - implicants.push({ name: newName, row: test }) + implicants.push({ name: newName, row: cmp.row }) matched.push(other) } } @@ -158,7 +176,7 @@ export function primeImplicants(namedMinTerms) { for (let i = 0; i < remaining.length - 1; i++) { for (let row of remaining[i]) { // compare this row against all rows in the following group - let { implicants, matched } = compareRowAgainst(row, remaining[i+1], iterSteps) + let { implicants, matched } = compareRowAgainst(row, remaining[i+1]) // save matched implicants so we know not to make them prime if (matched.length > 0) { checked.push(row) @@ -169,8 +187,8 @@ export function primeImplicants(namedMinTerms) { } } - checked = dedupImplicantsByName(checked) - newRemaining = dedupImplicantsByName(newRemaining) + checked = dedupImplicantsByRow(checked) + newRemaining = dedupImplicantsByRow(newRemaining) // one last iteration over each row to determine if it was matched with another // if not, add it to the prime set @@ -183,6 +201,7 @@ export function primeImplicants(namedMinTerms) { } } + console.log('step complete: ', iterSteps, newRemaining) iterSteps += 1 remaining = groupBy(newRemaining, named => trueCountInRow(named.row)) } while(checked.length > 0) From d7578a52ce4108ba13ecef51516a7516cc05a085 Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Thu, 19 May 2022 22:16:52 -0600 Subject: [PATCH 10/11] Reorganize code to more relevant first, add documentation --- src/QuineMcCluskey.js | 228 ++++++++++++++++++++++++++---------------- 1 file changed, 140 insertions(+), 88 deletions(-) diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js index bd18d11..d645ffd 100644 --- a/src/QuineMcCluskey.js +++ b/src/QuineMcCluskey.js @@ -16,99 +16,22 @@ import { boolFreeVars, isFalse, isTrue, mkAnd, mkOr, mkNot, mkVar, TRUE, FALSE, truthTable, showBool } from "./Bools.js"; -export let DASH = {type: '-'} - -function groupBy(items, pred) { - return items.reduce(function(groups, item) { - const val = pred(item) - groups[val] = groups[val] || [] - groups[val].push(item) - return groups - }, []).filter(val => val !== undefined) -} - -function rowToInt(row) { - return row.reduce((res, val, ind) => res + (val === TRUE ? 1 << ind : 0), 0) -} - /** - * Given a row entry from a truth table, returns the number of elements - * in the row that are True. + * Truth rows for implicants may contain a DASH '-' during reduction to + * the set of prime implicants, which is used to track where overlaps occur + * during reduction iteration steps. */ -function trueCountInRow(row) { - return row.reduce((ts, e) => ts + (isTrue(e) ? 1 : 0), 0) -} - -function dashCountInRow(row) { - return row.reduce((ts, e) => ts + (e.type === '-' ? 1 : 0), 0) -} - -function tryGenerateComparedRow(left, right) { - let difference = false - let row = [] - for (let i = 0; i < left.length; i++) { - if (left[i] != right[i]) { - // have we already seen a difference? if so, these rows are too different - if (difference) { - return { compared: false } - } - difference = true - row.push(DASH) - } else { - row.push(left[i]) - } - } - return difference ? { compared: true, row: row } : { compared: false } -} +export let DASH = {type: '-'} /** - * Compares `row` against the rows in `others` to determine which differ by - * one elements. Returns an object containing those rows in `others` which - * differed from `row` by only one element, alongisde those same rows with - * the differing element replaced by a dash (the generated implicant). + * Generates a minimal Boolean equation equivalent to the given Boolean equation, in + * sum of product form. The major steps of the algorithm: + * 1. compute initial minTerms via truth table + * 2. iteratively generate set of prime implicants + * 3. determine essential prime implicants + * 4. for remaining minTerms uncovered by essential implicants, find a covering + * implicant using Petricks method */ -function compareRowAgainst(row, others) { - let implicants = [] - let matched = [] - for (let other of others) { - // generate a new implicant with non-matching elements replaced by '-' - let cmp = tryGenerateComparedRow(row.row, other.row) - if (cmp.compared) { - let nameLeft = row.name.split(",") - let nameRight = other.name.split(",") - let newName = [...new Set(nameLeft.concat(nameRight))].join(",") - implicants.push({ name: newName, row: cmp.row }) - matched.push(other) - } - } - return { implicants: implicants, matched: matched } -} - -function dedupBy(pred, seq) { - var seen = []; - for (let item of seq) { - let add = true - for (let s of seen) { - if (pred(item, s)) { - add = false - break - } - } - if (add) { - seen.push(item) - } - } - return seen -} - -function dedupImplicantsByName(rows) { - return dedupBy((item, cmp) => item.name === cmp.name, rows) -} - -function dedupImplicantsByRow(rows) { - return dedupBy((item, cmp) => item.row.every((rElem, ind) => cmp.row[ind] === rElem), rows) -} - export function quineMcCluskeyMinimize(term) { console.log('reducing: ', showBool(term)) let free = boolFreeVars(term) @@ -154,6 +77,21 @@ export function quineMcCluskeyMinimize(term) { return finalTerm.reduce((final, sub) => mkOr(final, sub), FALSE) } +/** + * Given a set of named minTerms for a Boolean equation, generates the set + * of implicants that cannot be 'covered' by a 'more general' implicant, i.e. + * the set of prime implicants. Note that these prime implicants are not + * necessarily essential; some of them may overlap with each other with respect + * to the original equation, but none can be further reduced according to the + * given set of minTerms. + * + * The process here is iterative, checking off elements that reduce, accumulating + * those that do not, and repeating on the set of new reduced elements until no + * further reductions can take place. + * + * Returned implicants will be sorted such that the implicant that covers the most + * minTerms will be first. + */ export function primeImplicants(namedMinTerms) { // Group the minTerms by the number of T elements each row contains. // We will compare rows with no Ts against rows with one T, rows with @@ -212,6 +150,53 @@ export function primeImplicants(namedMinTerms) { return dedupImplicantsByRow(primes) } +/** + * Given two lists of (maybe dashed) Boolean values, checks whether they differ zipwise + * by one element. If they do, this returns an object indicating that the rows differed + * by one, as well as the compared row with the difference replaced by a dash. If they + * differ by more or less than one, return an object indicating as such with no result row. + */ + function tryGenerateComparedRow(left, right) { + let difference = false + let row = [] + for (let i = 0; i < left.length; i++) { + if (left[i] != right[i]) { + // have we already seen a difference? if so, these rows are too different + if (difference) { + return { compared: false } + } + difference = true + row.push(DASH) + } else { + row.push(left[i]) + } + } + return difference ? { compared: true, row: row } : { compared: false } +} + +/** + * Compares `row` against the rows in `others` to determine which differ by + * one elements. Returns an object containing those rows in `others` which + * differed from `row` by only one element, alongisde those same rows with + * the differing element replaced by a dash (the generated implicant). + */ +function compareRowAgainst(row, others) { + let implicants = [] + let matched = [] + for (let other of others) { + // generate a new implicant with non-matching elements replaced by '-' + let cmp = tryGenerateComparedRow(row.row, other.row) + if (cmp.compared) { + let nameLeft = row.name.split(",") + let nameRight = other.name.split(",") + let newName = [...new Set(nameLeft.concat(nameRight))].join(",") + implicants.push({ name: newName, row: cmp.row }) + matched.push(other) + } + } + return { implicants: implicants, matched: matched } +} + /** * Given a set of prime implicants, and a set of minTerms that they collectively cover, * returns the implicants which are 'essential'. An essential implicant is one which covers @@ -337,4 +322,71 @@ function reduceByAbsorption(sums) { */ function productIsSubset(test, cmp) { return test.every(prime => cmp.includes(prime)) +} + +/** + * Given a list of implicants, returns a new list where elements with the same name have been + * deduplicated. + */ +function dedupImplicantsByName(rows) { + return dedupBy((item, cmp) => item.name === cmp.name, rows) +} + +/** + * Given a list of implicants, returns a new list where elements with the same truth row + * have been deduplicated. + */ +function dedupImplicantsByRow(rows) { + return dedupBy((item, cmp) => item.row.every((rElem, ind) => cmp.row[ind] === rElem), rows) +} + +/** + * Given a list of True and False values, converts the list to a number as if the + * list was a bitwise representation of the number. + */ + function rowToInt(row) { + return row.reduce((res, val, ind) => res + (val === TRUE ? 1 << ind : 0), 0) +} + +/** + * Given a row entry from a truth table, returns the number of elements + * in the row that are True. + */ + function trueCountInRow(row) { + return row.reduce((ts, e) => ts + (isTrue(e) ? 1 : 0), 0) +} + +/** + * Group the items in a list into a list of lists based on a predicate that returns the index + * of the sublist to which the item belongs. + */ + function groupBy(items, pred) { + return items.reduce(function(groups, item) { + const val = pred(item) + groups[val] = groups[val] || [] + groups[val].push(item) + return groups + }, []).filter(val => val !== undefined) +} + +/** + * Removes duplicates from the given sequence based on the supplied comparison predicate. + * If two elements are considered duplicates, the leftmost in the original sequence is kept. + * Returns a new list rather than modifying the input. + */ + function dedupBy(pred, seq) { + var seen = []; + for (let item of seq) { + let add = true + for (let s of seen) { + if (pred(item, s)) { + add = false + break + } + } + if (add) { + seen.push(item) + } + } + return seen } \ No newline at end of file From 2e07a4fe92538f1b148b72cc8d519bb5506ec947 Mon Sep 17 00:00:00 2001 From: Robert Kleffner Date: Fri, 17 Jun 2022 10:47:56 -0600 Subject: [PATCH 11/11] Removing some console logs, adding log for minimize timing --- src/QuineMcCluskey.js | 7 ------- src/components/Substitution.js | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/QuineMcCluskey.js b/src/QuineMcCluskey.js index d645ffd..8289b8d 100644 --- a/src/QuineMcCluskey.js +++ b/src/QuineMcCluskey.js @@ -36,30 +36,24 @@ export function quineMcCluskeyMinimize(term) { console.log('reducing: ', showBool(term)) let free = boolFreeVars(term) let truth = truthTable(term, free) - console.log('truth: ', truth) // minTerms are elements of the truth table where the result is T. // Using pop() here also removes the truth column element since we // don't want it when comparing rows. let minTerms = truth.filter(row => isTrue(row.pop())) let namedMinTerms = minTerms.map(row => { return { name: rowToInt(row).toString(), row: row }; }) - console.log('minTerms: ', namedMinTerms) // get the set of prime implicants from the minTerms let primes = primeImplicants(namedMinTerms) - console.log('primes: ', primes) // find which implicants are essential prime implicants; these must // be included because they are the only implicants that cover certain minTerms let { essentials, covered, remaining, uncovered } = essentialPrimes(primes, namedMinTerms) - console.log('original essentials: ', essentials) - console.log('uncovered', uncovered) // for the remaining uncovered minTerms, there are ambiguities in which should be chosen // Petrick's method is a way to systematically compute the result if (uncovered.length > 0) { let selectedImplicants = petricks(remaining, uncovered) essentials = essentials.concat(selectedImplicants) } - console.log('final essentials: ', essentials) // convert back to a bool term let finalTerm = [] @@ -139,7 +133,6 @@ export function primeImplicants(namedMinTerms) { } } - console.log('step complete: ', iterSteps, newRemaining) iterSteps += 1 remaining = groupBy(newRemaining, named => trueCountInRow(named.row)) } while(checked.length > 0) diff --git a/src/components/Substitution.js b/src/components/Substitution.js index 8fe363a..88402c9 100644 --- a/src/components/Substitution.js +++ b/src/components/Substitution.js @@ -64,7 +64,10 @@ class Substitution extends Component { renderTerm(term, logicSymbols, minimize, minimizeSubFormulas, minimizeQuineMcCluskey, parenthesize) { if (minimize) { + var start = new Date().getTime() term = minimizeTerm(term, minimizeSubFormulas, minimizeQuineMcCluskey); + var end = new Date().getTime() + console.log('Time taken to minimize: %f', (end - start) / 1000) } return