dimagic2
Authors/Creators
Description
A preprocessor for CNF-to-BDD compilation featuring:
- Backbone identification, occurrence reduction, and vivification via the external PMC tool (see the research paper)
- ONE-HOT and XOR recovery
- Multiple variable and clause ordering heuristics, including a re-implementation of MINCE (paper) using the hypergraph cutter KaHyPar (paper)
Artifact Contents
This artifact consists of multiple components split into different archive files:
src.zip: the source code of the reusable Dimagic tool, along with scripts to evaluate the various options Dimagic providesdocker-image.tar.zst: a Docker image with all the relevant tools installed, also including the input CNFs of the benchmark suiteinput-cnfs.tar.zst: a benchmark suite containing- 49 models from the test suite of the UnWise feature model sampler (paper)
- 116 CDL feature models (paper), already converted to DIMACS CNF using FeatureIDE (paper)
preprocessed.tar.zst: the input CNFs after preprocessing using Dimagiccompilation-logs.tar.zst: log files produced by the BDD compilers OxiDD (paper) and Logic2BDD (paper) when compiling the preprocessed (X)CNFs to BDDsresults.zip: tables and figures of the experiment resultscompiler-bin.tar.zst: binaries ofoxidd-cli,Logic2BDD, and Logic2BDD’s version of CUDD from the Docker image (for running the benchmarks outside the container)
.zip files can be unpacked with unzip <archive.zip>, .tar.zst files can be unpacked using tar --zstd -xvf <archive.tar.zst>. When unpacking all archives except docker-image.tar.zst, your directory tree should look like this:
. ├── bench-gen source code of dimagic-bench-gen, the tool used to preprocess all CNFs (part │ └── ... of src.zip) ├── bin (compiler-bin.tar.zst) │ ├── Logic2BDD │ └── oxidd-cli ├── data │ ├── compilation-logs (compilation-logs.tar.zst) │ │ ├── cdl │ │ │ └── <seed> │ │ │ └── <model> │ │ │ └── <model>-<seed>-<preprocessing>-<var order>-<clause order>-<engine>-t<threads>.log │ │ └── unwise │ │ └── ... │ ├── preprocessed (preprocessed.tar.zst) │ │ ├── cdl │ │ │ └── <seed> │ │ │ └── <model> │ │ │ └── <model>-<seed>-<preprocessing>-<var order>-<clause order>.dimacs │ │ └── unwise │ │ └── ... │ └── results (results.zip) │ ├── plots │ │ └── ... │ └── ... ├── dimagic source code of dimagic (src.zip) │ └── ... ├── eval evaluation scripts (src.zip) │ └── ... ├── experiments experiment definitions used by run-all.sh/run.py (src.zip) │ └── ... ├── input-cnfs benchmark suite (input-cnfs.tar.zst) │ ├── cdl │ │ └── <model>.dimacs │ └── unwise │ └── <model>.dimacs ├── kahypar source code of our Rust bindings for KaHyPar (src.zip) │ └── ... ├── kahypar.ini KaHyPar preset (src.zip) ├── lib (compiler-bin.tar.zst) │ ├── libcudd-3.0.0.so.0 │ └── libcudd-3.0.0.so.0.0.0 ├── mt-kahypar source code of our Rust bindings for Mt-KaHyPar (src.zip) │ └── ... ├── patches (src.zip) │ ├── logic2bdd patches for Logic2BDD │ │ ├── 0001-negated-one-hot.patch │ │ └── 0002-sat-count.patch │ └── sharpsat-td patch for SharpSAT-TD to allow running it from any location │ └── 0001-flow-cutter-from-path.patch ├── pp-all.sh script to preprocess all CNFs for our experiments (src.zip) ├── run-all.sh script to run the BDD compilation experiments (src.zip) ├── run.py runner script used by run-all.sh (src.zip) └── ... (src.zip)
Depending on your use case, you will only want to run a selection of the steps listed below. Especially the larger components (docker-image.tar.zst, preprocessed.tar.zst, compilation-logs.tar.zst) are needed for certain steps only. We indicate the respective prerequisites below.
(Re-)using Dimagic
Requires: dimagic (either in the Docker container or installed from source, see below)
Dimagic generally takes a DIMACS (X)CNF as input, preprocesses it, applies ordering heuristics, and outputs it either as (X)CNF, as circuit (for use with oxidd-cli), or in Logic2BDD’s input format. For instance, you can run:
RUST_LOG=info dimagic --pmc --recover-one-hots --var-heuristic=remince --var-kahypar-preset=kahypar.ini --clause-heuristic=remince --clause-kahypar-preset=kahypar.ini input-cnfs/unwise/automotive2_4.dimacs automotive2_4.nnf
Here, input-cnfs/unwise/automotive2_4.dimacs is the input file and automotive2_4.nnf is the output. Dimagic will run PMC and recover ONE-HOT clauses, then it will determine variable and clause orders using ReMINCE with the KaHyPar preset from the kahypar.ini file (in the current working directory). Our evaluation indicates that this set of command line options yields very good results. Setting the RUST_LOG environment variable to info is optional and will make Dimagic print some statistics.
The input DIMACS CNF file used in the command above as well as kahypar.ini are included in the Docker container (paths relative to the home directory /root) as well as in input-cnfs.tar.zst or src.zip, respectively. The command above takes roughly half a minute to execute on a recent laptop.
You can use oxidd-cli to compile the circuit in the NNF file into a BDD using the command below (which should take less than a second). oxidd-cli is included in the Docker container. With a recent Rust installation, you can also install oxidd-cli on your host system via cargo install oxidd-cli@0.3.0.
oxidd-cli --read-var-order --count-models automotive2_4.nnf
Dimagic has a quite a few other command line options. To list them all, run:
dimagic --help
Reproducing the Experiments
For longer running tasks we give rough time estimates based on our benchmarking system, an 16-core AMD Ryzen 9 5950X CPU with 128 GiB RAM running Ubuntu 24.04 (Linux 6.8).
Loading & Running the Docker Image
Requires: docker-image.tar.zst, an x86_64 system with Docker or Podman installed (for use with Podman, replace docker by podman in the commands below)
To load the Docker image, run:
docker load -i docker-image.tar.zst
Create a folder data in the current working directory if you have not done so. Then, run the Docker container as follows:
docker run --rm -it --name=dimagic -v ./data:/root/data:z --memory=7g --memory-swap=7g dimagic:2.0-jss
Note that Docker may require root privileges, so you may need to prepend sudo to all docker commands. The options of docker run command have the following effects:
--rmmeans that the container (i.e., all the changes made on top of the image) will be removed after exiting the container-iand-tare relevant for interactive access--name=...assigns a name to the container. This may be useful if you want to access the container via an extra shell.-v ./data:/root/data:zmounts the path./datainto the Docker container at/root/data. The directorydataon your host system will be created if not existent.zis relevant on some systems with mandatory access control (e.g., SELinux).--memory=...limits the RAM available for the container. If you want to run the benchmarks from within the Docker container, you can use this flag to enforce the memory limit of 7 GiB we used in the benchmarks.--memory-swap=...limits the amount of memory including swap space. Setting this to the same value as--memorydisables the use of swap space.
Installing Dimagic from Source
Requires: src.zip, a recent Rust installation (1.84 and higher should work), CMake 3.26 or newer, a C/C++ compiler supporting C++20
Note that we tested this on an x86_64 Linux system only. macOS should work similarly (but there is no support for the original MINCE tool), Windows is unsupported (there is no PMC build available).
First, build and install KaHyPar as described on GitHub (currently, that is steps 1-3 in the section “Building KaHyPar” plus the install command for using the C-style interface). Additionally or alternatively, you can also use Mt-KaHyPar as the hypergraph cutter in ReMINCE. However, preliminary experiments indicated that KaHyPar yields better results than Mt-KaHyPar. (Note that when trying to run dimagic on macOS, it might be necessary to set DYLD_LIBRARY_PATH=/usr/local/lib.)
With src.zip unpacked in the current working directory, run:
cargo install --path dimagic
If you want to enable support for Mt-KaHyPar, append --features mt_kahypar to the command line. If you do not want KaHyPar support, you can use --no-default-features.
When running Dimagic with the --pmc option, it will look for the pmc executable in your path. Download pmc (Linux, macOS) and either move it to some location in your path (run echo $PATH to list the respective directories) or place it in some directory and add that directory to your path via export PATH="/path/to/your/pmc/dir:$PATH". Note that the latter is not persistent; for every new shell you will need to run export PATH="/path/to/your/pmc/dir:$PATH" again. Also note that the downloaded file will probably be called pmc_linux or pmc_mac. You must rename it to pmc. Make the binary executable by running chmod +x /path/to/pmc.
Similarly, Dimagic will look for an executable called MetaPlacerTest0.exeS in your path when using the option --var-heuristic=mince or --clause-heuristic=mince. This executable is included in the tar file of the MINCE tool.
To ease the preprocessing with multiple different options as we used them in our evaluation, we implemented another executable called dimagic-bench-gen. You can install it via
cargo install --path dimagic-bench-gen
Preprocessing All Input CNFs
Requires: the Docker container (alternatively dimagic-bench-gen—see Installing Dimagic from Source—, Python 3.11+, and GNU parallel installed as well as src.zip and input-cnfs.tar.zst extracted in the current working directory)
The following command preprocesses the input CNFs with various different options as described in the paper:
./pp-all.sh
The resulting (X)CNFs are placed in data/preprocessed and should be equivalent to those in preprocessed.tar.zst. The generation takes roughly 80 min with 32 processes running in parallel within the Docker container on our benchmarking system. The generated (X)CNF files use the following naming scheme:
<model>-<seed>-<preprocessing>-<variable order>-<clause order>.dimacs
<preprocessing> may contain any combination of the three flags p for PMC, o for ONE-HOT recovery, and x for XOR recovery. The ordering heuristics may be either of
rfor random,ffor FORCE,mfor MINCE, orrmfor ReMINCE, our re-implementation of the MINCE heuristic.
In <variable order>, we also encode the weights for the different clause kinds. E.g., f+1+2+4 denotes the FORCE heuristic with weight 1 for OR clauses, weight 2 for ONE-HOT clauses, and weight 4 for XOR clauses. For ReMINCE, we also encode the imbalance parameter ε, e.g., rm+1+1+1+0.1 stands for clauses weight 1 for all clause kinds and ε = 0.1. <clause order> contains the ordering heuristic, only followed by the ε value in case of ReMINCE.
dimagic-bench-gen (which is used by pp-all.sh) will create symbolic links if certain preprocessing techniques are ineffective. For instance, you will find that all files with pox (i.e., PMC preprocessing as well as ONE-HOT and XOR recovery) are symbolic links to the respective file without x: All XORs in the CNFs of our benchmark set are ONE-HOTs as well. Also, dimagic-bench-gen will create a link from a file with clause weights 1+2+4 to 1+1+1 if the given CNF does not contain any ONE-HOT or XOR clauses. However, dimagic-bench-gen will not create links if two different ordering heuristics lead to the same ordering.
Additionally, dimagic-bench-gen generally avoids to recompute comparatively expensive preprocessing steps. If the output file for some step is already present, it will not overwrite that file.
Besides (X)CNF files, the directory data/preprocessed will also contain logs as well as CSV files showing the running times of the various preprocessing steps. To concatenate the CSV files into one large table, run:
eval/cat_pp_times.py
The pp.csv table is also included in the results.zip file. We do not include the logs and the small CSV files in preprocessed.tar.zst because these do not provide any additional value.
Running the Benchmarks
There are two ways to benchmark the compilation of preprocessed (X)CNF files into BDDs, either inside or outside the Docker container. Running the benchmarks from within the container has one major drawback compared to running them natively on a Linux host: Parallel execution without interferences between processes requires some way to limit their resources. The run.py script can achieve this using systemd-run, which in turn uses Linux’ CGroups, but systemd-run cannot simply be installed in the Docker container. Thus, we do not support parallel benchmark execution in the Docker container, meaning that the execution of all benchmarks will take multiple weeks in total. In the native setup on our benchmarking system, the benchmarks take about a week to complete. Still, the Docker container might be a bit more portable.
In both cases, you will run the run-all.sh script, which writes the outputs of oxidd-cli or Logic2BDD, respectively, to the data/compilation-logs directory. run-all.sh is just a simple wrapper around the run.py script, which runs the experiments defined in the experiments directory (included in both the /root directory of the Docker container and src.zip). The experiment files correspond to the following research questions / experiments in the paper:
preprocessing.toml: RQ1 / E1remince.toml: RQ1b / E2ordering.toml: RQ1 / E3multithreading.toml: RQ1d / E4size.toml: RQ1d / E4cdl.toml: RQ2
You can interrupt the experiments at any point in time (using CTRL-C). When restarting it later, the script will automatically continue where it stopped (re-running the aborted BDD compilations).
The timings as well as node and SAT counts can be extracted from the logs using the script eval/logs2csv.py. This will create two files, data/results/benchmarks.csv and data/results/benchmarks-links-resolved.csv. The run.py script will not execute the BDD compilation for input files that are symbolic links, instead it will run the compilation of the original file exactly once. The data/results/benchmarks.csv file only contains data for the actual compilations whereas data/results/benchmarks-links-resolved.csv also contains the entries for symbolic links.
As a preparation step, you can tell a Linux system to always use transparent hugepages (THPs), by running the following command on your host system:
echo always | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
For many Linux distributions, the default value is madvise, which means that programs explicitly need to call the madvise function to enable THPs for a memory region. By using always, the number of TLB cache misses can be reduced significantly, leading to lower BDD compilation times. The value persists only until the next reboot of your host system.
Inside the Docker Container
Requires: the Docker container as well as the data/preprocessed directory from either preprocessed.tar.zst or the Preprocessing all Input CNFs step
Just run:
./run-all.sh
eval/logs2csv.py
Natively on a Linux System
Requires:
- A sufficiently recent x86_64 Linux system (e.g., Debian 12, Ubuntu 24.04, or newer). Different processor architectures might work as well, but you would need to build the BDD compilers on your own (i.e., you cannot use the pre-built ones from
compiler-bin.tar.zst). systemd-run(should typically be installed already, check viacommand -v systemd-run)- Python 3.11 or newer and the Python package
psutil(can be installed viasudo apt install python3-psutilon Debian and Ubuntu; we usedpsutilversion 5.9.4) src.zipandcompiler-bin.tar.zstextracted in the current working directory- The
data/preprocesseddirectory from eitherpreprocessed.tar.zstor the Preprocessing all Input CNFs step
Now run:
PATH="$(pwd)/bin" LD_LIBRARY_PATH="$(pwd)/lib" ./run-all.sh
eval/logs2csv.py
SAT Counting
Requires: the Docker container
To check for preprocessing or BDD compilation errors, we computed the counts of satisfying assignments using the #SAT solver SharpSAT-TD. The following commands will run SharpSAT-TD on all input CNFs in parallel and aggregate the results in the CSV file data/results/sat-counts.csv:
parallel --timeout 600 --results sat-counts sharpSAT -tmpdir /tmp -decot 5 -decow 100 -cs 8192 ::: input-cnfs/*/*.dimacs
mkdir -p data/results
(cd sat-counts/1 && grep -r "^c s exact arb int" .) | sed 's|\\_|/|g;s|^./input-cnfs/||;s|\.dimacs/stdout:c s exact arb int |,|' > data/results/sat-counts.csv
The timeout of 10 min per process is sufficient for all models except unwise/linux-2.6.33.3.dimacs. However, we were not able to compile a BDD for that model in any configuration either. Overall, executing the commands does not take much longer than 10 min on our benchmarking system since the huge models can be processed in parallel.
Evaluation
Requires: a system with Python 3.11+ as well as the Python packages pandas (tested with 1.5.3), scipy (1.10.1), and matplotlib (3.6.3) installed, e.g., the Docker container, additionally results.zip extracted as data/results or the respective CSV files in data/results from the steps Preprocessing All Input CNFs, Running the Benchmarks, and SAT Counting
To check that the SAT counts computed by the BDD compilers match the SAT counts of the #SAT solver, run:
eval/check_sat_counts.py
If all SAT counts match (as expected), the script will just print “success.” We remark that some BDD compilations were successful but the BDD compiler was not able to compute the SAT count with the given resources.
The script eval/preprocessing_times.py prints some statistics about preprocessing times.
The following script creates the tables data/results/remince-*.csv, which contain the construction times and node counts, either separately for the five seeds, or aggregated (min, max, mean, median):
eval/remince.py
The following script creates the plots in Figure 3 in the paper as well as the LaTeX source for Table 1:
eval/ordering.py
The plots will be placed in data/results/plots. By prepending PLOT_FORMAT=<pdf|pgf|...> to the command you may change the output format of the plots (default is pdf). Setting PLOT_PAPER=1 removes the title and legends and adjusts the figure size.
To create the plot from Figure 4 (and similar ones for other models), run:
eval/size_profile.py
The output files will be placed in data/results/plots/size-profiles. The PLOT_FORMAT and PLOT_PAPER environment variables can be used here as well.
The following script creates the plot of Figure 5 in the paper:
eval/cdl.py
The plot will be saved to data/results/plots/cdl-min.<PLOT_FORMAT>.
License
The contents of src.zip (as well as the source repository) are MIT/Apache-2.0 dual-licensed, see LICENSE-MIT and LICENSE-APACHE. Third-party software included in docker-image.tar.zst is provided under its original licenses. The CNFs in input-cnfs.tar.zst and preprocessed.tar.zst come under their original licenses, see https://zenodo.org/records/10303558 and https://github.com/AlexanderKnueppel/is-there-a-mismatch. Data in compilation-logs.tar.zst and results.zip is provided under CC-BY-4.0. All licenses permit the evaluation of this artifact.
Acknowledgements
This work is partially supported by the German Research Foundation (DFG) under the projects TRR 248 (see https://perspicuous-computing.science, project ID 389792660) and EXC 2050/1 (CeTI, project ID 390696704, as part of Germany’s Excellence Strategy).
Files
src.zip
Files
(533.7 MB)
| Name | Size | |
|---|---|---|
|
md5:df6b17e6dc2115aee301ed44ae2e5243
|
78.1 MB | Download |
|
md5:b33077790c14bacf39ec99b4116f11db
|
5.1 MB | Download |
|
md5:dbce93706ca7e73bafbdb6714e87f67b
|
265.5 MB | Download |
|
md5:c5517f62baa947d1ca826c567779f27d
|
3.6 MB | Download |
|
md5:1e6d4265b6919a8b831ad3bcaf7d7c89
|
177.1 MB | Download |
|
md5:0358870c5525d11cd473c43b87cf8c71
|
4.3 MB | Preview Download |
|
md5:c504056eb07b5c19ac3bb9dbe5dcd6f4
|
105.1 kB | Preview Download |
Additional details
Related works
- Is new version of
- Software: 10.5281/zenodo.12707100 (DOI)