I'm trying to create a BDD with a particular structure. I have a 1D sequence of boolean variables x_i, e.g. x_1, x_2, x_3, x_4, x_5. My condition is satisfied if there are no isolated ones or zeros (except possibly at the edges).
I have implemented this using pyeda as follows. The condition is equivalent to examining consecutive triples ([x_1, x_2, x_3]; [x_2, x_3, x_4]; ...) and checking that their truth values are one of [[1,1,1], [0,0,0], [1,1,0], [0,1,1], [1,0,0], [0,0,1]].
from functools import reduce
from pyeda.inter import bddvars
def possible_3_grams(farr):
farr = list(farr)
poss = [[1,1,1], [0,0,0], [1,1,0], [0,1,1], [1,0,0], [0,0,1]]
truths = [[farr[i] if p[i] else ~farr[i] for i in range(3)] for p in poss]
return reduce(lambda x, y: x | y, [reduce(lambda x, y: x & y, t) for t in truths])
X = bddvars('x', k)
Xc = [X[i-1:i+2] for i in range(1,k-1)]
cont_constraints = [possible_3_grams(c) for c in Xc]
cont_constr = reduce(lambda x, y: x & y, cont_constraints)
print(cont_constr.to_dot())
The final diagram looks like this:
This works well for short sequences, but the last reduction becomes extremely slow when the length gets beyond about 25. I would like something that works for much longer sequences.
I wondered if it would be more efficient in this case to build the BDD directly, since the problem has a lot of structure. However, I can't find any way to directly manipulate BDDs in pyeda.
Does anyone know how I can get this working more efficiently?
This example can be solved for large numbers of variables using the package dd, which can be installed with
pip install dd
For example,
from functools import reduce
from dd.autoref import BDD
def possible_3_grams(farr):
farr = list(farr)
poss = [[1, 1, 1], [0, 0, 0], [1, 1, 0], [0, 1, 1], [1, 0, 0], [0, 0, 1]]
truths = [[farr[i] if p[i] else ~ farr[i] for i in range(3)] for p in poss]
return reduce(lambda x, y: x | y, [reduce(lambda x, y: x & y, t) for t in truths])
def main():
k = 100
xvars = [f'x{i}' for i in range(k)]
bdd = BDD()
bdd.declare(*xvars)
X = [bdd.add_expr(x) for x in xvars]
Xc = [X[i-1:i+2] for i in range(1, k-1)]
cont_constraints = [possible_3_grams(c) for c in Xc]
cont_constr = reduce(lambda x, y: x & y, cont_constraints)
print(cont_constr)
bdd.dump('example.png', [cont_constr])
The above uses the pure Python implementation of BDDs in the module dd.autoref. There is a Cython implementation available in the module dd.cudd that interfaces to the CUDD library in C. This implementation can be used by replacing the import statement
from dd.autoref import BDD
with the statement
from dd.cudd import BDD
The above script using the class dd.autoref.BDD works for k = 800 (with the bdd.dump statement commented). The above script using the class dd.cudd.BDD works for k = 10000, provided dynamic reordering is first disabled bdd.configure(reordering=False), and constructs a BDD with 39992 nodes (with the bdd.dump statement commented).
The diagram for k = 100 is the following:
If two-level logic minimization is also of interest, it is implemented in the package omega, an example can be found at: https://github.com/tulip-control/omega/blob/master/examples/minimal_formula_from_bdd.py
Related
I trying to create compact code in prolog that sovles systems of equations.
For example, in this case, the assumptions must be
A+B-C-D=4, A+B+C+D=14, A-B+C-D=2.
I'm trying to have it where it solves all combinations possible for A, B, C, and D but satisfies all 3 equations. They can only be #'s 0-9 though but somehow show all possible solutions/combinations.
So after running the query, it would output something like
Crypto(A,B,C,D)
A = 8, B = 1, C = 0, D = 5.
^That would be one solution. But I need to show all possible.
I'm kind of lost as to how to satisfy all 3 in Prolog. Thank you.
You can solve it by taking out one element from the domain of the variables and assign it to them such that every variable has a different number assigned to it. It's a brute force method.
takeout(X, [X|R], R).
takeout(X, [Y|Xs], [Y|Ys]):- takeout(X, Xs, Ys).
aas(X,L,L1):- takeout(X,L,L1).
crypto(A,B,C,D):-
L=[0,1,2,3,4,5,6,7,8,9],
aas(A,L,L1),aas(B,L1,L2),aas(C,L2,L3),aas(D,L3,_),
A+B-C-D=:=4,
A+B+C+D=:=14,
A-B+C-D=:=2,
nl.
aas(X,L,L1). used for assigning values to the variables.
takeout function is used for taking out one element and return a list excluding the element taken out.
OUTPUT
?- crypto(A,B,C,D).
A = 3,
B = 6,
C = 5,
D = 0
A = 5,
B = 4,
C = 3,
D = 2
A = 7,
B = 2,
C = 1,
D = 4
A = 8,
B = 1,
C = 0,
D = 5
This program prints all the possible solutions to that equation A+B-C-D=4, A+B+C+D=14, A-B+C-D=2. Hope this answers your question.
I'm trying to write a prolog predicate that generates a horizontal line using SWI-Prolog 7. I started with a simple generator referenced here: Simple Prolog generator
In playing around, I came up with a result I don't understand. If I create these rules (hline is intended to take two x coordinates (X and A) and a single Y coordinate Y, and generate the X and Y coordinates of a horizontal line in S and T:
gen(Cur, Top, Cur) :- =<(Cur, Top).
gen(Cur, Top, Next):- =<(Cur, Top), is(Cur1, +(Cur, 1)), gen(Cur1, Top, Next).
hline(X,A,Y,S,T) :- gen(X,A,S), T is Y.
When I run this query:
hline(0,5,0,X,Y).
I get these results. The first one is the one I don't understand:
X = Y, Y = 0
X = 1,
Y = 0
X = 2,
Y = 0
X = 3,
Y = 0
X = 4,
Y = 0
X = 5,
Y = 0
Can someone explain why the first result is X=Y, Y=0? I can't see anywhere that X and Y are even in the same clause through unification or reduction...
I think it's very easy but I have no idea how to do that.
I tried by attribuition, doing a list receive another list but don't work.
% H is the head of a coordenate and T the tail
% E is the element that will be placed in the position T
findLine([L0,L1,L2,L3,L4,L5,L6,L7,L8,L9,L10],H,T,E,NewTray) :-
H is 1,replace(L1,T,E,N),L1 = N;
H is 2,replace(L2,T,E,N),L2 = N;
...
H is 10,replace(L10,T,E,N),L10 = N;
NewTray = [L0,L1,L2,L3,L4,L5,L6,L7,L8,L9,L10].
I need that L1 be the N in this clause, I don't know how I can create a clause to modify the L1 inside the clause findLine. I thought in create clause to remove all elements and add the new ones one by one and call this at the attribuition place:
%L is the list, C a counter and N the new list
rewrite(L,C,N) :-
Q is C,
removeByIndex(Q,L,R),
(Q \== 0 -> rewrite(R,Q-1,N), !.
removeByIndex(0,[_|T],T):- !.
removeByIndex(I,[H|T],R):- X is I - 1, removeByIndex(X, T, Y), insert(H, Y, R).
But I continous with the same problem: the L1 are not modified :(
The idea is modify a line and replace on the tray.
PS: I'm sorry for my english, but the prolog topics are almost inative in the portuguese forum
I'm really unsure what you're trying to accomplish here, but I can point to a few things that strike me as symptoms of a misunderstanding.
First of all, you bind all the variables at the top and then you have essentially a bottom-out else case that looks like this:
NewTray = [L0,L1,L2,L3,L4,L5,L6,L7,L8,L9,L10].
Well, you never assign to NewTray in any of your other cases, so NewTray is going to be uninstantiated most of the time. That does not seem likely to be what you intend to me.
Second, your cases have this structure:
H is 1,replace(L1,T,E,N),L1 = N;
First mistake here is that H is 1; is/2 is for evaluating arithmetic expressions; there's no difference between this and H = 1, and the equivalence of L1 and N means that this whole predicate could probably be written as:
findLine([L0,L1,L2,L3,L4,L5,L6,L7,L8,L9,L10],1,T,E,_) :-
replace(L1,T,E,L1).
findLine([L0,L1,L2,L3,L4,L5,L6,L7,L8,L9,L10],2,T,E,_) :-
replace(L2,T,E,L2).
findLine(Line, _, _, Line).
I'm still confused by what you're trying to do, looking at that.
I suspect that you think L1 will have some value on the way into the relation and suddenly have a new, different value after the relation is used. That is emphatically not the case: variables in Prolog are bound exactly once; your assignment L1 = N or whatever is not going to cause L1 to "receive a new value" (because such a thing cannot happen in Prolog); instead it informs Prolog that L1 and N should be bound to the same value. What this means depends on circumstances; if they are both ground and not equal it will cause your predicate to fail, for instance, but if either of them is non-ground they will accept the value of the other.
I'm looking at what you're doing here and I can't help but think that you're essentially trying to do this:
replace([], _, _, []).
replace([H|T], 1, X, [X|T]).
replace([H|T], N, X, [H|Replaced]) :-
N > 1, succ(N0, N), replace(T, N0, X, Replaced).
Use it like this:
?- replace([1,2,3,4,5], 3, foo, Result).
Result = [1, 2, foo, 4, 5]
I just can't for the life of me figure out what you're trying to do, and I don't know why you're bothering to bind all the variables in your list at once if you don't need them all at once.
Anyway, I hope this helps! Maybe if you show us more of what you're trying to do it will be more clear how we can help.
Edit: Elaboration on = and unification
Let's mess around with = and see what happens:
?- X = 3.
X = 3.
Probably nothing surprising about this.
?- 3 = X.
X = 3.
Unification is different from assignment. As you can see, it is not directional. This line would not have worked in any other language.
?- X = [1,Y,3].
X = [1, Y, 3].
Notice that Prolog has no issues with having variables remain free.
?- X = [1,Y,3], Y = 2.
X = [1, 2, 3],
Y = 2.
Now, because Y is the same in both positions, when you bound Y to 2, the middle value in X became 2 as well. There are data structures unique to Prolog that make use of this feature (difference lists).
?- X = [1,Y,3], Q = X, Q = [1,2,3].
X = Q, Q = [1, 2, 3],
Y = 2.
Now what makes this interesting is that we did not explicitly tell Prolog that Y is 2. Prolog inferred this by unification. You can see some more examples of that here:
?- X = [H|T], H = 3, T = [4,5].
X = [3, 4, 5],
H = 3,
T = [4, 5].
So here we said, X is composed of H and T and then told it what H and T are. But Prolog's unification doesn't care much about the order you do things:
?- X = [H|T], X = [1,2,3].
X = [1, 2, 3],
H = 1,
T = [2, 3].
Unification is transitive.
So what happens when Prolog cannot unify?
?- X = [1,Y,3], Q = X, Q = [1,2,3], Y = 4.
false.
Y has to be 2 for the first step, but it has to be 4 for the last step. Once a variable is bound, there's no changing it. This is just a more complex way of saying:
?- X = 2, X = 4.
false.
Prolog does not have "assignables", just variables.
so i am trying to solve kenken using prolog but i ran into several problems from the start, first of all lets say i run it like kenken([X1,X2,X3,.....X16]). and i want to solve for this x's with rules i defined before. so lets say the first cell has 3 values X1,X2,and X3 and i want to get 2 by using multiplication meaning that X1*X2*X3=2, now how do i set up a rule to see all posible solutions if i had something like that.
also how would i tell my x's to only use a range of values 1-4.
i tried to do something like
:- use_module(library(clpr)).
solve([X1,X2,X3]):-
{X1*X2*X3=2}.
but its gives me a really weird output.
Since you reason over integers, not floats, consider using library(clpfd) instead of CLP(R). In SICStus, SWI and YAP, you can constrain a finite domain variable X to the integer range 1-4 with:
X in 1..4
You can use the built-in predicate label/1 to search for concrete solutions. Example with SWI-Prolog:
?- Vars = [A,B,C], A*B*C #= 2, Vars ins 1..4, label(Vars).
yielding:
Vars = [1, 1, 2], A = B, B = 1, C = 2 ;
Vars = [1, 2, 1], A = C, C = 1, B = 2 ;
Vars = [2, 1, 1], A = 2, B = C, C = 1.
You need the is operator to do arithmetic:
2 is X1*X2*X3
Note that this will not work unless the X's are all bound to numbers.
I am trying to write a function - decListRange(X,List) which give a list in range [X-1:1] by descending order. For example -
decListRange(9,List).
Will give -
List = [8,7,6,5,4,3,2,1].
I tried the following but it goes into infinite loop -
decListRange(1,[]) :- !.
decListRange(X,[H|Rest]) :-
H = X-1, NextX = X - 1 ,decListRange(NextX,Rest).
You have two problems. The first real one is that you need to use is instead of =:
H is X-1
This is needed to trigger arithmetic evaluation. Your second problem isn't a real problem but speaks to a bigger misunderstanding, which is that H and NextX are equivalent. Because Prolog only has bindings and not "assignables" as it were, you should never really need to create two "variables" with the same binding. There's no state being kept around for you to modify later.
Cleaning up both you get this:
decListRange(1, []) :- !.
decListRange(X, [H|Rest]) :-
X > 1,
H is X-1,
decListRange(H, Rest).
Edit 2: a clpfd implementation
:- use_module(library(clpfd)).
declist(N, L) :- N == 1, !, L = []. % green cut
declist(1, []).
declist(N, [N1|Ns]) :-
N #> 1,
N1 #= N - 1,
declist(N1, Ns).
This one has the properties #false mentions below in the comments:
?- declist(3, L).
L = [2, 1] ;
false.
?- declist(3, [2,1]).
true ;
false.
?- declist(N, [3,2,1]).
N = 4.
?- declist(N, X).
N = 1,
X = [] ;
N = 2,
X = [1] ;
N = 3,
X = [2, 1] ;
N = 4,
X = [3, 2, 1] ;
N = 5,
X = [4, 3, 2, 1] .
Edit: a short interlude on the difference between = and is.
In procedural languages = is almost always syntax for assigning a particular value to a variable. In Prolog, variables are bindings, and once established they cannot be directly modified by reassigning the variable a different value. Instead they work more like variables in math and logic, where the variable "stands in" for interesting values, but those values are themselves basically immutable. In Prolog, = essentially asks the unification engine to establish bindings. So if you were to do something like this:
?- name(X, Y) = name(bob, tony).
Prolog responds with variable bindings:
X = bob,
Y = tony.
Once those bindings exist, contradictory bindings will fail and affirmative bindings will succeed:
?- name(X, Y) = name(bob, tony), X = bob.
X = bob,
Y = tony.
?- name(X, Y) = name(bob, tony), X = william.
false.
The unification algorithm itself doesn't know anything about arithmetic. This has the pleasant side-effect that you can use any expression raw. For instance:
?- Expr = X + 3, Z + Q = Expr.
Expr = Z+3,
X = Z,
Q = 3.
This is probably really surprising looking. You may expect that somehow Prolog was smart enough to keep the expression around because it noticed X was a variable or something, but that isn't true either:
?- X = 4, Expr = X + 3, Z + Q = Expr.
X = 4,
Expr = 4+3,
Z = 4,
Q = 3.
Another way of looking at this is that Prolog is considering + to be just another operator, so X+3 is a fact just like add(X, 3) that doesn't necessarily have any special meaning. Whichever way you look at it, the is/2 operator exists to apply arithmetic reasoning and produce a value:
?- X = 4, Expr is X + 3.
X = 4,
Expr = 7.
Notice that Expr has the computed value but none of the original structure:
?- X = 4, Expr is X + 3, Z + Q = Expr.
false.
In practice, if you need to do a lot of reasoning with arithmetic, you will want to use a library like clpfd or clpqr depending on whether you're interested in integers or reals. This library enables you to do more interesting things more easily, like specify that an equation holds for values in a certain range and get those values out.