Skip to content

Commit bae2ddb

Browse files
committed
nuTerm for VCDs
Based upon https://dl.acm.org/do/10.1145/3554332/ and adjusted to use VCD files as source of traces rather than data sampled from running Java programs.
1 parent fe49803 commit bae2ddb

22 files changed

+3336
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/sh
2+
3+
BASE=$(dirname $0)
4+
5+
if ! which ebmc 2>/dev/null 1>&2 ; then
6+
echo "ebmc binary not found in PATH"
7+
exit 1
8+
fi
9+
10+
VERILOG_FILES=$@
11+
12+
cleanup()
13+
{
14+
rm -rf "$vcd_dir"
15+
}
16+
vcd_dir=$(mktemp -d vcd.XXXXX)
17+
trap cleanup EXIT
18+
19+
ebmc $VERILOG_FILES --random-traces \
20+
--trace-steps 100 --number-of-traces 10 \
21+
--vcd $vcd_dir/trace.vcd
22+
23+
## If we were to use --neural-engine:
24+
## python3 $BASE/nuterm/nuterm.py \
25+
## --strategy anticorr_sumofrelu2 \
26+
## $BASE/../../bla.vcd.* | \
27+
## tail -n 1 | cut -f2 -d: | \
28+
## sed 's/main\.//g' | sed 's/^/Candidate: /'
29+
## echo
30+
31+
python3 $BASE/nuterm/nuterm.py \
32+
--strategy anticorr_sumofrelu2 \
33+
--vcd-prefix $vcd_dir/trace.vcd \
34+
$VERILOG_FILES
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
nuTerm
2+
BSD 2-Clause License
3+
4+
Copyright (c) 2021, Julian Parsert, Mirco Giacobbe, Daniel Kröning
5+
All rights reserved.
6+
7+
Redistribution and use in source and binary forms, with or without
8+
modification, are permitted provided that the following conditions are met:
9+
10+
1. Redistributions of source code must retain the above copyright notice, this
11+
list of conditions and the following disclaimer.
12+
13+
2. Redistributions in binary form must reproduce the above copyright notice,
14+
this list of conditions and the following disclaimer in the documentation
15+
and/or other materials provided with the distribution.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
28+
29+
30+
----------------------------------------------------------------------
31+
nuTerm uses a json library for c++ found in ./deps/traceAgent/json
32+
with the following license:
33+
34+
MIT License
35+
36+
Copyright (c) 2013-2022 Niels Lohmann
37+
38+
Permission is hereby granted, free of charge, to any person obtaining a copy
39+
of this software and associated documentation files (the "Software"), to deal
40+
in the Software without restriction, including without limitation the rights
41+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42+
copies of the Software, and to permit persons to whom the Software is
43+
furnished to do so, subject to the following conditions:
44+
45+
The above copyright notice and this permission notice shall be included in all
46+
copies or substantial portions of the Software.
47+
48+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
54+
SOFTWARE.
55+
56+
57+
----------------------------------------------------------------------
58+
The license file for the SV-COMP problems can be found in
59+
benchmarking/termination-suite/sv-comp/termination-crafted-lit-C/LICENSE.txt
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# nuTerm
2+
3+
### Requirements
4+
python packages:
5+
- torch
6+
- sympy
7+
- matplotlib
8+
- datetime
9+
- pandas
10+
- numpy
11+
- tqdm
12+
- termcolor
13+
- psutil
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
2+
3+
# READ THIS BEFORE REMOVING !!!!!!!!
4+
# PLEASE LEAVE THIS THE WAY IT IS. If you remove this my workflow and everything get's destroyed. It is maybe not
5+
# nicest solution but it work for the time being. If you remove this one has to work from the terminal and export
6+
# all paths, which I do not think is a nice solution either, as I am not working from the terminal but the IDE.
7+
# PLEASE leave this until we have a nice and robust solution.
8+
import pathlib
9+
import os
10+
11+
from loopheads import get_loop_heads
12+
13+
file_path = str(pathlib.Path(__file__).parent.absolute())
14+
# os.environ['CLASSPATH'] = ':'.join([e.path for e in os.scandir(file_path + '/../libs/')])
15+
from jnius import autoclass, cast
16+
17+
import numpy as np
18+
19+
20+
def check(jar_name, class_name, method_name, offset, ranking_args, ranking_fun):
21+
#for x in os.environ['CLASSPATH']:
22+
# print(x)
23+
#exit(0)
24+
RankChecker = autoclass('javachecker.RankChecker')
25+
26+
URL = autoclass('java.net.URL')
27+
URLClassLoader = autoclass('java.net.URLClassLoader')
28+
List = autoclass('java.util.List')
29+
30+
urls = [URL('jar:file:' + jar_name + '!/')]
31+
cl = cast("java.lang.ClassLoader", URLClassLoader.newInstance(urls))
32+
#cl = URLClassLoader.newInstance(urls)
33+
34+
args = List.of(*ranking_args)
35+
fun = List.of(*ranking_fun)
36+
37+
res = RankChecker._check(cl, class_name, method_name, offset, args, fun)
38+
39+
return bool(res[0]), bool(res[1])
40+
41+
42+
def last_line_offset(jar_name, class_name, method_name, line):
43+
44+
URL = autoclass('java.net.URL')
45+
URLClassLoader = autoclass('java.net.URLClassLoader')
46+
47+
CFGAnalyzer = autoclass('javachecker.CFGAnalyzer')
48+
49+
urls = [URL('jar:file:' + jar_name + '!/')]
50+
cl = URLClassLoader.newInstance(urls)
51+
52+
line_to_offset = CFGAnalyzer.lineToLabelOffset(cl, class_name, method_name)
53+
return line_to_offset.get(line).last()
54+
55+
def check_sum_of_relu(jar_name, class_name, method_name, ranking_heads,
56+
ranking_args, ranking_out, ranking_W, ranking_b):
57+
58+
#for x in os.environ['LD_LIBRARY_PATH'].split(":"):
59+
# print(x)
60+
RankChecker = autoclass('javachecker.RankChecker')
61+
62+
URL = autoclass('java.net.URL')
63+
URLClassLoader = autoclass('java.net.URLClassLoader')
64+
List = autoclass('java.util.List')
65+
HashMap = autoclass('java.util.HashMap')
66+
67+
urls = [URL('jar:file:' + jar_name + '!/')]
68+
cl = cast("java.lang.ClassLoader", URLClassLoader.newInstance(urls))
69+
#cl = URLClassLoader.newInstance(urls)
70+
71+
#print(ranking_W)
72+
#print(ranking_b)
73+
#print(ranking_out)
74+
75+
assert len(ranking_W) == len(ranking_heads)
76+
assert len(ranking_b) == len(ranking_heads)
77+
assert len(ranking_out) == len(ranking_heads)
78+
fun = []
79+
for W,b in zip(ranking_W, ranking_b):
80+
81+
assert np.shape(W)[0] == np.shape(b)[0]
82+
SOR = []
83+
for i in range(0, np.shape(W)[0]):
84+
SOR.append([int(x) for x in W[i,:]] + [int(b[i])])
85+
fun.append(SOR)
86+
87+
args = List.of(*ranking_args)
88+
heads = List.of(*ranking_heads)
89+
out = List.of(*[List.of(*[int(x) for x in W[0,:]]) for W in ranking_out])
90+
hidden = List.of(*[List.of(*[List.of(*row) for row in relus]) for relus in fun])
91+
cex = HashMap()
92+
93+
res = RankChecker._checkLexiReluOrCex2(cl, class_name, method_name, heads, args, out, hidden, cex)
94+
95+
cexDict = {}
96+
i = cex.entrySet().iterator()
97+
while i.hasNext():
98+
e = i.next()
99+
(k,v) = e.getKey(),e.getValue()
100+
cexDict[k] = v
101+
102+
return bool(res[0]), bool(res[2]), cexDict
103+
104+
if __name__ == '__main__':
105+
106+
jarfile = "../benchmarking/termination-suite/termination-crafted-lit/terminating/NoriSharmaFSE2013Fig7/NoriSharmaFSE2013Fig7.jar"
107+
classname = "NoriSharmaFSE2013Fig7"
108+
methodname = "loop"
109+
input_vars = ['i', 'j', 'M', 'N', 'a', 'b', 'c']
110+
111+
loop_heads = get_loop_heads(jarfile, classname, methodname)
112+
113+
W_to_check = [np.array([[-1., 0., -2., 2., 1., 0., -0.],
114+
[-0., -2., 2., -2., 3., 0., -0.],
115+
[ 0., 0., 0., 0., 0., 0., 0.],
116+
[-2., 1., 3., -0., -2., 0., -0.],
117+
[-1., -1., -0., 2., -2., 0., 0.]])]
118+
b_to_check = [np.array([1., 1., 2., 3., 3.])]
119+
out_to_check = [np.array([[1., 1., 0., 1., 1.]])]
120+
121+
122+
123+
decrease, invar, cex = check_sum_of_relu(jarfile, classname, methodname,
124+
loop_heads, input_vars,
125+
out_to_check, W_to_check, b_to_check)
126+
127+
print("Decreasing: {}".format(decrease))
128+
print("Invar: {}".format(invar))
129+
print("Cex: {}".format(cex))
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from torch import optim
2+
3+
4+
def simple_learning(trace, loop_head, Model, loss_func, iterations=10000):
5+
"""
6+
Function for simplest learning algorithm.
7+
"""
8+
input_before, input_after = trace.get_pairing_of_all_traces_as_tensors(loop_head)
9+
print("Training data: {} pairs".format(len(input_after)))
10+
11+
input_dim = len(trace.get_pos_identifiers())
12+
input_vars = trace.get_pos_identifiers(frame=5)
13+
14+
# creating model with info from trace
15+
model = Model(input_dim, input_vars=input_vars)
16+
17+
# Learning
18+
optimiser = optim.AdamW(model.parameters(), lr=.01)
19+
for t in range(iterations):
20+
optimiser.zero_grad()
21+
22+
output_before = model(input_before)
23+
output_after = model(input_after)
24+
25+
loss = loss_func(output_before, output_after)
26+
27+
#print(t, "loss:", loss.item())
28+
if loss == 0.:
29+
break
30+
loss.backward()
31+
optimiser.step()
32+
33+
return model, loss.item()
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import os
2+
import pathlib
3+
4+
def get_loop_heads(jar, klass, function):
5+
from jnius import autoclass
6+
7+
# String = autoclass("java.lang.String")
8+
# calc_lh = autoclass('main.CalculateLoopHeads')
9+
10+
# x = calc_lh.calculateLoopHeadsHelper(String(jar), String(klass), String(function))
11+
12+
URL = autoclass('java.net.URL')
13+
URLClassLoader = autoclass('java.net.URLClassLoader')
14+
15+
#print()
16+
#for x in os.environ['CLASSPATH'].split(":"):
17+
# print(x)
18+
#print()
19+
CFGAnalyzer = autoclass('javachecker.CFGAnalyzer')
20+
21+
urls = [URL('jar:file:' + jar + '!/')]
22+
cl = URLClassLoader.newInstance(urls)
23+
heads = CFGAnalyzer.loopHeaders(cl, klass, function)
24+
print("Done")
25+
26+
return heads.toArray()
27+
28+
29+
if __name__ == '__main__':
30+
from jnius import autoclass
31+
32+
os.environ['CLASSPATH'] = str(pathlib.Path(__file__).parent.absolute()) + "../libs/CalculateLoopHeads.jar"
33+
34+
String = autoclass("java.lang.String")
35+
calc_lh = autoclass('main.CalculateLoopHeads')
36+
37+
exit(0)
38+
x = get_loop_heads("./examples/GCD/GCD.jar", "classes.GCD", "gcd")
39+
print(x)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import torch
2+
import torch.nn as nn
3+
import torch.nn.functional as F
4+
import sympy as sp
5+
6+
7+
class SumOfReluWithBias(nn.Module):
8+
def __init__(self, n_in, n_out=1, n_summands=2, input_vars=None):
9+
super(SumOfReluWithBias, self).__init__()
10+
11+
self.n_summands = n_summands
12+
self.n_in = n_in
13+
self.n_out = n_out
14+
15+
if input_vars is not None:
16+
self.input_vars = input_vars
17+
18+
self.fc1 = nn.Linear(n_in, n_summands * n_out, bias=True)
19+
self.out = []
20+
for i in range(0, n_out):
21+
row = torch.ones(n_summands)
22+
for j in range(0, i):
23+
row = torch.cat((torch.zeros(n_summands), row), 0)
24+
for j in range(i + 1, n_out):
25+
row = torch.cat((row, torch.zeros(n_summands)), 0)
26+
self.out.append(row)
27+
28+
self.out = torch.stack(self.out, dim=0)
29+
30+
def forward(self, state):
31+
layer = self.fc1(state)
32+
layer = F.relu(layer)
33+
layer = F.linear(layer, self.out)
34+
return layer
35+
36+
def get_weights(self):
37+
return [self.fc1.weight.data.numpy()]
38+
39+
def get_bias(self):
40+
return [self.fc1.bias.data.numpy()]
41+
42+
def get_round_weights(self):
43+
return self.fc1.weight.data.numpy().round()
44+
45+
def get_round_bias(self):
46+
return self.fc1.bias.data.numpy().round()
47+
48+
def sympy_expr(self):
49+
relu = sp.Function('relu')
50+
51+
expr = sp.Matrix(sp.symbols(self.input_vars, real=True))
52+
53+
print("##########")
54+
#print( self.fc1.bias.data.numpy())
55+
56+
expr1 = self.fc1.weight.data.numpy() * expr + self.fc1.bias.data.numpy()[:, None]
57+
expr1 = expr1.applyfunc(relu)
58+
return expr1
59+
60+
def sympy_rounded_expr(self):
61+
relu = sp.Function('relu')
62+
63+
expr = sp.Matrix(sp.symbols(self.input_vars, real=True))
64+
expr1 = self.fc1.weight.data.numpy().round() * expr + self.fc1.bias.data.numpy().round()[:, None]
65+
expr1 = expr1.applyfunc(relu)
66+
return expr1
67+
68+
def print_sympy(self):
69+
expr1 = self.sympy_expr()
70+
71+
for e in expr1:
72+
print(e)
73+
74+
print("After rounding:")
75+
expr = self.sympy_rounded_expr()
76+
for e in expr:
77+
print(e)

0 commit comments

Comments
 (0)