Skip to content

[experimental] PInfer: learning specifications from event traces #862

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 196 commits into
base: experimental/pinfer
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
196 commits
Select commit Hold shift + click to select a range
aca014c
[init] PInfer backend
AD1024 Jun 12, 2024
7d51b72
save
AD1024 Jun 13, 2024
72dbbe2
[add] predicate enumeration
AD1024 Jun 14, 2024
729819f
[save] compilation to java src
AD1024 Jun 15, 2024
910c5df
[save] generating terms and predicates
AD1024 Jun 17, 2024
c74b1ec
[fix] predicates and term enumeration
AD1024 Jun 17, 2024
0f30ec3
[save] structural equality for AST nodes
AD1024 Jun 17, 2024
e6485b1
[add] serialize to string for types
AD1024 Jun 18, 2024
8a410ce
[save] Java codegen
AD1024 Jun 19, 2024
ef5daef
[refactor] predicate and function store
AD1024 Jun 19, 2024
45b8d3c
[refactor] tweak Java codegen backend to support PInfer codegen
AD1024 Jun 19, 2024
f9f562d
[wip] verification template generation
AD1024 Jun 20, 2024
079c157
[fix] unwrap alias
AD1024 Jun 20, 2024
ab0eeb7
[save] enum type conversion
AD1024 Jun 20, 2024
eff95b9
[save] finish trace parser codegen
AD1024 Jun 21, 2024
222d15a
[fix] event payload get for json objects
AD1024 Jun 21, 2024
fe5f162
[save] forall-only template wip
AD1024 Jun 26, 2024
6b03109
[add] forall template; term orders
AD1024 Jun 26, 2024
f42ec80
[save] populating contraditions for atomic predicates
AD1024 Jun 27, 2024
d0a0541
[add] include types
AD1024 Jun 28, 2024
0c5e4f4
[save] predicate check enumeratior
AD1024 Jun 29, 2024
966bfd7
[save] remove project-dependent code
AD1024 Jun 29, 2024
daa400a
[save] more automation on workflow
AD1024 Jul 2, 2024
bb44b64
[add] more java codegen
AD1024 Jul 2, 2024
1933d5f
[save] container types
AD1024 Jul 5, 2024
cb7c278
[save] more tweaks of templates
AD1024 Jul 6, 2024
2708d2b
[fix] more tweaks on infer infra
AD1024 Jul 8, 2024
706f6c2
[save] more fixes
AD1024 Jul 10, 2024
3f616b2
[save] isolate templates
AD1024 Jul 10, 2024
89b709c
[tweak] automatically generating enum comparisons
AD1024 Jul 11, 2024
1628f24
[save] generate boolean eq cmp
AD1024 Jul 11, 2024
23bac7e
[major refactor] switch to P Java runtime
AD1024 Jul 12, 2024
1c9a69d
[save] more fixes
AD1024 Jul 12, 2024
1488041
[tweak] use array sequences
AD1024 Jul 12, 2024
af958f1
[add] exist-only templates
AD1024 Jul 15, 2024
af2e469
[save] forall-exists
AD1024 Jul 16, 2024
c716d7a
[add] PInfer trace dump options for PChecker
AD1024 Jul 16, 2024
9106bb9
[save] better multi-processing
AD1024 Jul 17, 2024
1dc86e5
fix typo in path
AD1024 Jul 18, 2024
31026e4
[add] dependencies
AD1024 Jul 18, 2024
6dfb570
[save] changes
AD1024 Jul 18, 2024
6bf9e1e
[save] fix codegen for custom functions with sequence types
AD1024 Jul 18, 2024
95548cd
[major refactor] forall/exists template
AD1024 Jul 20, 2024
b30ca8d
[fix] forgot to call it!
AD1024 Jul 20, 2024
4fb5f15
[save] fix daikon output converter
AD1024 Jul 20, 2024
68ebb3c
[save] subst
AD1024 Jul 20, 2024
807c53f
[add] subst for headers
AD1024 Jul 20, 2024
3e2d8fe
[fix] check event name instead of event variable name
AD1024 Jul 20, 2024
8769923
[fix] daikon output header builder
AD1024 Jul 20, 2024
498b83b
[add] compliation phase
AD1024 Jul 20, 2024
908f539
[save] small fixes
AD1024 Jul 21, 2024
8f6aae6
[fix] template generation for events with the same names
AD1024 Jul 21, 2024
f021e4d
[add] todos
AD1024 Jul 22, 2024
d961176
[save] gt predicate
AD1024 Jul 23, 2024
bb9159f
[fix] template generator
AD1024 Jul 23, 2024
d8562b3
[fix] task pool monitors numFinished instead of ptr
AD1024 Jul 25, 2024
3ad68a3
[save] some tweaks
AD1024 Jul 26, 2024
cd30ac5
[fix] template names
AD1024 Jul 26, 2024
a16e661
Merge branch 'master' of github.com:AD1024/P
AD1024 Jul 26, 2024
513657f
Merge branch 'master' of github.com:AD1024/P
AD1024 Jul 26, 2024
5ac485b
some tweaks
AD1024 Jul 27, 2024
ca5d9bf
save
AD1024 Jul 29, 2024
8a56a05
[save] FromDaikon
AD1024 Aug 1, 2024
e55b17b
[add] cli
AD1024 Aug 1, 2024
cf132b9
[add] data type annotations
AD1024 Aug 1, 2024
dc357b1
save
AD1024 Aug 2, 2024
6733af0
[add] post-infer clean ups
AD1024 Aug 2, 2024
7976284
[save] some ui tweaks
AD1024 Aug 2, 2024
02fa1d3
[add] post-hoc pruning
AD1024 Aug 5, 2024
830dc10
[fix] upper case arg
AD1024 Aug 5, 2024
646d4f7
[try tweak] use static function instead of constructor
AD1024 Aug 5, 2024
57743e3
fix
AD1024 Aug 5, 2024
ceb4d37
[tweak] output to a text file
AD1024 Aug 5, 2024
ad3cab9
fix bug
AD1024 Aug 5, 2024
2091215
[add] interactive mode
AD1024 Aug 7, 2024
2aad986
tweaks!!
AD1024 Aug 7, 2024
96cef4d
[tweak] order
AD1024 Aug 7, 2024
992dbaf
[save] snapshot for hyperparameter search
AD1024 Aug 15, 2024
4c51135
[save] hints!!
AD1024 Aug 18, 2024
58ad832
[impl] run hints
AD1024 Aug 18, 2024
62fcc07
[tweak] hint search space exploration
AD1024 Aug 19, 2024
f8fffba
[save] begin `auto`
AD1024 Aug 19, 2024
099e5a5
[add] hint/hint exact
AD1024 Aug 19, 2024
88a921c
[save] search start!
AD1024 Aug 20, 2024
a88bf29
[save] some fixes
AD1024 Aug 20, 2024
f01c59d
[add] trace indexer; tweak PChecker infer mode
AD1024 Aug 21, 2024
d7ad637
fix bugs
AD1024 Aug 21, 2024
58b8f7c
[save] `indexof` metafunction
AD1024 Aug 21, 2024
f96dd1a
[init] CC-based pruning
AD1024 Aug 22, 2024
124b979
[save] contradictions
AD1024 Aug 22, 2024
bcd9b2a
[add] more pruning utilities
AD1024 Aug 23, 2024
d2883a9
[save] type-based pruning
AD1024 Aug 23, 2024
9c3b8e6
[save] refine pruning
AD1024 Aug 24, 2024
a1845cf
save
AD1024 Aug 25, 2024
66ab74b
[tweak] logic-based pruning
AD1024 Aug 25, 2024
0cb8cd6
save
AD1024 Aug 25, 2024
b12f0e1
save
AD1024 Aug 26, 2024
5861236
tweak
AD1024 Aug 26, 2024
6a9a76f
[near finalized] pruning
AD1024 Aug 26, 2024
791befd
[save] finish pruning. may have little bugs tho
AD1024 Aug 26, 2024
0939fa3
Merge pull request #1 from AD1024/pinfer-pruning
AD1024 Aug 26, 2024
143973e
save
AD1024 Aug 26, 2024
6105534
tweak utilities
AD1024 Aug 26, 2024
749d333
[add] lt generation for numeric types
AD1024 Aug 27, 2024
65c800f
[tweak] look at events sent in entry function alone
AD1024 Aug 27, 2024
e17c49f
[remove] logger
AD1024 Aug 27, 2024
5d2c327
[add] also run user-defind hints in auto mode
AD1024 Aug 27, 2024
367b989
[fix] bug of grid search
AD1024 Aug 27, 2024
e72b006
[fix] wrong arity
AD1024 Aug 27, 2024
e27442e
fix again
AD1024 Aug 27, 2024
0c5d613
yet another fix
AD1024 Aug 27, 2024
79febb7
[add] unary ops
AD1024 Aug 27, 2024
5989456
[add] AST comparer for SizeOfExpr
AD1024 Aug 27, 2024
6ebd51b
more fixes
AD1024 Aug 27, 2024
383bf56
[mark save] better support for custom predicates
AD1024 Aug 27, 2024
b800f45
[tweak] add contains expr support
AD1024 Aug 27, 2024
04a30fc
[save] add eq for same field types
AD1024 Aug 27, 2024
fe439b7
[save] several fixes to better support custom hints
AD1024 Aug 28, 2024
25501ef
[save] fix for nested arrays
AD1024 Aug 28, 2024
fdfdcb6
[add] trace indexing
AD1024 Aug 28, 2024
cef3fd2
[save] guide
AD1024 Aug 29, 2024
99a6bb8
save one more line
AD1024 Aug 29, 2024
c875adf
[save] add traces commandline args to docus
AD1024 Aug 29, 2024
7310c9d
fix typo
AD1024 Aug 29, 2024
7ffa0f1
[fix] checker extension
AD1024 Aug 29, 2024
0c2842b
tweaks
AD1024 Aug 29, 2024
5206a47
save
AD1024 Sep 5, 2024
da19922
[rollback] forall-exists pruning
AD1024 Sep 6, 2024
ddc2c4a
[add] chunk size limit
AD1024 Sep 6, 2024
f8af2a0
[add] transform back to P code
AD1024 Sep 6, 2024
06ed859
save
AD1024 Sep 6, 2024
8a78850
Merge branch 'master' of github.com:AD1024/P
AD1024 Sep 6, 2024
f0af7f7
[save] to P monitor
AD1024 Sep 7, 2024
0adde69
[fix] monitor generation for forall-only templates
AD1024 Sep 7, 2024
3dfe9d3
[save] fix bugs in monitor generation
AD1024 Sep 11, 2024
db8913e
save
AD1024 Sep 11, 2024
3c7a039
[fix] resolution using negations
AD1024 Sep 13, 2024
166dbe7
[save] do not generate new folders
AD1024 Sep 13, 2024
6755d30
[tweak] explorer
AD1024 Sep 14, 2024
6c9faa2
[save] checkpoint
AD1024 Sep 17, 2024
a6c8929
[save] tweak pruning
AD1024 Sep 19, 2024
82f7070
[fix] template gen: existentials should be falsified by us!
AD1024 Sep 21, 2024
75ee8a4
[save] parse for boolean constant comp
AD1024 Sep 24, 2024
5751315
[tweak] avoid generating predicates that may lead to symmetric invari…
AD1024 Sep 26, 2024
e8e5f28
[save] write stats
AD1024 Oct 1, 2024
01f935f
save
AD1024 Oct 3, 2024
4aa968c
[add] benchmark models
AD1024 Oct 3, 2024
3d2ac20
[add] trace parsing mode and smt solver integration
AD1024 Oct 4, 2024
108af36
save
AD1024 Oct 4, 2024
41fb8e3
[save] pruning mode
AD1024 Oct 7, 2024
17dc57f
add z3 checker switch
AD1024 Oct 7, 2024
52d6df5
optimize pruning
AD1024 Oct 16, 2024
fa81685
save
AD1024 Oct 16, 2024
97f7d96
[save] aggregating results
AD1024 Oct 17, 2024
0859e19
[add] pinfer tutorial
AD1024 Oct 20, 2024
a0ea2a5
add ring leader
AD1024 Oct 20, 2024
87e75d9
[save] benchmark results
AD1024 Oct 24, 2024
f35f69c
add pruned invs for clocks
AD1024 Oct 24, 2024
b0323d1
[save] readme
AD1024 Oct 25, 2024
128c20f
fix some issues with custom functions
AD1024 Oct 27, 2024
30d3e65
[tweak] trace generator
AD1024 Oct 29, 2024
661003b
remove files
AD1024 Oct 29, 2024
7bac4ca
save
AD1024 Nov 3, 2024
921f8d0
[save] more pruning
AD1024 Nov 11, 2024
f900017
[update] 2PC example
AD1024 Nov 12, 2024
e098e0e
save prepare traces
AD1024 Nov 12, 2024
d8310ac
save constants
AD1024 Nov 12, 2024
e790804
[update] invariants
AD1024 Nov 19, 2024
73989f8
save raft invariants
AD1024 Nov 19, 2024
3b19356
save clockbound invariants
AD1024 Nov 19, 2024
6189028
save all
AD1024 Nov 19, 2024
f3b8af5
save chain replication
AD1024 Nov 26, 2024
cca23eb
remove slurm job
AD1024 Nov 26, 2024
d89e402
subsumption: checks contra-positives
AD1024 Dec 2, 2024
bddca25
[add] statistics of number of activated guards
AD1024 Jan 28, 2025
c913914
save
AD1024 Feb 6, 2025
55308da
save
AD1024 Feb 13, 2025
774e10e
save
AD1024 Feb 16, 2025
6dd0244
save
AD1024 Feb 18, 2025
e2f494e
save new benchmarks and readme
AD1024 Feb 18, 2025
b666058
update
AD1024 Feb 20, 2025
b9f3b2e
hotfix
AD1024 Feb 21, 2025
d3eddd8
hotfix
AD1024 Feb 21, 2025
6d9f2cd
save
AD1024 Mar 5, 2025
969c0a4
fix
AD1024 Mar 5, 2025
ff653e0
save
AD1024 Mar 11, 2025
e2bbc35
sace
AD1024 Mar 17, 2025
955aa0e
save hotfix
AD1024 Mar 19, 2025
7e6c333
save hotfix
AD1024 Mar 19, 2025
794aea9
save
AD1024 Mar 19, 2025
83bbce9
save
AD1024 Mar 27, 2025
df01f5d
save
AD1024 Apr 2, 2025
6acf92d
save
AD1024 Apr 9, 2025
1a8b2a6
save invariant files
AD1024 Apr 16, 2025
8d2af94
remove duplicated benchmark code
AD1024 May 19, 2025
06bb44b
save merge
AD1024 May 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions P.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CheckerCore", "Src\PChecker
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PTool", "PTool", "{8BAB6184-8545-4E17-8199-86110AC5228F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{5976592D-2EC6-4991-89A9-7EC97EEC0FFD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PInfer", "Src\PInfer\PInfer.csproj", "{3DFC4318-5D40-41A0-B46D-F0896180B45C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -41,6 +45,10 @@ Global
{37778F65-BDBF-4AEA-BA34-01026A223083}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37778F65-BDBF-4AEA-BA34-01026A223083}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37778F65-BDBF-4AEA-BA34-01026A223083}.Release|Any CPU.Build.0 = Release|Any CPU
{3DFC4318-5D40-41A0-B46D-F0896180B45C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DFC4318-5D40-41A0-B46D-F0896180B45C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DFC4318-5D40-41A0-B46D-F0896180B45C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DFC4318-5D40-41A0-B46D-F0896180B45C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -50,6 +58,7 @@ Global
{41F39ADD-B75F-473F-AC41-D1A9CDA9A9CD} = {1B21372A-3030-4532-8B30-2A1A3E74527A}
{37778F65-BDBF-4AEA-BA34-01026A223083} = {F7D2A6F5-841A-4E22-9ECD-2E1CF736F7EA}
{C1A8AF94-F550-4EC7-889A-9D0CCA259502} = {8BAB6184-8545-4E17-8199-86110AC5228F}
{3DFC4318-5D40-41A0-B46D-F0896180B45C} = {5976592D-2EC6-4991-89A9-7EC97EEC0FFD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BD6B0E25-71B7-4A8F-8D0F-946BA6D47C7C}
Expand Down
124 changes: 124 additions & 0 deletions PInferGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# PInfer - Automatic Invariant Learning from Traces
## Prerequisites
> Note that PInfer has only been tested on Amazon Linux.

### Installing Z3 (before building P)
1. Clone the Z3 repository: https://github.com/Z3Prover/z3 by `git clone [email protected]:Z3Prover/z3.git`
2. Run `python scripts/mk_make.py -x --dotnet`
3. `cd build; make -j$(nproc)`
4. `sudo make install`
5. Add `export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/path-to-z3/build/"` to `.bashrc` (or `.zshrc` if using Zsh) where `path-to-z3` is the directory of the cloned Z3 repository in step 1.

#### Environment setups
- Follow the instruction of [P language here](https://p-org.github.io/P/getstarted/install/) and install dotnet. The version used for developing is 8.0.401.
- Install Java 22 and maven
+ For amazon linux, [follow the guide here](https://docs.aws.amazon.com/corretto/latest/corretto-22-ug/generic-linux-install.html#rpm-linux-install-instruct)
+ Maven version: Apache Maven 3.5.2. Can be installed via `sudo yum install maven`
- Install PJavaRuntime: go to [Src/Runtimes/PJavaRuntimes](./Src/PRuntimes/PJavaRuntime/) and run `mvn install`
- Compile P: run `dotnet build -c Release` under the root of this repo.

## Step 1: Getting traces
### Using the python script for generating traces
There is a `1_prepare_traces.py` under `Tutoiral/PInfer`. To use this script to generate traces, first add an entry to `configurations` dictionary in `Tutoiral/PInfer/constants.py`.
The key should be the name of the folder under `Tutorial/PInfer` that contains the P source code of the benchmark, and the value is a list of names of test cases to run to obtain the traces.

You may optionally set an environment variable called `PINFER_TRACE_DIR` (e.g. `export PINFER_TRACE_DIR=/home/PInferTraces) that specifies the path to store the generated traces.

After making the changes, run with
```
> python3 1_prepare_traces.py --benchmarks [names of benchmarks] --num_traces [a list of numbers representing number of traces to generate] [--trace_dir <path-to-store-traces>]
```

For instance, to generate 10k traces for Paxos storing into `$PINFER_TRACE_DIR`, run the following
> `python3 1_prepare_traces.py --benchmarks paxos --num_traces 10000`

### Event filters
By default, the generated traces have all Send and Announce events being recorded.
For some benchmarks, we may want to only record events that are relevant to the protocol.
To do so, add an entry in `event_combs` dictionary in `constants.py`, where the key is the name of directory of the benchmark and the value is in the form of `[(e1, e2, e3...)]` where `e1, e2, e3...` are events that are relevant to the protocol.

**Removing traces:** If you want to remove certain traces, simply delete the files. You don't need to remove the metadata in the JSON file.

**How are trace metadata being used?** When checking `n` events of type e1, e2, ..., en, PInfer will first look for any *exact* match on the index, i.e. a folder that have traces that contains events that are *exactly* of type e1, ..., en. If no such folder is found, PInfer looks for any folder that holds traces that is a superset of (e1, ..., en).

> Alternatively, some of the codebase have `generateTrace.sh`. You can simply run `./generateTrace.sh <num schedules> <e1> <e2> ... <en>` and it will gather traces containing e1, e2,...,en using all test cases.

## Step 2: Running PInfer
For our benchmarks, simply execute `run.sh` under the benchmark folders.

To setup your own experiments:
### Fully-automated mode
Simply run `p infer`, it will infer combinations of events that might yield interesting properties and then perform the search over the lattice.

If traces are stored under some other paths, you can run it with `p infer -t <path-to-traces>`. Note that there must be a `metadata.json` under the provided path.

If traces were generated using `1_prepare_traces.py`, then under `PINFER_TRACE_DIR`, traces are stored in folders that have the same name as the benchmark. For example `PINFER_TRACE_DIR/paxos/10000` stores 10k traces for paxos. This is the path you will need to pass to PInfer using `-t` commandline argument. For example, running PInfer under `paxos` with 10k traces is

```
> p infer -t $PINFER_TRACE_DIR/paxos/10000
```

To enable SMT-based pruning, add a `-z3` flag, e.g.,
```
> p infer -t $PINFER_TRACE_DIR/paxos/10000 -z3
```

Default parameters (upper bounds):
- `term_depth`: 2
- `num_guards`: 2
- `num_filters`: 2
- `exists`: 1
- `config_event`: null

`arity` is determined by the maximum arity of prediates generated.

These can be configured using command line argumets:
> p infer -td <`max term depth`> -max-guards <`max conj in guards`> -max-filters <`max conj. in filters`> -ce <`event name`>

### Hints
Hints is a construct in P program that provides manual hints for PInfer.

Hints can be defined as follows:
```
hint <hint name> (e1: Event1 ... en: EventN) {
term_depth = <int>;
exists = <int>;
num_guards = <int>;
num_filters = <int>;
arity = <int>;
config_event = <event name>;
include_guards = <bool expr>; // predicates that must be included in the guards, e.g. e0.key == e1.key; conjuntion only.
include_filters = <bool expr>; // similar, but must be included in filters
}
```

If any of the field is not provided, the default values are:
- `exists` = 0
- `num_guards` = 0
- `num_filters` = 0
- `arity` = 1
- `config_event` = null
- `include_guards` = true
- `include_filters` = true

#### Generating SpecMiner for a hint
> p infer --action compile --hint <`name of the hint`>

PInfer will generate the SpecMiner specifically for the provided hint.

#### Searching the space defined by a hint
> p infer --action run --hint <`name of hint`> \[--traces <`folder`>\]

PInfer will search the formula in the form of `forall* G -> exists* F /\ R` that holds for all traces. PInfer starts with the provided parameters in the hint and search till the upperbound.
By default PInfer looks for traces under `traces` folder. Notice that the traces must be indexed, i.e. having a `metadata.json` mapping sets of events to folders containing corresponding traces.

#### Searching a specific grid
**exact hints:** Mainly used for debugging purposes. When declaring a hint with `exact` keyword, i.e. `hint exact ...` then running it, PInfer will only check the search space defined by the given parameters in the hint but will not go up the grid.

#### List out predicates generated
> p infer -lp
#### List out terms generated
> p infer -lt

## Step 3: Outputs
After PInfer finishes, invariants will be stored in `invariants.txt` in the folder PInfer was executed. Another file `inv_running.txt` will be updated as PInfer finds new invariants. Mined properties are pretty-printted.
235 changes: 235 additions & 0 deletions PInferTemplates/FromDaikon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import java.util.stream.Collectors;

public class FromDaikon {
public int numExists;
private Map<Set<Integer>, List<Main.RawPredicate>> termsToPredicates;
private List<Main.RawTerm> terms;
private String templateHeaderCar;
private String templateHeaderCdr;
private int pruningLevel;
private static final String[] QUANTIFIERS = { %QUANTIFIERS% };
private static final String[] VARNAME = { %QUANTIFIED_VARS% };
private static final String[] FILTERED_INVS = { "!= null", ".getClass().getName()", "[] ==" };
private static final String[] COMP_OPS = { "!=", "<=", "<", ">=", ">" };
private static final Map<String, String> substs = new HashMap<>();

public FromDaikon(Map<Set<Integer>, List<Main.RawPredicate>> termsToPredicates,
List<Main.RawTerm> terms, String templateFamily, int numExtQuantfiers, int pruningLevel) {
this.termsToPredicates = termsToPredicates;
this.terms = terms;
this.templateHeaderCar = "";
this.templateHeaderCdr = "";
this.numExists = numExtQuantfiers;
this.pruningLevel = pruningLevel;
StringBuilder sb = new StringBuilder();
switch (templateFamily) {
case "Forall":
for (int i = 0; i < QUANTIFIERS.length; ++i) {
sb.append("∀").append(VARNAME[i])
.append(": ").append(QUANTIFIERS[i]).append(i == QUANTIFIERS.length - 1 ? " :: " : " ");
}
templateHeaderCar = sb.toString();
break;
case "Exists":
for (int i = 0; i < QUANTIFIERS.length; ++i) {
sb.append("∃").append(VARNAME[i])
.append(":").append(QUANTIFIERS[i]).append(i == QUANTIFIERS.length - 1 ? " :: " : " ");
}
templateHeaderCar = sb.toString();
break;
case "ForallExists":
for (int i = 0; i < QUANTIFIERS.length - numExtQuantfiers; ++i) {
sb.append("∀").append(VARNAME[i])
.append(":").append(QUANTIFIERS[i]).append(i == QUANTIFIERS.length - numExtQuantfiers - 1 ? " :: " : " ");
}
templateHeaderCar = sb.toString();
sb = new StringBuilder();
for (int i = QUANTIFIERS.length - numExtQuantfiers; i < QUANTIFIERS.length; ++i) {
sb.append("∃").append(VARNAME[i])
.append(":").append(QUANTIFIERS[i]).append(i == QUANTIFIERS.length - 1 ? " :: " : " ");
}
templateHeaderCdr = sb.toString();
break;
default:
throw new IllegalArgumentException("Unknown template family: " + templateFamily);
}
substs.put(".toString", "");
substs.put("one of", "∈");
substs.put(".getPayload()", "");
substs.put("[] elements", "");
substs.put("(elementwise)", "");
}

public String getFormulaHeader(String guards, String filters) {
String guardsStr = runSubst(guards);
String arrow = guardsStr.length() == 0 ? " " : " -> ";
return this.templateHeaderCar + guardsStr + arrow + this.templateHeaderCdr + runSubst(filters);
}

public String convertOutput(String line, List<Main.RawPredicate> guards, List<Main.RawPredicate> filters,
List<Main.RawTerm> forallTerms, List<Main.RawTerm> existsTerms) {
if (!checkValidity(line, guards, filters, forallTerms, existsTerms)) {
return null;
}
List<Main.RawTerm> substTerms = new ArrayList<>();
for (int i = 0; i < forallTerms.size(); ++i) {
if (line.contains("f" + i)) {
substTerms.add(forallTerms.get(i));
line = line.replace("f" + i, forallTerms.get(i).shortRepr());
}
}
for (int i = 0; i < existsTerms.size(); ++i) {
String fieldPHName = "f" + (i + forallTerms.size());
if (line.contains(fieldPHName)) {
// do not check sorted by for aggregated array
if (line.contains("sorted by")) return null;
substTerms.add(existsTerms.get(i));
line = line.replace(fieldPHName, existsTerms.get(i).shortRepr());
}
}
boolean didSth = !substTerms.isEmpty();
if (!didSth && !line.contains("_num_e_exists_")) return null;
if (line.contains("_num_e_exists_")) {
// _num_e_exists_ should be on lhs
if (!line.startsWith("_num_e_exists_")) return null;
// check # exists is related not only existentially quantified events
if (didSth) {
Set<Integer> boundedEvents = substTerms.stream()
.flatMap(x -> x.events().stream())
.collect(Collectors.toSet());
boolean containsForallEvent = false;
for (int i = 0; i < QUANTIFIERS.length - numExists; ++i) {
if (boundedEvents.contains(i)) {
containsForallEvent = true;
break;
}
}
if (!containsForallEvent) return null;
}
}
return runSubst(line);
}

private String runSubst(String line) {
for (var subst : substs.entrySet()) {
line = line.replace(subst.getKey(), subst.getValue());
}
if (line.contains(" in ")) {
line = line.replace(" in ", " == ");
}
line = line.replace("[]", "");
return line;
}

private boolean isNumber(String x) {
if (x == null || x.isEmpty()) return false;
try {
Double.parseDouble(x);
} catch (NumberFormatException e) {
return false;
}
return true;
}

private boolean containsTerm(String line, int forallTermCount, int existsTermCount) {
for (int i = 0; i < forallTermCount + existsTermCount; ++i) {
if (line.contains("f" + i)) return true;
}
return false;
}

private Main.RawTerm getTermByTemplateField(String f, List<Main.RawTerm> forallTerms, List<Main.RawTerm> existsTerms) {
for (int i = 0; i < forallTerms.size(); ++i) {
if (f.contains("f" + i)) return forallTerms.get(i);
}
for (int i = 0; i < existsTerms.size(); ++i) {
if (f.contains("f" + (i + forallTerms.size()))) return existsTerms.get(i);
}
return null;
}

private Map.Entry<List<Main.RawTerm>, List<Main.RawTerm>>
getTermSubsts(String line, List<Main.RawTerm> forallTerms, List<Main.RawTerm> existsTerms) {
List<Main.RawTerm> forallSubsts = new ArrayList<>();
List<Main.RawTerm> existsSubsts = new ArrayList<>();
for (int i = 0; i < forallTerms.size(); ++i) {
if (line.contains("f" + i)) forallSubsts.add(forallTerms.get(i));
}
for (int i = 0; i < existsTerms.size(); ++i) {
int j = i + forallTerms.size();
if (line.contains("f" + j)) existsSubsts.add(existsTerms.get(i));
}
return new AbstractMap.SimpleEntry<>(forallSubsts, existsSubsts);
}

private boolean checkValidity(String line, List<Main.RawPredicate> guards,
List<Main.RawPredicate> filters,
List<Main.RawTerm> forallTerms,
List<Main.RawTerm> existsTerms) {
if (pruningLevel == 0) {
return true;
}
if (pruningLevel >= 1) {
// prune nullptr comparisons
// and type name comparisons
for (var stub : FILTERED_INVS) {
if (line.contains(stub)) return false;
}
}
if (pruningLevel >= 2) {
// prune if the set of terms of the mined property
// equals to some selected atomic predicates
Set<Integer> minedPropertyBoundedTerms = new HashSet<>();
for (int i = 0; i < forallTerms.size(); ++i) {
if (line.contains("f" + i)) {
minedPropertyBoundedTerms.add(forallTerms.get(i).order());
}
}
for (int i = 0; i < existsTerms.size(); ++i) {
if (line.contains("f" + (i + forallTerms.size()))) {
minedPropertyBoundedTerms.add(existsTerms.get(i).order());
}
}
for (Main.RawPredicate p: guards) {
if (p.terms().equals(minedPropertyBoundedTerms)) return false;
}
for (Main.RawPredicate p: filters) {
if (p.terms().equals(minedPropertyBoundedTerms)) return false;
}
}
if (pruningLevel >= 3) {
// exclude comparisons with constants
for (String op: COMP_OPS) {
if (line.contains(op)) {
String[] args = line.split(op);
if (args.length < 2) continue;
String lhs = args[0].trim();
String rhs = args[1].trim();
if (op.equals("!=")) {
var lhsTerm = getTermByTemplateField(lhs, forallTerms, existsTerms);
var rhsTerm = getTermByTemplateField(rhs, forallTerms, existsTerms);
if (lhsTerm != null && rhsTerm != null) {
if (lhsTerm.shortRepr().endsWith(".index()")
&& rhsTerm.shortRepr().endsWith(".index()")) return false;
}
}
boolean rhsIsConst = isNumber(rhs) || (rhs.startsWith("\"") && rhs.endsWith("\""));
if (!containsTerm(lhs, forallTerms.size(), existsTerms.size())) {
if (!rhsIsConst && !rhs.startsWith("size") && containsTerm(rhs, forallTerms.size(), existsTerms.size())) {
// if a comparison does not involve a term on lhs,
// it should not involve a term on rhs either (avoid `_num_e_exists_ > [some term]`)
// however, we do want to see size([some term]) / config constants on rhs.
return false;
}
break;
}
// if (rhsIsConst) {
// return false;
// }
break;
}
}
}
return true;
}
}
Loading
Loading