There is a newer version of the record available.

Published February 20, 2025 | Version v1

dimagic2

  • 1. ROR icon Saarland University
  • 2. ROR icon TU Dresden
  • 3. ROR icon Eindhoven University of Technology

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 provides
  • docker-image.tar.zst: a Docker image with all the relevant tools installed, also including the input CNFs of the benchmark suite
  • input-cnfs.tar.zst: a benchmark suite containing
  • preprocessed.tar.zst: the input CNFs after preprocessing using Dimagic
  • compilation-logs.tar.zst: log files produced by the BDD compilers OxiDD (paper) and Logic2BDD (paper) when compiling the preprocessed (X)CNFs to BDDs
  • results.zip: tables and figures of the experiment results
  • compiler-bin.tar.zst: binaries of oxidd-cliLogic2BDD, 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.zstpreprocessed.tar.zstcompilation-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:

  • --rm means that the container (i.e., all the changes made on top of the image) will be removed after exiting the container
  • -i and -t are 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:z mounts the path ./data into the Docker container at /root/data. The directory data on your host system will be created if not existent. z is 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 --memory disables 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 (LinuxmacOS) 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 PMCo for ONE-HOT recovery, and x for XOR recovery. The ordering heuristics may be either of

  • r for random,
  • f for FORCE,
  • m for MINCE, or
  • rm for 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 / E1
  • remince.toml: RQ1b / E2
  • ordering.toml: RQ1 / E3
  • multithreading.toml: RQ1d / E4
  • size.toml: RQ1d / E4
  • cdl.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 via command -v systemd-run)
  • Python 3.11 or newer and the Python package psutil (can be installed via sudo apt install python3-psutil on Debian and Ubuntu; we used psutil version 5.9.4)
  • src.zip and compiler-bin.tar.zst extracted in the current working directory
  • The data/preprocessed directory from either preprocessed.tar.zst or 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 CNFsRunning 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)

Funding

Deutsche Forschungsgemeinschaft
Transregional Collaborative Research Centre 248 “Foundations of Perspicuous Software Systems” 389792660
Deutsche Forschungsgemeinschaft
Centre for Tactile Internet with Human-in-the-Loop (CeTI) 390696704
Dutch Research Council
VI.Veni.222.431

Software

Programming language
Rust , Python