3

I am looking for a way to expand a logical expression (in a string) of the form:

'(A or B) and ((C and D) or E)'

in Python to produce a list of all positive sets, i.e.

['A and C and D', 'A and E', 'B and C and D', 'B and E'] 

but I have been unable to find how to do this. I have investigated pyparser, but I cannot work out which example is relevant in this case. This may be very easy with some sort of logic manipulation but I do not know any formal logic. Any help, or a reference to a resource that might help would be greatly appreciated.

1
  • What do you mean by "positive sets"? Are you looking for some set of boolean expression b1, b2, .. bN consisting only of variables and literals, such that b1 OR b2 OR .. OR bN is equivalent to the input? Commented Jul 8, 2013 at 20:54

2 Answers 2

3

Here's the pyparsing bit, taken from the example SimpleBool.py. First, use infixNotation (formerly known as operatorPrecedence) to define an expression grammar that supports parenthetical grouping, and recognizes precedence of operations:

from pyparsing import * term = Word(alphas) AND = Keyword("and") OR = Keyword("or") expr = infixNotation(term, [ (AND, 2, opAssoc.LEFT), (OR, 2, opAssoc.LEFT), ]) sample = '(A or B) and ((C and D) or E)' result = expr.parseString(sample) from pprint import pprint pprint(result.asList()) 

prints:

[[['A', 'or', 'B'], 'and', [['C', 'and', 'D'], 'or', 'E']]] 

From this, we can see that the expression is at least parsed properly.

Next, we add parse actions to each level of the hierarchy of operations. For parse actions here, we actually pass classes, so that instead of executing functions and returning some value, the parser will call the class constructor and initializer and return a class instance for the particular subexpression:

class Operation(object): def __init__(self, tokens): self._tokens = tokens[0] self.assign() def assign(self): """ function to copy tokens to object attributes """ def __repr__(self): return self.__class__.__name__ + ":" + repr(self.__dict__) __str__ = __repr__ class BinOp(Operation): def assign(self): self.op = self._tokens[1] self.terms = self._tokens[0::2] del self._tokens class AndOp(BinOp): pass class OrOp(BinOp): pass expr = infixNotation(term, [ (AND, 2, opAssoc.LEFT, AndOp), (OR, 2, opAssoc.LEFT, OrOp), ]) sample = '(A or B) and ((C and D) or E)' result = expr.parseString(sample) pprint(result.asList()) 

returns:

[AndOp:{'terms': [OrOp:{'terms': ['A', 'B'], 'op': 'or'}, OrOp:{'terms': [AndOp:{'terms': ['C', 'D'], 'op': 'and'}, 'E'], 'op': 'or'}], 'op': 'and'}] 

Now that the expression has been converted to a data structure of subexpressions, I leave it to you to do the work of adding methods to AndOp and OrOp to generate the various combinations of terms that will evaluate overall to True. (Look at the logic in the invregex.py example that inverts regular expressions for ideas on how to add generator functions to the parsed classes to generate the different combinations of terms that you want.)

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot for the pointers, this is exactly what I was looking for. I am currently working through it to create the right methods. One thing I am a little stuck on though: do I have to create a class for my terms containing a generator function that just returns the value of that term? Or have I missed something?
Yes, you do - that is exactly what I had to do in the regex inverter.
2

It sounds as if you want to convert these expressions to Disjunctive Normal Form. A canonical algorithm for doing that is the Quine-McCluskey algorithm; you can find some information about Python implementations thereof in the relevant Wikipedia article and in the answers to this SO question.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.