I am trying to model graph connectivity with Z3. Specifically I am partitioning a graph and need the subgraphs to remain connected. However TransitiveClosure doesn't work as I expect. I model edges with F
Here's a MWE:
s = Solver()
N = DeclareSort('N')
a,b,c = Consts('a b c', N)
F = Function(N,N,BoolSort())
s.add(F(A,B) == True)
s.add(F(B,A) == True)
s.add(F(B,C) == False)
s.add(F(C,B) == False)
s.add(F(A,C) == False)
s.add(F(C,A) == False)
s.add(A != B, B != C, C != A)
FX = TransitiveClosure(F)
s.add(FX(A,C))
This is apparently SAT, which doesn't make much sense to me. If I change s.add(FX(A,C)) to s.add(Not(FX(A,C))).
Why is this? C should not be a member of FX. Am I somehow setting FX(A,C) == True) by adding it to the model? Why doesn't that conflict with the definition of FX.
The output/connection lines of the model are hard to understand so I'm not quite sure what's going on.
I am working with tclosures as well and found this helpful: https://theory.stanford.edu/~nikolaj/programmingz3.html#sec-transitive-closure
Maybe there is an answer to your 2nd question as well.
Related
I want to use z3py to implement an access policy analyzer just like AWS Zelkova. The first step I need to do is to encode the policy language into logical expressions. For instance, a control policy
effect:Allow
principal:students
action: getObject
resource: cs240/Example.pdf,cs240/Answer.pdf
should be converted into
p = students ∧ a = getObject ∧ (r = cs240/Example.pdf ∨ r = cs240/Answer.pdf)
and using z3py I can represent it as
s.add(x1 == And(a == StringVal("GetObject"),p == StringVal("tas"),Or(r == StringVal("cs240/Exam.pdf"),r == StringVal("cs240/Answer.pdf"))))
Here comes the question. When input a policy, After parsing the policy, I may get an Array of values about one key and I need to use a loop to call Or() in order to get the result as Or(r[0],r[1],...). How can I do that? I have tried something like this but obviously it doesn't work.
from z3 import *
Action = ["getObject"]
Principal = ["tas"]
Resource = ["cs240/Exam.pdf","cs240/Answer.pdf"]
a,p,r,x = Bools('a p r x')
a_t,p_t,r_t = Strings('a_t p_t r_t')
s = Solver()
for act in Action:
a = Or(a,a_t == StringVal(act))
for principal in Principal:
p = Or(p,p_t == StringVal(principal))
for resource in Resource:
r = Or(r,r_t == StringVal(resource))
s.add(And(a,p,r))
print(s.check())
print(s.model())
That's the result of my program:
sat
[a_t = "", p = True, r_t = "", a = True, p_t = "", r = True]
You should build the expression one piece at a time and add it all together. Pseudocode:
foo = False
for i in items:
foo = Or(foo, i == ...whatever it should equal...)
s.add(foo)
When you build the expression, make sure to start the variable at False. Something like:
from z3 import *
Action = ["getObject"]
Principal = ["tas"]
Resource = ["cs240/Exam.pdf","cs240/Answer.pdf"]
a = False
p = False
r = False
a_t,p_t,r_t = Strings('a_t p_t r_t')
s = Solver()
for act in Action:
a = Or(a,a_t == StringVal(act))
for principal in Principal:
p = Or(p,p_t == StringVal(principal))
for resource in Resource:
r = Or(r,r_t == StringVal(resource))
s.add(And(a,p,r))
print(s.check())
print(s.model())
This prints:
sat
[r_t = "cs240/Exam.pdf", p_t = "tas", a_t = "getObject"]
I can't tell whether this is a correct answer as I haven't really studied your constraints, but the model seems more relevant to the question.
I have a set of simple but non linear constraints like:
v0= Real('v0')
v1= Real('v1')
v2= Real('v2')
v3= Real('v3')
v4= Real('v4')
v5= Real('v5')
v6= Real('v6')
v7= Real('v7')
v8= Real('v8')
v9= Real('v9')
v10= Real('v10')
v11= Real('v11')
v12= Real('v12')
v13= Real('v13')
v14= Real('v14')
v15= Real('v15')
v16= Real('v16')
v17= Real('v17')
v18= Real('v18')
v19= Real('v19')
s = Solver()
s.set(unsat_core=True)
s.add(v0>=0, v0<=1)
s.add(v1>=0, v1<=1)
s.add(v2>=0, v2<=1)
s.add(v3>=0, v3<=1)
s.add(v4>=0, v4<=1)
s.add(v5>=0, v5<=1)
s.add(v6>=0, v6<=1)
s.add(v7>=0, v7<=1)
s.add(v8>=0, v8<=1)
s.add(v9>=0, v9<=1)
s.add(v10>=0, v10<=1)
s.add(v11>=0, v11<=1)
s.add(v12>=0, v12<=1)
s.add(v13>=0, v13<=1)
s.add(v14>=0, v14<=1)
s.add(v15>=0, v15<=1)
s.add(v16>=0, v16<=1)
s.add(v17>=0, v17<=1)
s.add(v18>=0, v18<=1)
s.add(v19>=0, v19<=1)
s.add(v0==v1*v4)
s.add(v4==(v5+v6+v19)-(v5*v6+v5*v19+v6*v19)+(v5*v6*v19))
s.add(v6==v7*v11)
s.add(v11==(v12+v15+v18)-(v12*v15+v12*v18+v15*v18)+(v12*v15*v18))
s.add(v15==(v16+v17)-(v16*v17))
s.add(v12==v13*v14)
s.add(v7==(v8+v9+v10)-(v8*v9+v8*v10+v9*v10)+(v8*v9*v10))
s.add(v1==(v2+v3)-(v2*v3))
(this is basically probability computation)
I want to add a set of contraints like
v0_pred = Bool('v0_pred')
s.add(Implies(v0_pred, v0==0.0046))
v19_pred = Bool('v19_pred')
s.add(Implies(v19_pred, v19==0.015))
v16_pred = Bool('v16_pred')
s.add(Implies(v16_pred, v16==0.0094))
v12_pred = Bool('v12_pred')
s.add(Implies(v12_pred, v12==0.0172))
v5_pred = Bool('v5_pred')
s.add(Implies(v5_pred, v5==0.0038))
result = s.check(v0_pred,v19_pred,v16_pred,v12_pred,v5_pred)
and to track these 5 constraints into the unsat core. But then, more often than not, the solver reports that it cannot find a solution (unknown). If I do just s.add(v0==0.0046, v19==0.015, v16==0.0094, v12==0.0172, v5==0.0038), it works excellently and quickly finds a solution (sat). But using just s.add() I cannot get the unsat core for unsat cases. What am I doing wrong?
UPD:
Example of the "unknown" problem with Solver.assert_and_track():
from z3 import *
def is_number(s):
try:
float(s.numerator_as_long())
return True
except AttributeError:
return False
v0= Real('v0')
v1= Real('v1')
v2= Real('v2')
v3= Real('v3')
v4= Real('v4')
v5= Real('v5')
v6= Real('v6')
v7= Real('v7')
v8= Real('v8')
v9= Real('v9')
v10= Real('v10')
v11= Real('v11')
v12= Real('v12')
v13= Real('v13')
v14= Real('v14')
v15= Real('v15')
v16= Real('v16')
v17= Real('v17')
v18= Real('v18')
v19= Real('v19')
s = Solver()
s.set(unsat_core=True)
s.add(v0>=0, v0<=1)
s.add(v1>=0, v1<=1)
s.add(v2>=0, v2<=1)
s.add(v3>=0, v3<=1)
s.add(v4>=0, v4<=1)
s.add(v5>=0, v5<=1)
s.add(v6>=0, v6<=1)
s.add(v7>=0, v7<=1)
s.add(v8>=0, v8<=1)
s.add(v9>=0, v9<=1)
s.add(v10>=0, v10<=1)
s.add(v11>=0, v11<=1)
s.add(v12>=0, v12<=1)
s.add(v13>=0, v13<=1)
s.add(v14>=0, v14<=1)
s.add(v15>=0, v15<=1)
s.add(v16>=0, v16<=1)
s.add(v17>=0, v17<=1)
s.add(v18>=0, v18<=1)
s.add(v19>=0, v19<=1)
s.add(v0==v1*v4)
s.add(v4==(v5+v6+v19)-(v5*v6+v5*v19+v6*v19)+(v5*v6*v19))
s.add(v6==v7*v11)
s.add(v11==(v12+v15+v18)-(v12*v15+v12*v18+v15*v18)+(v12*v15*v18))
s.add(v15==(v16+v17)-(v16*v17))
s.add(v12==v13*v14)
s.add(v7==(v8+v9+v10)-(v8*v9+v8*v10+v9*v10)+(v8*v9*v10))
s.add(v1==(v2+v3)-(v2*v3))
v0_pred = Bool('v0_pred')
s.assert_and_track(v0==0.0046, v0_pred)
v19_pred = Bool('v19_pred')
s.assert_and_track(v19==0.015, v19_pred)
v16_pred = Bool('v16_pred')
s.assert_and_track(v16==0.0094, v16_pred)
v12_pred = Bool('v12_pred')
s.assert_and_track(v12==0.0172, v12_pred)
v5_pred = Bool('v5_pred')
s.assert_and_track(v5==0.0038, v5_pred)
result = s.check()
print result
if result == z3.sat:
m = s.model()
for d in m.decls():
if (is_number(m[d])):
print "%s = %s" % (d.name(), float(m[d].numerator_as_long())/float(m[d].denominator_as_long()))
else:
print "%s = %s" % (d.name(), m[d])
elif result == z3.unsat:
print s.unsat_core()
It returns me unknown, while it is sat, and is solved if using Solver.add() instead.
You should use assert_and_track to track them into unsat cores. See this: https://z3prover.github.io/api/html/classz3py_1_1_solver.html#ad1255f8f9ba8926bb04e1e2ab38c8c15
Handling non-linearity
Since your problem includes non-linear constraints, z3 will possibly have hard time handling those with default settings. Heuristic selections depend on a lot of factors and picking the right tactics is more of an art. For the problem you posted, the following works:
t = z3.Then('simplify', 'qfnra-nlsat')
s = t.solver()
result = s.check()
With other problems, your mileage might vary. But I suspect the above tactic should work for you so long as the structure of your problem stays the same.
How would you check a table for three identical elements (looking for three L's)?
table = {nil, nil, L, nil, L} -> false
table = {L, L, nil, nil, L} -> true
Really would appreciate some help!
EDIT: Ok I've got this, but it only outputs false even when there are three or more L's (and does so five times for every check?). Sorry if it seemed like I was trying to get the code for it, I'm genuinely trying to learn! :)
for k, v in pairs( threeL_table ) do
local count = 0
if k == 'L' then
count = count + 1
end
if count == 3 then
print('true')
else
print('false')
end
end
You were almost there. You need to test the values v against 'L', not the keys k. Also, I suppose you want to print the message only once after the scan is concluded; if so, put the if-statement outside of the for-loop. (In this case, you should define count outside of the for-loop, too, otherwise you would not see it once it has ended).
local count = 0
for k, v in pairs( threeL_table ) do
if v == 'L' then -- you need to check for the values not the keys
count = count + 1
end
end
if count == 3 then -- move this out of the for-loop
print('true')
else
print('false')
end
I will not give you any code as you did not show any own efforts to solve the problem.
How would you check a table for three identical elements? Well you count them.
Loop over the table and for every distinct value you create a new counter. You could use another table for that. Once one of those counters reaches 3 you know that you have three identical values.
Another way to solve this.
function detectDup(t,nDup)
table.sort(t)
local tabCount = {}
for _,e in ipairs(t) do
tabCount[e] = (tabCount[e] or 0) + 1
if tabCount[e] >= 3 then
print("The element '" .. e .. "' has more than 3 repetitions!")
return true
end
end
return false
end
print(detectDup({'L', 'L','A','B'},3))
print(detectDup({'L', 'L','A','B','L',},3))
I have the task, and I can understand how to deal with it (in what direction I need to start). Application need to understand user-inputted condition like that:
((VAR1 != 1 && VAR2 == 2) OR (VAR3 != 1 && VAR4 == 2)) AND (VAR5 = 2)
I have that variables inside my database, so it's no problem to replace VAR1 with real data. How in what way I can process that condition inside application. I thought about separating full condition to little blocks, but I don't understand yet how to separate, there is no symbol that I can split string.
So can you help in what direction I need to start working to process such conditions in Swift app?
Thanks to #Sulthan, NSPredicate is solution.
let expressionString = "((1 == 1) or (1 == 2)) and (1 == 2)"
let predicate = NSPredicate(format: expressionString)
print(predicate.evaluateWithObject(nil))
// Output: false
I have a logic problem for an iOS app but I don't want to solve it using brute-force.
I have a set of integers, the values are not unique:
[3,4,1,7,1,2,5,6,3,4........]
How can I get a subset from it with these 3 conditions:
I can only pick a defined amount of values.
The sum of the picked elements are equal to a value.
The selection must be random, so if there's more than one solution to the value, it will not always return the same.
Thanks in advance!
This is the subset sum problem, it is a known NP-Complete problem, and thus there is no known efficient (polynomial) solution to it.
However, if you are dealing with only relatively low integers - there is a pseudo polynomial time solution using Dynamic Programming.
The idea is to build a matrix bottom-up that follows the next recursive formulas:
D(x,i) = false x<0
D(0,i) = true
D(x,0) = false x != 0
D(x,i) = D(x,i-1) OR D(x-arr[i],i-1)
The idea is to mimic an exhaustive search - at each point you "guess" if the element is chosen or not.
To get the actual subset, you need to trace back your matrix. You iterate from D(SUM,n), (assuming the value is true) - you do the following (after the matrix is already filled up):
if D(x-arr[i-1],i-1) == true:
add arr[i] to the set
modify x <- x - arr[i-1]
modify i <- i-1
else // that means D(x,i-1) must be true
just modify i <- i-1
To get a random subset at each time, if both D(x-arr[i-1],i-1) == true AND D(x,i-1) == true choose randomly which course of action to take.
Python Code (If you don't know python read it as pseudo-code, it is very easy to follow).
arr = [1,2,4,5]
n = len(arr)
SUM = 6
#pre processing:
D = [[True] * (n+1)]
for x in range(1,SUM+1):
D.append([False]*(n+1))
#DP solution to populate D:
for x in range(1,SUM+1):
for i in range(1,n+1):
D[x][i] = D[x][i-1]
if x >= arr[i-1]:
D[x][i] = D[x][i] or D[x-arr[i-1]][i-1]
print D
#get a random solution:
if D[SUM][n] == False:
print 'no solution'
else:
sol = []
x = SUM
i = n
while x != 0:
possibleVals = []
if D[x][i-1] == True:
possibleVals.append(x)
if x >= arr[i-1] and D[x-arr[i-1]][i-1] == True:
possibleVals.append(x-arr[i-1])
#by here possibleVals contains 1/2 solutions, depending on how many choices we have.
#chose randomly one of them
from random import randint
r = possibleVals[randint(0,len(possibleVals)-1)]
#if decided to add element:
if r != x:
sol.append(x-r)
#modify i and x accordingly
x = r
i = i-1
print sol
P.S.
The above give you random choice, but NOT with uniform distribution of the permutations.
To achieve uniform distribution, you need to count the number of possible choices to build each number.
The formulas will be:
D(x,i) = 0 x<0
D(0,i) = 1
D(x,0) = 0 x != 0
D(x,i) = D(x,i-1) + D(x-arr[i],i-1)
And when generating the permutation, you do the same logic, but you decide to add the element i in probability D(x-arr[i],i-1) / D(x,i)