Releases: SSoelvsten/adiar
v2.0.0-beta.3
New Features
adiar_set_domain(...)
Overloaded to allow creating the domain 0, 1, ..., n-1 when given the number of variables n.
Optimisations
access_mode
enum
If set to AUTO or to RA thenbdd_apply
,zdd_binop
, and their derivatives will use random access on one of the inputs (if possible). This circumvents using an entire priority queue, thereby improving performance on average by 7%. This is especially beneficial when applying an operator to a very large decision diagram together with a narrow one.
Breaking Changes
- The semantics of the
zdd_ithvar
function has been changed to be more akin to the BDD semantics. That is,zdd_ithvar(i)
is the set of all bitvectors where i is true. Symmetrically, thezdd_nithvar(i)
function has been added. The original semantics ofzdd_ithvar
is still provided with thezdd_singleton
function.
Contributors
- Casper Moldrup Rysgaard ( @Crowton )
- Steffan Sølvsten ( @SSoelvsten )
v2.0.0-beta.2
Following up on the prior major rewrite and updating the interface for nested sweeping.
New Features
- Added new overloads for
bdd_exists
,bdd_forall
andzdd_project
using [1] (pure) label predicates, [2] (stateful) label generator functions, and [3] iterators. For example,bdd_exists
is now overloaded with the following alternativesbdd_exists(f, pred)
: quantify all variables i in f where pred(i) evaluates to true.bdd_exists(f, gen)
: quantify all variables i generated by gen(). Here it is important that gen() provides the variables in descending order.bdd_exists(f, begin, end)
: quantify all variables i from begin to end (assuming these are sorted in descending order).
Bug Fixes
- The result of
adiar_stats()
is now fixed such that the values for reduce and substitute are correct. - Many small fixes to make Adiar compile with GCC, Clang, and MSVC on Linux, Mac, and Windows. Simultaneously, we have now set up continuous integration, such that we can truly ensure we support all platforms.
Breaking Changes
- Turned
assignment
into a class similar to the changes with v2.0.0-beta.1 - Generalized
assignment
into avar_map
class with an enum for its second value. This is used for inheritance to the (new)evaluation
class used for outputs ofbdd_satmin
andbdd_satmax
. This will probably again be broken later, as their default output is turned into abdd
.
Contributors
- Steffan Sølvsten ( @SSoelvsten )
Also thanks to Ayoub Aboutarbouch ( @itsmeYO92 ), Maxim Smolskiy ( @MaximSmolskiy ), and Sai Rugveth Vankayala ( @rugveth1210 ) for their small fixes and clean-ups.
v2.0.0-beta.1
Major rewrite of the internal logic of Adiar to pay off technical debt and to get Adiar ready for implementing the I/O-efficient version of the much more complex BDD operations (e.g. multi-variable quantification).
At the same time, this also brings Adiar much closer to support other types of decision diagrams (e.g. QMDDs) and to store BDDs on disk and loading them again later.
Breaking Changes
- Fundamental data types in <adiar/data.h> have been moved to each their own file (most of which are placed in the <adiar/internal/data_types/...>).
- The fundamental data types have been converted from C-like structs with manipulating functions to C++ (immutable) classes. That is, the entire interface for node, arc etc. have completely changed. Most of these functions have been deprecated to make transitioning easier.
- Files, streams and their writers have also been moved to <adiar/internal/...> leaving <adiar/file.h> only to expose the public parts of the API.
- Files have been rewritten and renamed.
- If you have been using a label_file or an assignment_file you should now use the shared_file<T> template instead. To read from it use the file_stream<T> and to write use the file_writer<T>.
- The node_file class is replaced with the adiar::internal::shared_levelized_file<node>. You can read from it with the
adiar::internal::node_stream and write to it with the adiar::internal::node_writer as before. But, we highly encourage you to transition over to using the adiar::bdd_builder and adiar::zdd_builder instead.
- All internal logic in <adiar/internal/...> has been moved into its nested namespace
adiar::internal
. If you use anything from this namespace (e.g. the node and the node_writer classes) then you are not guaranteed non-breaking changes.
That is, there are only breaking changes if you have been interacting with Adiar's files directly, e.g. you have used the bdd_restrict
, bdd_exists
, bdd_forall
functions or have created BDDs by hand with the node_writer.
Contributors
- Steffan Sølvsten ( @SSoelvsten )
v1.2.2
Bug Fixes
-
zdd_project(A, dom) An accidental swapping of arguments for a helper function resulted in the generated recursion requests are for the wrong elements and hence the wrong ZDD was constructed.
-
Fixes C++ and CMake such that Adiar compiles and runs on Mac computers with default Clang.
Contributors
- Steffan Sølvsten ( @SSoelvsten )
v1.2.1
Bug Fixes
adiar_printstats()
Makes sure to print levelized priority queue statistics, when only the unbucketed priority queue has been used.
Deprecations
- adiar/bdd.h
output_dot(bdd f, std::string file_name)
->bdd_printdot(bdd f, std::string file_name)
- adiar/zdd.h
output_dot(zdd A, std::string file_name)
->zdd_printdot(zdd A, std::string file_name)
Binary Decision Diagrams
bdd_satcount(bdd f)
If the global domain is set, then that value will take precedence over the number of levels in f (assuming f has fewer levels than the domain claims to exist).bdd_printdot(bdd f, std::ostream out)
Added to allow more flexibility when outputting DOT files.
Zero-suppressed Decision Diagrams
zdd_printdot(zdd A, std::ostream out)
Added to allow more flexibility when outputting DOT files.
Documentation
Instead of separate Markdown files, the documentation is generated directly from the C++ codebase with Doxygen. You can generate the documentation (assuming Doxygen is intalled) with the docs
Makefile target.
Contributors
- Steffan Sølvsten ( @SSoelvsten )
License
Adiar uses the TPIE library which is licensed under LGPL v3, and so by extension any binary file with Adiar is covered by the very same license.
1.2.0
Drastically improves performance for small instances by using purely internal memory data structures.
Performance
Below is the running time for the N-Queens for v1.0, v1.1, and v1.2. As is evident, it is an improvement all across the board. Furthermore, the change in v1.1 that improved running time for large instances but severely crippled the small ones has been mitigated.
N | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|---|---|---|---|---|
v1.0 (s) | 7.4s | 8.2s | 7.8s | 9.4s | 28.5s | 28.5s | 119s | 660s | 3944s | 31017s |
v1.1 (s) | 19.3s | 25.6s | 32.2s | 39.5s | 48.7s | 58.4s | 751s | 1296s | 4270s | 24544s |
v1.2 (s) | 0.1s | 0.2s | 0.4s | 0.8s | 3.1s | 14.8s | 82.7s | 502s | 3337s | 23127s |
Domain
The global domain over which one works can now be set as a label_file in a global variable.
adiar_set_domain(label_file dom)
Set the global domain variable.adiar_has_domain()
Whether a global domain already is set.adiar_get_domain()
Obtain the current label_file that acts as the global domain (assumingadiar_has_domain()
evaluates totrue
).
Binary Decision Diagrams
New Features
bdd_builder
A new class to replace using the node_writer directly. This allows you to much more naturally construct BDDs bottom-up by hiding away several details. Furthermore, it makes use of exceptions rather than immediately terminating assertions.bdd_from(zdd A)
Converts from a ZDD to a BDD using the global domain.bdd_equal(bdd f, bdd g)
Alternative to using the==
operator.bdd_unequal(bdd f, bdd g)
Alternative to using the!=
operator.bdd_varprofile(bdd f)
Obtain a label_file containing all of the variables present in a BDD.
Bug Fixes
bdd_nithvar(label_t i)
andbdd_ithvar(label_t i)
Is now marked as canonical and so can be used with the linear-scan equality checking.- Fixed the reduction phase may use 2 MiB more memory than is available.
Zero-suppressed Decision Diagrams
New Features
zdd_builder
A new class to replace using the node_writer directly. This allows you to much more naturally construct ZDDs bottom-up by hiding away several details. Furthermore, it makes use of exceptions rather than immediately terminating assertions.zdd_complement(zdd A)
Complementation within the global domain.zdd_from(bdd f)
Converts from a BDD to a ZDD using the global domain.zdd_varprofile(zdd A)
Obtain a label_file containing all of the variables present in a ZDD.
Bug Fixes
zdd_ithvar(label_t i)
Is now marked as canonical and so can be used with the linear-scan equality checking.- Fixed the reduction phase may use 2 MiB more memory than is available.
- DOT files are now properly produced with the terminals printed as Ø and {Ø}.
Statistics
New Features
- Statistics have been heavily extended with information on how often each type of auxiliary data structures (internal or external) have been used.
- All statistics variables are now fixed-precision numbers (using the CNL library) making sure there are no overflows in the provided numbers.
adiar_statsreset()
Reset all statistics values back to 0.
Bug Fixes
- Fixed fine grained statistics (ADIAR_STATS_EXTRA) are turned on when only coarse-grained statistics (ADIAR_STATS) was desired.
Deprecations
The word sink has been replaced with the word terminal that is more commonly used in the context of decision diagrams.
- adiar/data.h
create_sink_uid(bool val)
->create_terminal_uid(bool val)
create_sink_ptr(bool val)
->create_terminal_ptr(bool val)
create_sink(bool val)
->create_terminal(bool val)
- adiar/bdd.h
is_sink(bdd f)
->is_terminal(bdd f)
bdd_sink(bool val)
->bdd_terminal(bool val)
- adiar/zdd.h
is_sink(zdd A)
->is_terminal(zdd A)
zdd_sink(bool val)
->zdd_terminal(bool val)
Breaking Changes
The terminal predicates is_any
, is_true
and is_false
with the prior is_sink(zdd A, pred)
functions were too complicated. The above performance improvements allows us for a much simpler (and faster) implementation. Deprecation was not possible due to name conflicts with their replacements below. Considering the niche usage of this, it does not seem to warrant an increment in version number.
- adiar/data.h
is_sink(ptr_t p)
,is_true(ptr_t p)
, andis_false(ptr_t p)
.
- adiar/bdd.h
is_sink(bdd f)
,is_true(bdd f)
andis_false(bdd f)
.
- adiar/zdd.h
is_sink(zdd A)
,is_null(zdd A)
andis_empty(zdd A)
.
Contributors
- Steffan Sølvsten ( @SSoelvsten )
- Anna Blume Jakobsen ( @AnnaBlume99 )
Thanks for the support and advice from Jaco van de Pol ( @jacopol ) . Also, thanks to Erik Funder Carstensen ( @halvko ) for fixing a few compiler warning in statistics.
License
Adiar uses the TPIE library which is licensed under LGPL v3, and so by extension any binary file with Adiar is covered by the very same license.
1.1.0
Major refactor and rewrite of the codebase to also support zero-suppressed decision diagrams!
Binary Decision Diagrams
bdd_eval(bdd f, assignment_func x)
Alternative to using an assignment_file you can provide any function that given the variable label outputs the boolean value assigned to it.
Zero-suppressed Decision Diagrams
Adds support for zero-suppressed decision diagrams with the zdd
class. All operations on ZDDs are based on the family of sets semantics as in the original paper by Minato.
Constructors
zdd zdd_sink(bool v)
andzdd_empty()
andzdd_null()
as alternativeszdd zdd_ithvar(label_t i)
zdd zdd_vars(label_file vars)
to construct an and chain.zdd zdd_singletons(label_file vars)
to construct an *or chain.zdd zdd_powerset(label_file vars)
to construct a don't care chain.zdd zdd_sized_set<pred_t>(label_file vars, k, pred)
to construct the sets of variables in vars whose size satisfies the given predicate in relation to k.
Basic Manipulation
zdd zdd_binop(zdd A, zdd B, bool_op op)
to apply a binary operator to two families of setszdd zdd_change(zdd A, label_file vars)
to compute the symmetric difference.zdd zdd_complement(zdd A, label_file dom)
to construct the complement.zdd zdd_expand(zdd A, label_file vars)
to expand the domain with new variables.zdd zdd_offset(zdd A, label_file vars)
to compute the subset without the given variables.zdd zdd_onset(zdd A, label_file vars)
to compute the subset with the given variables.zdd zdd_project(zdd A, label_file is)
to project onto a (smaller) domain.
Counting Operations
uint64_t zdd_nodecount(zdd A)
to obtain the number of (non-leaf) nodes.uint64_t zdd_varcount(zdd A)
to obtain the number of levels present (i.e. variables).uint64_t bdd_size(bdd A)
to compute the number of elements in the family of sets.
Predicates
bool zdd_equal(zdd A, zdd B)
to check for set equality.bool zdd_unequal(zdd A, zdd B)
to check set inequality.bool zdd_subseteq(zdd A, zdd B)
to check for weak subset inclusion.bool zdd_subset(zdd A, zdd B)
to check for strict subset inclusion.bool zdd_disjoint(zdd A, zdd B)
to check for the sets being disjoint.
Set Elements
bool zdd_contains(zdd A, label_file a)
to compute whether the set of variables a is in Astd::optional<label_file> zdd_minelem(zdd A)
to obtain the a in A that is lexicographically smallest.std::optional<label_file> zdd_maxelem(zdd A)
to obtain the a in A that is lexicographically largest.
Other Functions
output_dot(bdd f, std::string filename)
to output a visualizable _.dot file.zdd zdd_from(bdd f, label_file dom)
andbdd bdd_from(zdd f, label_file dom)
to convert between BDDs and ZDDs interpreted in the given domain.
Statistics
Compile Adiar with ADIAR_STATS
or ADIAR_STATS_EXTRA
to gather statistics about the execution of the internal algorithms. With ADIAR_STATS
you only gather statistics that introduce a small constant time overhead to every operation. ADIAR_STATS_EXTRA
also gathers much more detailed information, such as the bucket-hits of the levelized priority queue, which does introduce a linear-time overhead to every operation.
stats_t adiar_stats()
to obtain a copy of the raw data values.void adiar_printstat(std::ostream)
to print all statistics to an output stream.
Performance
We have (yet) not done a proper comparison concerning changes in performance. But, our initial tests throughout development hint at a 4% performance decrease where we can regain about 2% of it with some extra templating.
Contributors
- Steffan Sølvsten ( @SSoelvsten )
Thanks for the support and advice from Jaco van de Pol ( @jacopol ) . Also, thanks to Jakob Schneider Villumsen ( @jaschdoc ) for his additions to the setup of the git repository.
License
Adiar uses the TPIE library which is licensed under LGPL v3, and so by extension any binary file with Adiar is covered by the very same license.
1.0.1
Bug fixes
- 2ab5b18 : Fix tuples in
bdd_apply
have their ties onstd::min
orstd::max
not broken correctly. This resulted in that the same recursion request could potentially be handled multiple times independently, since all its "calls" ended up interleaving with other tied requests in the priority queues. - f80a924 : Slightly improve performance of some
bool_op
s. Most likely this is negligible. - d222569: Fix
bdd_counter
returns trivially false cases as true. - 8fcc04e : Now Adiar compiles with C++17 regardless of its parent project. This allows the user to omit the use of
set_target_properties(<target> PROPERTIES CXX_STANDARD 17)
in their own CMake settings.
Changes
- aac630f :
adiar_init
now takes its memory argument in bytes rather than in MiB. - Equality Checking (
==
) is improved in its performance from its prior O(N2 log N2) time comparison. See pull request #127 for all changes, though most important to highlight are the following two commits:- cee73b3 : If both given BDDs are canonical (as defined in the documentation) and have the same value in their negation flag, then equality checking is done with a simple (and much faster) linear scan.
- c2812a9 : In all other cases the prior time-forward processing algorithm is used. But this one has been improved to be an O(N log N) time comparison algorithm. That is, equality checking is not only a constant improvement computing
~(f ^ g) == bdd_true()
but it is provably faster (both in terms of time and I/Os).
Performance
Apply
We can compare the performance we recorded for our BDD benchmarks run on the CSCAA Grendel-S cluster last winter for v1.0.0 to the ones we are currently running for v1.0.1. Shown below we have the relative performance difference for the N-Queens example.
N | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
---|---|---|---|---|---|---|---|---|---|---|---|
% | -44.70% | -33.92% | -16.94% | -18.81% | -26.65% | -2.04% | -3.20% | -4.56% | -3.51% | -6.55% | -5.52% |
Negative numbers are good.
Equality Checking
We have no numbers to compare to, but the new Picotrav benchmark added to our BDD benchmarks provide an insight into its performance.
-
The largest circuits we have verified were the
mem_ctrl
ones. Here, every BDD was on average 499 MiB in size, i.e. on average about 1 GiB of BDD nodes were compared. These were compared in 0.2 GiB/S. -
The
voter
benchmark has a single output gate, where each BDD takes up 5.74 MiB. One version requires us to use the O(N log N) time-forwarded algorithm and the other the much faster O(N) linear scan algorithm. Our experiments show that the linear scan is about 10 times faster than the time-forwarded comparison, and (depending on your SSD speed) you can compare 1.86 GiB/s for equality.
Contributors
- Steffan Sølvsten ( @SSoelvsten )
Thanks for the support and advice from Jaco van de Pol ( @jacopol ) .
License
Adiar uses the TPIE library which is licensed under LGPL v3, and so by extension any binary file with Adiar is covered by the very same license.
1.0.0
Initial release of Adiar!
BDD functions
bdd
class to hide away management of files and running the reduce algorithm. This takes care of reference counting and optimal garbage collection.
Constructors
bdd_sink(bool v)
or use the alternatives:bdd_true()
andbdd_false()
bdd_ithvar(label_t i)
bdd_nithvar(label_t i)
bdd_and(label_file ls)
to construct an and chain.bdd_or(label_file ls)
to construct an or chain.bdd_counter(label_t min_label, label_t max_label, label_t threshold)
to construct whether exactly threshold many variables in the given interval are true.
Furthermore, the node_writer
class is also provided as a means to construct a BDD manually bottom-up.
Basic Manipulation
bdd_apply(bdd f, bdd g, bool_op op)
to combine two BDDs with a binary operator (also includes aliases for every possible op)bdd_ite(bdd f, bdd g, bdd h)
to compute the if-then-elsebdd_not(bdd f)
to negate a bddbdd_restrict(bdd f, assignment_file as)
to fix the value of one or more variablesbdd_exists(bdd f, label_t i)
bdd_forall(bdd f, label_t i)
to existentially or forall quantify a single variable. Overloading also allows quantifying multiple labels. But, these are still quantified one by one!
Counting Operations
bdd_nodecount(bdd f)
to obtain the number of (non-leaf) nodes.bdd_varcount(bdd f)
the number of levels present (i.e. variables).bdd_pathcount(bdd f)
the number of unique paths to the true sink.bdd_satcount(bdd f, size_t varcount)
the number of satisfying assignments.
Other Functions
bdd_eval(bdd f, assignment_file x)
to compute f(x).bdd_satmin(bdd f)
, resp.bdd_satmax(bdd f)
to find the lexicographical smallest, resp. largest, satisfying assignment.output_dot(bdd f, std::string filename)
to output a visualizable .dot file.
Contributors
- Steffan Sølvsten( @SSoelvsten )
- Anna Blume Jakobsen ( @AnnaBlume99 )
- Mathias Weller Berg Thomasen ( @MathiasWeller42 )
Thanks for the support and advice from Jaco van de Pol ( @jacopol ) . Also, thanks to the following people for helping with bugfixes and guidance on I/O algorithms, TPIE and C++ from Asger Hautop Drewsen ( @tyilo ), Lars Arge, and Mathias Rav ( @Mortal ).
License
Adiar uses the TPIE library which is licensed under LGPL v3, and so by extension any binary file of Adiar is covered by the very same license.