Find possible boolean values to evaluate an expression to true - logic

If I have a given boolean expression (AND and OR operations) with many boolean variables, then I want to evaluate this expression to true, how can I find the set of all possible boolean values to achive a true epxression?
For example, I have 4 boolean variable a, b, c, d and an expression:
(a ^ b) v (c ^ d)
The slowest way I've tried to do is:
I build an expression tree to get all variables in the expression, I get a {a,b,c,d} set.
I find all subsets of the set: {a}, {b}, {c}, {d}, {a,b}, {a,c}, {a,d}, {b,c}, {b,d}, {c,d}, {a,b,c}, {a,b,d}, {a,c,d}, {b,c,d}, {a,b,c,d}
For each subset, I set each of variables to true, then evaluate the expression. If the expression returns true, I save the subset with the values.
EDIT: I eliminate the NOT operator to make the problem simpler.

I think I see a way to compute this without having to try all the permutations, and my high level outline, described below, is not really very complicated. I'll outline the basic approach, and you will have two follow-up tasks to do on your own:
Parsing a logical expression, like "A && (B || C)" into a classical parse tree, that represents the expression, which each node in the tree being either a variable, or a boolean operation, either "&&", "||", or "!" (NOT), with two children being its operands. This is a classical parse tree. Plenty of examples of how to do this can be found in Google.
Translating my outline into actual C++ code. That's going to be up to you also, but I think that the implementation should be rather obvious, once you wrap your brain around the overall approach.
To solve this problem, I'll use a two-phase approach.
I will use the general approach of proof by induction in order to come up with a tentative list of all potential sets of values of all the variables for which the boolean expression will evaluate to true.
In the second phase I'll eliminate from the list of all potential sets those sets that are logically impossible. This might sound confusing, so I'll explain this second phase first.
Let's use the following datatypes. First, I'll use this datatype of possible values for which the boolean expression will evaluate to either true or false:
typedef std::set<std::pair<std::string, bool>> values_t;
Here, std::pair<std::string, bool> represents variable, whose name is std::string, has this bool value. For example:
{"a", true}
Means that the value of variable "a" has the value of true. It follows that this std::set represents a set of variables and their corresponding values.
All of these potential solutions are going to be an:
typedef std::list<values_t> all_values_t;
So this is how we will represent a list of all combinations of values of all variables, that produce the result of either true or false. You can use a std::vector instead of a std::list, it doesn't really matter.
Now notice that it is possible for a values_t to have both:
{"a", true}
and
{"a", false}
in the set. This means that in order for the expression to evaluate to true or false, "a" must be simultaneously true and false.
But this is, obviously, logically impossible. So, in phase 2 of this solution you will need to go simply go through all the individual values_t in all_values_t, and remove the "impossible" values_t that contain both true and false for the some variable. The way to do this should seem rather obvious, and I won't waste time on describing it, but phase 2 should be straightforward, once phase 1 is complete.
For phase 1 our goal is to come up with a function that's roughly declared like this:
all_values_t phase1(expression_t expr, bool goal);
expr is a parsed representation of your boolean expression, as a parse tree (as I mentioned in the beginning, doing this part will be up to you). goal is how you want the parsed expression to be evaluated to: phase1() returns all possible all_values_t for which expr evaluates to either true or false, as indicated by "goal". You will, obviously, call phase1() passing true for "goal" for your answer, because that's what you want to figure out. But phase1() will call itself recursively, with either a true or a false "goal", to do its magic.
Before proceeding, it is important now to read and understand the various resources that describe how a proof by induction works. Don't proceed any further until you understand this general concept fully.
Ok, now you understand the concept. If you do, then you must now agree with me that phase1() is already done. It works! Proof by induction starts by assuming that phase1() does what it is supposed to do. phase1() will make recursive calls to itself, and since phase1() returns the right result, phase1() can simply rely on itself to figure everything out. See how easy this is?
phase1() really has one "simple" task at hand:
Check what the top level node of the parse tree is. It will be either a variable node or an expression node (see above).
Return the appropriate all_values_t, based on that.
That's it. We'll take both possibilities, one at a time.
The top level node is a variable.
So, if your expression is just a variable, and you want the expression to return goal, then:
values_t v{ {name, goal} };
There's only one possible way for the expression to evaluate to goal: an obvious no-brainer: the variable, and goal for its value.
And there's only one possible solution. No other alternatives:
all_values_t res;
res.push_back(v);
return res;
Now, the other possibility is that the top-level node in the expression is one of the boolean operations: and, or, or not.
Again, we'll divide and conquer this, and tackle each one, one at a time.
Let's say that it's "not". What should we do then? That should be easy:
return phase1(child1, !goal);
Just call phase1() recursively, passing the "not" expression's child node, with goal logically inverted. So, if your goal was true, use phase1() to come back with what the values for "not" sub-expression being false, and vice-versa. Remember, proof by induction assumes that phase1() works as advertised, so you can rely on it to get the correct answer for the sub-expression.
It should now start becoming obvious how the rest of phase1() works. There are only two possibilities left: the "and" and the "or" logical operation.
For the "and" operation, we'll consider, separately, whether the "goal" of the "and" operation should be true or false.
If goal is true, you must use phase1() to come up with all_values_t for both subexpressions being true:
all_values_t left_result=phase1(child1, true);
all_values_t right_result=phase1(child2, true);
Then just combine the two results together. Now, recall that all_values_t is a list of all possible values. Each value in all_values_t (which can be an empty list/vector) represents one possible solution. Both the left and the right sub-expressions must be logically combined, but any possible solution from the left_result can go together with any right_result. Any potential solution with the left subexpression being true can (and must) go with any potential solution with the right subexpression being true.
So the all_values_t that needs to be returned, in this case, is obtained by doing a cartesian product between the left_result and the right_result. That is: taking the first value, the first values_t std::set in the left_result, then adding to this set the first right_result std::set, then the first left_result with the second right_result, and so on; then the second left_result with the first right_result, then the second right_result and so on. Each one of these combinations gets push_back()ed into the all_values_t that gets returned from this call to phase1().
But your goal is to have the "and" expression return false, instead, you simply have to do a variation of this three times. The first time by calling phase1(child1, false) with phase1(child2, false); then phase1(child1, true) with phase1(child2, false); and finally phase1(child1, false) and phase1(child2, true). Either child1 or child2, or both, must evaluate to false.
So that takes care of the "and" operation.
The last, and the final possibility for phase1() to deal with is the logical or operation. You should be able to, now, figure out how to do it by yourself, but I'll just briefly summarize it:
If goal is false, you must call phase1(child1, false) with phase1(child2, false), then combine both results together, as a cartesian product. If goal is true, you will make three sets of recursive calls, for the three other possibilities, and combine everything together.
You're done. There's nothing else for phase1() to do, and we completed our proof by induction.
Well, I lied a little bit. You'll also need to do a small "Phase 3". Recall that in "Phase 2" we eliminated all impossible solution. Well, it is possible that, as a result of all this, the final list of possible solution will have the same values_t occur more than one time in the all_values_t, so you'll just have to dedupe it.
P.S. It's also possible to avoid a discrete phase 2 by doing it on the fly, as part of phase 1. This variation is going to be your homework assignment, too.

Related

Resolution refutation in proportion logic with 2 clashing literals

I have done process of elimination for the resolution rule and ended up with the set.
{pq, not p, not q}.
according to my text book: Lemma : if two clauses clash on more than one literal their resolvent is a trivial cause ... then goes on to say it is not strictly incorrect to perform resolution on such clauses but since trivial clauses contribute nothing to the satisfiability or unsatisfiability of a set of clauses we agree to delete them...
But elsewhere I Have read not to remove them since there is no reason that both of those clauses could be true.
So would the able clauses leave me with the empty set {} making my final answer that the set is unsatisfiable? Or do I leave that as my final answer? The problem said Prove that it IS satisfiable, so I'm guessing I should leave the clauses in the set so that it is, but the textbook says to remove them.
In your example, there aren't any two clauses that would clash in two literals. Two such clauses could be {p,r,q} and {~p,~r,s} and you would get {r,~r,q,s} which is always satisfiable (tautology). So, you would remove it us useless.
In your example, you will end up with an empty set after applying two resolution steps to three clauses: {pq}, ~p yields q and {q}, {~q} yields an empty set. So, the set of clauses is not satisfiable.
If the task was to prove something is satisfiable, there must be something wrong earlier in the derivation.

The list [[a,b]|c] in Prolog

During my exploration of different ways to write down lists, I am intrigued by the following list [[a,b]|c] which appears in the book 'Prolog and Natural Language Analysis' by Pereira and Shieber (page 42 of the digital edition).
At first I thought that such a notation was syntactically incorrect, as it would have had to say [[a,b]|[c]], but after using write_canonical/1 Prolog returned '.'('.'(a,'.'(b,[])),c).
As far as I can see, this corresponds to the following tree structure (although it seems odd to me that structure would simply end with c, without the empty list at the end):
I cannot seem to find the corresponding notation using comma's and brackets though. I thought it would correspond to [[a,b],c] (but this obviously returns a different result with write_canonical/1).
Is there no corresponding notation for [[a,b]|c] or am I looking at it the wrong way?
As others have already indicated, the term [[a,b]|c] is not a list.
You can test this yourself, using the | syntax to write it down:
?- is_list([[a,b]|c]).
false.
You can see from write_canonical/1 that this term is identical to what you have drawn:
| ?- write_canonical([[a,b]|c]).
'.'('.'(a,'.'(b,[])),c)
In addition to what others have said, I am posting an additional answer because I want to explain how you can go about finding the reason of unexpected failures. When starting with Prolog, you will often ask yourself "Why does this query fail?"
One way to find explanations for such issues is to generalize the query, by using logical variables instead of concrete terms.
For example, in the above case, we could write:
?- is_list([[A,b]|c]).
false.
In this case, I have used the logical variable A instead of the atom a, thus significantly generalizing the query. Since the generalized query still fails, some constraint in the remaining part must be responsible for the unexpected failure. We this generalize it further to narrow down the cause. For example:
?- is_list([[A,B]|c]).
false.
Or even further:
?- is_list([[A,B|_]|c]).
false.
And even further:
?- is_list([_|c]).
false.
So here we have it: No term that has the general form '.'(_, c) is a list!
As you rightly observe, this is because such a term is not of the form [_|Ls] where Ls is a list.
NOTE: The declarative debugging approach I apply above works for the monotonic subset of Prolog. Actually, is_list/1 does not belong to that subset, because we have:
?- is_list(Ls).
false.
with the declarative reading "There is no spoon list." So, it turns out, it worked only by coincidence in the case above. However, we could define the intended declarative meaning of is_list/1 in a pure and monotonic way like this, by simply applying the inductive definition of lists:
list([]).
list([_|Ls]) :- list(Ls).
This definition only uses pure and monotonic building blocks and hence is monotonic. For example, the most general query now yields actual lists instead of failing (incorrectly):
?- list(Ls).
Ls = [] ;
Ls = [_6656] ;
Ls = [_6656, _6662] ;
Ls = [_6656, _6662, _6668] .
From pure relations, we expect that queries work in all directions!
I cannot seem to find the corresponding notation using comma's and brackets though.
There is no corresponding notation, since this is technically speaking not a real list.
Prolog has syntacical sugar for lists. A list in Prolog is, like a Lisp list, actually a linked list: every element is either an empty list [], or a node .(H,T) with H the head and T the tail. Lists are not "special" in Prolog in the sense that the intepreter handles them differently than any other term. Of course a lot of Prolog libraries do list processing, and use the convention defined above.
To make complex lists more convenient, syntactical sugar was invented. You can write a node .(H,T) like [H|T] as well. So that means that in your [[a,b]|c]. We have an outer list, which has one node .(H,c) and the ? is another list, with two nodes and an empty list H = .(a,.(b,[])).
Technically speaking I would not consider this a "real" list, since the tail of a list should have either another node ./2, or an empty list.
You can however use this with variables like: [[a,b]|C] in order to unify the tail C further. So here we have some sort of list with [a,b] as first element (so a list containing a list) and with an open tail C. If we later for instance ground C to C = [], then the list is [[a,b]].

Prolog get the number of nested list + 1 where an element is into a list

I'd like to return the depth level or number of nested list where certain element is. Also as condition the list doesn't have repeated elements. I am trying to understand this solution where I have two main doubts:
profundidad([],_,0):-!.
profundidad([A],A,1):-!.
profundidad([A|_],A,1):-!.
profundidad([H|_],A,N):-
profundidad(H,A,R),N is R+1.
profundidad([_|X],A,N):-
profundidad(X,A,N),!.
The correct output would be:
profundidad([2,3,4,5,[[6]],10],6,X).
X = 3
First, why we do put the cut operator ! from 1-3 statements? I know it prevents compiler from considering later statements when a solution is found.
Second, how we could read 4th and 5th cases in natural language?
The depth of an element A when the list is splitted by the head H and the rest _, is equal to the number R of steps plus 1.
profundidad([H|_],A,N):-
profundidad(H,A,R),N is R+1.
And those two sentences I think they are the same as previous ones but to go forward into the list:
profundidad([_|X],A,N):-
profundidad(X,A,N),!.
Plus, I am doubting now about why to not put [] into recursive called to:
profundidad(X,A,N),!.
I think it is to go deep into the nested lists but I am not sure.
Thank you.
It's better to avoid cuts when possible, in the following rewrite and simplification the test H\=A make the three clauses disjunctive.
profundidad([A|_],A,1).
profundidad([H|_],A,N):-
% H\=A,
profundidad(H,A,R),
N is R+1.
profundidad([H|T],A,N):-
H\=A,
profundidad(T,A,N).
The second clause doesn't need the test, since it goes down a level in the list, meaning it will succeed when it's a list, and then cannot unify with the target element anyway. For clarity it could stay there - it doesn't harm.
If your Prolog has dif/2, you could use that instead of (\=)/2, depending on your requirements about the generality (WRT variables instantiation) of the solution.

Recursive procedure explanation

So I have the following working code in Prolog that produces the factorial of a given value of A:
factorial(0,1).
factorial(A,B) :- A>0, C is A-1, factorial(C,D), B is A*D.
I am looking for an explanation as to how this code works. I.e, what exactly happens when you ask the query: factorial(4, Answer).
Firstly,
factorial(0, 1).
I know the above is the "base case" of the recursive definition. What I am not sure of why/how it is the base case. My guess is that factorial(0, 1) inserts some structure containing (0, 1) as a member of "factorial". If so, what does the structure look like? I know if we say something like "rainy(seattle).", this means that Seattle is rainy. But "factorial(0, 1)"... 0, 1 is factorial? I realize it means factorial of 0 is 1, but how is this being used in the long run? (Writing this is helping me understand more as I go along, but I would like some feedback to make sure my thinking is correct.)
factorial(A,B) :- A>0, C is A-1, factorial(C,D), B is A*D.
Now, what exactly does the above code mean. How should I read it?
I am reading it as: factorial of (A, B) is true if A>0, C is A-1, factorial(C, D), B is A*D. That does not sound quite right to me... Is it?
"A > 0". So if A is equal to 0, what happens? It must not return at this point, or else the base case would never be used. So my guess is that A > 0 returns false, but the other functions are executed one last time. Did recursion stop because it reached the base case, or because A was not greater than 0? Or a combination of both? At what point is the base case used?
I guess that boils down to the question: What is the purpose of having both a base case and A > 0?
Sorry for the badly formed questions, thank you.
EDIT: In fact, I removed "A > 0" from the procedure and the code still works. So I guess my questions were not stupid at least. (And that code was taken from a tutorial.)
It is counterproductive to think of Prolog facts and rules in terms of data structures. When you write factorial(0, 1). you assert a fact to the Prolog interpreter that is assumed to be universally true. With this fact alone Prolog can answer questions of three types:
What is the factorial of 0? (i.e. factorial(0, X); the answer is X=1)
A factorial of what number is 1? (i.e. factorial(X,1); the answer is X=0)
Is it true that a factorial of 0 is 1? (i.e. factorial(0,1); the answer is "Yes")
As far as the rest of your Prolog program is concerned, only the first question is important. That is the question that the second clause of your factorial/2 rule will be asking at the end of evaluating a factorial.
The second rule uses comma operator, which is Prolog's way of saying "and". Your interpretation can be rewritten in terms of variables A and B like this:
B is a factorial of A when A>0, and C is set to A-1, and D is set to the factorial of C, and B is set to A times D
This rule covers all As above zero. The reference to factorial(C,D) will use the same rule again and again, until C arrives to zero. This is when this rule stops being applicable, so Prolog would grab the "base case" rule, and use 1 as its output. At this point, the chain of evaluating factorial(C, D) starts unwrapping, until it goes all the way to the initial invocation of the rule. This is when Prolog computes the final answer, and factorial/2 returns "Yes" and produces the desired output value.
In response to your edit, removing the A>0 is not dangerous only for getting the first result. Generally, you can ask Prolog to find you more results. This is when the factorial/2 with A>0 removed would fail spectacularly, because it would start going down the invocation chain of the second clause with negative numbers - a chain of calls that will end in numeric overflow or stack overflow, whichever comes first.
If you come from a procedural language background, the following C++ code might help. It mirrors pretty accurately the way the Prolog code executes (at least for the common case that A is given and B is uninstantiated):
bool fac(int a, int &b)
{
int c,d;
return
a==0 && (b=1,true)
||
a>0 && (c=a-1,true) && fac(c,d) && (b=a*d,true);
}
The Prolog comma operates like the sequential &&, and multiple clauses like a sequential ||.
My mental model for how prolog works is a tree traversal.
The facts and predicates in a prolog database form a forest of trees. When you ask the Prolog engine to evaluate a predicate:
?- factorial(6,N).
the Prolog engine looks for the tree rooted with the specified functor and arity (factorial/2 in this case). The Prolog engine then performs a depth-first traversal of that tree trying to find a solution using unification and pattern matching. Facts are evaluated as they are; For predicates, the right-hand side of the :- operator is evaluated, walking further into the tree, guided by the various logical operators.
Evaluation stops with the first successful evaluation of a leaf node in the tree, with the prolog engine remembering its state in the tree traversal. On backtracking, the tree traversal continues from where it left off. Execution is finally complete when the tree traversal is completed and there are no more paths to follow.
That's why Prolog is a descriptive language rather than an imperative language: you describe what constitutes truth (or falsity) and let the Prolog engine figure out how to get there.

Algorithm for testing for equivalence of two algebraic expressions

I have to write a program that tests whether two algebraic expressions are equivalent. It should follow MDAS precedence and parenthesis grouping. To solve the problem about precedence, I'm thinking I should implement a Infix to Postfix Notation converter for these expressions. But by doing this, I could not conclude their equivalence.
The program should look like this:
User Input: a*(a+b) = a*a + a*b
Output : Equivalent
For this problem I'm not allowed to use Computer Algebraic Systems or any external libraries. Please don't post the actual code if you have one, I just need an idea to work this problem out.
If you are not allowed to evaluate the expressions, you will have to parse them out into expression trees.
After that, I would get rid of all parenthesis by multiplying/dividing all members so a(b - c) becomes a*b - a*c.
Then convert all expressions back to strings, making sure you have all members alphabetically sorted (a*b, not b*a) ,remove all spaces and compare strings.
That's an idea:
You need to implement building expression tree first because it's a very natural representation of expression.
Then maybe you'll need to simplify it by open brackets and etc. using associative or distributive algebraic properties.
Then you'll have to compare trees. It's not obvious because you need to take care of all branch permutations in commutative operations and etc. E.g. you can sort them (I mean branches) and then compare for equality. Also you need to keep in mind possible renaming of parameters, i.e. a + b need to be equal x + y.

Resources